راهنمای کامل URL Dispatcher در Django: نحوهٔ مسیردهی، URLconf، مبدل‌ها و الگوهای URL

این مقاله نحوهٔ کار URL dispatcher در Django را توضیح می‌دهد. از ساخت URLconf و نحوهٔ پردازش درخواست‌ها، تا تعریف الگوهای URL، استفاده از path converters، و ساخت مبدل‌های سفارشی. همچنین مثال‌های عملی برای درک بهتر نحوهٔ مسیردهی در Django ارائه شده است.

URLconf، URL dispatcher، path، re_path، path convertersregister_converterDjango routing

~10 دقیقه مطالعه • بروزرسانی ۱۹ اسفند ۱۴۰۴

۱. مقدمه: اهمیت طراحی URLهای تمیز

در یک وب‌اپلیکیشن حرفه‌ای، داشتن URLهای تمیز، قابل‌خواندن و پایدار اهمیت زیادی دارد. Django به شما اجازه می‌دهد بدون هیچ محدودیتی URLها را دقیقاً همان‌طور که می‌خواهید طراحی کنید.

برای تعریف URLها، یک ماژول پایتونی به نام URLconf می‌سازید که URLها را به viewها نگاشت می‌کند.

۲. URLconf چیست؟

URLconf یک فایل پایتونی است که شامل یک لیست به نام urlpatterns است. هر عضو این لیست یک الگوی URL است که به یک view متصل می‌شود.

۳. Django چگونه یک درخواست را پردازش می‌کند؟

  1. Django ابتدا URLconf اصلی را پیدا می‌کند (از ROOT_URLCONF یا مقدار تعیین‌شده در middleware).
  2. ماژول URLconf را بارگذاری کرده و به دنبال urlpatterns می‌گردد.
  3. الگوهای URL را به ترتیب بررسی می‌کند و اولین الگویی که با URL درخواست‌شده مطابقت داشته باشد انتخاب می‌شود.
  4. view مربوطه فراخوانی می‌شود و:
    • شیء HttpRequest
    • آرگومان‌های موقعیتی (اگر گروه‌های بدون نام وجود داشته باشد)
    • آرگومان‌های کلیدی (اگر گروه‌های نام‌دار وجود داشته باشد)
    به آن ارسال می‌شود.
  5. اگر هیچ الگویی مطابقت نداشته باشد، Django یک view خطا (مثل 404) را اجرا می‌کند.

۴. مثال یک URLconf


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),
]

نکات مهم:

  • برای گرفتن مقدار از URL از براکت‌های زاویه‌دار استفاده کنید.
  • می‌توانید نوع داده را مشخص کنید (مثل <int:year>).
  • نیازی به اسلش ابتدایی نیست.
  • ترتیب الگوها مهم است؛ اولین الگوی مطابق انتخاب می‌شود.

مثال درخواست‌ها:

  • /articles/2005/03/ → فراخوانی views.month_archive(request, year=2005, month=3)
  • /articles/2003/ → الگوی اول را match می‌کند.
  • /articles/2003 → هیچ الگویی match نمی‌شود (اسلش انتهایی لازم است).
  • /articles/2003/03/building-a-django-site/ → فراخوانی views.article_detail(...)

۵. مبدل‌های مسیر (Path Converters)

مبدلتوضیح
strهر رشتهٔ غیرخالی به‌جز "/" (پیش‌فرض)
intعدد صحیح مثبت یا صفر → تبدیل به int
slugحروف، اعداد، خط تیره و زیرخط
uuidUUID معتبر → تبدیل به شیء UUID
pathهر رشتهٔ غیرخالی شامل "/"

۶. ساخت مبدل سفارشی

اگر مبدل‌های پیش‌فرض کافی نباشند، می‌توانید مبدل خودتان را بسازید.

یک مبدل سفارشی باید شامل موارد زیر باشد:

  • regex — الگوی تطبیق
  • to_python() — تبدیل مقدار URL به نوع پایتونی
  • to_url() — تبدیل مقدار پایتونی به رشتهٔ URL

مثال: مبدل سال چهاررقمی


class FourDigitYearConverter:
    regex = "[0-9]{4}"

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

    def to_url(self, value):
        return "%04d" % value

ثبت مبدل سفارشی:


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),
]

۷. چرا URLconf قدرتمند است؟

  • کاملاً پایتونی است و می‌تواند پویا باشد.
  • می‌توان URLconfهای جداگانه برای هر اپ ساخت.
  • پشتیبانی از بین‌المللی‌سازی URLها.
  • امکان ساخت URLهای تمیز، پایدار و قابل‌خواندن.

جمع‌بندی

URL dispatcher یکی از ویژگی‌های قدرتمند Django است که کنترل کامل بر ساختار URLها را در اختیار شما قرار می‌دهد. با استفاده از URLconf، path converters و مبدل‌های سفارشی، می‌توانید ساختارهای URL تمیز، قابل‌گسترش و حرفه‌ای برای پروژهٔ خود طراحی کنید.

۱. چرا از Regular Expression استفاده کنیم؟

اگر path() و مبدل‌های آن برای نیازهای شما کافی نباشند، Django امکان استفاده از re_path() را فراهم می‌کند تا بتوانید URLهای پیچیده‌تر را با regex تعریف کنید.

۲. سینتکس گروه‌های نام‌دار در regex

در پایتون، گروه‌های نام‌دار به شکل زیر تعریف می‌شوند:


(?Ppattern)

۳. مثال URLconf با re_path()


urlpatterns = [
    path("articles/2003/", views.special_case_2003),
    re_path(r"^articles/(?P[0-9]{4})/$", views.year_archive),
    re_path(r"^articles/(?P[0-9]{4})/(?P[0-9]{2})/$", views.month_archive),
    re_path(
        r"^articles/(?P[0-9]{4})/(?P[0-9]{2})/(?P[\w-]+)/$",
        views.article_detail,
    ),
]

تفاوت‌ها با path()

  • regex کنترل دقیق‌تری روی الگوها می‌دهد (مثلاً سال دقیقاً ۴ رقمی).
  • تمام مقادیر به صورت string به view ارسال می‌شوند.
  • در صورت تغییر از path به re_path، ممکن است نیاز باشد viewها را تطبیق دهید.

۴. گروه‌های بدون‌نام


([0-9]{4})

این روش توصیه نمی‌شود، چون:

  • به‌راحتی باعث اشتباه در ترتیب آرگومان‌ها می‌شود.
  • اگر با گروه‌های نام‌دار ترکیب شود، گروه‌های بدون‌نام نادیده گرفته می‌شوند.

۵. Nested Arguments (آرگومان‌های تو در تو)

regex اجازهٔ گروه‌بندی تو در تو را می‌دهد، اما این کار می‌تواند مشکلاتی ایجاد کند.

مثال بد:


re_path(r"^blog/(page-([0-9]+)/)?$", blog_articles)

مثال خوب:


re_path(r"^comments/(?:page-(?P[0-9]+)/)?$", comments)

نکتهٔ مهم:

  • فقط مقادیری را capture کنید که view واقعاً به آن‌ها نیاز دارد.
  • برای گروه‌هایی که view به آن‌ها نیاز ندارد از non‑capturing group استفاده کنید: (?:...)

۶. URLconf دقیقاً چه چیزی را بررسی می‌کند؟

فقط مسیر URL را بررسی می‌کند، نه:

  • پارامترهای GET
  • پارامترهای POST
  • نام دامنه
  • نوع درخواست (GET/POST/HEAD)

مثال:

در URL زیر:


https://example.com/myapp/?page=3

URLconf فقط myapp/ را بررسی می‌کند.

۷. تعیین مقادیر پیش‌فرض برای view

می‌توانید برای viewها آرگومان پیش‌فرض تعیین کنید:


urlpatterns = [
    path("blog/", views.page),
    path("blog/page/", views.page),
]

def page(request, num=1):
    ...

۸. Performance

regexها فقط یک‌بار کامپایل می‌شوند و سپس کش می‌شوند، بنابراین سرعت پردازش بالا می‌ماند.

۹. مدیریت خطاها

می‌توانید viewهای خطا را در URLconf اصلی تنظیم کنید:

  • handler400
  • handler403
  • handler404
  • handler500

۱۰. include کردن URLconfهای دیگر

برای ساختاردهی بهتر پروژه، می‌توانید URLconfهای دیگر را include کنید:


urlpatterns = [
    path("community/", include("aggregator.urls")),
    path("contact/", include("contact.urls")),
]

نکته:

Django بخش match شده را حذف می‌کند و ادامهٔ URL را به URLconf جدید می‌فرستد.

۱۱. ارسال پارامترهای اضافی به view


path("blog//", views.year_archive, {"foo": "bar"})

در این حالت:


views.year_archive(request, year=2005, foo="bar")

تعارض پارامترها

اگر نام پارامتر در URL و دیکشنری یکی باشد، مقدار دیکشنری استفاده می‌شود.

جمع‌بندی

استفاده از re_path() در Django قدرت و انعطاف بسیار زیادی برای تعریف URLهای پیچیده فراهم می‌کند. با درک صحیح گروه‌های نام‌دار، گروه‌های بدون‌نام، nested arguments، include، و مدیریت خطاها، می‌توانید URLهایی تمیز، قابل‌گسترش و حرفه‌ای طراحی کنید.

۱. ارسال گزینه‌های اضافی به include()

Django اجازه می‌دهد هنگام استفاده از include()، آرگومان‌های اضافی به صورت دیکشنری ارسال کنید. این آرگومان‌ها به تمام الگوهای URL داخل URLconf واردشده منتقل می‌شوند.

دو URLconf که از نظر عملکرد یکسان هستند

مجموعهٔ اول:


# main.py
urlpatterns = [
    path("blog/", include("inner"), {"blog_id": 3}),
]

# inner.py
urlpatterns = [
    path("archive/", views.archive),
    path("about/", views.about),
]

مجموعهٔ دوم:


# main.py
urlpatterns = [
    path("blog/", include("inner")),
]

# inner.py
urlpatterns = [
    path("archive/", views.archive, {"blog_id": 3}),
    path("about/", views.about, {"blog_id": 3}),
]

نکتهٔ مهم: گزینه‌های اضافی به تمام viewهای داخل URLconf واردشده ارسال می‌شوند، حتی اگر view آن‌ها را قبول نکند. بنابراین فقط زمانی از این تکنیک استفاده کنید که مطمئن باشید همهٔ viewها این پارامترها را پشتیبانی می‌کنند.

۲. معکوس‌سازی URLها (URL Reversing)

معکوس‌سازی URL یعنی تولید URL بر اساس نام view و آرگومان‌های آن، بدون نیاز به هاردکد کردن مسیرها. این کار باعث می‌شود ساختار URL پروژه تغییر کند بدون اینکه مجبور باشید کل کد را اصلاح کنید.

ابزارهای Django برای URL reversing:

  • در قالب‌ها: تگ {% url %}
  • در پایتون: تابع reverse()
  • در مدل‌ها: متد get_absolute_url()

مثال URLconf:


path("articles/<int:year>/", views.year_archive, name="news-year-archive")

در قالب:


2012 Archive

در پایتون:


return HttpResponseRedirect(reverse("news-year-archive", args=(2006,)))

اگر ساختار URL تغییر کند، فقط URLconf را اصلاح می‌کنید و همهٔ reverseها همچنان درست کار می‌کنند.

۳. نام‌گذاری الگوهای URL

برای استفاده از reverse، باید الگوهای URL را نام‌گذاری کنید.

بهترین روش‌ها:

  • نام‌های یکتا انتخاب کنید.
  • از پیشوند اپلیکیشن استفاده کنید (مثلاً blog-archive).
  • می‌توانید عمداً نام یکسان بدهید تا view دیگری را override کنید (مثل login).
  • می‌توانید چند الگوی URL با نام یکسان داشته باشید اگر آرگومان‌هایشان متفاوت باشد.

۴. URL Namespaces

Namespaces برای جلوگیری از تداخل نام‌ها و پشتیبانی از چندین نمونه از یک اپلیکیشن استفاده می‌شوند.

دو نوع namespace:

نوعتوضیح
Application namespaceنام اپلیکیشن (مثلاً 'admin')
Instance namespaceنام نمونهٔ خاص از اپلیکیشن

مثال:

صفحهٔ اصلی admin با این نام شناخته می‌شود:


'admin:index'

Namespaces تو در تو:


'sports:polls:index'

این یعنی:

  • namespace سطح بالا: sports
  • namespace داخلی: polls
  • نام URL: index

۵. چرا Namespaces مهم هستند؟

  • از تداخل نام‌ها جلوگیری می‌کنند.
  • اجازه می‌دهند یک اپلیکیشن را چند بار در پروژه استفاده کنید.
  • معکوس‌سازی URL را دقیق و قابل‌اعتماد می‌کنند.

جمع‌بندی

ویژگی‌های پیشرفتهٔ مسیردهی در Django — شامل extra options برای include، معکوس‌سازی URL، نام‌گذاری الگوها و استفاده از namespaces — ابزارهایی قدرتمند برای ساخت معماری URL تمیز، مقیاس‌پذیر و قابل نگهداری هستند. با رعایت این اصول، پروژهٔ شما ساختاری حرفه‌ای و قابل توسعه خواهد داشت.

۱. مقدمه: چرا Namespaced URLs مهم هستند؟

وقتی چند اپلیکیشن یا چند نمونه از یک اپلیکیشن در پروژه دارید، ممکن است الگوهای URL نام‌های یکسان داشته باشند. Django با استفاده از namespaces این مشکل را حل می‌کند و امکان معکوس‌سازی دقیق URLها را فراهم می‌سازد.

۲. نحوهٔ معکوس‌سازی URLهای namespaced

وقتی Django یک نام مثل 'polls:index' را دریافت می‌کند، آن را به دو بخش تقسیم می‌کند:

  • namespace → polls
  • view name → index

مراحل Django برای پیدا کردن URL مناسب:

  1. جستجوی application namespace Django ابتدا به دنبال اپلیکیشنی با namespace برابر polls می‌گردد.
  2. اگر current_app مشخص شده باشد Django از همان instance استفاده می‌کند. این مقدار می‌تواند از طریق:
    • current_app=... در reverse()
    • request.current_app در قالب‌ها
    تعیین شود.
  3. اگر current_app وجود نداشته باشد Django به دنبال default instance می‌گردد؛ یعنی instanceای که instance namespace آن برابر application namespace باشد.
  4. اگر default instance وجود نداشته باشد Django آخرین instance ثبت‌شده را انتخاب می‌کند.
  5. اگر namespace پیدا نشود Django namespace را به عنوان instance namespace بررسی می‌کند.
  6. برای namespaceهای تو در تو این مراحل برای هر بخش namespace تکرار می‌شود تا فقط view name باقی بماند.

۳. مثال: دو instance از اپلیکیشن polls

urls.py


urlpatterns = [
    path("author-polls/", include("polls.urls", namespace="author-polls")),
    path("publisher-polls/", include("polls.urls", namespace="publisher-polls")),
]

polls/urls.py


app_name = "polls"

urlpatterns = [
    path("", views.IndexView.as_view(), name="index"),
    path("<int:pk>/", views.DetailView.as_view(), name="detail"),
]

نتایج:

  • اگر در instance author-polls باشید:
    
    reverse("polls:index", current_app="author-polls")
    
    نتیجه: /author-polls/
  • در قالب:
    
    {% url 'polls:index' %}
    
    اگر current_app = 'author-polls' باشد → همان مسیر را تولید می‌کند.
  • اگر current_app وجود نداشته باشد:
    • Django آخرین instance ثبت‌شده را انتخاب می‌کند → publisher-polls
  • 'author-polls:index' همیشه به instance مربوطه اشاره می‌کند.
  • اگر instanceای به نام polls نیز وجود داشته باشد:
    • این instance به عنوان default انتخاب می‌شود.

۴. تعریف application namespace در URLconf

برای اینکه include() بتواند namespace را تشخیص دهد، باید در فایل URLconf اپلیکیشن مقدار app_name را تعریف کنید:


app_name = "polls"
urlpatterns = [...]

سپس در فایل اصلی:


path("polls/", include("polls.urls"))

در این حالت، application namespace برابر polls خواهد بود.

۵. تعریف namespace با استفاده از tuple

می‌توانید یک tuple شامل الگوهای URL و application namespace را include کنید:


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)),
]

۶. instance namespace

برای تعیین instance namespace از آرگومان namespace= در include() استفاده کنید:


path("author-polls/", include("polls.urls", namespace="author-polls"))

اگر namespace مشخص نشود، instance namespace همان application namespace خواهد بود.

جمع‌بندی

Namespaced URLs یکی از قدرتمندترین ویژگی‌های Django برای مدیریت پروژه‌های بزرگ و چندبخشی است. با درک نحوهٔ کار application namespace، instance namespace، و منطق Django در انتخاب namespace مناسب، می‌توانید ساختار URL پروژه را بسیار تمیز، قابل‌گسترش و پایدار طراحی کنید.

نوشته و پژوهش شده توسط دکتر شاهین صیامی