# Build paths inside the project like this: BASE_DIR / 'subdir'.
BASE_DIR = Path(__file__).resolve().parent.parent
Path(file) : 현재 파일의 위치 .resolve : 절대 경로 .parent : 상위 경로base_dir 위치는 settings.py의 절대 경로에서 상위로 2번 이동한 경로이다.
# SECURITY WARNING: keep the secret key used in production secret!
SECRET_KEY = 'django-insecure-*2#5z(m5v)+aw6cycu+))zh(m!@n$$*--hu-8(v_$@ujwo3=36'
시크릿 키는 Django 설치 시 자동으로 제공되는 유일하고 예측 불가한 값으로 text나 bytes로 가정할 수 없으며 force_str() or force_bytes()로 변환하여 사용된다. 이 키의 용도는 아래와 같다.
INSTALLED_APPS = [
'django.contrib.admin',
'django.contrib.auth',
'django.contrib.contenttypes',
'django.contrib.sessions',
'django.contrib.messages',
'django.contrib.staticfiles',
]
Django 프로젝트 내부에 사용되는 모든 앱이 기록되는 곳으로 아래 내용이 추가됨
MIDDLEWARE = [
'django.middleware.security.SecurityMiddleware',
'django.contrib.sessions.middleware.SessionMiddleware',
'django.middleware.common.CommonMiddleware',
'django.middleware.csrf.CsrfViewMiddleware',
'django.contrib.auth.middleware.AuthenticationMiddleware',
'django.contrib.messages.middleware.MessageMiddleware',
'django.middleware.clickjacking.XFrameOptionsMiddleware',
]
Middleware is a framework of hooks into Django’s request/response processing. It’s a light, low-level “plugin” system for globally altering Django’s input or output. https://docs.djangoproject.com/en/4.0/topics/http/middleware/
ROOT_URLCONF = 'clone_pb.urls'
root URLConf의 위치를 나타냄
TEMPLATES = [
{
'BACKEND': 'django.template.backends.django.DjangoTemplates',
'DIRS': [BASE_DIR / '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',
],
},
},
]
장고에서 사용되는 모든 템플릿 엔진이 위치하는 설정으로 각 엔진에 대한 옵션이 설정되어 있다.
BACKEND : 백엔드 템플릿
DIR : 템플릿 엔진이 사용되는 템플릿 위치
APP_DIRS : 설치된 애플리케이션 내에서 템플릿 위치를 찾을 것인지 여부 기본값=True
OPSTIONS : 백엔드 템플릿에 전달할 추가 인자로 사용 가능한 인자는 백엔드 팀플릿에 따라 다름
context_processors : context_processors is a list of dotted Python paths to callables that are used to populate the context when a template is rendered with a request. These callables take a request object as their argument and return a dict of items to be merged into the context.https://docs.djangoproject.com/en/4.0/ref/templates/api/
https://docs.djangoproject.com/en/4.0/topics/auth/passwords/#password-validation
admin 애플리케이션에서 사용되는 인증 과정에서 패스워드를 검증하는 프로세스
LANGUAGE_CODE = 'ko-kr'
TIME_ZONE = 'Asia/Seoul'
USE_I18N = True
USE_TZ = True
# Static files (CSS, JavaScript, Images)
# https://docs.djangoproject.com/en/4.0/howto/static-files/
STATIC_URL = 'static/'
STATIC_ROOT = BASE_DIR / 'staticfiles'
STATICFILES_DIRS = [
BASE_DIR / "static",
]
MEDIA_URL = 'media/'
MEDIA_ROOT = BASE_DIR / "media"
STATIC_URL : 정적 파일이 위치하는 URL (실제 urlConf를 조작해서 해당 위치로 연결 가능)
STATIC_ROOT : 배포 진행 시 정적 파일을 모아두는 장소 (배포시 모든 정적 파일이 해당 위치로 복사 됨)
STATICFILES_DIRS : 실제 정적 파일이 위치하는 장소
MEDIA_URL, MEDIA_ROOT : STATIC과 동일한 개념
MDEIA_UPLOAD : For example, if your [MEDIA_URL](https://docs.djangoproject.com/en/4.0/ref/settings/#std:setting-MEDIA_URL)
is defined as media/
, you can do this by adding the following snippet to your [ROOT_URLCONF](https://docs.djangoproject.com/en/4.0/ref/settings/#std:setting-ROOT_URLCONF)
from django.contrib import admin
from django.urls import path, include
from django.conf import settings
from django.conf.urls.static import static
urlpatterns = [path('admin/', admin.site.urls),
path('project/', include('project.urls')),
path('blog/', include('blog.urls')), ]
urlpatterns += static(settings.MEDIA_URL, document_root=settings.MEDIA_ROOT)
# Default primary key field type
# https://docs.djangoproject.com/en/4.0/ref/settings/#default-auto-field
DEFAULT_AUTO_FIELD = 'django.db.models.BigAutoField'
When a user requests a page from your Django-powered site, this is the algorithm the system follows to determine which Python code to execute
from django.urls import path
from . import views
urlpatterns = [
path('articles/2003/', views.special_case_2003),
path('articles/<int:year>/', views.year_archive),
path('articles/<int:year>/<int:month>/', views.month_archive),
path('articles/<int:year>/<int:month>/<slug:slug>/', views.article_detail),
]
Note
Example requests:
/articles/2005/03/
would match the third entry in the list. Django would call the function views.month_archive(request, year=2005, month=3)
./articles/2003/
would match the first pattern in the list, not the second one, because the patterns are tested in order, and the first one is the first test to pass. Feel free to exploit the ordering to insert special cases like this. Here, Django would call the function views.special_case_2003(request)
/articles/2003
would not match any of these patterns, because each pattern requires that the URL end with a slash./articles/2003/03/building-a-django-site/
would match the final pattern. Django would call the function views.article_detail(request, year=2003, month=3, slug="building-a-django-site")
.slug
- Matches any slug string consisting of ASCII letters or numbers, plus the hyphen and underscore characters. For example, building-your-1st-django-site
.regular expression uses [re_path()](https://docs.djangoproject.com/en/4.0/ref/urls/#django.urls.re_path)
instead of [path()](https://docs.djangoproject.com/en/4.0/ref/urls/#django.urls.path)
.
from django.urls import path, re_path
from . import views
urlpatterns = [
path('articles/2003/', views.special_case_2003),
re_path(r'^articles/(?P<year>[0-9]{4})/$', views.year_archive),
re_path(r'^articles/(?P<year>[0-9]{4})/(?P<month>[0-9]{2})/$', views.month_archive),
re_path(r'^articles/(?P<year>[0-9]{4})/(?P<month>[0-9]{2})/(?P<slug>[\w-]+)/$', views.article_detail),
]
As well as the named group syntax, e.g. (?P<year>[0-9]{4})
, you can also use the shorter unnamed group, e.g. ([0-9]{4})
.
# URLconf
from django.urls import path
from . import views
urlpatterns = [
path('blog/', views.page),
path('blog/page<int:num>/', views.page),
]
# View (in blog/views.py)
def page(request, num=1):
# Output the appropriate page of blog entries, according to num.
...
위 코드에서 URL 패턴은 동일한 뷰(page)를 가르키고 있다. 그러나 첫번째 패턴은 동작하지 않을 것이고 두번째 패턴 또한 num 변수에 1이 오지 않는 이상 동작하지 않을 것이다.
Each regular expression in a urlpatterns
is compiled the first time it’s accessed. This makes the system blazingly fast.
urlpatterns은 다른 URLconf 모듈을 include할 수 있다. 아래의 예제처럼 community/ url로 접속 할 경우 include된 aggregator 앱의 URLconf로 연결된다. 이때 community 문자열을 자르고 나머지를 include된 URLconf로 보내게 된다.
from django.urls import include, path
urlpatterns = [
# ... snip ...
path('community/', include('aggregator.urls')),
path('contact/', include('contact.urls')),
# ... snip ...
]
다른 방법으로는 path() 리스트 인스턴스를 생성하여 추가되는 URL 패턴을 include하는 방식이다.
from django.urls import include, path
from apps.main import views as main_views
from credit import views as credit_views
extra_patterns = [
path('reports/', credit_views.report),
path('reports/<int:id>/', credit_views.report),
path('charge/', credit_views.charge),
]
urlpatterns = [
path('', main_views.homepage),
path('help/', include('apps.help.urls')),
path('credit/', include(extra_patterns)),
]
위 예제에서 /credit/reports/ URL은 credit_views.report()
뷰에서 처리된다.
아래 예제는 싱글 패턴 프리픽스가 반복적으로 사용되는 URLconf에서 중복을 제거하는 방법이다.
from django.urls import path
from . import views
urlpatterns = [
path('<page_slug>-<page_id>/history/', views.history),
path('<page_slug>-<page_id>/edit/', views.edit),
path('<page_slug>-<page_id>/discuss/', views.discuss),
path('<page_slug>-<page_id>/permissions/', views.permissions),
]
위 코드는 아래와 같이 중복을 제거함으로써 향상시킬 수 있다.
from django.urls import include, path
from . import views
urlpatterns = [
path('<page_slug>-<page_id>/', include([
path('history/', views.history),
path('edit/', views.edit),
path('discuss/', views.discuss),
path('permissions/', views.permissions),
])),
]
An included URLconf receives any captured parameters from parent URLconfs, so the following example is valid:
# In settings/urls/main.py
from django.urls import include, path
urlpatterns = [
path('<username>/blog/', include('foo.urls.blog')),
]
# In foo/urls/blog.py
from django.urls import path
from . import views
urlpatterns = [
path('', views.blog.index),
path('archive/', views.blog.archive),
]
In the above example, the captured "username"
variable is passed to the included URLconf, as expected.
Django provides tools for performing URL reversing that match the different layers where URLs are needed:
[url](https://docs.djangoproject.com/en/4.0/ref/templates/builtins/#std:templatetag-url)
template tag.[reverse()](https://docs.djangoproject.com/en/4.0/ref/urlresolvers/#django.urls.reverse)
function.[get_absolute_url()](https://docs.djangoproject.com/en/4.0/ref/models/instances/#django.db.models.Model.get_absolute_url)
method.from django.urls import path
from . import views
urlpatterns = [
#...
path('articles/<int:year>/', views.year_archive, name='news-year-archive'),
#...
]
According to this design, the URL for the archive corresponding to year nnnn is /articles/<nnnn>/
.
You can obtain these in template code by using:
<a href="{% url 'news-year-archive' 2012 %}">2012 Archive</a>
{# Or with the year in a template context variable: #}
<ul>
{% for yearvar in year_list %}
<li><a href="{% url 'news-year-archive' yearvar %}">{{ yearvar }} Archive</a></li>
{% endfor %}
</ul>
or python code
from django.http import HttpResponseRedirect
from django.urls import reverse
def redirect_to_year(request):
# ...
year = 2006
# ...
return HttpResponseRedirect(reverse('news-year-archive', args=(year,)))
URL 리버싱이 실행되기 위해 named URL 패턴이 필요하다. URL 패턴을 지정할 때는 다른앱의 명칭과 충돌하지 않도록 해야한다. 만약 comment라는 URL 패턴 호출했는데 다른 앱에 동일한 URL 패턴이 존재한다면 reverse()는 프로젝트 urlpatterns에서 마지막으로 생성된 패턴을 찾게된다. 이를 방지하기 위해 애플리케이션 명칭을 프리픽스로 URL 명칭 앞에 두도록 한다. (such as myapp-comment instead of comment)
의도적으로 동일한 URL 명칭을 사용하는 경우도 있는데 가장 보편적인 유즈케이스는 LoginView와 같이 다른 앱에서도 동일한 기능으로 동작하는 기능에 대해서는 의도적으로 URL 명칭을 동일하게 작성하여 오버라이딩 한다. 또한 동일한 URL 명칭을 사용하더라도 아규먼트가 다른경우에도 사용 가능하다.
URL 네임스페이스는 우리가 고유하게 URL 패턴을 리버스할 수 있게 해준다. 만약 다른 애플리케이션에서 동일한 URL 명칭을 사용한다고 하더라도 가능하다. 항상 네임스페이스 URL을 사용하는것은 좋은 사례이다. 비슷하게 여러개의 애플리케이션 인스턴스가 배포되어도 우리는 URL을 리버스할 수 있다. 즉, 단일 애플리케이션의 여러인스턴스가 named URL을 공유하기 떄문에 네임스페이스는 named URL을 구분하는 방법을 제공한다.
URL 네임스페이스는 아래와 같이 두 부분으로 나윕니다.
application namespace
배포된 애플리케이션 이름을 설명한다. 단일 애플리케이션의 모든 인스턴스는 동일한 애플리케이션 네임스페이스를 갖는다. 예를들어, 장고 어드민 애플리케이션은 admin이라는 다소 예측 가능한 애플케이션 네임스페이스가 있다.
instance namespace
애플리케이션의 특정 인스턴스를 식별한다. 인스턴스 네임스페이스는 프로젝트 전반에서 유일한 값이어야 한다. 반면 인스턴스 네임스페이스는 애플리케이션 네임스페이스와는 동일한 값을 갖을 수 있다. 이것은 앱의 디폴트 인스턴스를 지정하기 위해 사용된다. 예를들어 장고 어드민 인스턴스는 ‘admin’이라는 인스턴스 이름을 갖는다.
네임스페이스는 ‘:’ 구분자로 구분된다. 장고 어드민 메인 인덱스 페이지는 ‘admin:index’를 참고하고 이는 ‘admin’ 네임스페이스와 named URL인 ‘index’를 나타낸다. 또한 네임스페이스는 중첩이 가능하다. ‘sports:polls:index’는 최상위 네임스페이스 sports에 정의된 poll 네임스페이스에서 index를 찾을 것이다.
‘polls:index’라는 네임스페이스된 URL을 resolving하기 위해, 장고는 정규화된 명칭을 부분으로 분리하는 작업을 수행하고 아래의 절차에 따른다.
To show this resolution strategy in action, consider an example of two instances of the polls
application from the tutorial: one called 'author-polls'
and one called 'publisher-polls'
. Assume we have enhanced that application so that it takes the instance namespace into consideration when creating and displaying polls.
from django.urls import include, path
urlpatterns = [
path('author-polls/', include('polls.urls', namespace='author-polls')),
path('publisher-polls/', include('polls.urls', namespace='publisher-polls')),
]
from django.urls import path
from . import views
app_name = 'polls'
urlpatterns = [
path('', views.IndexView.as_view(), name='index'),
path('<int:pk>/', views.DetailView.as_view(), name='detail'),
...
]
app_name
attribute in polls/urls.pynamespace
argument in urls.pyUsing this setup, the following lookups are possible:
'author-polls'
- 'polls:index'
will resolve to the index page of the 'author-polls'
instance; i.e. both of the following will result in "/author-polls/"
.In the method of a class-based view:
reverse('polls:index', current_app=self.request.resolver_match.namespace)
and in the template:
{% url 'polls:index' %}
'polls:index'
will resolve to the last registered instance of polls
. Since there is no default instance (instance namespace of 'polls'
), the last instance of polls
that is registered will be used. This would be 'publisher-polls'
since it’s declared last in the urlpatterns
.'author-polls:index'
will always resolve to the index page of the instance 'author-polls'
(and likewise for 'publisher-polls'
) .If there were also a default instance - i.e., an instance named 'polls'
- the only change from above would be in the case where there is no current instance (the second item in the list above). In this case 'polls:index'
would resolve to the index page of the default instance instead of the instance declared last in urlpatterns
.
Think of instance namespace as your nickname and application namespace as your real name.
People can have many nicknames for you, but your real name doesn't change.
Django uses the application namespace (your real name) to reverse urls, cause as an application you should not care how many instance namespaces (nicknames) there are.
But to differentiate between one instance and the next, you will use the instance namespaces (nicknames) in urlconfs.
Let's say your app deals with customers and store employees. Both of these have physical addresses and you might want to use an app that just handles all address information: forms, validation, model structure, geo location etc. Our fictional app is a reusable django app called "django_super_address".
Your root url conf may look like this:
urlpatterns = [
path('customer/address/',
include('django_super_address.urls',
namespace='customer_address')
),
path('employee/address/',
include('django_super_address.urls',
namespace='employee_address')
),
]
# File: django_super_address/urls.py
from . import views
app_name = 'django_super_address'
urlpatterns = [
path('create/', views.SomeAddressCreateView.as_view(),
name='address_create'),
path('create/success/', views.SuccessView(),
name='address_create_success'),
...
]
# File django_super_address/views.py
class SomeAddressCreateView(generic.CreateView):
def success_url(self):
return reverse(
'django_super_address:address_create_success'
)
...
When Django gets the URL `/employee/address/create/':
employee_address
django_super_address
django_super_address
and associates the urls.py with that application namespace.In the view:
create/success
root
+ employee/address/
+ create/success/
= /employee/address/create/success/
URLconfs를 포함하는 애플리케이션 네임스페이스는 두가지 방식으로 정의된다.
첫번째로 included URLconf 모듈에 app_name 속성을 추가한다. 실제로 모듈 또는 모듈에 대한 참조를 urlpatterns 자체 목록이 아니라 include()에 전달해야 한다.
# polls/urls.py
from django.urls import path
from . import views
app_name = 'polls'
urlpatterns = [
path('', views.IndexView.as_view(), name='index'),
path('<int:pk>/', views.DetailView.as_view(), name='detail'),
...
]
# urls.py
from django.urls import include, path
urlpatterns = [
path('polls/', include('polls.urls')),
]
polls.urls로 정의된 URL은 polls라는 네임스페이스를 갖게된다.
두번째로 내장된 네임스페이스 데이터를 포함한 오브젝트를 include 할 수 있다. re_path(), path()의 목록을 include() 하면 오브젝트에 포함된 URL은 글로벌 네임스페이스로 추가된다. 또한 2-tuple을 포함하는 include()도 사용 가능하다.
from django.urls import include, path
from . import views
polls_patterns = ([
path('', views.IndexView.as_view(), name='index'),
path('<int:pk>/', views.DetailView.as_view(), name='detail'),
], 'polls')
urlpatterns = [
path('polls/', include(polls_patterns)),
]
뷰 함수 혹은 짧은 표현으로 뷰는 web request, response를 처리하는 파이썬 함수이다. reponse의 종류는 HTML 웹 페이지 컨텐츠, 리다이렉션, 404 에러, XML 다큐먼트, 이미지 등 모든것이 가능하다. 뷰 자체에는 reponse를 리턴을 위해 필요한 임의의 로직이 포함되어 있다. 이 코드가 파이썬 경로에 위치하는 한 어디에서나 동작한다. 코드를 어딘가에 두기 위해 규칙은 views.py로 불리는 파일에 위치하게 되며 이 파일은 장고 프로젝트 혹은 애플리케이션 디렉토리에 위치한다.
아래 예제는 현재 날짜와 시간을 HTML 형태로 리턴한다.
from django.http import HttpResponse
import datetime
def current_datetime(request):
now = datetime.datetime.now()
html = "<html><body>It is now %s.</body></html>" % now
return HttpResponse(html)
Django’s Time Zone
장고는 America/Chicago를 기본값으로 TIME_ZONE을 세팅한다. 이것을 변경하기 위해 settings 파일을 변경해야 한다.
위 예제의 뷰 함수는 현재 시간과 날짜를 담은 HTML 페이지를 리턴한다. 특정한 URL에 해당 뷰를 연결하고자 한다면 URLconf를 설정해줘야 한다. (자세한 내용은 URL dispatcher 참고)
장고는 HTTP 에러 코드를 리턴할 수 있도록 도와준다. 여기는 수많은 공통 HTTP 상태 코드가 HttpResponse 하위 클래스로 존재한다. 우리는 사용가능한 하위 클래스 리스트를 request/response 도큐멘트에서 참고할 수 있다. 특정 에러를 구분하기 위해 일반적인 HttpResponse 대신 하위 클래스중 하나의 인스턴스를 아래와 같이 리턴할 수 있다.
from django.http import HttpResponse, HttpResponseNotFound
def my_view(request):
# ...
if foo:
return HttpResponseNotFound('<h1>Page not found</h1>')
else:
return HttpResponse('<h1>Page was found</h1>')
가능한 모든 HTTP 응답 코드에 대한 하위 클래스를 구분하지는 않는다. 대부분의 응답은 일반적이지 않을 것이기 떄문이다. 또한 HttpResponse 도큐멘트에 따라서 HttpResponse 객체에 HTTP status 코드를 전달 할 수도 있다.
from django.http import HttpResponse
def my_view(request):
# ...
# Return a "created" (201) response code.
return HttpResponse(status=201)
404 오류는 가장 일반적인 HTTP 오류이기 때문에 더 쉽게 처리하는 방법이 있다.
[class django.http.Http404
](https://docs.djangoproject.com/en/4.0/topics/http/views/#django.http.Http404)
HttpResponseNotFound를 리턴할 때, 에러 페이지 결과의 HTML을 지정해야 한다.
return HttpResponseNotFound('<h1>Page not found</h1>')
편의를 위해 그리고 사이트 전반에 일관적인 404 에러 페이지를 갖는 것이 좋다. 장고는 Http404 예외를 제공하는데 만약 Http404가 어떤 뷰 함수 위치에서 발생하던지 장고는 이것을 캐치하여 HTTP 오류 코드 404화 함께 애플리케이션에 대한 표준 에러 페이지를 반환한다.
from django.http import Http404
from django.shortcuts import render
from polls.models import Poll
def detail(request, poll_id):
try:
p = Poll.objects.get(pk=poll_id)
except Poll.DoesNotExist:
raise Http404("Poll does not exist")
return render(request, 'polls/detail.html', {'poll': p})
장고가 404를 리턴할 때 커스텀 HTML을 보여주기 위해 404라는 HTML 템플릿을 만들고 템플릿 트리 최상위에 위치 시킨다. 이 템플릿은 DEBUG가 False로 설정되어 있는 경우 제공된다. DEBUG가 True일 경우 Http404 메세지를 제공할 수 있고 이는 표준 404 debug 템플릿을 보여준다. 이것은 일반적으로 운영 환경의 404 템플릿으로 적합하지 않다.
from django.core.exceptions import PermissionDenied
from django.http import HttpResponse
from django.test import SimpleTestCase, override_settings
from django.urls import path
def response_error_handler(request, exception=None):
return HttpResponse('Error handler content', status=403)
def permission_denied_view(request):
raise PermissionDenied
urlpatterns = [
path('403/', permission_denied_view),
]
handler403 = response_error_handler
# ROOT_URLCONF must specify the module that contains handler403 = ...
@override_settings(ROOT_URLCONF=__name__)
class CustomErrorHandlerTests(SimpleTestCase):
def test_handler_renders_template_response(self):
response = self.client.get('/403/')
# Make assertions on the response here. For example:
self.assertContains(response, 'Error handler content', status_code=403)