Search
📘

Django Web Programming - 김석훈

1. Django 웹 프레임워크

가. Django란

1) 개요

파이썬 웹 프레임워크 중 하나로 보안이 우수하고 유지보수가 편리한 웹사이트를 신속하게 개발 가능 - MDN -

2) 특징

Batteries Included: 개발자들이 원하는 것은 결국 완전한 무언가의 일부이므로 미리 달려간 사람이 만들어 놓은 배터리를 적극 활용할 것
→ 후발주자는 충분히 준비된 배터리를 이용하여 빠르게 개발 가능
opinionated: 올바른 방법에 대해 Django의 분명한 의견이 존재함
→ Django의 의견에 따른다면 쉽고 빠르게 개발할 수 있음

3) 구조

MVT(Model View Template) 아키텍쳐

나. Django 구성 요소

1) URL

특정 요청을 뷰로 알맞게 전달
→ path, re_path의 첫번째 매개변수는 경로, 두번째 매개변수는 호출 함수
urlpatterns = [ path('admin/', admin.site.urls), path('book/<int:id>/', views.book_detail, name='book_detail'), path('catalog/', include('catalog.urls')), re_path(r'^([0-9]+)/$', views.best), ]
Python
복사

2) View

요청 수신, 처리, 응답 전송
→ index 함수는 최소 뷰 함수로, HttpRequest의 인스턴스를 매개변수로 받아서 HttpResponse를 반환함
## filename: views.py (Django view functions) from django.http import HttpResponse def index(request): # Get an HttpRequest - the request parameter # perform operations using information from the request. # Return HttpResponse return HttpResponse('Hello from Django!')
Python
복사

3) Model

데이터 모델 정의: 파이썬 객체 활용하여 데이터를 관리하고 쿼리함
→ 데이터베이스에 직접 접근 X, Django에서 데이터베이스와 파이썬 객체 간 연동작업 처리함
## filename: views.py from django.shortcuts import render from .models import Team def index(request): # "U09"에 정확히 일치하는 것을 찾아서 반환함 list_teams = Team.objects.filter(team_level__exact="U09") context = {'youngest_teams': list_teams} return render(request, '/best/index.html', context)
Python
복사

4) Template

HTML 템플릿: 데이터 렌더링, 동적으로 생성될 데이터에 대한 자리 표시자를 사용하여 HTML의 구조를 만들 수 있음
## filename: best/templates/best/index.html <!DOCTYPE html> <html lang="en"> <head> <meta charset="utf-8"> <title>Home page</title> </head> <body> {% if youngest_teams %} <ul> {% for team in youngest_teams %} <li>{{ team.team_name }}</li> {% endfor %} </ul> {% else %} <p>No teams are available.</p> {% endif %} </body> </html>
Python
복사

다. 설치

이하 링크의 첫번째 챕터 참고

라. 장고 어플리케이션 개발 방식

1) MVT 패턴

MVT 패턴에 따른 웹 어플케이션의 처리 순서
→ 클라이언트 요청 발생 시 'URLconf'에서 URL 분석
→ URL 분석 결과에 따라 해당 요청을 담당하여 처리할 'View'가 결정됨
→ 'View'의 로직 실행 중 DB처리가 필요하다면 'Model'을 통해 처리하고 그 결과를 'View'에게 반환
→ 'View'는 자신의 로직에 대한 처리가 완료되면 'Template'으로 클라이언트에게 전달할 HTML 파일 생성
→ 'View'는 최종 결과물로써 HTML 파일을 클라이언트에게 응답함

2) Model - 데이터베이스 정의

하나의 모델 클래스는 하나의 테이블에 매핑됨
→ 모델과 테이블이 1:1 관게??
모델 클래스는 models.py에 정의함
Primary Key는 클래스에서 정의하지 않아도 장고에서 자동으로 부여함
→ 개발자 직접 부여 가능

3) URLconf - URL 정의

urls.py 에 URL과 이를 처리하는 함수(View)를 맵핑하여 정의함
→ 이러한 맵핑을 URLconf로 지칭
클라이언트의 요청 경로(URL)이 urls.py에 정의된 URL 패턴에 포함되는 지 확인

4) View - 로직 정의

함수 또는 클래스의 메소드로 작성됨
웹 요청을 받고 응답을 반환함
일반적으로 views.py에 작성하지만 파이썬 경로에 있다는 전제하에 다른 파일에 작성 가능

5) Template - 화면 UI 정의

장고 자체 템플릿 엔진을 활용하여 화면 구성
쉽게 이해할 수 있는 문법 구조
settings.py의 TEMPLATES 및 INSTALLED_APPS에 지정된 경로에 템플릿 파일이 위치해야 함
→ 장고가 찾을 수 있도록 설정

6) MVT 개발 순서

일반적으로 독립성이 강한 모델을 우선 작업함
뷰가 간단한 경우 뷰 → 템플릿 순서로 작업
뷰가 복잡한 경우 템플릿 → 뷰 순서로 작업
→ 구체적으로 뷰에 어떤 부분이 필요한 지 알 수 있음

마. 프로젝트 뼈대 생성

1) 프로젝트, 어플리케이션 생성

이하 링크 참고

2) 설정 파일 변경

프로젝트에 포함된 모든 어플리케이션은 설정 파일에 등록되어야 함
→ 설정클래스(PollsConfig)를 등록해야 장고가 찾을 수 있음
settings.py에서 데이터베이스 변경 가능
타임존도 변경 가능

3) 관리자(Super user) 계정 생성

python3 manage.py createsuperuser
Shell
복사

바. 모델 작업

1) 모델 생성

models.py에 모델 생성
class Question(models.Model): question_text = models.CharField(max_length=200) pub_date = models.DateTimeField('date published') def __str__(self): return self.question_text class Choice(models.Model): choice_text = models.CharField(max_length=200) votes = models.IntegerField(default=0) # CASCADE: 부모 객체 삭제 시 자식 객체도 함께 삭제됨 question = models.ForeignKey(Question, on_delete=models.CASCADE) def __str__(self): return self.choice_text
Python
복사
admin.py에 위의 모델 등록
→ admin.site.register()
from django.contrib import admin from polls.models import Question, Choice # Register your models here. admin.site.register(Question) admin.site.register(Choice)
Python
복사

2) DB에 반영

모델 작업 시 migration 작업 필수
python3 manage.py makemigrations python3 manage.py migrate
Python
복사

사. 뷰 및 템플릿 작업

1) URLconf 작업

mysite/urls.py와 polls/urls.py로 분리해서 URL 작업 권장
→ 도메인에 따라 URL을 분류하면 추후 재사용 용이
# mysite/urls.py from django.contrib import admin from django.urls import path, include urlpatterns = [ path('admin/', admin.site.urls), path('polls/', include('polls.urls')), ]
Python
복사
# polls/urls.py from django.urls import path from . import views urlpatterns = [ path('', views.index, name='index'), path('<int:question_id>/', views.detail, name='detail'), path('<int:question_id>/results/', views.results, name='results'), path('<int:question_id>/vote/', views.vote, name='vote'), ]
Python
복사

2) 뷰 함수 + 템플릿

뷰함수와 템플릿은 서로에게 영향을 미치므로 함께 묶어서 작업하는 것이 일반적
→ 템플릿을 먼저 작성하는 경우, 어떤 변수가 필요한지에 대해 명확해짐

2. Django 핵심 컴포넌트

가. Model

1) Meta 내부 클래스

모델에 필요한 속성 중 필드(또는 컬럼)를 제외한 것을 Meta 내부 클래스에 정의
→ 필드는 모델 클래스의 속성으로 정의함
ordering: DB에서 가져온 데이터를 출력할 때 영향을 주는 속성
→ DB 내부의 정렬에 영향을 주지 않음
db_table: DB에 저장되는 테이블 이름 지정하는 속성

2) Manager 속성

DB 테이블 레벨의 동작에 영향을 줌
→ Manager Class를 통해 DB Query 생성
→ 모델 클래스의 속성 중 유일하게 필드에 맵핑되지 않음
→ default는 objects
→ 모델 클래스에서만 접근 가능(모델 인스턴스로 접근 불가)
# Album: 모델 클래스(모델 인스턴스 객체 아님) # objects: Manager 속성명 # all(): Manager class method Album.objects.all()
Python
복사
복수의 Manger 속성 정의
→ 디폴트 매니저 속성을 정의하고 이를 기준으로 새로운 Manager 속성 정의 가능
# Album.objects.all(): 모든 앨범 레코드 반환 # Album.second_objects.all(): 소유자가 bae인 앨범 레코드 반환 class SecondAlbumManager(models.Manager): def get_queryset(self): return super().get_queryset().filter(owner__username = 'bae') class Album(models.Model): owner = models.ForeignKey('auth.User', on_delete=models.CASCADE) objects = models.Manager() # 디폴트 매니저 second_objects = SecondAlbumManager() # 추가 매니저
Python
복사

3) 모델 간 관계

관계에 대한 장고 ORM의 자동 정의 기능
→ 관계는 양방향 개념이지만 한쪽에서 관계를 정의하면 반대쪽의 관계는 장고가 자동으로 정의함
→ 한쪽 관계 변경에 따라 다른 한쪽의 관계에도 자동으로 영향이 미치므로 주의해야 함
1:N: 'N 모델'에 ForeignKey 필드 정의하고 ForeignKey의 매개변수로 '1 모델'의 모델 지정
→ ForeignKey의 매개변수로 '1 모델'과 on_delete은 필수로 할당해야 함
class Album(models.Model): name = models.CharField(~~) owner = models.ForeignKey('auth.User', on_delete=models.CASCADE)
Python
복사
N:M: 한쪽 모델에만 ManyToManyField 필드 정의
→ 양쪽에 ManyToManyField 필드 정의하면 안됨
class Publication(models.Model): title = models.CharField(max_length=30) albums = models.ManyToManyField(Album) class Album(models.Model): name = models.CharField(~~) owner = models.ForeignKey('auth.User', on_delete=models.CASCADE)
Python
복사
1:1: OneToOneField 필드를 한쪽에만 정의함
→ ForeignKey의 매개변수로 '1 모델'과 on_delete은 필수로 할당해야 함
class Restaurant(models.Model): place = models.OneToOneField(Place, on_delete=models.CASCADE) name = models.CharField(max_length=50) serves_pizza = models.BooleanField(default=False)
Python
복사

4) 관계매니저(RelatedManager)

정의: 모델 간 집합 관계를 다루기 위한 Manager Class
→ 모델 간 관계에 관한 기능 및 DB Query 담당
1:1 관계: 집합 관계가 아니므로 관계 매니저가 사용되지 않음
1:N 관계: '1 모델'에서 'N 모델' 방향으로 접근 시 사용
# album_set이 관계 매니저 클래스의 인스턴스 user1.album_set
Python
복사
→ 'N 모델'에서 '1 모델' 접근의 경우, ForeignKey 활용하면 됨
N:M 관계: 양쪽 방향으로 관계 매니저 사용 가능
관계 매니저 메소드
→ 관계 매니저 메소드 사용 시 바로 DB에 반영되므로 save() 메소드 호출 불필요
→ Blog:Entry=1:N 관계라는 가정 하에 이하 예제 명시함
→ add(**objs, bulk=True): 매개변수로 전달된 모델 인스턴스 객체를 관계 객체의 집합에 추가하고 DB에 자동반영
# bulk=True이면 QuerySet.update() 호출 # bulk=False이면 e.save() 호출 b = Blog.objects.get(id=1) e = Entry.objects.get(id=234) b.entry_set.add(e)
Python
복사
→ create(**kwargs): 새로운 모델 인스턴스 객체 생성 및 DB에 자동반영
b = Blog.objects.get(id=1) e = Entry.objects.create( blog=b, headline='Hello', )
Python
복사
→ remove(**objs, bulk=True): 매개변수로 전달한 모델 인스턴스 객체와의 관계 제거
→ clear(bulk=True): 관계 객체 집합의 모든 인스턴스 객체 삭제
→ set(objs, bulk=True, clear=False): 관계 객체 집합의 내용 변경
# clear=False, 기본값, 기존 항목 체크하여 선택적 갱신 # clear=True, 기존 항목 모두 제거 및 매개변수로 전달된 값 추가 new_list = [obj1, obj2, obj3] e.related_set(new_list)
Python
복사

5) ORM Filter

이하 링크의 ‘2-가-1) filter’ 참고

나. View

1) 클래스형 뷰의 장점

상속과 효율적인 메소드 구분
HTTP 메소드 처리 작업을 효율적으로 수행
→ View(상위 클래스)에 미리 정의된 인스턴스 메소드(HTTP 메소드 처리)를 오버라이딩하여 작업 가능
→ HTTP 요청이 들어오면 View에 정의된 dispatch() 호출에 따라 요청 내 HTTP 메소드를 확인하고 구분하여 해당 HTTP 메소드를 처리할 수 있는 인스턴스 메소드를 호출하도록 중계함

2) 클래스형 제네릭 뷰

웹 개발에 필요한 필수로직을 미리 개발하여 쉽게 상속 받아 사용할 수 있는 뷰
제네릭 뷰는 클래스형 뷰로 구성됨
Base View: 기본 제네릭 뷰, 다른 제네릭 뷰의 부모 클래스
Generic Display View: 객체 조회(리스트 및 특정 객체의 상세 정보)
Generic Edit View: 폼으로 객체에 갱신(생성, 수정, 삭제)
Generic Data View: 날짜 기반의 연/월/일 페이지로 구분

3) 속성 오버라이딩(제네릭 뷰)

model: 작업 대상 데이터가 들어 있는 모델 지정
→ model 대신 queryset 속성으로도 지정 가능
# 두 가지 표현은 동일 의미 model = Bookmark queryset = Bookmark.objects.all()
Python
복사
queryset: model과 동일한 기능이지만 우선순위가 높음
→ queryset 속성이 지정되면 model 속성은 무시함
기타 등등

4) 메소드 오버라이딩(제네릭 뷰)

get_queryset(): 출력 대상인 객체 리스트 또는 객체 반환
→ 디폴트는 queryset 속성값을 반환함
→ queryset 속성값이 할당되지 않았다면 model 클래스의 all() 메소드를 호출하여 queryset 객체를 생성 후 반환해야 함

5) Mixin 활용

3. Django Middleware

가. 동작 흐름

1) 흐름도

Middleware는 HttpRequest에 맵핑된 view function이 실행되기 전에 두 개의 hook이 호출됨
→ process_request, process_view
view function 실행 후 세 가지의 hook이 호출됨
→ process_exception, process_template_response, process_response

나. Hooks Before View

다. Hooks After View

4. Django Logger

가. 동작 흐름

1) Loggers → (Filters) → Handlers → Formatters

나. Loggers

1) Logger란

Logger는 Logging System 전체에서 처음으로 메시지를 기록함
→ 각각의 Logger는 Logging System에서 Bucket으로 명명됨
→ Logger에 기록되는 메시지는 Log Record로 명명됨

2) Log Level 처리

Logger와 Log Record에 Log Level이 부여됨
Log Record의 Level이 Logger의 Level과 같거나 높다면 추가적으로 처리될 수 있음
→ Log Record의 Level이 Logger의 Level 아래라면 해당 Log Record는 무시됨

3) Log Levels

DEBUG: 디버깅 목적의 낮은 수준의 시스템 정보
INFO: 일반적인 수준의 시스템 정보
WARNING: 작은 문제와 연관된 정보
ERROR: 중요 문제와 연관된 정보
CRITICAL: 매우 중요한 문제와 연관된 정보

나. Handlers

1) Handler란

Handdler는 각각의 Log Record에 대한 행동을 결정함

2) Log Level 처리

Handler에도 Log Level이 부여됨
Log Level에 맞는 Log Record에 대해서만 처리함
→ Logger와 달리 Handler에 부여된 Log Level과 일치하는 Log Record만 처리함

3) Muliple Handlers

하나의 Logger에는 Log Level별로 Handler를 설정할 수 있음

다. Filters

1) Filter란

Filter는 Logger나 Handler에 설정하여 Log Record에 대한 추가적인 제어를 걸 수 있음

2) Logger 단계의 Filtering

Logger가 받은 ERROR Log Level의 Log Record에 대해 특정 조건을 만족하면 Log Level을 WARNING으로 격하할 수 있음

3) Handler 단계의 Filtering

ERROR(Log Level) 중에서도 특정 소스에서 생성되는 Log Record에 대해서만 Handler가 동작하도록 제어 가능

라. Formatters

1) Formatter란

Formatter는 기본적으로 LogRecord Object(provided by python)을 활용하여 Log Record를 기록함

3. 기타

가. MySQL 연동

1) 연동 드라이버 모듈 설치

mysqlclient 모듈 설치를 권장하지만 M1 환경에서 에러 발생에 따라 pymysql 설치
DB 연동 드라이버 모듈 설치
pip3 install pymysql
settings.py에 해당 모듈 import
import pymysql pymysql.install_as_MySQLdb()
Plain Text
복사

2) DB 설정 변경

mysql server 작동 중인 것으로 가정
settings.p에 DB 변경사항
DATABASES = { 'default': { 'ENGINE': 'django.db.backends.mysql', 'NAME': 'test', 'USER': 'root', 'PASSWORD': 'mysql123456' 'HOST': 'localhost', # 로컬환경에서 생략 가능 'PORT': '3306' # 기본포트(3306)인 경우 생략 가능 } }
Python
복사
설정 변경사항 반영
python3 manage.py migrate
Python
복사

3) 작업 확인

관리자(superadmin) 생성
python3 manage.py createsuperuser
Python
복사
서버 동작
python3 manage.py runserver
Python
복사

나. Trouble Shooting

1) package 설치

requirements에 잊고 명시하지 않은 패키지 목록 확인
→ 로컬의 venv 파일 지우고 패키지 목록에 따라 재설치 후 테스트 시도

2) 웹 어플리케이션 서버 확인

gunicorn 정상 동작 확인
→ 로컬에서 관련 명령으로 테스트해볼 것

Reference

(기초편) 파이썬 웹 프로그래밍, 김석훈, 한빛미디어
(실전편) 파이썬 웹 프로그래밍, 김석훈, 한빛미디어
장고 앱 간 모델 이동, https://realpython.com/move-django-model/