백엔드/Django

[Django] Documnet 따라 읽기(2) - View, Model, CRUD API(1)

안용감한호랑이 2023. 11. 3. 22:52

[백엔드/Django] - Django Document 따라 읽기(1) - Tutorial(설치, 데이터베이스 연결)

 

[Django] Document 따라 읽기(1) - Tutorial(설치, 데이터베이스 연결)

본 글은 Django의 공식 Document에 있는 Tutorial을 따라 하는 과정입니다. Django 공식 Document Django The web framework for perfectionists with deadlines. docs.djangoproject.com 가상환경 생성 공식 Document에는 Python 3.8 이상

dog-foot-writen.tistory.com


 

 

 

먼저 polls라는 애플리케이션(설문조사 애플리케이션)을 생성하겠습니다.

python manage.py startapp polls

 

 

 

첫 번째 뷰 작성(Hello World)

# polls/views.py
from django.http import HttpResponse


def index(request):
    return HttpResponse("Hello, world. You're at the polls index.")

위와 같이 간단한 뷰를 작성하였습니다.

단순하게 HttpResponse에 있는 문자열을 반환하는 뷰입니다. 해당 뷰를 호출하려면 URL에 매핑해야 하며 이를 위해서 URLconf가 필요합니다.

 

 

# polls/urls.py
from django.urls import path

from . import views

urlpatterns = [
    path("", views.index, name="index"),
]
# mysite/urls.py
from django.contrib import admin
from django.urls import include, path

urlpatterns = [
    path("polls/", include("polls.urls")),
    path("admin/", admin.site.urls),
]

mysite/urls.py에서 include를 통해 polls에 있는 urls.py를 가져올 수 있도록 합니다.

Django.urls의 include()는 다른 URLconf를 참조할 수 있도록 합니다. Django는 include 함수를 통해 해당 URL의 어떤 부분과 일치하는지 잘라내고 나머지 문자열을 포함된 URLconf로 보내 추가 처리를 수행합니다. 이를 통해 다른 기타 경로 아래에 파일을 배치할 수 있으며 앱을 작동시킵니다.

 

http://localhost:8000/polls/  로 접속한다면 아래와 같은 화면이 나오게 됩니다.

 

 

path()의 파라미터

  • route : URL 패턴을 포함하는 문자열. 요청을 처리할 때 Django는 첫 번째 패턴에서 시작하여 urlpatterns 목록 아래로 내려가며 일치하는 패턴을 찾을 때까지 요청된 URL을 각 패턴과 비교합니다. 패턴은 GET과 POST 요청의 파라미터, 도메인명은 찾지 않습니다.
  • view : Django가 해당하는 패턴을 찾으면 객체를 첫 번째 인수로 사용하고 경로에서 캡처된 값을 키워드 인수로 사용하여 지정된 뷰 함수를 호출합니다.
  • kwargs : 임의의 키워드 인수는 dictinoary를 통해 타겟 뷰로 전달됩니다.
  • name : URL에 이름을 지정하면 Django의 다른 곳, 특히 템플릿 내에서 명확하게 참조할 수 있습니다. 이 기능을 사용하면 단일 파일만 사용하며 프로젝트의 URL 패턴을 전체적으로 변경할 수 있습니다.

 

 

 

모델 생성

추가 메타데이터를 사용하여 기본적으로 데이터베이스 레이아웃인 model을 정의합니다.

model은 데이터에 대한 정보의 최종적인 단일 소스입니다. 여기에는 저장하는 데이터의 필수 필드와 동작이 포함되어 있습니다. Django는 DRY원칙(목표는 한 곳에서 데이터 모델을 정의하고 그로부터 자동으로 파생되는 것)을 따릅니다.

 

설문조사 앱에서는 Question 및 Choice 두 가지의 모델을 생성합니다. 

  • Question : 질문과 출판날짜(published date)를 가지고 있습니다.
  • Choice : 선택한 텍스트와 투표의 집계를 가지고 있습니다.
  • Choice는 각각의 Question과 연관되어 있습니다.

 

이러한 개념은 Python 클래스로 표현됩니다.

# polls/models.py

from django.db import models



class Question(models.Model):
    question_text = models.CharField(max_length=200)
    pub_date      = models.DateTimeField("date published")
    
class Choice(models.Model):
    question    = models.ForeignKey(Question, on_delete=models.CASCADE)
    choice_text = models.CharField(max_length=200)
    votes       = models.IntegerField(default=0)

 

각 모델들은 django.db.models.Model의 하위클래스로 표시됩니다. 각 모델들은 여러 클래스 변수가 있으며 각 변수는 모델의 데이터베이스 필드를 나타냅니다.

 

각 필드는 클래스의 필드 인스턴스로 표시됩니다.

  • CharField : chracter filed
  • DateTimeField : datetimes

Django에게 각 필드가 어떤 유형의 데이터를 가지고 있는지 알려줍니다.

 

question_text, pub_date와 같은 필드의 인스턴스의 이름은 Python에서 해당 값을 사용하고 데이터베이스는 해당 값을 열 이름으로 사용합니다.

 

 

CharField 같은 일부 Field 클래스에서는 필수로 들어가야 하는 항목들이 존재할 수 있습니다. CharField 같은 경우 max_length를 넣어 주어야 합니다. 해당 값들은 유효성 검사, 데이터베이스 스키마 등에서 사용됩니다.

 

 

 

위와 같은 코드는 Django에게 많은 정보들을 제공합니다.

  • CREATE TABLE
  • Python이 database의 Question, Choice에 접근하는 API object 등등

 

 

프로젝트에 앱을 포함하려면 settings.py의 INSTALLED_APPS에 해당 구성 클래스에 대한 참조를 추가해야 합니다.

PollsConfig 클래스는 polls/apps.py파일에 존재하기 때문에 아래와 같이 구성해 줍니다.

# mysite/settings.py

INSTALLED_APPS = [
    "polls.apps.PollsConfig", # 해당 라인만 추가하였으며 아래 코드는 원래 추가되어 있습니다.
    "django.contrib.admin",
    "django.contrib.auth",
    "django.contrib.contenttypes",
    "django.contrib.sessions",
    "django.contrib.messages",
    "django.contrib.staticfiles",
]

 

 

 

 

이제 Django가 polls 앱을 포함하는 것을 알게 되었기 때문에 makemigrations를 통해 모델을 일부 변경했고 변경 사항을 저장하고 싶다고 알리게 됩니다.

마이그레이션은 Django가 모델(및 데이터베이스 스키마)에 대한 변경 사항을 저장하는 방법입니다. 마이그레이션은 디스크에 있는 파일입니다. 원하는 경우 새 모델에 대한 마이그레이션을 읽을 수 있습니다.

명령어는 아래와 같습니다.

python manage.py makemigrations polls

 

 

어떤 SQL 명령문이 실행되는지 확인하고 싶다면 아래와 같은 명령어로 확인할 수 있습니다.

python manage.py sqlmigrate polls 0001

 

BEGIN;
--
-- Create model Question
--
CREATE TABLE "polls_question" (
    "id" bigint NOT NULL PRIMARY KEY GENERATED BY DEFAULT AS IDENTITY,
    "question_text" varchar(200) NOT NULL,
    "pub_date" timestamp with time zone NOT NULL
);
--
-- Create model Choice
--
CREATE TABLE "polls_choice" (
    "id" bigint NOT NULL PRIMARY KEY GENERATED BY DEFAULT AS IDENTITY,
    "choice_text" varchar(200) NOT NULL,
    "votes" integer NOT NULL,
    "question_id" bigint NOT NULL
);
ALTER TABLE "polls_choice"
  ADD CONSTRAINT "polls_choice_question_id_c5b4b260_fk_polls_question_id"
    FOREIGN KEY ("question_id")
    REFERENCES "polls_question" ("id")
    DEFERRABLE INITIALLY DEFERRED;
CREATE INDEX "polls_choice_question_id_c5b4b260" ON "polls_choice" ("question_id");

COMMIT;

 

주의사항

  • 정확한 출력은 사용 중인 데이터베이스에 따라 달라지며 위 예시는 PostgreSQL입니다.
  • 테이블 이름은 앱 이름과 모델의 소문자 이름을 결합하여 자동 생성됩니다. ( 해당 사항은 재정의 하여 사용할 수 있습니다.)
  • PRIMARY KEY는 자동으로 추가됩니다. ( 해당 사항도 재정의 하여 사용할 수 있습니다.)
  • sqlmigrate 명령은 실제로 데이터베이스에서 마이그레이션을 실행하지 않습니다. 대신 Django가 요구된다고 판단한 SQL문을 보여줍니다.

 

 

 

이제 migrate를 실행하여 데이터베이스에 해당 모델 테이블을 만들겠습니다. migrate 명령어는 적용되지 않은 모든 마이그레이션을 가져와 데이터베이스에 대해 실행합니다. 기본적으로 모델에 대한 변경 사항을 스키마와 동기화합니다. 마이그레이션은 데이터 손실 없이 데이터베이스를 실시간으로 업그레이드하는데 특화되어 있습니다. 

이후 튜토리얼 후반부에 더 자세하게 다룰 예정입니다.

 

아래와 같은 순서라고 생각하면 됩니다.

  • models.py 변경
  • 해당 변경사항에 대한 마이그레이션 생성(python manage.py makemigrations)
  • 해당 변경사항을 데이터베이스에 적용(python manage.py migrate)

 

 

API 활용(CRUD)

Django가 제공하는 API를 통해 위에서 만든 model의 class들을 다루어 보겠습니다.

python manage.py shell
# 작성한 Choice와 Question을 import
from polls.models import Choice, Question


# Question이 가진 데이터를 모두 가져온다
# SELECT * FROM polls_question; 과 동일
Question.objects.all()




# model의 CRUD API
# ######################################
# 1. CREATE
# 값 생성
from django.utils import timezone
q = Question(question_text = "What's new?", pub_date = timezone.now())
q.save()

print(q.id)  # 1이 출력
print(q.question_text) # "What's new?" 가 출력


# ######################################
# 2. READ
# 값 조회
Question.objects.all()
Question.objects.all()[:5]

# Question이 가진 데이터 중 id = 1인 항목 필터
# SELECT * FROM polls_question where id = 1; 과 동일
Question.objects.filter(id=1)


# ######################################
# 3. UPDATE
# 값 변경
q.question_text = "What's up?"
q.save()

print(q.question_text) # "What's up?"이 출력


# ######################################
# 4. DELETE
# 값 삭제
Question.objects.filter(question_text="What's up?").delete()