본문 바로가기
Web 개발/Django

Django urlpatterns 함수: path, re_path, include, register_converter, static

by yororing 2024. 3. 20.

00 개요: urls.py, django.urls, django.conf.urls

1. urls.py의 역할

  • Client가 웹서버의 하위 url에 접속할때 무엇을 보여줄 것인지(views) 정의하는 파일
  • 해당 django 프로젝트/앱의 설정파일인 settings.py에서 ROOT_URLCONF을 이 파일(urls.py)로 잡음
    ROOT_URLCONF = '앱이름.urls'
  • 파일 내용 예
    from django.urls import include, path
    from products.views import ProductsView
    
    urlpatterns = [
        path('index/', views.index, name='main-view'),
        path('bio/<username>/', views.bio, name='bio'),
        path('articles/<slug:title>/', views.article, name='article-detail'),
        path('articles/<slug:title>/<int:section>/', views.section, name='article-section'),
        path('blog/', include('blog.urls')),
        path('products/', ProductsView.as_view()),
        ...
    ]​

2. django.urls의 구성

  • urls.py가 참조하는 django.urls 폴더는 다음과 같은 구조와 내용을 지님
    • lib > pyton3.6 > site-packages > django > urls
          > __pycache__
          __init__.py     # 중요
          base.py
          conf.py			# 중요
          converters.py	# 중요
          exceptions.py
          resolvers.py
          utils.py

1) _init__.py 파일 내용

from .base import (
    clear_script_prefix, clear_url_caches, get_script_prefix, get_urlconf,
    is_valid_path, resolve, reverse, reverse_lazy, set_script_prefix,
    set_urlconf, translate_url,
)
from .conf import include, path, re_path
from .converters import register_converter
from .exceptions import NoReverseMatch, Resolver404
from .resolvers import (
    LocalePrefixPattern, ResolverMatch, URLPattern, URLResolver,
    get_ns_resolver, get_resolver,
)
from .utils import get_callable, get_mod_func

__all__ = [
    'LocalePrefixPattern', 'NoReverseMatch', 'URLPattern',
    'URLResolver', 'Resolver404', 'ResolverMatch', 'clear_script_prefix',
    'clear_url_caches', 'get_callable', 'get_mod_func', 'get_ns_resolver',
    'get_resolver', 'get_script_prefix', 'get_urlconf', 'include',
    'is_valid_path', 'path', 're_path', 'register_converter', 'resolve',
    'reverse', 'reverse_lazy', 'set_script_prefix', 'set_urlconf',
    'translate_url',
]

2) conf.py 파일 내용

"""Functions for use in URLsconfs."""
from functools import partial
from importlib import import_module

from django.core.exceptions import ImproperlyConfigured

from .resolvers import (
    LocalePrefixPattern, RegexPattern, RoutePattern, URLPattern, URLResolver,
)


def include(arg, namespace=None):
    app_name = None
    if isinstance(arg, tuple):
        # Callable returning a namespace hint.
        try:
            urlconf_module, app_name = arg
        except ValueError:
            if namespace:
                raise ImproperlyConfigured(
                    'Cannot override the namespace for a dynamic module that '
                    'provides a namespace.'
                )
            raise ImproperlyConfigured(
                'Passing a %d-tuple to include() is not supported. Pass a '
                '2-tuple containing the list of patterns and app_name, and '
                'provide the namespace argument to include() instead.' % len(arg)
            )
    else:
        # No namespace hint - use manually provided namespace.
        urlconf_module = arg

    if isinstance(urlconf_module, str):
        urlconf_module = import_module(urlconf_module)
    patterns = getattr(urlconf_module, 'urlpatterns', urlconf_module)
    app_name = getattr(urlconf_module, 'app_name', app_name)
    if namespace and not app_name:
        raise ImproperlyConfigured(
            'Specifying a namespace in include() without providing an app_name '
            'is not supported. Set the app_name attribute in the included '
            'module, or pass a 2-tuple containing the list of patterns and '
            'app_name instead.',
        )
    namespace = namespace or app_name
    # Make sure the patterns can be iterated through (without this, some
    # testcases will break).
    if isinstance(patterns, (list, tuple)):
        for url_pattern in patterns:
            pattern = getattr(url_pattern, 'pattern', None)
            if isinstance(pattern, LocalePrefixPattern):
                raise ImproperlyConfigured(
                    'Using i18n_patterns in an included URLconf is not allowed.'
                )
    return (urlconf_module, app_name, namespace)


def _path(route, view, kwargs=None, name=None, Pattern=None):
    if isinstance(view, (list, tuple)):
        # For include(...) processing.
        pattern = Pattern(route, is_endpoint=False)
        urlconf_module, app_name, namespace = view
        return URLResolver(
            pattern,
            urlconf_module,
            kwargs,
            app_name=app_name,
            namespace=namespace,
        )
    elif callable(view):
        pattern = Pattern(route, name=name, is_endpoint=True)
        return URLPattern(pattern, view, kwargs, name)
    else:
        raise TypeError('view must be a callable or a list/tuple in the case of include().')


path = partial(_path, Pattern=RoutePattern)
re_path = partial(_path, Pattern=RegexPattern)

3) converters.py 파일 내용

import uuid
from functools import lru_cache

class IntConverter:
    regex = '[0-9]+'

    def to_python(self, value):
        return int(value)

    def to_url(self, value):
        return str(value)

class StringConverter:
    regex = '[^/]+'

    def to_python(self, value):
        return value

    def to_url(self, value):
        return value

class UUIDConverter:
    regex = '[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}'

    def to_python(self, value):
        return uuid.UUID(value)

    def to_url(self, value):
        return str(value)

class SlugConverter(StringConverter):
    regex = '[-a-zA-Z0-9_]+'

class PathConverter(StringConverter):
    regex = '.+'

DEFAULT_CONVERTERS = {
    'int': IntConverter(),
    'path': PathConverter(),
    'slug': SlugConverter(),
    'str': StringConverter(),
    'uuid': UUIDConverter(),
}

REGISTERED_CONVERTERS = {}

def register_converter(converter, type_name):
    REGISTERED_CONVERTERS[type_name] = converter()
    get_converters.cache_clear()

@lru_cache(maxsize=None)
def get_converters():
    return {**DEFAULT_CONVERTERS, **REGISTERED_CONVERTERS}

def get_converter(raw_converter):
    return get_converters()[raw_converter]

3. django.conf.urls의 구성

  • urls.py가 참조하는 django.urls 폴더는 다음과 같은 구조와 내용을 지님
    • lib > pyton3.6 > site-packages > django > conf > urls
          > __pycache__
          __init__.py		# 중요
          i18n.py
          static.py		# 중요

1) _init__.py 파일 내용

import warnings

from django.urls import include, re_path
from django.utils.deprecation import RemovedInDjango40Warning
from django.views import defaults

__all__ = ['handler400', 'handler403', 'handler404', 'handler500', 'include', 'url']

handler400 = defaults.bad_request
handler403 = defaults.permission_denied
handler404 = defaults.page_not_found
handler500 = defaults.server_error


def url(regex, view, kwargs=None, name=None):
    warnings.warn(
        'django.conf.urls.url() is deprecated in favor of '
        'django.urls.re_path().',
        RemovedInDjango40Warning,
        stacklevel=2,
    )
    return re_path(regex, view, kwargs, name)

2) static.py 파일 내용

import re
from urllib.parse import urlsplit

from django.conf import settings
from django.core.exceptions import ImproperlyConfigured
from django.urls import re_path
from django.views.static import serve


def static(prefix, view=serve, **kwargs):
    """
    Return a URL pattern for serving files in debug mode.

    from django.conf import settings
    from django.conf.urls.static import static

    urlpatterns = [
        # ... the rest of your URLconf goes here ...
    ] + static(settings.MEDIA_URL, document_root=settings.MEDIA_ROOT)
    """
    if not prefix:
        raise ImproperlyConfigured("Empty static prefix not permitted")
    elif not settings.DEBUG or urlsplit(prefix).netloc:
        # No-op if not in debug mode or a non-local prefix.
        return []
    return [
        re_path(r'^%s(?P<path>.*)$' % re.escape(prefix.lstrip('/')), view, kwargs=kwargs),
    ]

 

01 path( )

  • 기능: URL경로(route)를 views 함수(view)에 전달
from django.urls import path
path(route, view, kwargs=None, name=None)
  • 사용 예
    from django.urls import include, path
    
    urlpatterns = [
        path("index/", views.index, name="main-view"),
        path("bio/<username>/", views.bio, name="bio"),
        path("articles/<slug:title>/", views.article, name="article-detail"),
        path("articles/<slug:title>/<int:section>/", views.section, name="article-section"),
        path("blog/", include("blog.urls")),
        ...,
    ]​
  • return 값: urlpattern에 들어갈 요소
  • parameters:
    • route (required): str 또는 gettext_lazy( ) 자료형의 URL pattern
      • route가 빈 따옴표('')면 root url을 의미
      • URL 일부를 캡쳐해서 keyword argument로 view에 보내는 역할을 하는 angle brackets (<keyword argument>) 포함 가능
    • view (required): views.함수명 또는 클래스명.as_view() 또는 django.urls.include( )   
      • Function-Based View의 경우 views.함수명 (i.e., view 함수)
      • Class-Based View의 경우 클래스명.as_view( ) (i.e., as_view( )의 결과값)
    • kwargs (optional): view function이나 method에 넣을 dict 자료형의 추가적인 인자
    • name (optional): 해당 URL pattern에 사용할 str 자료형의 urlpattern 이름
      • URL reversing을 하려면 URL pattern의 이름을 정해줘야 함
      • 즉, URL별로 변수를 만들어 활용하는 경우 사용
  • converter로 제한 (converter specification)
    • converter specification을 통해 매칭되는 characters를 제한하고 view에 전달되는 변수의 타입 변경 가능
    • <converter:전달할keyword_argument명> 로 입력시 입력받는 데이터를 제한 가능
    • converter 종류
      • str : 경로 구분자를 제외한 비어 있지 않은 문자열
      • path: 경로 구분자를 포함한 비어 있지 않은 문자열
      • int : 0 또는 임의의 양의 정수
      • slug : 문자 또는 숫자와 하이픈 및 밑줄 문자로 구성된 슬러그 문자열 (예: SHOW-ME-THE-MONEY)
    • 사용 예
      # urls.py
      ... 생략
      urlpatterns = [ 
      	... 생략 
          path('<username>/<int:articleId>', views.get_user_article), 
      ]​
      # views.py
      ...생략
      def get_user_article(request, username, articleId):
          print("username : ", username)
          print("articleId", articleId)
          return HttpResponse("{}의 블로그 {}번 글이 출력됩니다!".format(username, articleId))
      ... 생략
      • 테스트 서버를 실행하여 아래 경로로 들어가보기:
        • http://127.0.0.1:8000/path/suwoni/1
        • http://127.0.0.1:8000/path/suwoni/03
        • http://127.0.0.1:8000/path/suwoni/test
      • 결과:
        • 첫번째, 두번째는 정상적으로 화면 출력됨
        • 두번째는 03이 3으로 변환되어 출력됨
        • 세번째는 int타입이 아닌 test문자열로 인해 Page not found(404) 에러 발생

02 re_path( )

  • 기능: 정규식으로 표현된 URL경로(route)를 views 함수(view)에 전달
  • NOTE:
    • django 2.0에서 새롭게 추가된 함수
    • django 2.0 이전에 사용하던 url 함수와 동일
from django.urls import re_path
re_path(route, view, kwargs=None, name=None)
  • 사용 예
    from django.urls import include, re_path
    
    urlpatterns = [
        re_path(r"^index/$", views.index, name="index"),
        re_path(r"^bio/(?P<username>\w+)/$", views.bio, name="bio"),
        re_path(r"^blog/", include("blog.urls")),
        ...,
    ]
  • return 값: urlpattern에 들어갈 요소
  • parameters:
    • route: str 또는 gettext_lazy() 자료형의 Python 의 re 모듈 정규식
      • 기호 종류
        • ^: 정규식 시작 
        • $: 정규식 종료
        • r: escape
      • str은 raw string syntax (r' ') 을 사용
        • 이때 \d의 escape 를 위한 \ 가 추가로 필요하지 않음
      • match 성공시 정규식(regular expression)으로 캡쳐된 group은 (이름이 지정된 경우) 이름이 지정된 인자 또는 (이름이 지정되지 않은 경우) positional 인자로 view에 str으로 전달됨
      • route 가 $로 끝나는 경우 전체 요청 URL은 regular expression pattern 과 일치해야 함 (re.fullmatch() 사용)
    • view: view function 또는 (class-based view 의 경우) as_view() 의 결과값 또는 django.urls.include()
    • kwargs: view function이나 method에 넣을 추가적인 인자
    •  name: 해당 URL pattern에 사용할 str 자료형의 urlpattern 이름
      • URL reversing을 하려면 URL pattern의 이름을 정해줘야 함

03 include( )

  • 기능: 다른 app의 urls.py (URLconf 모듈)를 연결/포해주는 함수
from django.urls import include
# 3가지 방법
include(module, namespace=None)
include(pattern_list)
include((pattern_list, app_namespace), namespace=None)
  • 사용 예
    from django.conf.urls import url
    url(r'^dcv/account/', include('allauth.urls'))
  •  parameters:
    • module (required): URLconf 모듈 (또는 모듈명)
    • namespace (optional): str 자료형의 instance namespace for the URL entries being included
      • Optionally, the application namespace and instance namespace where the entries will be included into can also be specified.
      • Usually, the application namespace should be specified by the included module. If an application namespace is set, the namespace argument can be used to set a different instance namespace.
    • pattern_list: iterable of path() and/or re_path() instances
    • app_namespace : str 자료형의 application namespace for the URL entries being included

04 register_converter()

  • 기능: registers a converter for use in path() routes
register_converter(converter, type_name)
  • 사용 예
    from django.urls import path, register_converter
    from . import converters, views
    
    register_converter(converters.FourDigitYearConverter, "yyyy")
    
    urlpatterns = [
        path("articles/2003/", views.special_case_2003),
        path("articles/<yyyy:year>/", views.year_archive),
        ...,
    ]
  • 파라미터:
    • converter: converter class
    • type_name: path pattern에 사용할 str 자료형의 converter 이름

05 static( )

  • 기능: Helper function to return a URL pattern for serving files in debug mode
from django.conf.urls.static import static
static(prefix, view=django.views.static.serve, **kwargs)
  • 사용 예
    from django.conf import settings
    from django.conf.urls.static import static
    
    urlpatterns = [
        # ... the rest of your URLconf goes here ...
    ] + static(settings.STATIC_URL, document_root=settings.STATIC_ROOT)

 

2024.03.20 - [분류 전체보기] - Django Static 파일 관리하기 (image, JavaScript, CSS, etc.)

참조

  1. https://docs.djangoproject.com/en/5.0/ref/urls/
  2. https://holsui.tistory.com/98
  3. https://velog.io/@stresszero/%EC%9E%A5%EA%B3%A0-URL-%EC%A0%95%EB%A6%AC
  4. https://suwoni-codelab.com/django/2018/03/23/Django-Url-function/
  5. https://docs.djangoproject.com/en/5.0/howto/static-files/  
  6.  

 

 

 

 

'Web 개발 > Django' 카테고리의 다른 글

QuerySet API (쿼리셋 API)  (0) 2024.03.25
Django의 values() vs values_list()  (0) 2024.03.25
Django Static 파일 관리하기 (image, JavaScript, CSS, etc.)  (0) 2024.03.20
Django  (0) 2024.03.18