آدرسها را اسمگذاری کنید، نه هاردکد
URLconf جنگو فهرستی از الگوهاست که از بالا به پایین بررسی میشوند و اولین تطابق برنده است. در پروژهی تمیز، config/urls.py فقط مسیرهای اصلی را به اپها «include» میکند و هر اپ فایل urls خودش را دارد.
# config/urls.py
from django.conf import settings
from django.conf.urls.static import static
from django.contrib import admin
from django.urls import include, path
urlpatterns = [
path("manage-7f3a/", admin.site.urls), # آدرس ادمین را حدسزدنی نگذارید
path("orders/", include("apps.orders.urls")),
path("api/", include("apps.api.urls")),
path("", include("apps.pages.urls")),
]
if settings.DEBUG:
urlpatterns += static(settings.MEDIA_URL, document_root=settings.MEDIA_ROOT)
# apps/orders/urls.py
from django.urls import path, register_converter
from . import converters, views
register_converter(converters.TrackingCodeConverter, "tcode")
app_name = "orders" # namespace
urlpatterns = [
path("", views.OrderListView.as_view(), name="list"),
path("new/", views.OrderCreateView.as_view(), name="create"),
path("<int:pk>/", views.order_detail, name="detail"),
path("<int:pk>/cancel/", views.cancel_order, name="cancel"),
path("track/<tcode:code>/", views.track, name="track"),
]
converterها
| converter | تطبیق | نوع در View |
|---|---|---|
| int | ارقام 0 تا 9 | int |
| str | هر چیزی جز / (پیشفرض) | str |
| slug | حروف و ارقام لاتین، - و _ | str |
| uuid | UUID با خط تیره | UUID |
| path | هر چیزی حتی / | str |
converter اختصاصی فقط یک کلاس با regex، to_python و to_url است:
# apps/orders/converters.py
class TrackingCodeConverter:
regex = r"KSH[0-9A-F]{8}"
def to_python(self, value):
return value.upper()
def to_url(self, value):
return value
reverse: ساخت آدرس از روی نام
from django.urls import reverse, reverse_lazy
reverse("orders:detail", kwargs={"pk": 42}) # '/orders/42/'
reverse("orders:track", args=["KSH1A2B3C4D"])
class OrderCreateView(CreateView):
success_url = reverse_lazy("orders:list") # در سطح کلاس، lazy لازم است
در قالب: {% url 'orders:detail' pk=order.pk %}. اگر فردا آدرس را از orders/ به sefaresh/ تغییر دهید، هیچ لینکی نمیشکند.
نکتههایی که کمتر کسی میداند
- اگر
to_pythonیک converter استثنایValueErrorبدهد، جنگو آن را «عدم تطابق» حساب میکند و سراغ الگوی بعدی میرود؛ راهی تمیز برای رد کردن مقادیر نامعتبر پیش از رسیدن به View. - با
APPEND_SLASH(پیشفرض True) درخواست GET به/orders/42به/orders/42/ریدایرکت میشود؛ اما برای POST در DEBUG خطای RuntimeError میگیرید، چون دادهی POST در ریدایرکت از دست میرود. - در سطح کلاس و ماژول (success_url، تنظیمات LOGIN_URL) از
reverse_lazyاستفاده کنید؛reverseدر زمان import، قبل از بارگذاری URLconf، خطا میدهد. python manage.py show_urlsجزو جنگو نیست (از django-extensions است)؛ بدون آن هم میتوانید در shell باget_resolver().reverse_dictالگوها را ببینید، یاresolve("/orders/42/")بزنید تا بفهمید آدرس به کدام View میرسد.- ثبت دوبارهی یک converter با همان نام در Django 5.1 به بعد منسوخ و خطاساز است؛
register_converterرا فقط یک بار و در یک ماژول انجام دهید.