Django is a python web framework
웹 프레임워크가 기본적인 구조나 필요한 코드들은 알아서 제공해주기 때문에, 사용자는 그냥 좋은 웹 서비스를 만드는 것에 집중할 수 있다.
django는 파이썬으로 작성된 오픈소스 웹 애플리케이션 프레임워크로, 모델-뷰-컨트롤러 모델 패턴을 따르고 있다.
django에서는 MVC 패턴에서 M, V, C에 해당하는 요소를 각각 Model, Template, View로 지칭한다.
model
template
view
프로젝트 생성
django-admin sartproject 프로젝트이름
서버 실행
python manage.py runserver
애플리케이션 생성
python manage.py startapp 애플리케이션이름
python manage.py startapp 앱이름
) 이후에 settings.py에서 app 등록__init__.py
asgi.py
settings.py
url.py
wsgi.py
__init__.py
admin.py
apps.py
models.py
test.py
views.py
firstpjt/firstpjt/settings.py
# Application definition
INSTALLED_APPS = [
# 1. local apps
'articles',
# 2. 3rd-party apps
# 3. django apps
'django.contrib.admin',
'django.contrib.auth',
'django.contrib.contenttypes',
'django.contrib.sessions',
'django.contrib.messages',
'django.contrib.staticfiles',
]
마지막에 콤마가 붙어있는데, django에서는 이를 trailing comma 라고 부른다. django에서 권장하는 사항이다.
Internalization에서 국제화 및 국지화 설정을 한다
firstpjt/firstpjt/settings.py
# Internationalization
# https://docs.djangoproject.com/en/3.1/topics/i18n/
LANGUAGE_CODE = 'ko-kr'
TIME_ZONE = 'Asia/Seoul'
USE_I18N = True
USE_L10N = True
USE_TZ = True
- LANGAGE_CODE를 'en-us'에서 'ko-kr'로 바꿔준다.
- TIME_ZONE은 'UTC'에서 'Asia/Seoul'로 바꿔준다. (DB Time Zone)
urls.py
는 장고 서버로 http 요청(request)이 들어오면 그 요청이 어디로 가야하는지 인식하고 관련된 함수(view)로 넘겨준다.views.py
에서 만든 함수를 연결시켜 준다.firstpjt/firstpjt/urls.py
urlpatterns = [
path('admin/', admin.site.urls),
]
firstpjt/firstpjt/urls.py
from django.contrib import admin
from django.urls import path
from articles import views
urlpatterns = [
path('admin/', admin.site.urls),
path('index/', views.index),
]
위의 urls.py
에서 클라이언트의 요청에 따라 적절한 view로 연결시켜줌
from 앱이름 import views.py
를 해줘야 적절한 앱의 적절한 view 함수를 연결해줄 수 있다.
참고로, 웹 브라우저에서 url을 입력할 때 혹은 서버에서 링크를 작성할 때 항상 'index/'와 같이 슬래쉬(/)까지 붙여줘야 한다.
APPEND_SLASH = True
로 설정되어 있기 때문이다.path()
함수에서 'index'와 같이 뒤에 슬래쉬를 안 붙이면 브라우저에서 'index'와 같이 끝에 슬래쉬를 붙이지 않고 접근한다고 해도 페이지를 받아올 수 없다. 이는 장고가 요청된 주소의 끝에 '/'가 없음을 감지하고 자동으로 '/'를 붙여서 리다이렉트 하기 때문이다.(HTTP response 순서: 301 → 404)return render(request, '템플릿 경로')
형식으로 작성해서 템플릿을 띄워준다{{ variable }}
render()
를 사용하여 views.py에서 정의한 변수를 template 파일로 넘겨 사용하는 것dot(.)
를 사용하여 변수 속성에 접근할 수 있음 (article.title
)render()
의 세 번째 인자로 {'key': value}
와 같이 딕셔너리 형태로 넘겨주며, 여기서 정의한 key에 해당하는 문자열이 template에서 사용 가능한 변수명이 됨firstpjt/articles/views.py
from django.shortcuts import render
def greeting(request):
foods = ['apple', 'banana', 'coconut', ]
info = {
'name': 'Harry'
}
context = {
'info': info,
'foods': foods,
}
return render(request, 'greeting.html', context)
firstpjt/articles/templates/index.html
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Document</title>
</head>
<body>
<h1>안녕하세요 저는 {{ info.name }}입니다.</h1>
<p>제가 가장 좋아하는 음식은 {{ foods }}입니다.</p>
<p>{{ foods.0 }}를 가장 좋아합니다.</p>
</body>
</html>
보여지는 화면
{{ variable|filter }}
|
)를 사용하여 적용{{ name|lower }}
{{ variable|truncatewords:30 }}
firstpjt/articles/views.py
def dinner(request):
foods = ['족발', '피자', '햄버거', '초밥',]
pick = random.choice(foods)
context = {
'pick': pick,
'foods': foods,
}
return render(request, 'dinner.html', context)
firstpjt/articles/templates/dinner.html
<h1>오늘 저녁은 {{ pick }}!!</h1>
<p>{{ pick }}은 {{ pick|length }}</p>
화면
{{ tag }}
{% tag %} ... {% endtag %}
firstpjt/articles/views.py - 동일
firstpjt/articles/templates/dinner.html
<body>
<h1>오늘 저녁은 {{ pick }}!!</h1>
<p>{{ pick }}은 {{ pick|length }}</p>
<p>메뉴판</p>
<ul>
{% for food in foods %}
<li>{{ food }}</li>
{% endfor %}
</ul>
</body>
화면
{# lorem ipsum #}
{# {% if ... %}text{% else %} #}
{% comment %}
와 {% endcomment %}
사이에 입력템플릿 상속은 기본적으로 코드의 재사용성에 초점을 맞춤
템플릿 상속을 사용하면 사이트의 모든 공통 요소를 포함하고, 하위 템플릿이 override할 수 있는 블록을 정의하는 기본 'skeleton' 템플릿을 만들 수 있음
주로 자주 사용되는 기본적인 공통 템플릿의 경우 전체 프로젝트 내의 프로젝트명 폴더 안의 templates 폴더에 base.html을 만들고 이 템플릿을 다른 템플릿들에서 상속하여 재사용한다. ('project/project/templates/base.html'
)
firstpjt/firstpjt/settings.py
TEMPLATES = [
{
'BACKEND': 'django.template.backends.django.DjangoTemplates',
'DIRS': [BASE_DIR / 'firstpjt' / 'templates', ],
'APP_DIRS': True,
'OPTIONS': {
'context_processors': [
'django.template.context_processors.debug',
'django.template.context_processors.request',
'django.contrib.auth.context_processors.auth',
'django.contrib.messages.context_processors.messages',
],
},
},
]
BASE_DIR / 'firstpjt' / 'templates'
와 같이 경로를 표현하는 이유는 OS에 상관 없이 경로가 호환되도록 하기 위함이다. (각 OS에 맞게 경로를 하드코딩 했을 때의 문제를 피할 수 있음) (python 'pathlib' 라이브러리 참고)firstpjt/firstpjt/settings.py
from pathlib import Path
# Build paths inside the project like this: BASE_DIR / 'subdir'.
BASE_DIR = Path(__file__).resolve().parent.parent
extends
tag
{% extends %}
block
tag
{% block %}
{% block content %}
{% endblock content %}
템플릿 상속 예시
firstpjt/firstpjt/templates/base.html
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<link href="https://cdn.jsdelivr.net/npm/bootstrap@5.0.0-beta2/dist/css/bootstrap.min.css" rel="stylesheet" integrity="sha384-BmbxuPwQa2lc/FVzBcNJ7UAyJxM6wuqIj61tLrc4wSX0szH/Ev+nYRRuWlolflfl" crossorigin="anonymous">
<title>Document</title>
</head>
<body>
<nav class="navbar navbar-light bg-light">
<div class="container-fluid">
<a class="navbar-brand" href="#">Navbar</a>
</div>
</nav>
<div class="container">
{% block content %}
{% endblock %}
</div>
<script src="https://cdn.jsdelivr.net/npm/bootstrap@5.0.0-beta2/dist/js/bootstrap.bundle.min.js" integrity="sha384-b5kHyXgcpbZJO/tY9Ul7kGkf1S0CWuKcCD38l8YkeH8z8QjE0GmW1gYU5S9FOnJ0" crossorigin="anonymous"></script>
</body>
</html>
firstpjt/articles/templates/index.html
{% extends 'base.html' %}
{% block content %}
<h1>만나서 반가워요!!!</h1>
<a href="{% url 'greeting' %}">greeting</a>
<a href="{% url 'dinner' %}">dinner</a>
<a href="{% url 'throw' %}">throw</a>
{% endblock %}
참고: https://developpaper.com/djangos-design-philosophy-and-philosophy/
?key=value&key=value
형태로 전달HttpRequest
objectviews.py
내에 정의한 뷰 함수에 전달되는, HTTP 요청 간의 모든 정보를 담고있는 변수HttpRequest
객체를 만든다.HttpRequest
객체를 뷰 함수의 첫 번째 인자로 전달한다.HttpResponse
객체를 반환한다.https://docs.djangoproject.com/en/3.1/ref/request-response/#module-django.http
path()
또한 많아지고, app 또한 더 작성되기 때문에 프로젝트의 urls.py에서 모두 관리하는 것은 코드 유지보수에 좋지 않음.urls.py
를 만들고 프로젝트 urls.py
에서 각 앱의 urls.py
파일로 URL 매핑을 위탁하게 할 수 있다.firstpjt/articles/urls.py
from django.urls import path
# 명시적 상대경로 표현
from . import views
urlpatterns = [
path('index/', views.index),
path('greeting/', views.greeting),
path('dinner/', views.dinner),
path('throw/', views.throw),
path('catch/', views.catch),
]
urlpatterns
리스트가 없는 경우 에러가 발생한다.firstpjt/firstpjt/urls.py
from django.contrib import admin
from django.urls import path, include
urlpatterns = [
path('admin/', admin.site.urls),
path('articles/', include('articles.urls')),
path('pages/', include('pages.urls')),
]
include()
함수 활용하여 path()
함수의 첫 번째 인자와 매칭된 이후의 부분은 연결된 앱의 urls.py로 넘겨줘서 처리하게 함include()
urls.py
)들을 참조할 수 있도록 도움include()
를 만나게 되면, URL의 그 지점까지 일치하는 부분을 잘라내고, 남은 문자열 부분을 후속 처리를 위해 include된 URLconf로 전달한다.views.py
내에서 인자로 들어오기 때문에 활용하기 좋다.'hello/<str:name>/'
'hello/<name>/'
firstpjt/firstpjt/urls.py
from django.urls import path
# 명시적 상대경로 표현
from . import views
urlpatterns = [
...
path('hello/<str:name>/', views.hello),
# path('hello/<name>/', views.hello),
]
firstpjt/articles/views.py
def hello(request, name): # url 주소에 포함되어 있던 name이 인자로 들어옴
context = {
'name': name,
}
return render(request, 'hello.html', context)
hello.tml
{% extends 'base.html' %}
{% block content %}
<h1>만나서 반가워요 {{ name }}님!</h1>
{% endblock %}
path()
함수의 name 인자를 정의해서 사용path()
함수에 작성한 name을 사용할 수 있음views.py
파일: path('index/', views.index)
→ path('index/', views.index, name='index'),
<a href="/index/">메인 페이지</a>
→ <a href="{% url 'index' %}">메인 페이지</a>
{% url '' %}
개체를 구분할 수 있는 범위를 나타내는 namespace
# pages/urls.py
from django.urls import path
from . import views
urlpatterns = [
path('index/', views.index, name='index'),
]
# pages/views.py
def index(request):
return render(request, 'index.html')
<!-- pages/templates/index.html -->
{% extends 'base.html' %} {% block content %}
<h1>두번째 앱의 index</h1>
{% endblock %}
<!-- articles/templates/index.html -->
{% extends 'base.html' %} {% block content %}
<h1>만나서 반가워요!</h1>
<a href="{% url 'greeting' %}">greeting</a>
<a href="{% url 'dinner' %}">dinner</a>
<a href="{% url 'dtl_practice' %}">dtl-practice</a>
<a href="{% url 'throw' %}">throw</a>
<h2><a href="{% url 'index' %}">두번째 앱 index로 이동</a></h2>
{% endblock %}
2가지 문제
urls.py
파일 안에 app_name
attribute 작성
# pages/urls.py
app_name = 'pages'
urlpatterns = [
path('index/', views.index, name='index'),
]
# articles/urls.py
app_name = 'articles'
urlpatterns = [
...,
]
app_name
attribute
urls.py
에 app_name
attribute 값 작성url 참조
:
연산자를 사용하여 지정app_name
이 articles
고 URL name이 index
인 주소 참조는 articles:index
URL tag 변경
<!-- articles/templates/index.html -->
{% extends 'base.html' %}
{% block content %}
<h1>만나서 반가워요!</h1>
<a href="{% url 'articles:greeting' %}">greeting</a>
<a href="{% url 'articles:dinner' %}">dinner</a>
<a href="{% url 'articles:throw' %}">throw</a>
<h2><a href="{% url 'pages:index' %}">두번째 앱 index로 이동</a></h2>
{% endblock %}
app_name/templates/
경로에 있는 templates 파일들만 찾을 수 있으며, settings.py
안의 INSTALLED_APPS
리스트에 작성한 app 순서로 template을 검색 후 렌더링한다.app_name/templates/app_name
형태로 변경해 임의로 이름 공간 생성 후 변경된 추가 경로 작성# articles/views.py
return render(request, 'articles/index.html')
# pages/views.py
return render(request, 'pages/index.html')