Работа с URL
Зачем красивые адреса
Адрес /blog/17/ ничего не говорит ни человеку, ни поисковой системе. Сравните с /blog/2026/10/25/kak-ustroen-django/:
- видно, о чём статья и когда опубликована;
- поисковики учитывают слова в адресе;
- адресом удобно делиться.
Сделаем адрес поста из даты публикации и слага.
Уникальность слага
Два поста с одинаковым слагом в один день сделают адрес неоднозначным. Параметр unique_for_date запрещает такие дубликаты:
class Post(models.Model):
...
slug = models.SlugField(max_length=250, unique_for_date="publish")
Это проверка уровня валидации модели (админка, ModelForm), а не ограничение базы. Миграция всё равно нужна, чтобы зафиксировать изменение поля:
python manage.py makemigrations blog
python manage.py migrate
Новый маршрут
blog/urls.py:
urlpatterns = [
path("", views.post_list, name="post_list"),
path(
"<int:year>/<int:month>/<int:day>/<slug:post>/",
views.post_detail,
name="post_detail",
),
]
Конвертеры int и slug проверят формат: /blog/2026/ten/25/.../ не совпадёт с маршрутом.
Новая view
def post_detail(request, year, month, day, post):
post = get_object_or_404(
Post,
status=Post.Status.PUBLISHED,
slug=post,
publish__year=year,
publish__month=month,
publish__day=day,
)
return render(request, "blog/post/detail.html", {"post": post})
Параметр называется post, а не slug, — чтобы совпасть с именем в маршруте. После поиска мы переиспользуем имя для объекта — так часто делают, но если путаетесь, назовите параметр slug, изменив и маршрут.
Часовые пояса
При USE_TZ = True lookups __year/__month/__day работают в текущем часовом поясе (TIME_ZONE). Поэтому пост, опубликованный в 01:00 по Москве, будет иметь в адресе «московскую» дату, хотя в базе хранится время UTC.
Канонический адрес
Главное изменение — в одном месте:
def get_absolute_url(self):
return reverse(
"blog:post_detail",
args=[self.publish.year, self.publish.month, self.publish.day, self.slug],
)
Все шаблоны используют {{ post.get_absolute_url }}, поэтому ссылки на сайте обновились автоматически. Вот она — польза от get_absolute_url и DRY.
Проверка
Откройте /blog/, кликните по посту — адрес теперь вида /blog/2026/10/25/moj-post/. Старый адрес /blog/5/ больше не работает. Если сайт уже был проиндексирован, стоит оставить старый маршрут с постоянным редиректом на новый:
def post_by_id(request, id):
post = get_object_or_404(Post.published, id=id)
return redirect(post, permanent=True)
Итог
- Адрес поста:
год/месяц/день/слаг. unique_for_date="publish"не даёт создать два поста с одинаковым слагом в один день.- View ищет по слагу и lookups
publish__year/month/day. - Благодаря
get_absolute_urlссылки во всех шаблонах обновились сами.
Практика решено 0 из 5
Напишите маршрут для адресов вида /blog/2026/10/25/moj-post/: параметры year, month, day (целые)
и post (слаг), view — views.post_detail, имя — post_detail.
path("<int:year>/<int:month>/<int:day>/<slug:post>/", views.post_detail, name="post_detail")
Перепишите get_absolute_url, чтобы он строил новый адрес: год, месяц, день из self.publish и self.slug.
def get_absolute_url(self):
return reverse(
"blog:post_detail",
args=[self.publish.year, self.publish.month, self.publish.day, self.slug],
)
Обновите get_object_or_404 в post_detail(request, year, month, day, post):
ищите опубликованный пост по слагу post и году, месяцу, дню даты publish.
def post_detail(request, year, month, day, post):
post = get_object_or_404(
Post,
status=Post.Status.PUBLISHED,
slug=post,
publish__year=year,
publish__month=month,
publish__day=day,
)
return render(request, "blog/post/detail.html", {"post": post})
Сделайте поле slug уникальным в пределах даты публикации, используя параметр поля
unique_for_date.
slug = models.SlugField(max_length=250, unique_for_date="publish")
Где проверяется unique_for_date?
Войдите, чтобы отмечать пройденные уроки.