from django.contrib import admin
from bbs.model import Bbs
class BbsAdmin(admin.ModelAdmin):
list_display=('title','author','created',)
admin.site.register(Bbs, BbsAdmin)
5.url설정 요청 url에 대한 설정을 합니다. 먼저 프로젝트 url 파일을 수정합니다.
pjt_bbs/pjt_bbs/urls.py
from django.contrib import admin
from django.urls import path, include
urlpatterns = [
path('admin/', admin.site.urls),
path('bbs/', include('bbs.urls')),
]
그리고 bbs 앱 디렉토리에 urls.py 파일을 생성하고 설정해줍니다.
pjt_bbs/bbs/urls.py
from django.urls import path, re_path, include
from rest_framework.urlpatterns import format_suffix_patterns
from bbs import views
urlpatterns = [
re_path(r'^$', views.bbs_list),
re_path(r'^(?P<pk>[0-9]+)/$', views.bbs_detail),
]
urlpatterns = format_suffix_patterns(urlpatterns)
다. 개발
1.Serializer 작성 Serializer 는 데이터를 직렬화(마치 스트림데이터처럼 변경) 해줍니다. 이 직렬화를 통해서 메모리상에 있는 변수와 값의 세트를 json 데이터로 변환해서 사용할 수 있게 됩니다. serializers.py 파일을 생성하고 아래와 같이 입력해줍니다.
bbs_pjt/bbs/serializers.py
from rest_framework import serializers
from bbs.models import Bbs
class BbsSerializer(serializers.ModelSerializer):
# ModelSerializer 를 이용해서 아래와 같이 짧은 코드로 직렬화 필드를 정의할 수 있다
class Meta:
model = Bbs
fields = ('id','title','author','pw','content')
# 신규 Bbs instance를 생성해서 리턴해준다
def create(self, validated_data):
return Bbs.objects.create(**validated_data)
# 생성되어 있는 Bbs instance 를 저장한 후 리턴해준다
def update(self, instance, validated_data):
instance.title = validated_data.get('title', instance.title)
instance.author = validated_data.get('author', instance.author)
instance.pw = validated_data.get('pw', instance.pw)
instance.content = validated_data.get('content', instance.content)
instance.save()
return instance
2.View 작성 위에서 작성한 serializer를 이용해서 웹으로 부터 요청을 받을 View 파일을 작성합니다. url에서 bbs/ 로 끝나는 요청이 GET일경우 목록으로 처리하고, POST로 들어오면 입력으로 처리합니다. 그리고 bbs/ 다음에 숫자값이 입력되어 있을경우(예를들어 bbs/15)에 GET일 경우 한개의 데이터, PUT은 수정, DELETE 는 삭제처리를 해줍니다. views.py 파일을 열고 아래와 같이 작성해줍니다.
프로젝트디렉토리/bbs/views.py
from rest_framework import status
from rest_framework.decorators import api_view
from rest_framework.response import Response
from bbs.models import Bbs
from bbs.serializers import BbsSerializer
# 요청 url 인 bbs/ 에 대해서 urls.py 에 정의된 view.bbs_list 가 호출된다.
@api_view(['GET', 'POST'])
def bbs_list(request, format=None):
if request.method == 'GET':
bbs = Bbs.objects.all()
serializer = BbsSerializer(bbs, many=True) # many 값이 True 이면 다수의 데이터 instance를 직렬화할수 있다
return Response(serializer.data)
elif request.method == 'POST':
serializer = BbsSerializer(data=request.data)
if serializer.is_valid():
serializer.save()
return Response(serializer.data, status=status.HTTP_201_CREATED)
return Response(serializer.errors, status=status.HTTP_400_BAD_REQUEST)
# 요청 url 인 bbs/번호 에 대해서 urls.py 에 정의된 view.bbs_detail 이 호출된다
@api_view(['GET', 'PUT', 'DELETE'])
def bbs_detail(request, pk, format=None):
try:
bbs = Bbs.objects.get(pk=pk)
except Bbs.DoesNotExist:
return Response(status=status.HTTP_404_NOT_FOUND)
if request.method == 'GET':
serializer = BbsSerializer(bbs)
return Response(serializer.data)
elif request.method == 'PUT':
serializer = BbsSerializer(bbs, data=request.data)
if serializer.is_valid():
serializer.save()
return Response(serializer.data)
return Response(serializer.errors, status=status.HTTP_400_BAD_REQUEST)
elif request.method == 'DELETE':
bbs.delete()
return Response(status=status.HTTP_204_NO_CONTENT)
3.migration 이제 설정한 파일들을 실제 적용하기 위해 migration 명령어를 실행해 줍니다.
$ python3 manage.py makemigrations # bbs 하위 디렉토리에 마이그레이션 파일 생성
$ python3 manage.py migrate # 정의한 Bbs 모델로 데이터베이스 테이블 생성
마. restful API 테스트
1.서버실행 프로젝트 디렉토리에서 서버를 실행합니다.
python manage.py runserver
2.포스트맨을 통한 데이터입력 요청 우선 데이터 입력처리 확인을 위해 포스트맨을 열고 아래와 같이 입력한 후 [Send] 버튼을 클릭합니다.
주소 : http://127.0.0.1:8000/bbs/
Method : POST
Headers : JSON
Body (type=Raw) :
{
"title":"beat it",
"author":"michael jacson",
"pw":"qwer1234",
"content":"They told him: Dont you ever come around here"
}
3. 브라우저에서 데이터 요청 위 방식으로 몇개 데이터를 더 입력한 후 브라우저를 통해 목록을 요청하면 아래와 같이 Django restframework 에서 지원하는 json 데이터 페이지가 출력됩니다. 웹 브라우저를 통해 요청하면 아래와 같이 완성된 형태의 페이지가 보여지게 되고, ajax 나 모바일 앱을 통해 요청을 하면 데이터만 받아서 사용할 수 있게 됩니다.
본 예제에서는 이미 id=1과 id=2 데이터를 삭제하여 id=3과 id=4 인 데이터가 조회되고 있습니다.
여기서 id 컬럼은 dajngo framework에서 생성한 컬럼으로 자동증가 속성을 가진 컬럼입니다.
포스트맨을 열고 아래와 같이 url을 입력한 후 DELETE를 선택한 후 [Send] 버튼을 클릭하면 id=1 인 데이터가 삭제됩니다. 삭제 대상 데이터가 존재하지 않는 경우, "404Not Found" 메시지가 출력됩니다.
포스트맨을 열고 아래와 같이 url을 입력한 후 PUT를 선택한 후 [Send] 버튼을 클릭하면 id=1 인 데이터를 업데이트합니다. 수정 대상 데이터가 존재하지 않는 경우, "404Not Found" 메시지가 출력됩니다. 본 문서에서는 id=3인 데이터로 실행했습니다.
주소 : http://127.0.0.1:8000/bbs/3/
Method : PUT
Headers : JSON
Body (type=Raw) :
{
"title":"thriller",
"author":"michael jacson",
"pw":"qwer1234",
"content":"You hear the door slam"
}
아래와 같이 "200 OK" 성공 메시지와 함께 수정된 결과값을 보여줍니다.
브라우저에 http://127.0.0.1:8000/bbs/ 입력 후 조회하면 변경된 데이터가 출력되는 것을 확인 할 수 있습니다.
라. 클래스 기반 뷰처리
이번에는 코드를 조금 수정해서 기존 함수기반의 뷰 처리를 클래스 기반으로 변경해 보겠습니다. 클래스 기반으로 변경하는 이유는 rest framework 라이브러리 클래스에 CRUD에 대한 기본처리가 정의되어 있기 때문에 더 짧은 코드로 CRUD 를 완성할수 있기 때문입니다. 먼저 views.py 파일을 열고 아래와 같이 코드전체를 수정합니다.
1.views 수정
프로젝트디렉토리/bbs/views.py
# 클래스 기반의 Rest CRUD 처리
from bbs.models import Bbs
from bbs.serializers import BbsSerializer
from rest_framework import generics
# generics 에 목록과 생성 API 가 정의되어 있다
class BbsList(generics.ListCreateAPIView):
queryset = Bbs.objects.all()
serializer_class = BbsSerializer
# generics 에 상세, 수정, 삭제 API가 정의되어 있다
class BbsDetail(generics.RetrieveUpdateDestroyAPIView):
queryset = Bbs.objects.all()
serializer_class = BbsSerializer
2.urls 수정
프로젝트디렉토리/bbs/urls.py
from django.conf.urls import url
from rest_framework.urlpatterns import format_suffix_patterns
from bbs import views
urlpatterns = [
# 함수형 처리시 호출되는 함수
# url(r'^$', views.bbs_list),
# url(r'^(?P<pk>[0-9]+)/$', views.bbs_detail),
# 클래스를 호출하고 해당클래스의 as_view() 함수를 호출
url(r'^$', views.BbsList.as_view()),
url(r'^(?P<pk>[0-9]+)/$', views.BbsDetail.as_view()),
]
urlpatterns = format_suffix_patterns(urlpatterns)
이제 브라우저에서 테스트 주소를 다시 입력해보면 이전과 동일하게 동작하는 것을 확인할 수 있습니다.
이와 같이 Django의 Rest Framework을 사용할 경우 클래스기반으로 코드를 작성하게 되면 더 짧은 코드로 빠른 시간안에 완성도 높은 결과물을 만들 수 있게됩니다.
$ python3 manage.py runserver
Watching for file changes with StatReloader
Performing system checks...
...
Django version 4.0.2, using settings 'my_django_project.settings'
Starting development server at http://127.0.0.1:8000/
Quit the server with CONTROL-C.
브라우저로 http://127.0.0.1:8000/ 접속합니다. 아래와 같은 화면이 표시됩니다.
Django 모델 설정
my_api/models.py 파일에 Person 과 Species class를 추가합니다.
from django.db import models
class Species(models.Model):
name = models.CharField(max_length=100)
classification = models.CharField(max_length=100)
language = models.CharField(max_length=100)
#foreignkey 설정할 때 web에서 primary key의 name으로 보여주게됨. but db에는 id로 저장됨
#아니면 primary key 그대로 1번 2번 이렇게 보임.
def __str__(self):
return self.name
class Person(models.Model):
name = models.CharField(max_length=100)
birth_year = models.CharField(max_length=10)
eye_color = models.CharField(max_length=10)
species = models.ForeignKey(Species, on_delete=models.DO_NOTHING)
모델이 추가된 후 마이그레이션을 실행하여 Django가 데이터베이스에 2개의 새 테이블을 추가할 것임을 알려줍니다.
makemigrations 명령을 실행하여 migrations을 위한 my_api/migrations 디렉토리와 파일을 만듭니다.
$ python3 manage.py makemigrations
Migrations for 'my_api':
my_api/migrations/0001_initial.py
- Create model Species
- Create model Person
migrate 명령을 실행하여 sqlite DB에 테이블들을 생성합니다.
$ python3 manage.py migrate
Operations to perform:
Apply all migrations: admin, auth, contenttypes, my_api, sessions
Running migrations:
Applying contenttypes.0001_initial... OK
... ...
Applying my_api.0001_initial... OK
Applying sessions.0001_initial... OK
DRF 직렬 변환기 설정
모델을 추가하고 테이블을 생성했으므로 이제 DRF에 모델을 직렬화합니다. 직렬 변환기는 Person 모델과 Species 모델을 API에서 사용자에게 데이터를 반환하는 데 사용할 JSON으로 변환합니다. 새 파일을 만들어 직렬 변환기를 추가합니다 my_aapi/serializers.py
from rest_framework import serializers
from my_api.models import Person, Species
class PersonSerializer(serializers.ModelSerializer):
class Meta:
model = Person
fields = ('id', 'name', 'birth_year', 'eye_color', 'species')
class SpeciesSerializer(serializers.ModelSerializer):
class Meta:
model = Species
fields = ('id', 'name', 'classification', 'language')
라우터 설정 및 API URL 생성
직렬 변환기가 생성된 후에는 API에 대한 view를 생성하고 Django URL에 연결합니다. 새 파일에서 생성한 각 모델에 대해 2개의 뷰 세트를 추가합니다. 뷰 세트는 여러 논리 세트를 단일 클래스로 결합해 줍니다.
my_api/views.py
from rest_framework import viewsets
from my_api.serializers import PersonSerializer, SpeciesSerializer
from my_api.models import Person, Species
class PersonViewSet(viewsets.ModelViewSet):
queryset = Person.objects.all()
serializer_class = PersonSerializer
class SpeciesViewSet(viewsets.ModelViewSet):
queryset = Species.objects.all()
serializer_class = SpeciesSerializer
뷰셋이 정의되면 이제 DRF에서 제공하는 라우터 기능을 사용하여 원하는 API 엔드포인트를 주어진 뷰셋으로 라우팅할 수 있습니다. 아래와 같이 새 파일을 만들고 라우터 구성을 추가합니다 .
my_api/urls.py
from django.urls import include, path
from rest_framework import routers
from my_api.views import PersonViewSet, SpeciesViewSet
router = routers.DefaultRouter()
router.register(r'people', PersonViewSet)
router.register(r'species', SpeciesViewSet)
urlpatterns = [
path('', include(router.urls)),
]
앱의 URL 파일을 가리 키도록 Django URL을 연결합니다.
my_django_project/urls.py
from django.urls import path, include
urlpatterns = [
path('star-wars/', include('my_api.urls')),
]
라우터에 의해 생성된 URL에는 모든 API 메소드(GET, POST 및 PUT)가 있습니다. 라우터가 자동으로 URL을 핸들러 .get(), .list(), 및 .create()뷰셋에 매핑하기 때문입니다. API를 테스트하고 모든 것이 예상대로 작동하는지 확인합니다.
API 사용 시작
브라우저로 http://127.0.0.1:8000/star-wars/species/ 에 접속하면 API를 사용할 수 있습니다.
API를 사용하여 데이터를 데이터베이스에 추가합니다. 데이터가이 성공적으로 생성되면 서버는 아래와 같이 페이로드와 함께 201 성공 메시지로 응답합니다.
"Raw data" 탭을 사용하여 다른 데이터를 추가합니다.
http://127.0.0.1:8000/star-wars/people/ 접속하여 People API에 액세스할 수도 있습니다 . Person 모델에는 Species 모델에 대한 외래 키가 있으므로 Browsable API는 드롭다운을 제공합니다.
[POST] 버튼을 클릭하면 새로운 Person Dooku가 생성됩니다.
[Raw data] 탭을 사용하여 다른 사람을 추가해 보겠습니다. 그 사람에 대해 species의 id를 입력해야 합니다.
[POST] 버튼을 클릭하면 이제 데이터베이스에 새로운 Chewbacca 가 입력됩니다.
Chrome 브라우저에JSONView 확장 프로그램을 설치하면 정리된 JSON을 볼 수 있습니다. 모든 API 엔드포인트가 올바르게 작동하는지 확인합니다.
아래 내용을 브라우저에 입력한 후 접속하면 species 에 등록된 데이터를 조회할 수 있습니다.
Django(/dʒæŋɡoʊ/ jang-goh/쟁고/장고)는 파이썬으로 만들어진 무료 오픈소스 웹 애플리케이션 프레임워크(web application framework)입니다. 쉽고 빠르게 웹사이트를 개발할 수 있도록 돕는 구성요소로 이루어진 웹 프레임워크입니다.
웹사이트를 구축할 때, 비슷한 유형의 요소들이 항상 필요합니다. 회원가입, 로그인, 로그아웃과 같이 사용자 인증을 다루는 방법이나 웹사이트의 관리자 패널, 폼, 파일 업로드와 같은 것들이 있습니다.
그래서 많은 사람들이 바로 사용할 수 있는 구성요소들을 갖춘 여러 프레임워크를 만들습니다. 장고도 그 중 하나입니다.
비유를 하자면, 편지(request, 요청)가 도착했는지 확인해주는 메일박스(port, 포트)가 있고 이 것은 웹서버가 해주는 일입니다. 웹 서버는 받은 편지를 읽고 웹 페이지와 함께 답장을 준니다. 그런데 무언가를 주고 싶을 때는 그 안에 내용이 있어야 하는데, 장고는 그 특정 콘텐츠를 만들 수 있는 역할을 합니다.
아래 명령어로 장고를 설치합니다.
pip install django
Django 프로젝트
이번장에서는 Django 기본 프로젝트를 생성하고, 생성한 프로젝트로 서버를 실행한 후 브라우저를 통해 접속하는 과정을 알아보겠습니다. Django는 node.js의 express 모듈처럼 간단한 명령어로 기본 프로젝트를 생성할 수 있으며 관리자 기능까지 제공합니다.
manage.py는 사이트 관리를 도와주는 유틸리티입니다.
settings.py는 웹사이트 설정이 있는 파일입니다.
urls.py파일은 urlresolver가 사용하는 패턴 목록을 포함하고 있습니다.
가. Django 프로젝트 생성
프로젝트를 생성하기 위해 wsl에 접속하여 프로젝트를 생성할 디렉토리로 이동합니다. 본 문서에서는 /mnt/d/django 아래에 생성하겠습니다. /mnt/d/django 디렉토리에서 아래와 같이 명령어를 입력합니다.
mkdir /mnt/d/django
cd /mnt/d/django
django-admin startproject mysite
그러면 mysite라는 디렉토리가 하나 생성되고, 디렉토리 내부에는 manage.py 파일과 함께 프로젝트이름과 동일한 디렉토리가 하나 더 생성되어 있는것을 확인할 수 있습니다. 아래와 같은 디렉토리와 파일이 생성됩니다.
데이터베이스를 생성하기 위해서 콘솔 창에서 아래 코드를 실행합니다. 위에서 생성한 /mnt/d/django/mysite 디렉토리에서
python3 manage.py migrate 명령어를 실행합니다. 데이터베이스와 함께 테이블들을 생성합니다.
/mnt/d/django/mysite$ python3 manage.py migrate
Operations to perform:
Apply all migrations: admin, auth, contenttypes, sessions
Running migrations:
Applying contenttypes.0001_initial... OK
Applying auth.0001_initial... OK
Applying admin.0001_initial... OK
Applying admin.0002_logentry_remove_auto_add... OK
Applying admin.0003_logentry_add_action_flag_choices... OK
Applying contenttypes.0002_remove_content_type_name... OK
Applying auth.0002_alter_permission_name_max_length... OK
Applying auth.0003_alter_user_email_max_length... OK
Applying auth.0004_alter_user_username_opts... OK
Applying auth.0005_alter_user_last_login_null... OK
Applying auth.0006_require_contenttypes_0002... OK
Applying auth.0007_alter_validators_add_error_messages... OK
Applying auth.0008_alter_user_username_max_length... OK
Applying auth.0009_alter_user_last_name_max_length... OK
Applying auth.0010_alter_group_name_max_length... OK
Applying auth.0011_update_proxy_permissions... OK
Applying auth.0012_alter_user_first_name_max_length... OK
Applying sessions.0001_initial... OK
다. Django 웹서버 실행
이제 /mnt/d/django/mysite디렉토리에서 아래 명령어를 입력하여 웹서버를 실행합니다.
python3 manage.py runserver
/mnt/d/django/mysite$ python3 manage.py runserver
Watching for file changes with StatReloader
Performing system checks...
System check identified no issues (0 silenced).
February 09, 2022 - 21:25:47
Django version 4.0.2, using settings 'mysite.settings'
Starting development server at http://127.0.0.1:8000/
Quit the server with CONTROL-C.
python3 manage.py makemigrations를 실행해 변경사항에 대한 마이그레이션 파일 생성
python3 manage.py migrate를 실행해 변경사항을 데이터베이스에 적용
모든 Model 객체는 blog/models.py 파일에 선언하여 모델을 만듭니다. 이 파일에 블로그 글 모델도 정의합니다.
blog/models.py 파일을 열어서 안에 모든 내용을 삭제한 후 아래 코드를 추가합니다.
/mnt/d/django/mysite/blog/models.py
from django.db import models
from django.conf import settings
from django.utils import timezone
class Post(models.Model):
author = models.ForeignKey(settings.AUTH_USER_MODEL, on_delete=models.CASCADE)
title = models.CharField(max_length=200)
text = models.TextField()
created_date = models.DateTimeField(default=timezone.now)
published_date = models.DateTimeField(blank=True, null=True)
def publish(self):
self.published_date = timezone.now()
self.save()
def __str__(self):
return self.title
str양 옆에 언더스코어(_) 를 두 개를 써야 합니다. 파이썬에서 자주 사용되는데, "던더(dunder; 더블-언더스코어의 준말)"라고도 합니다.
class Post(models.Model):는 모델을 정의하는 코드입니다. (모델은 객체입니다.)
class는 객체를 정의합니다.
Post는 모델의 이름입니다. 클래스 이름의 첫 글자는 대문자로 써야 합니다.
models은 Post가 장고 모델임을 의미합니다. 이 코드 때문에 장고는 Post가 데이터베이스에 저장되어야 한다고 알게 됩니다.
이제 속성을 정의합니다. title, text, created_date, published_date, author 속성을 정의하기 위해, 필드마다 어떤 종류의 데이터 타입을 가지는지를 정해야 합니다. 여기서 데이터 타입에는 텍스트, 숫자, 날짜, 사용자 같은 다른 객체 참조 등이 있습니다.
models.CharField - 글자 수가 제한된 텍스트를 정의할 때 사용합니다. 글 제목같이 짧은 문자열 정보를 저장할 때 사용합니다.
def publish(self):는 publish라는 메서드(method) 입니다. def는 이것이 함수/메서드라는 뜻이고, publish는 메서드의 이름입니다. 메서드는 무언가를 되돌려 주기도 합니다. 예로 __str__ 메서드를 보면 __str__를 호출하면 Post 모델의 제목 텍스트(string)를 return 받게 됩니다.
데이터베이스에 모델을 위한 테이블 만들기
데이터베이스에 Post 모델을 추가합니다. 먼저 장고 모델에 변화가 생겼다는 걸 알려줍니다.
python manage.py makemigrations blog 를 입력해 봅니다.
/mnt/d/django/mysite$ python3 manage.py makemigrations blog
Migrations for 'blog':
blog/migrations/0001_initial.py:
- Create model Post
장고는 데이터베이스에 반영할 수 있도록 마이그레이션 파일(migration file)이 있습니다. 이제 python manage.py migrate blog 명령을 실행해, 실제 데이터베이스에 모델 추가를 반영합니다.
/mnt/d/django/mysite$ python3 manage.py migrate blog
Operations to perform:
Apply all migrations: blog
Running migrations:
Applying blog.0001_initial... OK
$ python3 manage.py sqlmigrate blog 0001
BEGIN;
--
-- Create model Post
--
CREATE TABLE "blog_post" ("id" integer NOT NULL PRIMARY KEY AUTOINCREMENT, "title" varchar(200) NOT NULL, "text" text NOT NULL, "created_date" datetime NOT NULL, "published_date" datetime NULL, "author_id" integer NOT NULL REFERENCES "auth_user" ("id") DEFERRABLE INITIALLY DEFERRED);
CREATE INDEX "blog_post_author_id_dd7a8485" ON "blog_post" ("author_id");
COMMIT;
장고 관리자
모델링한 글들을 장고 관리자에서 추가하거나 수정, 삭제할 수 있습니다.
blog/admin.py 파일을 열어서 내용을 다음과 같이 변경합니다.
/mnt/d/django/mysite/blog/admin.py
from django.contrib import admin
from .models import Post
admin.site.register(Post)
앞에서 정의했던 Post모델을 import 합니다. 관리자 페이지에서 만든 모델을 보려면 admin.site.register(Post)로 모델을 등록해야 합니다.
웹 서버를 실행하려면 콘솔 창에서 python manage.py runserver를 실행합니다.
/mnt/d/django/mysite$ python3 manage.py runserver
Watching for file changes with StatReloader
Performing system checks...
System check identified no issues (0 silenced).
February 09, 2022 - 23:05:26
Django version 4.0.2, using settings 'mysite.settings'
Starting development server at http://127.0.0.1:8000/
Quit the server with CONTROL-C.
로그인하기 위해 모든 권한을 가지는 슈퍼 사용자(superuser)를 생성합니다. 커맨드라인으로 돌아가서 python manage.py createsuperuser 를 입력하고 엔터를 누릅니다.
메시지가 나타나면 사용자 이름 (소문자, 공백 없이), 이메일 주소 및 암호를 입력합니다.
/mnt/d/django/mysite$ python3 manage.py createsuperuser
사용자 이름 (leave blank to use 'ypjeong'): admin
이메일 주소: admin@admin.com
Password:
Password (again):
Superuser created successfully.
브라우저에서 장고 관리자 페이지에서 슈퍼 사용자로 로그인한 후 대시보드를 확인합니다.
[+추가] 버튼을 클릭하여 블로그 포스트를 등록합니다.
Author 항목의 "+" 버튼을 클릭하여 사용자를 추가합니다.
Title, Text, Published date 를 입력한 후 [저장] 버튼을 클릭합니다.
URL(Uniform Resource Locators)은 웹 주소입니다. 웹 사이트를 방문할 때마다 브라우저의 주소창에 URL을 볼 수 있습니다. 127.0.0.1:8000이 바로 URL이고 https://djangogirls.org/도 URL입니다.
좀더 상세하게 예를 들어 "http://www.abcd.com:80/asd/df/sf.html" 이라는 사이트가 있다면,
http:// : 프로토콜
www.abcd.com :서버
asd/df : 자원의 위치
sf.html : 자원을 나타냅니다.
인터넷의 모든 페이지는 고유한 URL을 가지고 있습니다. 애플리케이션은 사용자가 URL을 입력하면 어떤 내용을 보여줘야 하는지 알고 있습니다. 장고는 URLconf (URL configuration)를 사용합니다. URLconf는 장고에서 URL과 일치하는 뷰를 찾기 위한 패턴들의 집합입니다.
장고 URL은 어떻게 작동할까요?
mysite/urls.py파일을 열어 보면 아래 내용이 보입니다.
/mnt/d/django/mysite/mysite/urls.py
"""mysite URL Configuration
[...]
"""
from django.contrib import admin
from django.urls import path
urlpatterns = [
path('admin/', admin.site.urls),
]
세 개의 따옴표들(""", """) 사이에 있는 줄들은 독스트링(docstring)입니다. 독스트링은 파일 제일 첫 부분, 클래스 또는 메서드 윗 부분에 작성해, 이들이 어떤 일을 수행하는지 알려줍니다. comment로 생각하면 됩니다. 파이썬은 이 부분을 실행하지 않습니다.
관리자 URL이 등록되어 있습니다.
path('admin/', admin.site.urls),
장고는 admin/로 시작하는 모든 URL을 view와 대조해 찾아냅니다. 무수히 많은 URL이 admin URL에 포함될 수 있어 일일이 모두 쓸 수 없어 정규표현식을 사용합니다.
from django.urls import path
from . import views
urlpatterns = [
path('', views.post_list, name='post_list'),
]
장고 함수인 path와 blog 애플리케이션에서 사용할 모든 views를 가져옵니다. 그 다음, 첫 번째 URL 패턴을 추가합니다.
이제 post_list라는 view가 루트 URL에 할당되었습니다. 이 URL 패턴은 빈 문자열에 매칭이 되며, 장고 URL 확인자(resolver)는 전체 URL 경로에서 접두어(prefix)에 포함되는 도메인 이름(i.e. http://127.0.0.1:8000/)을 제외하고 받아들입니다. 이 패턴은 장고에게 누군가 웹사이트에 'http://127.0.0.1:8000/' 주소로 들어왔을 때 views.post_list를 보여주라고 말해줍니다.
마지막 부분인 name='post_list'는 URL에 이름을 붙인 것으로 뷰를 식별합니다. 뷰의 이름과 같을 수도 다를 수도 있습니다.
템플릿은 blog/templates/blog 디렉토리에 저장됩니다. blog디렉터리 안에 하위 디렉터리인 templates을 생성하고 template 디렉토리 하위에 blog 디렉토리를 생성합니다.
blog
└───templates
└───blog
(왜 똑같은 blog디렉토리를 하나 더 만들어야하는지는 나중에 폴더 구조가 복잡해 질 때 좀더 쉽게 찾기 위해 사용하는 관습적인 방법입니다)
blog/templates/blog디렉토리 안에 post_list.html 이라는 새 파일을 만듭니다.
웹 서버를 다시 시작한 후 웹 사이트를 확인해 봅니다. 오류 메시지없이 빈 화면이 표시됩니다.
커맨드라인에서 Ctrl + C를 눌러 웹 서버를 중단한 후 python manage.py runserver명령을 실행해 서버를 재시작합니다.
post_list.html템플릿 파일에 아래 내용을 입력합니다.
blog/templates/blog/post_list.html
<html>
<head>
<title>Django mysite blog</title>
</head>
<body>
<div>
<h1><a href="">Django mysite Blog</a></h1>
</div>
<div>
<p>published: 14.06.2014, 12:14</p>
<h2><a href="">My first post</a></h2>
<p>Aenean eu leo quam. Pellentesque ornare sem lacinia quam venenatis vestibulum. Donec id elit non mi porta gravida at eget metus. Fusce dapibus, tellus ac cursus commodo, tortor mauris condimentum nibh, ut fermentum massa justo sit amet risus.</p>
</div>
<div>
<p>published: 14.06.2014, 12:14</p>
<h2><a href="">My second post</a></h2>
<p>Aenean eu leo quam. Pellentesque ornare sem lacinia quam venenatis vestibulum. Donec id elit non mi porta gravida at eget metus. Fusce dapibus, tellus ac cursus commodo, tortor mauris condimentum nibh, ut f.</p>
</div>
</body>
</html>
Object-Relational Mapping의 약자로 객체(Object)와 관계형 데이터베이스(Relational Database)의 데이터를 매핑(Mapping)해주는 것을 의미합니다. 객체 간의 관계를 바탕으로 SQL을 자동 생성하여 sql쿼리문 없이도 데이터베이스의 데이터를 다룰 수 있게 해줍니다. 데이터베이스의 테이블을 객체지향 프로그래밍에서 사용하는 객체(Class)처럼 사용할 수 있도록 해주는 기술입니다.
<예시>
book 이라는 객체에서 저자의 이름이 kim 인 책 목록을 가져오고 싶을 때,
SQL 쿼리문을 사용할 경우, sql 쿼리문을 작성하고 데이터를 가져오는 일련의 과정들을 코드에 적어야 합니다.
book_list = new list();
sql = "SELECT book FROM library WHERE author = 'kim'";
data = query(sql);
while (row = data.next()){
book = new Book();
book.setAuthor(row.get('author'));
book_list.add(book);
}
ORM을 사용할 경우, 간단하게 한 줄로 원하는 객체를 가져올 수 있습니다.
book_list = BookTable.query(author="kim")
쿼리셋이란 ?
쿼리셋(QuerySet)은 전달받은 모델의 객체 목록입니다(Django ORM에서 발생한 자료형). 쿼리셋은 데이터베이스로부터 데이터를 읽고, 필터를 걸거나 정렬을 할 수 있습니다. 리스트와 구조는 같지만 파이썬 기본 자료구조가 아니기 때문에 파이썬에서 읽고 쓰기 위해 자료형 변환(Casting)을 해줘야 합니다.
장고 쉘(shell)
로컬 콘솔에서 아래 명령을 입력합니다.
/mnt/d/django/mysite$ python3 manage.py shell
Python 3.8.10 (default, Nov 26 2021, 20:14:08)
[GCC 9.3.0] on linux
Type "help", "copyright", "credits" or "license" for more information.
(InteractiveConsole)
>>>
장고 인터랙티브 콘솔(interactive console)로 들어왔습니다. 파이썬 프롬프트와 비슷하지만, 장고의 명령어를 수행할 수 있습니다. 물론 파이썬의 모든 명령어를 여기서 사용할 수 있습니다.
모든 객체 조회하기
아래 명령어로 입력했던 글들을 출력합니다.
>>> from blog.models import Post
>>> Post.objects.all()
<QuerySet [<Post: 첫번째 블로그입니다.>]>
장고 관리자 인터페이스로 생성했던 글 목록이 출력됩니다.
객체 생성하기
데이터베이스에 새 글 객체를 저장하는 방법에 대해 알아봅시다. 먼저 User 모델과 Post 모델을 import 합니다.
사용자이름(username)이 '홍길동'인 User 인스턴스를 가져옵니다.
>>> from blog.models import Post
>>> from django.contrib.auth.models import User
>>> auth = User.objects.get(username='홍길동')
>>> Post.objects.create(author=auth, title='second title', text='두번째 블로그입니다.')
<Post: second title>
다시 입력했던 모든 글들을 출력해 봅니다.
>>> Post.objects.all()
<QuerySet [<Post: 첫번째 블로그입니다.>, <Post: second title>]>
글 더 추가하기
몇 개 글을 더 추가해서 잘 작동하는지 확인합니다.
필터링하기
쿼리셋의 중요한 기능은 데이터를 필터링입니다. 예를 들어, '홍길동'이라는 사용자가 작성한 모든 글을 찾고 싶다고 가정합니다. 이런 경우 Post.objects.all()에서 all대신, filter를 사용합니다. 쿼리셋 안에 있는 괄호 안에 원하는 조건을 넣어줍니다.
작성자(author)가 auth 인 조건을 입력합니다.
>>> Post.objects.filter(author=auth)
<QuerySet [<Post: 첫번째 블로그입니다.>, <Post: second title>]>
모든 글들 중, 제목(title)에 '블로그'이라는 글자가 들어간 글들만을 뽑아냅니다.
>>> Post.objects.filter(title__contains='블로그')
<QuerySet [<Post: 첫번째 블로그입니다.>]>
title와 contains 사이에 있는 밑줄(_)이 2개(__)입니다. 장고 ORM은 필드 이름("title")과 연산자 필터("contains")를 밑줄 2개를 사용해 구분합니다. 밑줄 1개만 입력한다면, FieldError: Cannot resolve keyword title_contains라는 오류가 발생합니다.
게시글 목록을 볼 수 있어요. 게시일(published_date)로 과거에 작성한 글을 필터링하면 목록을 불러올 수 있어요.
command-line
>>> from django.utils import timezone
>>> Post.objects.filter(published_date__lte=timezone.now())
<QuerySet [<Post: 첫번째 블로그입니다.>]>
정렬하기
퀘리셋은 객체 목록을 정렬할 수 있습니다. 이제 created_date필드를 정렬해 봅니다.
>>> Post.objects.order_by('created_date')
<QuerySet [<Post: 첫번째 블로그입니다.>, <Post: second title>]>
-을 맨 앞에 붙여주면 내림차순으로 정렬합니다.
>>> Post.objects.order_by('-created_date')
<QuerySet [<Post: second title>, <Post: 첫번째 블로그입니다.>]>
블로그 글은 각각 다른 장소에 나눠져 있습니다. Post모델은 models.py파일에, post_list모델은 views.py파일에 있습니다.
템플릿 동적 데이터에서는 콘텐츠(데이터베이스 안에 저장되어 있는 모델)를 가져와 템플릿에 넣는것을 수행해 봅니다.
뷰(view)는 모델과 템플릿을 연결하는 역할을 합니다. post_list를 뷰에서 보여주고 이를 템플릿에 전달하기 위해서는, 모델을 가져와야 합니다. 뷰가 템플릿에서 모델을 선택하도록 만들어야 합니다.
blog/views.py파일을 열어봅니다.
/mnt/d/django/mysite/blog/views.py
from django.shortcuts import render
def post_list(request):
return render(request, 'blog/post_list.html', {})
models.py 파일에 정의된 모델을 가져기 위해 from .models import Post을 추가합니다.
from 다음에 있는 마침표(.)는 현재 디렉토리 또는 애플리케이션을 의미합니다. 동일한 디렉터리 내 views.py, models.py파일이 있기 때문에 . 파일명 (.py확장자를 붙이지 않아도)으로 내용을 가져올 수 있습니다.
Post모델에서 블로그 글을 가져오기 위해서는 쿼리셋(QuerySet)을 추가하고, 글 목록을 게시일 published_date기준으로 정렬하기 위해 from django.utils import timezone 을 추가합니다.
from django.shortcuts import render
from django.utils import timezone
from .models import Post
def post_list(request):
posts = Post.objects.filter(published_date__lte=timezone.now()).order_by('published_date')
return render(request, 'blog/post_list.html', {})
render함수에는 매개변수 request(사용자가 요청하는 모든 것)와 'blog/post_list.html' 템플릿이 있습니다. {} 이곳에 템플릿을 사용하기 위해 매개변수를 추가합니다. 이 매개변수를 'posts' 라고 정의합니다. {'posts': posts} 이렇게 작성합니다.
: 이전에 문자열이 와야하고, 작은 따옴표''를 양쪽에 붙입니다.
이제 blog/views.py코드는 아래 코드처럼 작성되어야 합니다.
from django.shortcuts import render
from django.utils import timezone
from .models import Post
def post_list(request):
posts = Post.objects.filter(published_date__lte=timezone.now()).order_by('published_date')
return render(request, 'blog/post_list.html', {'posts': posts})
{% for %} 와 {% endfor %}사이에 넣은 모든 것은 목록의 모든 객체를 반복하게 됩니다. 페이지를 새로고침하면 아래 화면이 브라우저에 보여집니다.
{{ post.title }}라던가 {{ post.text }}같이 Post모델에서 정의한 각 필드의 데이터에 접근하기 위해 이 표기법을 사용합니다. 또한 |linebreaksbr같이 파이프 문자(|)도 사용해요. 이건 블로그 글 텍스트에서 행이 바뀌면 문단으로 변환하도록 하라는 의미입니다. 행바뀜을 문단으로 변환하는 필터를 적용한다는 표현을 쓰기도 합니다.
Django(/dʒæŋɡoʊ/ jang-goh/쟁고/장고)는 파이썬으로 만들어진 무료 오픈소스 웹 애플리케이션 프레임워크(web application framework)입니다. 쉽고 빠르게 웹사이트를 개발할 수 있도록 돕는 구성요소로 이루어진 웹 프레임워크입니다.
웹사이트를 구축할 때, 비슷한 유형의 요소들이 항상 필요합니다. 회원가입, 로그인, 로그아웃과 같이 사용자 인증을 다루는 방법이나 웹사이트의 관리자 패널, 폼, 파일 업로드와 같은 것들 말이지요.
그런데 정말 다행이게도, 오래전에 어떤 웹 개발자들이 새로운 웹 사이트를 개발할 때 서로 비슷한 문제들에 직면한다는 것을 깨달았습니다. 그래서 팀을 조직했고요. 바로 사용할 수 있는 구성요소들을 갖춘 여러 프레임워크를 만들었답니다.
Python 웹프레임워크에는 Django, Flask, FastAPI, Sanic 등이 있습니다.
장고도 그중에 하나인 거죠. 새로운 웹사이트를 개발할 때 뒤따르는 간접비용의 부담을 덜어준답니다.
왜 프레임워크가 필요한가요?
장고라는 것이 실제로 무엇을 위한 것인지 이해하기 위해서는 서버에 대해서 자세히 살펴볼 필요가 있어요. 먼저 서버가 여러분에게 웹 페이지를 제공하길 원하는지 알아야 해요.
편지(request, 요청)가 도착했는지 확인해주는 메일박스(port, 포트)가 있다고 상상해보세요. 이 것은 웹 서버가 해주는 일이에요. 웹 서버는 받은 편지를 읽고 웹 페이지와 함께 답장을 준답니다. 그런데 무언가를 주고 싶을 때는 그 안에 내용이 있어야 하죠. 장고는 그 특정 콘텐츠를 만들 수 있는 역할을 합니다.
누군가가 서버에 웹 사이트를 요청하면 어떤 일이 벌어질까요?
웹 서버에 요청이 오면 장고로 전달됩니다. 장고 urlresolver는 웹 페이지의 주소를 가져와 무엇을 할지 확인합니다.(urlresolver는 웹 사이트 주소인 URL(Uniform Resource Locator)을 통해 이해합니다). 패턴 목록을 가져와 URL과 맞는지 처음부터 하나씩 대조해 식별합니다. 만약 일치하는 패턴이있으면, 장고는 해당 요청을 관련된 함수(view)에 넘겨줍니다
집배원을 생각해보세요. 집배원은 거리를 걸으며 집집이 편지와 대조해서 주소와 번지를 확인합니다. 주소와 번지가 일치하면 그곳에 편지를 배달합니다. urlresolver가 바로 집배원과 같은 역할을 합니다.
모든 일들은 view 함수에서 처리됩니다: 특정 정보를 데이터베이스에서 찾을 수 있습니다. 그런데 만약 사용자가 데이터를 바꿔달라고 수정을 요청한다면 어떻게 될까요? "제 직업에 대한 설명을 바꿔주세요."와 같은 편지를 받았다고 생각해봅시다. view함수는 수정할 수 있는 권한이 있는지 확인하고 나서, 직업에 대한 설명을 수정해 다시 답장을 주겠지요. "완료했습니다!" 라고요. 그러고 나서 view는 답장을 생성하여, 장고는 그 답장을 그 사용자의 웹 브라우저에 보내주는 역할을 합니다.
Django의 동작 원리 및 구조
Django의 구조
Model (데이터 관리)
Template(사용자가 보면 화면, UI)
View(중간 관리자)
MTV Pattern
웹 프로그램 개발 시 일반적으로 언급되는 MVC(Model-View-Controller)패턴은 데이터, 사용자 인터페이스, 데이터를 처리하는 로직을 구분해서 한 요소가 다른 요소들에 영향을 주지 않도록 설계하는 방식이다. 이런 방식으로 개발을 진행하면 UI 디자이너와 개발자는 독립적인 영역에서 개발이 가능하게 된다.
Django 프레임 워크에서는 Model=ModelView=Template, Controller=View, 라고 표현하며 MVC를 MTV라고 한다.
앞서 말한 것 처럼 Model은 데이터 베이스에 저장되는 데이터의 영역, Template은 사용자에게 보여지는 영역, View는 실질적으로 프로그램 로직이 동작하여 적절한 처리 결과를 Template에게 전달하는 역할을 수행 한다.
Django의 동작 원리
동작 원리
클라이언트로 부터 요청(Request)를 받으면 URLconf 모듈을 이용하여 URL을 분석한다.
URL 분석 결과를 통해 해당 URL에 매칭되는 View를 실행한다.
View는 자신의 로직을 실행하고, 데이터베이스 처리가 필요하면 Model을 통해 처리하고 그결과를 반환 받는다.
View는 자신의 로직 처리가 끝나면 Template을 사용하여 클라이언트에 전송할 HTML 파일을 생성한다.