[DEV] 4주차. 장고 활용한 API서버 만들기(1)
1. 가상환경 생성 및 접속
python -m venv django-venv
source django-venv/bin/activate
# 비활성화
deactivate
2. Django 설치
pip install django
## 버전 확인
python -m django --version
3. Django 프로젝트 생성
django-admin startproject mysite
4. manage.py
- Django 프로젝트를 터미널에서 조작할 수 있는 명령어 제공
생성한 프로젝트 서버에서 실행
cd mysite
python manage.py runserver
- Django 프로젝트 default 페이지
5. App 생성
python manage.py startapp polls
- polls 폴더 생성됨
- admin.py
- apps.py
- models.py
- tests.py
- views.py
6. url 생성
polls/urls.py
수정
from django.urls import path
from . import views
urlpatterns = [
path('', views.index, name='index'),
]
url 뒤에 아무것도 없을 때, views에 있는 index 를 보여줘라
mysite/urls.py
수정
from django.contrib import admin
from django.urls import path, include
urlpatterns = [
path('polls/', include('polls.urls')),
path('admin/', admin.site.urls),
]
import include
추가
urlpatterns에 path('polls/', include('polls.urls')),
추가
7. 첫 화면 생성
polls/views.py
수정
from django.http import HttpResponse
def index(request):
return HttpResponse("Hello, world.")
Hellow, world. 출력
127.0.0.1:8000/polls/
접속
mysite/urls.py
에 path를 추가하고,polls/views.py
에서 해당하는 함수를 구현함으로써 페이지 추가 가능!
8. Model 만들기
-
DB에 저장된 값을 불러와서 보여주도록 함
polls/models.py
- DB를 테이블 별로 읽어서 하나의 테이블에 저장되어 있는 값을 읽어들일 수 있도록 도와줌
-
ORM 기능!
- class는 항상
models.Model
을 상속받음
모델 생성 과정
- 모델 생성
- 모델을 테이블에 써주기 위한 마이그레이션 생성
- 이 모델에 맞는 테이블 생성
- ex) 설문 조사
models.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)
choice_text = models.CharField(max_length=200)
votes = models.IntegerField(default=0)
9. 마이그레이션
app 등록
mystie/settings.py
수정INSTALLED_APPS
에'polls.apps.PollsConfig
추가
# Application definition
INSTALLED_APPS = [
'polls.apps.PollsConfig',
'django.contrib.admin',
'django.contrib.auth',
'django.contrib.contenttypes',
'django.contrib.sessions',
'django.contrib.messages',
'django.contrib.staticfiles',
]
make migration
- 처음 생성할 때, 모델 수정했을 때 실행!
python manage.py makemigrations polls
Migrations for ‘polls’:
polls/migrations/0001_initial.py
- Create model Question
- Create model Choice
마이그레이션 내용 살펴보기
python manage.py sqlmigrate polls 0001
BEGIN;
--
-- Create model Question
--
CREATE TABLE "polls_question" ("id" integer NOT NULL PRIMARY KEY AUTOINCREMENT, "question_text" varchar(200) NOT NULL, "pub_date" datetime NOT NULL);
--
-- Create model Choice
--
CREATE TABLE "polls_choice" ("id" integer NOT NULL PRIMARY KEY AUTOINCREMENT, "choice_text" varchar(200) NOT NULL, "votes" integer NOT NULL, "question_id" bigint NOT NULL REFERENCES "polls_question" ("id") DEFERRABLE INITIALLY DEFERRED);
CREATE INDEX "polls_choice_question_id_c5b4b260" ON "polls_choice" ("question_id");
COMMIT;
id
컬럼이 항상 자동으로 추가됨index
- 각 질문에 대한 choice 들을 자주 찾아보게 될 것
- 인덱싱이 되어있지 않다면 DB는 테이블을 full scan해서 찾아야 함
ForeignKey
에 대해서 항상 인덱싱을 하게 됨!
테이블 생성
python manage.py migrate
DB 접속
sqlite3 db.sqlite3
테이블 목록
.tables
- django_migrations : 마이그레이션 실행 목록
테이블 구조 확인
.schema polls_question
CREATE TABLE IF NOT EXISTS “polls_question” (“id” integer NOT NULL PRIMARY KEY AUTOINCREMENT, “question_text” varchar(200) NOT NULL, “pub_date” datetime NOT NULL, “average_score” real NOT NULL, “is_something” bool NOT NULL);
마이그레이션 되돌리기
python manage.py migrate polls 0001
polls의 마이그레이션을 0001 상태로 되돌림
- 이후 migration 0002 파일 삭제
polls/models.py
에서 제거할 field 삭제
10. Admin
CRUD
- Create 생성
- Read 읽기
- Update 갱신
- Delete 삭제
admin 계정 생성
python manage.py createsuperuser
Username (leave blank to use ‘bokyung’): admin
Email address:
Password:
Password (again):
Superuser created successfully.
admin 서버 접속
mysite/urls.py
urlpatterns = [
path('polls/', include('polls.urls')),
path('admin/', admin.site.urls),
]
-> <127.0.0.1:8000/admin/> 접속
서버에서 사용자 추가
- [Users] - [+add] - 정보 입력 후 [save]
11. Admin - 모델 CRUD
모델 등록
polls/admin.py
수정
from django.contrib import admin
from .models import *
admin.site.register(Question)
admin.site.register(Choice)
Polls 앱에 두 모델이 등록됨
데이터 추가
- [+add] 를 눌러서 데이터를 추가할 수 있음
- 자기 자신을 문자열로 정의할 때 어떻게 표현할 것인지 지정
polls/models.py
에서def __str__(self):
지정
class Question(models.Models):
...
def __str__(self):
return self.question_text
question_text를 그대로 표시하겠다
class Question(models.Models):
...
def __str__(self):
return f'제목: {self.question_text}, 날짜: {self.pub_date}'
텍스트 형식 지정
12. Django Shell
python manage.py shell
- 데이터 불러오기
Quesion.objects.all()
<QuerySet [<Question: 제목: 휴가를 어디서 보내고 싶으세요?, 날짜: 2023-11-01 07:37:20+00:00>, <Question: 제목: 가장 좋아하는 디저트는?, 날짜: 2023-11-01 07:37:49+00:00>]>
- django shell은 변경 사항이 바로 반영되지 않기 때문에, 수정될 경우 쉘을 껐다 다시 켜야 함
- 모델을 변수에 저장해놓고 각 필드를 불러올 수 있음
from polls.models import *
choice = Choice.objects.all()[0]
choice
# <Choice: 바다>
choice.id
# 1
choice.choice_text
# '바다'
choice.vote
# 0
choice.question
# <Question: 제목: 휴가를 어디서 보내고 싶으세요?, 날짜: 2023-11-01 07:37:20+00:00>
choice.question.question_text
# '휴가를 어디서 보내고 싶으세요?'
choice.question.pub_date
# datetime.datetime(2023, 11, 1, 7, 37, 20, tzinfo=datetime.timezone.utc)
choice.question.id
# 1
question.choice_set.all()
# <QuerySet [<Choice: 바다>]>
13. datetime
- django에서는
timezone
이용!
from datetime import datetime
from django.utils import timezone
datetime.now() # python
# datetime.datetime(2023, 11, 1, 16, 15, 41, 588899
timezone.now() # django
# datetime.datetime(2023, 11, 1, 16, 16, 53, 400371, tzinfo=datetime.timezone.utc)
14. shell에서 레코드 CRUD
생성
q1 = Question(question_text="커피 vs 녹차", pub_date=timezone.now())
q1.save()
레코드가 생성될 때 자동으로 시간 넣기
- 모델의 DateTimeField에서 `(auto_now_add=True)` 추가
polls/models.py
class Question(models.Model):
question_text = models.CharField(max_length=200)
pub_date = models.DateTimeField(auto_now_add=True)
def __str__(self):
return f'제목: {self.question_text}, 날짜: {self.pub_date}'
옵션 생성
q3 = Question(question_text="abc")
q3.save()
q3.choice_set.all()
# <QuerySet []>
## Question 모델에서 만드는 방법
q3.choice_set.create(choice_text="a")
q3.choice_set.create(choice_text="b")
q3.choice_set.all()
# <QuerySet [<Choice: a>, <Choice: b>]>
## 직접 Choice 모델로 추가하는 방법
choice_c = Choice(choice_text="c", question=q3)
choice_c.save()
q3.choice_set.all()
# <QuerySet [<Choice: a>, <Choice: b>, <Choice: c>]>
수정
q = Question.objects.last()
q.question_text
# 'abc'
q.question_text = q.question_text + '???'
q.save()
q.question_text
# 'abc???'
삭제
- 삭제는
.save()
하지 않음!- 변수에 담아서 삭제한 경우 쉘 메모리에 값이 남아있기 때문에 save하면 다시 테이블에 들어감
choice = Choice.objects.last()
choice.choice_text
# 'c'
q = choice.question
q.choice_set.all()
# <QuerySet [<Choice: a>, <Choice: b>, <Choice: c>]>
choice.delete()
# (1, {'polls.Choice': 1})
q.choice_set.all()
# <QuerySet [<Choice: a>, <Choice: b>]>
15. 모델 필터링
한 개만 가져오기 : get
# 같은 <Question: 제목: 휴가를 어디서 보내고 싶으세요?, 날짜: 2023-11-01 07:37:20+00:00> 값을
## id로 가져오기
Question.objects.get(id=1)
## 옵션으로 가져오기
Question.objects.get(question_text__startswith="휴가")
Question.objects.get(pub_date__second=20)
여러 개 가져오기 : filter
Question.objects.filter(pub_date__year=2023)
# <QuerySet [<Question: 제목: 휴가를 어디서 보내고 싶으세요?, 날짜: 2023-11-01 07:37:20+00:00>, <Question: 제목: 가장 좋아하는 디저트는?, 날짜: 2023-11-01 07:37:49+00:00>, <Question: 제목: 커피 vs 녹차, 날짜: 2023-11-01 16:21:27.196500+00:00>, <Question: 제목: abc???, 날짜: 2023-11-01 16:25:01.782268+00:00>]>
Question.objects.filter(pub_date__year=2023).count()
# 4
QuerySet SQL문
print(Question.objects.filter(pub_date__year=2023).query)
# SELECT "polls_question"."id", "polls_question"."question_text", "polls_question"."pub_date" FROM "polls_question" WHERE "polls_question"."pub_date" BETWEEN 2023-01-01 00:00:00 AND 2023-12-31 23:59:59.999999
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 '\'
q = Question.objects.get(pk=1)
print(q.choice_set.all().query)
# SELECT "polls_choice"."id", "polls_choice"."question_id", "polls_choice"."choice_text", "polls_choice"."votes" FROM "polls_choice" WHERE "polls_choice"."question_id" = 1
- filter를 복잡하게 줄 경우 SQL문이 잘 실행되고 있는지 파악할 때 활용
16. 모델 메소드
polls/models.py
에서 모델 클래스 내에 함수 생성- ex) 데이터가 생성된지 하루가 지나지 않았는지 판단하는 함수
was_published_recently
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)
def was_published_recently(self):
return self.pub_date >= timezone.now() - datetime.timedelta(days=1)
def __str__(self):
if self.was_published_recently():
new_badge = '[NEW]'
else:
new_badge = ''
return f'{new_badge} 제목: {self.question_text}, 날짜: {self.pub_date}'
- 하루가 지나지 않았으면 제목 앞에
[NEW]
가 붙도록__str__
함수도 수정