[데이터 엔지니어링 데브코스 2기] TIL-4주차-파트02 장고를 활용한 API서버 만들기(Django)

이재호·2023년 10월 30일
0

1. 개발 환경 설정하기

다음 순서에 따라 환경을 설정합니다.

  1. 파이썬 설치하기 및 파이썬 Path 시스템 변수에 추가.
  2. cmd 창을 켠 후, 원하는 디렉토리(디렉토리 이동 명령어 : cd dir-name)에서 py -m venv project-name 명령어 실행(파이썬 가상환경 생성).
  3. project-name\Scripts\activate.bat 명령어로 해당 가상환경 활성화.
  4. py -m pip install Django 명령어로 Django 설치 및
  5. django-admin version 명령어로 설치 확인.
  6. django-admin startproject project-name 명령어로 장고 프로젝트 만들기.
  7. python manage.py runserver 명령어로 해당 프로젝트 서버에서 실행하기.

manage.py는 터미널에서 장고를 제어하기 위한 파일입니다.




2. Django App 만들기

다음 순서에 따라 명령어를 입력합니다. (커맨드 창을 켜놓고 파일을 수정하면, 커맨드 창에서 수정된 결과에 대해 반환된 것을 확인할 수가 있습니다.)

  1. 장고 프로젝트 디렉토리에서 python manage.py startapp polls(앱이름) 명령어로 앱 생성.
  2. mysite/urls.py 파일의 코드를 다음 코드로 변경.
from django.contrib import admin
from django.urls import path, include

# urlpatterns에 있는 url이 들어올 경우, 해당하는 파일로 보낸다.
urlpatterns = [
	path("admin/", admin.site.urls),
    path("polls/", include('polls.urls')),
]
  1. polls/urls.py 파일의 코드를 다음 코드로 변경.
from django.urls import path
from . import views

urlpatterns = [
	# url이 비어서 들어올 경우(예, 127.0.0.1:8000/polls or 127.0.0.1:8000/polls/)
    # 해당 처리를 views.index로 보내며, 해당 이름은 'index'로 한다.
	path('', views.index, name='index'),
]
  1. polls/views.py 파일의 코드를 다음 코드로 변경.
from django.http import HttpResponse

def index(request):
	# 요청이 들어오면 "Hello World."를 리턴한다.
	return HttpResponse("Hello World.")
(출력 화면)


3. URL Path 설정하기

  1. polls/urls.py 파일에서 some_url에 대한 path 추가.
from django.urls import path
from . import views

urlpatterns = [
    path('', views.index, name="index"),
    path('some_url', views.some_url),
]
  1. polls/views.py 파일에서 some_url에 대한 view 추가.
from django.http import HttpResponse

def index(request):
    return HttpResponse("Hello World.")

def some_url(request):
    return HttpResponse("Some URL !!")
(출력 화면)


4. 모델 만들기

실제 웹 사이트의 경우, 위의 예시처럼 정적인 웹 페이지를 리턴하는 것이 아니라 동적으로 웹 페이지를 구성합니다. 이러한 작업을 하기 위해서는 DB에서 값을 읽어들여 이를 출력해야 합니다.

장고의 ("models.py"파일의) 모델은 DB를 테이블 별로 읽어서 값을 코드로 읽어주는 역할을 합니다. 이를 ORM이라고 부르기도 합니다.

절차는 크게 다음과 같습니다.

  1. 모델 생성.
  2. 수정(생성)된 모델을 테이블에 써 주기 위한 마이그레이션 생성.
  3. 해당 모델에 맞는 테이블 생성.

구체적인 절차는 다음과 같습니다.

  1. polls/models.py 수정하기
from django.db import models

# Create your models here.
# 모델 생성 -> 마이그레이션 생성(setting.py 설정) -> 모델에 대한 테이블 생성
# 질문: 어디로 놀러 가고 싶니?
# 대답: 산, 강, 바다, 도심 호캉스

# 질문 모델
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) # 외래키 Question을 CASCADE로 참조한다.
    choice_text = models.CharField(max_length=200) # 텍스트를 저장하는 필드 (산, 강, ..)
    votes = models.IntegerField(default=0) # 숫자를 저장하는 필드 (투표 개수)
  1. mysite/settings.py에서 APP 추가하기.
INSTALLED_APPS = [
    'django.contrib.admin',
    'django.contrib.auth',
    'django.contrib.contenttypes',
    'django.contrib.sessions',
    'django.contrib.messages',
    'django.contrib.staticfiles',
    'polls.apps.PollsConfig', # 추가된 앱
]
  1. 터미널에서 python manage.py makemigrations polls 명령어로 polls라는 이름의 마이그레이션 파일 생성하기. => 'polls'에 대해서 Question과 Choice라는 모델을 만드는 migration을 생성합니다.

  2. python manage.py sqlmigrate polls 0001 명령어로 SQL 문장 확인하기=> "id" integer NOT NULL PRIMARY KEY AUTOINCREMENT 를 보면, 장고에서는 자동으로 테이블을 만들 때, id 필드를 생성한다는 것을 알 수 있습니다.
    그리고, CREATE INDEX "~~~" ON "polls_choice" ("question_id")를 보면, 장고는 Question의 id에 대해서 인덱싱하고 있음을 알 수 있습니다. 이를 활용해서 예시로 "quetion 1에 대한 choice들을 바로 찾을 수 있습니다." (Choice 모델은 Question 모델을 ForeignKey로 참조하고 있기에 이런 일이 발생합니다.)

  3. python manage.py migrate 명령어로 마이그레이션 실행하기.




5. Django의 모델 필드 활용

django의 공식 document 사이트 https://docs.djangoproject.com/en/4.2/
에서 model layer 영역에서 Filed types에서 여러 가지 필드들에 대한 정보를 알 수가 있습니다. 다음은 몇 가지 예시입니다.

  • BooleanField (boolean값)
  • CharField (문자열)
  • DateField (날짜만, 시간x)
  • DateTimeField (날짜와 시간)
  • FloatField
  • JSONField
  • TextField (CharField보다 조금 더 긴 text 저장)

다음은 question 모델에 필드를 추가했다가 다시 삭제하는 예시입니다.

  1. polls/models.py에서 qeustion에 필드 추가하기.
from django.db import models

# Create your models here.
# 모델 생성 -> 마이그레이션 생성(setting.py 설정) -> 테이블 생성
# 질문 어디로 놀러 가고 싶니?
# 대답: 산, 강, 바다, 도심 호캉스

# 질문
class Question(models.Model):
    question_text = models.CharField(max_length=200) # 텍스트를 저장하는 필드
    pub_date = models.DateTimeField('date published') # 날짜를 저장하는 필드
    is_something = models.BooleanField(default=False) # 추가된 필드
    average_score = models.FloatField(default=0.0) # 추가된 필드

# 대답
class Choice(models.Model):
    question = models.ForeignKey(Question, on_delete=models.CASCADE) # 외래키 Question을 CASCADE로 참조한다.
    choice_text = models.CharField(max_length=200) # 텍스트를 저장하는 필드
    votes = models.IntegerField(default=0) # 숫자를 저장하는 필드
  1. sqlite3 db.sqlite3 명령어로 SQL 창 실행하기. 만약 sqlite3 명령어 실행이 되지 않는다면 https://www.sqlite.org/download.html 에서 sqlite를 설치하여 sqlite3.ext 파일을 해당 디렉토리로 위치시킵니다.

  2. SQL 창에서 .tables 명령어로 table 목록 확인하기.

  3. SELECT* FROM django_migrations; 명령어로 로그 확인하기.
    => 맨 아래, polls|0002_question~~ 를 보면, 새로운 migrations 0002에 대한 로그를 확인할 수가 있습니다.

  4. .schema polls_question 명령어로 polls_question에 대한 스키마 확인하기.
    => 위에서 수정했던 필드들에 대한 스키마가 추가된 것을 확인할 수가 있습니다.

  5. 이제 다시 삭제해보겠습니다. python manage.py migrate polls 0001 명령어로 0001버전(이전 버전)으로 롤백하기.

  6. polls/models.py에서 수정된 내용 반영하기.

from django.db import models

# Create your models here.
# 모델 생성 -> 마이그레이션 생성(setting.py 설정) -> 테이블 생성
# 질문 어디로 놀러 가고 싶니?
# 대답: 산, 강, 바다, 도심 호캉스

# 질문 모델
class Question(models.Model):
    question_text = models.CharField(max_length=200) # 텍스트를 저장하는 필드
    pub_date = models.DateTimeField('date published') # 날짜를 저장하는 필드
    # 삭제된 필드
    # is_something = models.BooleanField(default=False)
    # average_score = models.FloatField(default=0.0)

# 대답 모델
class Choice(models.Model):
    question = models.ForeignKey(Question, on_delete=models.CASCADE) # 외래키 Question을 CASCADE로 참조한다.
    choice_text = models.CharField(max_length=200) # 텍스트를 저장하는 필드
    votes = models.IntegerField(default=0) # 숫자를 저장하는 필드
  1. polls/migrations/0002_~~ 파일 삭제하기.



6. Admin 계정 생성 및 접속

CRUD란, Create, Read, Update, Delete라는 의미로, 개발 관련하여 자주 등장하는 용어입니다.

CURD를 수행하는 Admin 계정은 다음과 같이 생성합니다.

  1. python manage.py createsuperuser 명령어를 실행한 뒤, 계정 이름 및 패스워드를 입력.

  2. "http://127.0.0.1:8000/admin/" 에서 계정 이름 및 패스워드 입력.

  3. 로그인 후, 다음 화면에서도 User를 추가 및 관리할 수가 있습니다. Add를 클릭한 후, 새로운 유저 admin2를 생성한 후, 해당 admin2를 클릭하여 permissions에서 권한을 설정할 수도 있습니다.




7. Admin 사이트에 모델 등록하기

  1. polls/admin.py 파일에서 모델 등록.
from django.contrib import admin
from .models import *

# Register your models here.
admin.site.register(Question)
admin.site.register(Choice)
  1. POLLS에서 모델 옆의 Add 버튼 클릭.

  2. 필드값 입력 후 save.

  3. polls/models.py 파일에서 Question에 대한 출력 양식 입력하기.

from django.db import models

# Create your models here.
# 모델 생성 -> 마이그레이션 생성(setting.py 설정) -> 테이블 생성
# 질문 어디로 놀러 가고 싶니?
# 대답: 산, 강, 바다, 도심 호캉스

# 질문 모델
class Question(models.Model):
    question_text = models.CharField(max_length=200) # 텍스트를 저장하는 필드
    pub_date = models.DateTimeField('date published') # 날짜를 저장하는 필드
    # is_something = models.BooleanField(default=False)
    # average_score = models.FloatField(default=0.0)

    #해당 질문 모델을 출력할 때, question_text를 출력.
    def __str__(self):
        return f'제목: {self.question_text}, 날짜: {self.pub_date}'
    

# 대답 모델
class Choice(models.Model):
    question = models.ForeignKey(Question, on_delete=models.CASCADE) # 외래키 Question을 CASCADE로 참조한다.
    choice_text = models.CharField(max_length=200) # 텍스트를 저장하는 필드
    votes = models.IntegerField(default=0) # 숫자를 저장하는 필드

  1. 여러 가지 필드 입력 양식 확인하기. 이전 과정 참고.
from django.db import models

# Create your models here.
# 모델 생성 -> 마이그레이션 생성(setting.py 설정) -> 테이블 생성
# 질문 어디로 놀러 가고 싶니?
# 대답: 산, 강, 바다, 도심 호캉스

# 질문 모델
class Question(models.Model):
    question_text = models.CharField(max_length=200) # 텍스트를 저장하는 필드
    pub_date = models.DateTimeField('date published') # 날짜를 저장하는 필드
    # is_something = models.BooleanField(default=False)
    # average_score = models.FloatField(default=0.0)
    # 추가된 필드
    score = models.FloatField(default=0)
    is_something_wrong = models.BooleanField(default=False)
    json_field = models.JSONField(default=dict)

    #해당 질문 모델을 출력할 때, question_text를 출력.
    def __str__(self):
        return f'제목: {self.question_text}, 날짜: {self.pub_date}'
    

# 대답 모델
class Choice(models.Model):
    question = models.ForeignKey(Question, on_delete=models.CASCADE) # 외래키 Question을 CASCADE로 참조한다.
    choice_text = models.CharField(max_length=200) # 텍스트를 저장하는 필드
    votes = models.IntegerField(default=0) # 숫자를 저장하는 필드


=> 여러 필드에 대한 입력 폼을 확인할 수가 있습니다.




8. Django Shell

  1. python manage.py shell 명령어로 Django Shell을 열기.

  2. from polls.models import * 후, Question 실행해 보기.

  3. 모델이름.objects.all() 명령어로 테이블 확인하기.

  4. 관리자 페이지에서 choice 레코드 추가하기.

  5. polls/models.py 에서 Choice 모델에 대한 출력 양식 설정하기.

# 대답 모델
class Choice(models.Model):
    question = models.ForeignKey(Question, on_delete=models.CASCADE) # 외래키 Question을 CASCADE로 참조한다.
    choice_text = models.CharField(max_length=200) # 텍스트를 저장하는 필드
    votes = models.IntegerField(default=0) # 숫자를 저장하는 필드
    def __str__(self):
        return f'{self.choice_text}'
  1. Choice 확인하기.

  2. Choice 객체에 대한 여러 정보 확인하기 및 question 참조하기. choice.OOO

  3. Question에서 Choice 참조하기. question.choice_set.all()




9. Django Shell - 현재 시각 구하기

Django에서는 time zone 정보를 확인해야 합니다. 많은 웹서비스는 글로벌하게 이용되기 때문에 time zone 정보가 필요합니다.

따라서, Django에서는 Python의 datetime 대신에 timezone 을 사용합니다.

  1. from django.utils import timezone
  2. timezone.now()


=> time zone에 대한 정보도 있는 것을 확인할 수가 있습니다.




10. Django Shell - 레코드 생성하기

방법 1, 모델 객체 생성 후 저장하기.)

  1. 모델 객체 생성하기. q1 = Question(quetion_text = "coffee vs tea", pub_date = timezone.now())

  2. 해당 객체를 DB에 저장하기. q1.save()

  3. polls/models.py 에서 pub_date 필드에 대한 옵션 변경하기.

class Question(models.Model):
    question_text = models.CharField(max_length=200) 
    # auto_now=True : 해당 모델이 갱신될 때마다, 현재의 시각 정보 저장.
    # auto_now_add=True : 해당 모델이 생성될 때, 현재의 시각 정보 저장.
    pub_date = models.DateTimeField(auto_now_add=True) 

    def __str__(self):
        return f'제목: {self.question_text}, 날짜: {self.pub_date}'

방법 2-1, 저장되어 있는 모델 객체에서 바로 생성하기)

  1. 모델 객체 생성하기. q3 = Question(question_text="abc")
  2. 해당 객체를 DB에 저장하기. q3.save()
  3. 해당 객체의 choice_set.create 사용하기. q3.choice_set.create(choice_text='a')
    q3.choice_set.create(choice_text='b')

방법 2-2)

  1. choice_c = Choice(choice_text='c', question=q3)
  2. choice_c.save()




11. Django Shell - 레코드 수정 및 삭제 하기

  • 해당 객체의 필드값을 직접 수정하기. q.question_text = q.question_text + "???"
  • 해당 객체를 테이블에서 삭제하기. choice.delete()

12. Django Shell - 모델 필터링(Model Filtering)

  • get() 메서드 : 조건에 해당되는 객체 1개를 가져오기.
    Quetion.objects.get(id=1)
    Quetion.objects.get(question_text__startswith='휴가를')
    Quetion.objects.get(pub_date__year=2023)
    주의 사항 : 여러 개의 객체를 불러오는 경우 error가 발생함.
  • filter() 메서드 : 조건에 해당되는 객체를 여러 개 가져오기.
    Question.objects.filter(pub_date__year=2023)
    Question.objects.filter(pub_date__year=2023).count() : 해당되는 객체들의 총 개수 반환
  • query : 위의 필터링 메서드의 쿼리문이 실제로 어떻게 되어 있는지 확인 가능.
    print(Question.objects.filter(question_text__startswith='휴가를').query)
    =>결과) SELECT "polls_question"."id", "polls_question"."question_text", "polls_question"."pub_date" FROM "polls_question" WHERE "polls_question"."question_text" LIKE 휴가를% ESCAPE '\'

이 외에도 https://docs.djangoproject.com/en/4.2/ref/models/querysets/의 field-lookups 에서 여러 필터링 조건들에 대해서 확인할 수가 있습니다.

  • contains : 특정 문자열이 들어 있는지 확인.
>>> Question.objects.filter(question_text__contains='휴가')
<QuerySet [<Question: 제목: 휴가를 보내고 싶은 장소는?, 날짜: 2023-10-30 06:32:04+00:00>, <Question: 제목: 휴가를 가실 계획인 가요?, 날짜: 2023-10-30 08:39:36.707623+00:00>]>
  • gt : greater than. 더 큰 조건들을 확인.
>>> print(Choice.objects.filter(votes__gt=0).query)
SELECT "polls_choice"."id", "polls_choice"."question_id", "polls_choice"."choice_text", "polls_choice"."votes" FROM "polls_choice" WHERE "polls_choice"."votes" > 0
  • update() : 해당되는 레코드의 값 변경.
>>> Choice.objects.filter(votes__gt=0).update(votes=0)
1 //업데이트된 레코드 수 반환.
  • delete() : 해당되는 레코드 삭제.
>>> Choice.objects.filter(votes=0).delete()
(4, {'polls.Choice': 4}) //삭제된 레코드 수 반환.
  • regex : 정규표현식을 만족하는 레코드 확인.
>>> Question.objects.filter(question_text__regex=r'^휴가.*장소')
<QuerySet [<Question: 제목: 휴가를 보내고 싶은 장소는?, 날짜: 2023-10-30 06:32:04+00:00>]>

>>> print(Question.objects.filter(question_text__regex=r'^휴가.*장소').query)
SELECT "polls_question"."id", "polls_question"."question_text", "polls_question"."pub_date" FROM "polls_question" WHERE "polls_question"."question_text" REGEXP ^휴가.*장소

-----------------------------------------------------------------------------

위와 같은 결과 출력.
>>> Question.objects.filter(question_text__startswith='휴가').filter(question_text__contains='장소')
<QuerySet [<Question: 제목: 휴가를 보내고 싶은 장소는?, 날짜: 2023-10-30 06:32:04+00:00>]>

>>> print(Question.objects.filter(question_text__startswith='휴가').filter(question_text__contains='장소').query)
SELECT "polls_question"."id", "polls_question"."question_text", "polls_question"."pub_date" FROM "polls_question" WHERE ("polls_question"."question_text" LIKE 휴가% ESCAPE '\' AND "polls_question"."question_text" LIKE %장소% ESCAPE '\')
  • exclude() : 조건에 해당되지 않는 레코드를 확인.
>>> Question.objects.exclude(question_text__startswith='휴가')
<QuerySet [<Question: 제목: 가장 좋아하는 음식은?, 날짜: 2023-10-30 06:32:40+00:00>, <Question: 제목: coffee vs tea, 날짜: 2023-10-30 07:42:55.452696+00:00>]>



13. Django Shell - 모델 메서드 사용하기

  • model.save() : DB에서 해당 레코드 추가
  • model.delete() : DB에서 해당 레코드 삭제
  1. polls/models.py 파일에서 메서드 추가하기.
from django.utils import timezone
import datetime

class Question(models.Model):
    question_text = models.CharField(max_length=200)
    pub_date = models.DateTimeField(auto_now_add=True)

	# 생성 날짜가 최근 1일 이내인지 확인하는 메서드
    def was_published_recently(self):
        return self.pub_date >= timezone.now() - datetime.timedelta(days=1)

    #해당 질문 모델을 출력할 때, question_text를 출력.
    def __str__(self):
        if self.was_published_recently():
            new_badge = "New!!!"
        else:
            new_badge = ''
        return f'{new_badge} 제목: {self.question_text}, 날짜: {self.pub_date}'
  1. 확인
profile
천천히, 그리고 꾸준히.

0개의 댓글