如何使用反向工作在Django中创建Stackoverflow样式的URL,以允许slug更改?

时间:2015-06-23 13:19:59

标签: python django django-urls

如何制作与Stackoverflow类似的URL方案?

这与this one不是同一个问题,尽管它类似。不同之处在于我需要一个实现Stackoverflow所有智能的URL方案,并允许reverse生成完全slugged的URL。

具体来说,Stackoverflow行为模仿:

  1. 仅通过id查找,仅用于搜索引擎优化/可读性目的
  2. 当不正确的段塞或没有塞子时,转发纠正段塞。例如如果我有一个名为123的对象My Object Name

    /123/ redirects to /123/my-object-name/
    /123/something/ redirects to /123/my-object-name/
    
  3. 如果我将对象名称更改为My New Object Name,则重定向目标slug会相应更改(这是Stackoverflow的行为,如果您编辑问题的标题),例如:

    /123/my-object-name/ redirects to /123/my-new-object-name/
    
  4. 反向工作,以便{% url 'my_view' 123 %}返回/123/my-object-name/,并在编辑对象名称后返回/123/my-new-object-name/
  5. 我已经攻击了使用models.py

    的内容
    class MyModel(models.Model):
        name = models.CharField(max_length=70)
    
        def my_slugged_url(self):
            slug = slugify(self.name)
            if slug:
                return reverse('my_view', args=[self.id]) + slug + "/"
            return reverse('my_view', args=[self.id])
    

    ...和urls.py模式:

    url(r'^(\d+)/\S+/$', 'my_view')
    url(r'^(\d+)/$', 'my_view'),
    

    ...和views.py

    def my_view(request, id):
        obj = get_object_or_404(MyModel, pk=id)
        if request.path != obj.my_slugged_url():
            return redirect(obj.my_slugged_url())
    

    ...但这感觉不对,这意味着当我执行reverse{% url 'my_view' 123 %}时,它会返回/123/之类的网址,然后必须重定向到/123/my-object-name

    如何使这项工作像Stackoverflow一样?

2 个答案:

答案 0 :(得分:2)

鉴于你的反复评论 - “......模式将如何知道哪个slug返回”,看起来你很难理解它是如何工作的。我会尝试为你分解这个过程。

首先,您将编写两个指向一个视图的网址图案。请记住两种模式都不同name

# urls.py
...
url(r'^(?P<object_id>\d+)/$', 'my_view', name='my-view-no-slug'),
url(r'^(?P<object_id>\d+)/(?P<slug>\S+)/$', 'my_view', name='my-view-slug'),
...

现在,这就是它变得有趣的地方:

  1. 每当向/123/发出请求时,它都会匹配第一个网址格式。
  2. 当向/123/my-object-name/发出请求时,它将与第二种模式匹配。
  3. 当请求使用错误的段落时,例如 - /123/some-wrong-slug/,它也会匹配第二个模式。别担心,你会在视图中检查错误的slu ..
  4. 但是所有三个请求都将由一个视图处理。

    其次,在模型中定义名为property的{​​{1}}。您将使用它来生成和访问对象的slu ..

    slug

    最后,处理请求的视图应如下所示:

    # models.py
    
    class MyModel(...):
        ...
    
        @property
        def slug(self):
            return slugify(self.name)
    
        def get_absolute_url(self):
            return reverse('my-view-slug', args=[self.id, self.slug])
    

    我希望这能说清楚如何实现类似 StackOverflow的网址行为。

答案 1 :(得分:1)

# views
def detail(request, object_id, slug):
    obj = get_object_or_404(MyModel, pk=object_id)
    if obj.slug != slug:
        canonical = obj.get_absolute_url()
        return redirect(canonical)

    context = {"obj":obj}
    return render(request, "myapp/detail.html", context)


# urls
from myapp.views import detail
urlpatterns = ('',
    #...
    url(r'^(?P<object_id>\d+)/(<?P<slug>\S+)/$', detail, name="detail")
    url(r'^(\d+)/$', lambda request, pk: detail(request, pk, None), name="redirect-to-detail"),
    # ...
    )

# models
class MyModel(models.Model):
    def get_absolute_url(self):
        return reverse(
          "detail", 
          kwargs=dict(object_id=self.id, slug=self.slug)
          )

    @property
    def slug(self):
        return slugify(self.title)