Django 3如何将详情页URL从ID形式改为ID+标题形式
没问题,我来帮你把详情页URL改成包含标题的SEO友好形式,咱们一步步来操作:
步骤1:给模型添加Slug字段
首先需要在你的Deneme1Model里添加一个slug字段,用来存储标题的URL友好版本(比如把“Bu Konu Başlığı”转换成“bu-konu-basligi”)。修改models.py:
from django.db import models from django.utils import timezone from django.utils.text import slugify # 导入slugify工具 class Deneme1Model(models.Model): baslik = models.CharField(max_length=50, verbose_name='BAŞLIK') aKaydi = models.CharField(max_length=50, verbose_name='A KAYDI') dosyaYukle = models.FileField(upload_to='media', verbose_name='DOSYA YÜKLE') yuklemeTarihi = models.DateTimeField(default=timezone.now) yayinKontrol = models.BooleanField(default=True) slug = models.SlugField(max_length=255, blank=True, verbose_name='URL BAŞLIĞI') # 新增slug字段 def save(self, *args, **kwargs): # 如果slug为空,自动从标题生成 if not self.slug: self.slug = slugify(self.baslik) super().save(*args, **kwargs) def __str__(self): return self.baslik
步骤2:更新视图函数
现在URL里会包含pk和slug两个参数,所以需要修改detay视图的参数,同时把查询逻辑改成更高效的单个对象查询(原来的filter返回的是QuerySet,用get_object_or_404更合适,没找到对象会自动返回404页面):
from django.shortcuts import render, get_object_or_404 from django.utils import timezone from .models import * def index(request): girdiler = Deneme1Model.objects.filter(yuklemeTarihi__lte=timezone.now()).order_by('-yuklemeTarihi') context ={ 'girdiler':girdiler } return render(request, 'deneme1Uygulama/index.html', context) def ekle(request): return render(request, 'deneme1Uygulama/ekle.html') def detay(request, pk, slug): # 新增slug参数 girdi = get_object_or_404(Deneme1Model, pk=pk) # 获取单个对象 context ={ 'girdi': girdi # 传递单个对象到模板 } return render(request, 'deneme1Uygulama/detay.html', context) def sayfaYok(request): return render(request, 'deneme1Uygulama/404.html')
步骤3:修改URL配置
更新urls.py里的详情页路径,添加slug参数:
from django.urls import path from .import views from django.conf import settings from django.conf.urls.static import static urlpatterns = [ path('', views.index, name='index'), path('ekle/', views.ekle, name='ekle'), path('detay/<int:pk>/<slug:slug>/', views.detay, name='detay'), # 修改这里,添加slug部分 path('404/', views.sayfaYok, name='sayfaYok'), ] urlpatterns += static(settings.MEDIA_URL, document_root=settings.MEDIA_ROOT)
步骤4:更新模板中的URL链接
现在需要修改detay.html里的URL生成逻辑,同时因为视图现在传递的是单个对象,所以可以去掉循环(原来的循环是多余的,因为一个pk对应一个对象):
{% block content %} <div class="row"> <div class="col-12 d-flex justify-content-center"> <div class="card w-100"> <img class="card-img-top img-fluid umaxhe20" src="{{ girdi.dosyaYukle.url }}" alt="Card image cap"> <div class="card-body"> <h5 class="card-title">{{ girdi.baslik }}</h5> <p class="card-text">{{ girdi.aKaydi }}</p> <a href="{% url 'detay' girdi.pk girdi.slug %}" class="btn btn-primary">{{ girdi.yuklemeTarihi }}</a> </div> </div> </div> </div> {% endblock content %}
注意:如果你的index.html里也有指向详情页的链接,记得同样修改成{% url 'detay' girdi.pk girdi.slug %}。
步骤5:处理现有数据的Slug
因为我们新增了slug字段,需要先执行数据迁移,然后给已有的数据生成slug:
- 生成迁移文件:
python manage.py makemigrations
- 执行迁移:
python manage.py migrate
- 给现有数据批量生成slug:
打开Django Shell:
python manage.py shell
然后执行以下代码:
from deneme1Uygulama.models import Deneme1Model from django.utils.text import slugify for girdi in Deneme1Model.objects.all(): if not girdi.slug: girdi.slug = slugify(girdi.baslik) girdi.save()
完成这些步骤后,你的详情页URL就会变成http://localhost:8000/detay/1/bu-konu-basligi这样的形式啦!而且即使标题修改了,只要pk不变,旧的URL依然能访问(因为视图只靠pk查询),不会影响SEO。
内容的提问来源于stack exchange,提问作者sefaseker




