유저가 Django에게 HTTP request를 보낸다. Django,즉 서버는 URL에서 urls.py로 url을 인식한다.
만약 들어온 경로가 urls.py에 있다면 이를 View에 보낸다. views.py라는 파일에서 들어온 요청을 처리하게 된다. 즉 로직에 대한 처리를 하게 됨.
데이터베이스를 처리하게 되는 경우 Model에서 데이터베이스를 처리하게 된다.
데이터베이스(구조화!)
Relational DB -> pandas의 df와 비슷! row와 column을 통해 데이터 작업을 할 수 있다.
SQL : 데이터베이스 작업을 하기 위한 언어
장고의 경우, SQL을 이용하지 않고서도 데이터베이스와 상호작용할 수 있는 방법이 있는데, 바로 ORM이라는 방법이다.
ORM : 객체 단위로 데이터베이스를 다룰 수 있게 도와준다. 장고의 경우 ORM이 내장되어 있다.
이 ORM을 이용해 코드 단위로 데이터베이스를 접근해보자!
(물론 장고에서도 SQL을 활용해서 DB의 CRUD를 진행할 수는 있음.)
Model에서 models.py를 이용해 작업할 수 있다.
models.py를 살펴보자. 여기서 모델을 만들 때는 클래스 단위로 생성해야 한다.
models.Model를 상속받는 새로운 클래스를 만들어보자.
커피 데이터베이스를 만들기 위한 Coffee 모델을 만들어보자.
class Coffee의 attribute들이 실제 데이터의 column이 된다.
클래스를 바탕으로 만들어지는 객체는 하나의 row가 된다.
그리고 row는 각각의 field(column)을 가지게 된다.
class Coffee(models.Model):
field1 = models.FieldType()...
field1 = models.FieldType()...
field1 = models.FieldType()...
이렇게 여러개의 field를 적어줌으로써 한 행이 가져야 하는 column들의 타입을 지정해줄 수 있다.
field타입을 몇가지 소개하자면,
위를 참고하여 coffee 모델을 위한 필드들을 생성하자.
class Coffee(models.Model):
name = models.CharField(default="", max_length=30) # 커피 이름
price = models.IntegerField(default=0) # 커피 가격
is_ice = models.BooleanField(default=False) # ice인지 아닌지
각 field의 option을 설정해보자.
이런식으로 옵션을 통해 필드에 제약조건을 생성할 수 있다.
특정 필드는 어떤 파라미터를 반드시 필요하는 경우가 있는데,
여기서는 CharField의 경우 max_length라는 매개변수를 반드시 필요로 한다. 즉 최대 길이를 디폴트로 항상 정해줘야 한다.
admin은 장고가 기본적으로 제공하는 관리자 앱이다.
슈퍼유저를 통해 접근할 수 있다.
어드민은 모델을 자연스럽게 관리해줄 수 있다는 장점이 있다.
어떤 모델을 어드민과 연동을 하면, 관리자 페이지에서 모델을 관리해줄 수 있다.
admin.py에서 models.py에서 만든 Coffee 모델을 불러오자.
from .models import Coffee # 다른 파이썬 파일에서 coffee 모델 불러오기
admin.site.register(Coffee) # 이 한줄이면 어드민 페이지에서 커피 모델을 관리할 수 있다.
다음과 같이 coffee 모델이 뜨는 것을 확인할 수 있음.
Groups, Users또한 모델이다. 이 모델들을 장고 관리자 페이지에서 디폴트로 관리할 수 있다.
여기서 Coffee를 누르면 다음과 같은 "No such table"에러창이 뜬다.
이것은 우리가 아직 한가지 설정을 하지 않았기 때문인데, 데이터베이스에 변동사항이 생기면 이를 settings에서 반영해줘야 한다.
장고에서는 데이터베이스를 관리할 때, 어떤 모델을 models.py에 만드는 것 뿐만 아니라 이를 깃에 커밋하는 것과 유사한 migration이라는 단위로 관리한다.
따라서 실제로 DB의 필드 정보를 수정하더라도 당장 반영이 되지 않고, migration을 진행한 다음에야 DB의 수정사항이 반영되게 된다.
그럼 migration을 CLI 환경에서 진행해보자!
1. 가장 먼저 git add와 같은 기능을 하는 python manage.py makemigrations 앱이름
을 작성한다.
python3 manage.py makemigrations homepage
이렇게 coffee 모델을 만들었다는 것을 migration으로 만들었다.
2. 만들어진 migration들을 실제 DB에 반영 (마치 깃의 commit 기능)
python manage.py migrate
이렇게 해야 정상적으로 데이터베이스 환경에 우리가 만든 모델(coffee)이 잘 반영되었다고 볼 수 있다.
다시 admin 페이지에 들어가보자.
오류가 안나고 다음과 같이 정상 작동함!
Add coffee를 통해 coffee 추가할 수 있다.
우리가 만든 field를 입력으로 받는다.
장고가 알아서 만들어주므로 관리가매우매우 편리하다.
두개의 커피를 추가해줬더니 다음과 같이 두개의 object가 생성됨.
object라고만 떠서 열람하기 불편 -> 이름을 바꿔보자.
커피 오브젝트 리스트에서 커피 이름(name)이 바로 뜨도록 할 것이다.
models.py를 다음과 같이 수정
class Coffee(models.Model):
def __str__(self): # coffee 객체를 출력할 때 name을 return한다.
return self.name
name = models.CharField(default="", max_length=30)
price = models.IntegerField(default=0)
is_ice = models.BooleanField(default=False)
위와 같이 object가 아닌 커피 이름 name이 뜨게 된다.
이제 모델을 템플릿 상에서 보여주게 하는 실습을 해보자.
models.py에서 만든 Coffee모델을 index.html에 전달하여 보이게 할 것이다.
모델에서 템플릿으로 정보를 전달하기 위해서는 뷰를 거쳐야 한다.
먼저 views.py를 다음과 같이 수정한다.
from .models import Coffee
def coffee_view(request):
coffee_all = Coffee.objects.all() # Coffee모델에 해당하는 데이터베이스가 있을텐데 그것의 모든 오브젝트, 즉 모든 행을 전부 가지고 오라는 뜻
return render(request, 'coffee.html', { "coffee_list" : coffee_all})
이렇게 하면 커피의 모든 객체를 가져와서 렌더링할 수 있다.
이제 새로 coffee.html템플릿을 생성하고 다음과 같이 작성한다.
<!DOCTYPE html>
<html>
<head>
<title>Coffee List</title>
</head>
<body>
<h1>My Coffee List</h1>
<p>{{ coffee_list }}</p>
</body>
</html>
</code>
coffee_list가 제대로 출력되는지 보기 위해 urls.py도 수정해준다.
from homepage.views import index, coffee_view # homepage의 views에 있는 index, coffee_view 함수를 불러옴
urlpatterns = [
path('', index), # '' : 경로를 지정 안함, 즉 127.0.0.1/ 의 요청이 주어질 때 index함수를 실행하라
path('coffee/', coffee_view), # 127.0.0.1/coffee/ 요청이 주어질 때 coffee_view 함수 실행
path('admin/', admin.site.urls), # 127.0.0.1/admin/ 요청이 들어올 때 할 행동
]
다음과 같이 데이터베이스 객체들이 리턴된다. 이것을 템플릿 단에서 파싱하여 접근해야 한다.(for 태그 사용)
coffee.html을 다음과 같이 수정하자.
<!DOCTYPE html>
<html>
<head>
<title>Coffee List</title>
</head>
<body>
<h1>My Coffee List</h1>
{% for coffee in coffee_list %} # view에서 전달된 key의 이름이 coffee_list이므로
<p>{{ coffee.name }}, {{ coffee.price }}</p> # 각 커피에 대한 name과 price를 출력한다.
{% endfor %}
</body>
</html>
coffee_all = Coffee.objects.all() # .get(), .filter(), ...
템플릿에서 모델의 정보를 갱신하는 작업을 해보자.
만들었던 homepage 앱 안에 forms.py라는 새로운 파일을 하나 만들자.
(기존에 있던 것을 제외하고는 처음으로 만드는 새로운 파이썬 파일이다.)
form을 작성하는 것은 형태가 정해져 있기 때문에, 한번 익혀두면 응용해 쓰기 편하다. forms.py를 다음과 같이 수정하자.
한번쯤 사용해본 적 있는 구글폼과 마찬가지로 모델폼은 어떤 모델에 대해서 입력칸을 만들어주는 객체이다, 그 입력칸을 무엇으로 설정할 것인지를 Meta클래스 내에 fields에서 지정. 어떤 모델에 대한 form을 만들것인지는 model에 담음.
from django import forms
from .models import Coffee # Model 호출
class CoffeeForm(forms.ModelForm): # ModelForm을 상속받는 CoffeeForm
class Meta : # form을 만들기 위해 어떤 모델이 있어야하는지 여기에서 지정해준다
model = Coffee
fields = ('name', 'price', 'is_ice') # 어떤 필드를 CoffeeForm에서 받을 것인지 적어주는 곳
form을 만들었으면 뷰에서 이 정보를 템플릿으로 전달해야 한다.
views.py를 다음과 같이 수정하자.
from .forms import CoffeeForm
def coffee_view(request):
coffee_all = Coffee.objects.all()
form = CoffeeForm() # form객체를 만들어준다.
return render(request, 'coffee.html', { "coffee_list" : coffee_all, "coffee_form" : form}) # form 객체를 coffee.html 템플릿에 전달해준다.
form을 뷰에서 템플릿으로 전달했으니, 이제 템플릿에서 form을 활용한다.
coffee.html을 다음과 같이 수정하자
<!DOCTYPE html>
<html>
<head>
<title>Coffee List</title>
</head>
<body>
<h1>My Coffee List</h1>
{% for coffee in coffee_list %}
<p>{{ coffee.name }}, {{ coffee.price }}</p>
{% endfor %}
<form>
{{ coffee_form.as_p }}
</form>
</body>
</html>
html에서 form을 만드려면 form 태그를 사용해야함 < form >
form 태그 안에 뷰에서 보낸 form 객체(coffee_form)를 넣어준다.
as.p는 as.paragraph라는 뜻이다. 이를 통해 정확한 form의 형태로 나타낼 수 있다.
다음과 같이 form이 생성되었다.
하지만 문제점이 있다. form에 값을 입력하고 난 후 저장버튼이 없는 것!
우리는 form에 대한 틀만 만들었을 뿐이다. form에 대한 정보를 서버로 전송하는 버튼을 만들어보자.
coffee.html을 다음과 같이 수정하여 버튼을 만들자.
<!DOCTYPE html>
<html>
<head>
<title>Coffee List</title>
</head>
<body>
<h1>My Coffee List</h1>
{% for coffee in coffee_list %}
<p>{{ coffee.name }}, {{ coffee.price }}</p>
{% endfor %}
<form method = 'POST'>
{{ coffee_form.as_p }}
<button type = "submit">Save</button>
</form>
</body>
</html>
다음과 같이 간단하게 {% csrf_token %}
코드만 추가하면 됨.
<!DOCTYPE html>
<html>
<head>
<title>Coffee List</title>
</head>
<body>
<h1>My Coffee List</h1>
{% for coffee in coffee_list %}
<p>{{ coffee.name }}, {{ coffee.price }}</p>
{% endfor %}
<form method = 'POST'>{% csrf_token %}
{{ coffee_form.as_p }}
<button type = "submit">Save</button>
</form>
</body>
</html>
이제 Save버튼을 누르면 403에러는 뜨지 않지만 화면에 내가 추가한 커피 메뉴가 보이지 않을 것이다. 즉, DB에 제대로 반영되지 않는 것이다.
왜냐하면 뷰 입장에서 어떤 POST 요청이 들어왔을 때 어떤 로직을 행해야 하는지(DB에 반영해야하는지) 등등의 정보가 들어있지 않기 때문이다.
--> 뷰에서 POST 요청이 들어왔을때 어떻게 모델에 정보를 넣어줄지 적어줘야 한다.
views.py를 다음과 같이 수정한다.
def coffee_view(request):
coffee_all = Coffee.objects.all()
# 만약 request가 POST라면:
# POST를 바탕으로 Form을 완성하고
# Form이 유효하면 -> 저장!
if request.method == "POST":
form = CoffeeForm(request.POST) # html 파일 상에서 POST로 보내줬던 내용을 바탕으로 form을 완성시킨 것을 form으로 하자는 뜻. 완성된 Form
if form.is_valid(): # 채워진 Form이 유효하다면
form.save() # 이 Form 내용을 Model에 저장
form = CoffeeForm() # form객체를 만들어준다.
return render(request, 'coffee.html', { "coffee_list" : coffee_all, "coffee_form" : form})
작성한 form이 DB에 잘 추가되는 것을 확인할 수 있다.