웹 서버 프로그램의 기능의 주체는 미리 준비된 정보를 이용자(클라이언트)의 요구에 응답해 보내는 것이다. 그 때문에 서버 프로그램 그룹에서는 정보를 그 장소에서 동적으로 생성하고 클라이언트에 송신하려하는 조합을 작성하는 것이 불가능했다. 서버 프로그램에서 다른 프로그램을 불러내고, 그 처리 결과를 클라이언트에 송신하는 방법이 고안되었다. 이를 실현하기 위한 서버 프로그램과 외부 프로그램과의 연계법을 정한 것이 CGI이다
여러 언어들이 사용자들의 다양한 요청을 이해할 수 있게 "이 문(인터페이스)을 지나면 이러한 형태가 됩니다"하고 정해놓은 규약이자 프로그램
WSGI Web Server Gateway Interface
파이썬 스크립트가 웹 서버와 통신하기 위해 만들어진 인터페이스
CGI를 설명한 것과 같이 웹서버에서의 요청을 해석하여 파이썬 응용프로그램에 던지는 역할
Gunicorn이나 uWSGI는 Apache나 nginx로 들어오는 HttpRequest를 Python이 이해할 수 있게 동시통역하여 던져주는 애들
project를 시작할때 만든 settings.py의 위치에서 settings package를 생성
settings.py 이동
settings.py 를 base.py로 이름 변경 후 settings 패키지로 이동
local, deploy 파일 생성
settings 패키지안에 local.py, deploy.py 생성
환경변수 분리
공통 사항은 base.py
apps
static path ...
로컬 환경은 local.py
local에서 사용하는 db
debug, allowed host 등
배포 환경은 deploy.py
docker secrets 에서 가져오는 비밀번호 및 키
배포환경에서 사용하는 db
# local.py
import os, environ
from .base import *
env = environ.Env(
# set casting, default value
DEBUG=(bool, False)
)
# reading .env file
environ.Env.read_env(env_file=os.path.join(BASE_DIR, ".env"))
# Quick-start development settings - unsuitable for production
# See https://docs.djangoproject.com/en/3.2/howto/deployment/checklist/
# SECURITY WARNING: keep the secret key used in production secret!
SECRET_KEY = env("SECRET_KEY")
# SECURITY WARNING: don't run with debug turned on in production!
DEBUG = env("DEBUG")
ALLOWED_HOSTS = ["*"]
# Database
# https://docs.djangoproject.com/en/3.2/ref/settings/#databases
DATABASES = {
"default": {
"ENGINE": "django.db.backends.sqlite3",
"NAME": BASE_DIR / "db.sqlite3",
}
}
# deploy.py
import os, environ
from .base import *
# docker secrets에서 가져오는 비밀키
def read_secret(secret_name):
file = open("/run/secrets/" + secret_name)
secret = file.read()
secret = secret.rstrip().lstrip()
file.close()
return secret
env = environ.Env(
# set casting, default value
DEBUG=(bool, False)
)
# reading .env file
environ.Env.read_env(env_file=os.path.join(BASE_DIR, ".env"))
# Quick-start development settings - unsuitable for production
# See https://docs.djangoproject.com/en/3.2/howto/deployment/checklist/
# SECURITY WARNING: keep the secret key used in production secret!
SECRET_KEY = read_secret("DJANGO_SECRET_KEY")
# SECURITY WARNING: don't run with debug turned on in production!
DEBUG = False
ALLOWED_HOSTS = ["*"]
# Database
# https://docs.djangoproject.com/en/3.2/ref/settings/#databases
DATABASES = {
"default": {
"ENGINE": "django.db.backends.mysql",
"NAME": "django",
"USER": "django",
"PASSWORD": read_secret("MYSQL_PASSWORD"),
"HOST": "mariadb",
"PORT": "3306",
}
}
manage.py 설정
아래의 os.environ.setdefault 부분 수정
두번째 인자에 python manage.py runserver 명령어를 사용할때 실행할 파일 연결
# root 폴더의 manage.py
#!/usr/bin/env python
"""Django's command-line utility for administrative tasks."""
import os
import sys
def main():
"""Run administrative tasks."""
os.environ.setdefault("DJANGO_SETTINGS_MODULE", "config.settings.local")
try:
from django.core.management import execute_from_command_line
except ImportError as exc:
raise ImportError(
"Couldn't import Django. Are you sure it's installed and "
"available on your PYTHONPATH environment variable? Did you "
"forget to activate a virtual environment?"
) from exc
execute_from_command_line(sys.argv)
if __name__ == "__main__":
main()
# WHERE id = 14;
Entry.objects.get(id__exact=14)
# WHERE id IS NULL;
Entry.objects.get(id__exact=None)
iexact
대소문자의 구분없이 정확하게 일치하는 값을 검색.
None값을 넣게 되면 SQL의 IS NULL구문을 사용 하는 것과 같다.
# WHERE name ILIKE 'beatles blog';
Blog.objects.get(name__iexact='beatles blog')
# WHERE name IS NULL;
Blog.objects.get(name__iexact=None)
contains
대소문자를 구분하고 문자열을 포함하고 있는 것을 검색.
# WHERE headline LIKE '%Lennon%';
Entry.objects.get(headline__contains='Lennon')
icontains
대소문자를 구분하지 않고 문자열을 포함하고 있는 것을 검색.
# WHERE headline ILIKE '%Lennon%';
Entry.objects.get(headline__icontains='Lennon')
in
주어진 iterable한 객체(리스트, 튜플, 쿼리셋)에 포함되어 있는지 검색.
# WHERE id IN (1,3,4);
Entry.objects.filter(id__in=[1,3,4])
쿼리셋을 사용하여 동적으로도 검색이 가능하다.
# WHERE blog.id IN (SELECT id FROM ... WHERE NAME LIKE='%Cheddar%')
inner_qs = Blog.objects.filter(name__contains='%Cheddar%')
entries = Entry.objects.filter(blog__in=inner_qs)
values() 나 values_list()를 쓸 __in에 쓸 경우 결과 값은 하나의 필드만을 반환 해야 한다.
만약 두개의 필드로 반환하는 values()를 할 경우 TypeError를 raise를 시킬 것이다.
# Bad code! Will raise a TypeError.
inner_qs = Blog.objects.filter(name__contains='Ch').values('name', 'id')
entries = Entry.objects.filter(blog__name__in=inner_qs)
중첩된 쿼리를 사용 할 때는 데이터베이스의 성능 특성을 이해하고 써야 한다. 일부 데이터베이스의 경우 중첩 쿼리를 잘 처리 하지 못한다. 그래서 __in에 대한 list 목록을 미리 추출하여 두번째 쿼리에 해당 list를 사용 하는것이 더 좋다.
두번째 쿼리에서 list()를 사용하여 첫번째 쿼리를 강제로 실행. 중첩 쿼리를 실행하지 않게 한다. - in을 실행하면서 Entry를 계속 검색하는데 거기서 values 쿼리가 중복하여 실행된다. 그것을 방지.
gt, gte
gt : ~보다 큰.
gte : ~보다 크거나 같은.
# WHERE id > 4;
Entry.objects.filter(id__gt=4)
# WHERE id >= 4;
Entry.objects.filter(id__gte=4)
lt, lte
lt : ~보다 작은.
lte : ~보다 작거나 같은.
# WHERE id < 4;
Entry.objects.filter(id__lt=4)
# WHERE id <= 4;
Entry.objects.filter(id__lte=4)
startswith, istartswith
startswith : 대소문자를 구분하여 시작하는 문자열.
istartswith: 대소문자를 구분하지 않고 시작하는 문자열.
# WHERE headline LIKE 'Lennon%';
Entry.objects.filter(headline__startswith='Lennon')
# WHERE headline ILIKE 'Lennon%';
Entry.objects.filter(headline__istartswith='Lennon')
endswith, iendswith
endswith : 대소문자를 구분하여 끝나는 문자열.
iendswith: 대소문자를 구분하지 않고 끝나는 문자열.
# WHERE headline LIKE '%Lennon';
Entry.objects.filter(headline__endswith='Lennon')
# WHERE headline ILIKE '%Lennon';
Entry.objects.filter(headline__iendswith='Lennon')
range
범위를 안에 있는지 검색.
start_date = date(2005, 1, 1)
end_date = date(2005, 3, 31)
# WHERE pub_date BETWEEN '2005-01-01' and '2005-03-31';
Entry.objects.filter(pub_date__range(start_date, end_date))
SQL에서 BETWEEN을 날짜, 숫자, 문자에서 사용하는 것처럼 range도 사용 가능하다. 날짜가 있는 DateTimeField를 필터링 하는 경우 마지막 날의 항목은 포함하지 않는다. 왜냐하면 주어진 날짜의 오전 0시로 경계가 설정되기 때문.
WHERE pub_date BETWEEN '2005-01-01 00:00::00' and '2005-03-31 00:00:00';
일반적으로 dates와 datetimes를 섞어쓰면 안된다.
date
datetime 필드의 경우 값을 날짜로 변환한다. 추가 체인이 가능하고, date값을 사용 한다.
date 및 datetime의 필드에서 년도가 정확히 일치하는 것을 검색한다. 추가 체인 필드 검색이 가능하고 정수를 나타낸다.
# WHERE pub_date BETWEEN '2005-01-01' AND '2005-12-31';
Entry.objects.filter(pub_date__year=2005)
# WHERE pub_date >= '2005-01-01';
Entry.objects.filter(pub_date__year__gte=2005)
month
date 및 datetime의 필드에서 달이 정확히 일치하는 것을 검색한다. 추가 체인 필드 검색이 가능하고 정수1 부터 12까지 나타낸다.
# WHERE EXTRACT('month' FROM pub_date) = '12';
Entry.objects.filter(pub_date__month=12)
# WHERE EXTRACT('month' FROM pub_date) >= '6';
Entry.objects.filter(pub_date__month__gte=6)
day
date 및 datetime의 필드에서 일이 정확히 일치하는 것을 검색한다. 추가 체인 필드 검색이 가능하고, 정수를 나타낸다.
# WHERE EXTRACT('day' FORM pub_date) = '3';
Entry.objects.filter(pub_date__day=3)
# WHERE EXTRACT('day' FORM pub_date) >= '3';
Entry.objects.filter(pub_date__day__gte=3)
week
date 및 datetime의 필드에서 ISO-8601의 기준에 따라 주 번호(1-52 또는 53)을 반환한다. 즉 월요일에 시작하는 주 및 첫번째 주는 목요일에 시작한다.
datetime 및 time 필드에서 시간이 정확하게 일치하는 것을 검색한다. 추가 체인 필드 검색이 가능하며, 0에서 23사이를 나타낸다.
# WHERE EXTRACT('hour' FROM timestamp) = '23';
Event.objects.filter(timestamp__hour23)
# WHERE EXTRACT('hour' FROM time) = '5';
Event.objects.filter(time_hour=5)
# WHERE EXTRACT('hour' FROM timestamp) >= '12';
Event.objects.filter(timestamp__hour__gte=12)
minute
datetime 및 time 필드에서 분이 정확하게 일치하는 것을 검색한다. 추가 체인 필드 검색이 가능하며, 0에서 59사이를 나타낸다.
# WHERE EXTRACT('minute' FROM timestamp) = '29';
Event.objects.filter(timestamp__minute=29)
# WHERE EXTRACT('minute' FROM time) = '46';
Event.objects.filter(time__minute=46)
# WHERE EXTRACT('minute' FROM timestamp) >= '29';
Event.objects.filter(timestamp__minute__gte=29)
second
datetime 및 time 필드에서 초가 정확하게 일치하는 것을 검색한다. 추가 체인 필드 검색이 가능하며, 0에서 59사이를 나타낸다.
# WHERE EXTRACT('second' FROM timestamp) = '31';
Event.objects.filter(timestamp__second=31)
# WHERE EXTRACT('second' FROM time) = '2';
Event.objects.filter(time__second=2)
# WHERE EXTRACT('second' FROM timestamp) >= '31';
Event.objects.filter(timestamp__second__gte=31)
isnull
IS NULL 과 IS NOT NULL에 대한 True, False값을 검색한다.
# WHERE pub_date IS NULL;
Entry.objects.filter(pub_date__isnull=True)
regex, iregex
regex : 대소문자를 구분하여 정규식을 검색.
iregex : 대소문자를 구분하지 않고 정규식을 검색.
# WHERE title REGEXP '^(An?|The) +';
Entry.objects.get(title__regex=r'^(An|The) +')
# WHERE title REGEXP '(?i)^(an?|the) +'l;
Entry.objects.get(title__iregex=r'^(an?|the) +'
함수를 받아 명령을 추가한 뒤 이를 다시 함수의 형태로 반환하는 함수이다. 함수의 내부를 수정하지 않고 기능에 변화를 주고 싶을 때 사용한다 . 일반적으로 함수의 전처리나 후처리에 대한 필요가 있을때 사용을 한다. 또한 데코레이터를 이용해, 반복을 줄이고 메소드나 함수의 책임을 확장한다
decorator는 원래 작업의 앞 뒤에 추가적인 작업을 손쉽게 사용 가능하도록 도와주는 역할
데코레이터의 구조
함수형
def out_func(func): # 기능을 추가할 함수를 인자로
def inner_func(*args, **kwargs):
return func(*args, **kwargs)
return inner_func
example
# decorator 정의
def decorator(func):
def wrapper(*args, **kwargs):
print('전처리')
print(func(*args, **kwargs))
print('후처리')
return wrapper
# decorator 사용
@decorator
def example():
return '함수'
#############
example()
'''''''''
전처리
함수
후처리
'''''''''
클래스형
class Decorator:
def __init__(self, function):
self.function = function
def __call__(self, *args, **kwargs):
return self.function(*args, **kwargs)
example
class Decorator:
def __init__(self, function):
self.function = function
def __call__(self, *args, **kwargs):
print('전처리')
print(self.function(*args, **kwargs))
print('후처리')
@Decorator
def example():
return '클래스'
example()
'''''''''
전처리
클래스
후처리
'''''''''
django decorator
FBV
from django.contrib.auth.decorators import login_required
from django.shortcuts import render
# 방법 1
@login_required
def post_create(request):
# 생략
return render(request, 'core/index.html')
# 방법 2
def post_create(request):
# 생략
return render(request, 'core/index.html')
post_create = login_required(post_create)
CBV
방법 3 추천
# 방법 1
from django.contrib.auth.decorators import login_required
from django.views.generic import TemplateView
class MyTemplateView(TemplateView):
template_name= "core/index.html"
index = MyTemplateView.as_view()
index = login_required(index)
# 방법 2
from django.utils.decorators import method_decorator
class MyTemplateView(TemplateView):
template_name= "core/index.html"
@method_decorator(login_required)
def dispatch(self, *args, **kwargs):
return super().dispatch(*args, **kwargs)
index = MyTemplateView.as_view()
# 방법 3
@method_decorator(login_required, name="dispatch")
class MyTemplateView(TemplateView):
template_name= "core/index.html"
index = MyTemplateView.as_view()