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
나. 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
•
(기초편) 파이썬 웹 프로그래밍, 김석훈, 한빛미디어
•
(실전편) 파이썬 웹 프로그래밍, 김석훈, 한빛미디어
•
mysql client 설치 오류, https://velog.io/@dwenup/M1-Django-mysql-연동-error-Did-you-install-mysqlclient
•
•
Processing Form Data, https://newbedev.com/python-upload-a-excel-file-to-database-using-django-code-example
•
장고 앱 간 모델 이동, https://realpython.com/move-django-model/