[프로그래머스인공지능스쿨]Django으로 동적 웹 페이지 만들기

6 minute read

Model로 DB 구성하기

데이터베이스 구성 작업은 django의 MVT Pattern에서 Model부분에 해당한다.

Remind
png
유저가 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을 이용해 코드 단위로 데이터베이스를 접근해보자!
    Model에서 models.py를 이용해 작업할 수 있다.


models.py를 살펴보자. 여기서 모델을 만들 때는 클래스 단위로 생성해야 한다.
커피 데이터베이스를 만들기 위한 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타입을 몇가지 소개하자면,

class Coffee(models.Model):
    field1 = models.CharField()
    field1 = models.IntegerField()
    field1 = models.BooleanField()
    """
    문자열 : CharField
    숫자 : IntegerField, SmallIntegerField, ...
    논리형 : BooleanField
    시간/날짜 : DateTimeField
    ...
    """

이외에도 많은 타입이 있으니 도큐먼트를 참고하도록 하자.
그럼 커피 모델을 위한 필드들을 생성해보자.

class Coffee(models.Model):
    name = models.CharField(default="", max_length=30)
    price = models.IntegerField(default=0)
    is_ice = models.BooleanField(default=False)

커피 모델 완성! 각 필드에는 옵션을 넣어줄 수 있다.

  • default : 처음 모델의 행을 만들 때 이 행의 디폴트(기본)값이 어떤 값이어야 하는지 정해줄 수 있다.
  • null : null을 허용하는지를 나타낸다. 즉, 이 필드가 비어있어도 되는지를 정해주는 매개변수이다. null=False이면 이 필드는 반드시 값이 채워져야 한다. 기본적으로 디폴트값이 False이다.
    이런식으로 옵션을 통해 필드에 제약조건을 생성할 수 있다.
    특정 필드는 어떤 파라미터를 반드시 필요하는 경우가 있는데, 여기서는 CharField의 경우 max_length라는 매개변수를 반드시 필요로 한다. 즉 최대 길이를 디폴트로 항상 정해줘야 한다.


admin은 장고가 기본적으로 제공하는 관리자 앱이다.
슈퍼유저를 통해 접근할 수 있다.
어드민은 모델을 자연스럽게 관리해줄 수 있다는 장점이 있다.
어떤 모델을 어드민과 연동을 하면, 관리자 페이지에서 모델을 관리해줄 수 있다.
admin.py에서 models.py에서 만든 Coffee 모델을 불러오자.

from .models import Coffee
# Register your models here.
admin.site.register(Coffee) # 이 한줄이면 어드민 페이지에서 커피 모델을 관리할 수 있다!

png
서버를 실행한 뒤 http://127.0.0.1:8000/admin/ 에 접속하면 다음과같이 Coffee 모델이 뜨는 것을 확인할 수 있다.
Groups, Users또한 모델이다. 이 모델들을 장고 관리자 페이지에서 디폴트로 관리할 수 있다.
여기서 Coffee를 누르면 다음과 같은 에러창이 뜬다.
png
이것은 우리가 아직 한가지 설정을 하지 않았기 때문인데, 데이터베이스에 변동사항이 생기면 이를 settings에서 반영해줘야 한다.
장고에서는 데이터베이스를 관리할 때, 어떤 모델을 models.py에 만드는 것 뿐만 아니라 이를 깃에 커밋과 유사한 migration이라는 단위로 관리한다.
따라서 실제로 DB의 필드 정보를 수정하더라도 당장 반영이 되지 않고, migration을 진행한 다음에야 DB의 수정사항이 반영되게 된다.

그럼 migration을 CLI 환경에서 진행해보자!

yeochaelin@yeochaelin-ui-MacBookPro webproj % python3 manage.py makemigrations homepage
Migrations for 'homepage':
  homepage/migrations/0001_initial.py
    - Create model Coffee

이제 만든 migration을 적용해야 한다.

yeochaelin@yeochaelin-ui-MacBookPro webproj % python3 manage.py migrate
Operations to perform:
  Apply all migrations: admin, auth, contenttypes, homepage, sessions
Running migrations:
  Applying homepage.0001_initial... OK

여기까지 해주어야 정상적으로 데이터베이스 환경에 우리가 만든 모델이 잘 반영되었다고 볼 수 있다.
이제 다시 runserver를 한 후 admin페이지에 들어가, Coffee를 클릭하면 다음과 같이 뜬다.
png
png
ADD COFFEE + 버튼을 눌러 커피를 추가해줄 수도 있다.
추가해준 만큼 커피 오브젝트가 뜨는 것을 확인할 수 있다.
png
근데 object라고만 떠서 열람하기 불편하다! 이름을 바꿔보자.
커피 오브젝트 리스트에서 커피 이름이 바로 뜨도록 할 것이다.

class Coffee(models.Model):
    def __str__(self):
        return self.name
    name = models.CharField(default="", max_length=30)
    price = models.IntegerField(default=0)
    is_ice = models.BooleanField(default=False)

models.py를 다음과 같이 수정해주면, 리스트에 커피의 name을 리턴하여 다음과 같이 보기 편하게 뜬다.
png




이제 모델을 템플릿 상에서 보여주게 하는 실습을 해보자.
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></p>
    </body>
</html>
</code>

coffee_list가 제대로 출력되는지 보기 위해 urls.py도 수정해준다.

from django.urls import path
from django.contrib import admin
from homepage.views import index, coffee_view

urlpatterns = [
    path('', index), # 127.0.0.1/
    path('coffee/', coffee_view), # 127.0.0.1/coffee/
    path('admin/', admin.site.urls), # 127.0.0.1/admin/
]

png
서버를 실행하면 다음과 같이 데이터베이스 객체들이 리턴된다. 이것을 템플릿 단에서 파싱하여 접근해야 한다.

<!DOCTYPE html>
<html>
    <head>
        <title>Coffee List</title>
    </head>

    <body>
        <h1>My Coffee List</h1>
        
    </body>
</html>

coffee.html 템플릿을 열어 다음과 같이 수정해주면, 커피의 이름과 가격이 잘 출력되는 것을 볼 수 있다.
png
모델 단에서 관리한 정보를 템플릿 단에서 확인할 수 있다.

정리

  1. 모델을 만든다.
  2. 뷰에서 모델을 import하고, 모델에 해당하는 값을 가져와 딕셔너리 형태로 템플릿에 넘겨준다.
    coffee_all = Coffee.objects.all() # .get(), .filter(), ...
    
    • 다음과 같이 all이 아닌 특정 값들만 가져올 수 있다.
  3. 정보를 넘겨받은 템플릿은 템플릿 변수와 태그를 이용해 데이터를 동적으로 처리해준다. 템플릿은 url에 추가해주는 것, 잊지 말기!



템플릿에서 모델의 정보를 갱신하는 작업을 해보자.
만들었던 homepage 앱 안에 forms.py라는 새로운 파일을 하나 만들자.
기존에 있던 것을 제외하고는 처음으로 만드는 새로운 파이썬 파일이다.
form을 작성하는 것은 형태가 정해져 있기 때문에, 한번 익혀두면 응용해 쓰기 편하다. forms.py를 다음과 같이 수정하자.

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을 활용한다.

<!DOCTYPE html>
<html>
    <head>
        <title>Coffee List</title>
    </head>

    <body>
        <h1>My Coffee List</h1>
        

        <form>
            
        </form>
    </body>
</html>

as.pas.paragraph라는 뜻이다. 이를 통해 정확한 form의 형태로 나타낼 수 있다.
png
다음과 같이 form이 생성된 것을 확인할 수 있다.
그런데 여기서 문제가 발생한다. 내용은 적어줄 수 있는데, 저장 버튼이 없네..!
우리는 form에 대한 틀만 만들었을 뿐이다. form에 대한 정보를 서버로 전송하는 버튼을 만들어보자.

coffee.html을 다음과 같이 수정하여 버튼을 만들자.

<!DOCTYPE html>
<html>
    <head>
        <title>Coffee List</title>
    </head>

    <body>
        <h1>My Coffee List</h1>
        

        <form method="POST">
            
            <button type="submit">Save</button>
        </form>
    </body>
</html>

Flask에서 다뤘던 것처럼, POST로 서버에 전달해야 한다.
png
이제 이전과 다르게 Save 버튼이 생기는데, 문제는 이 버튼을 누르면 다음과 같은 에러가 뜬다.
png
403 오류!
form의 경우 기본적으로 보안 옵션이 들어가야 한다. CSRF를 구현하지 않으면 403 오류가 발생하게 된다.
서버 입장에서 CSRF 토큰을 삽입해줘야 하는 것이다.
다음과 같이 간단하게 csrf_token을 삽입하면 끝!

png

이걸로 끝이라면 좋겠지만, 문제가 하나 더 있다. 이제 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})

png
이제 새로 추가한 돌체라떼 메뉴가 DB에 잘 추가된 것을 볼 수 있다.