如何在Django中使用反向工作的Stackoverflowstyle url,让slug改变?

2024-09-29 23:27:51 发布

您现在位置:Python中文网/ 问答频道 /正文

我怎样才能创建一个像Stackoverflow一样工作的URL方案?

这个问题与this one不同,尽管它很相似。区别在于,我需要一个URL方案,它实现Stackoverflow的所有优点,并允许reverseing生成完全阻塞的URL。

具体来说,要模拟的Stackoverflow行为:

  1. 仅按id查找,slug仅用于SEO/可读性目的
  2. 当错误的slug或没有给出slug时,转发到正确的slug。e、 g.如果我有一个名为My Object Name的对象123

    /123/ redirects to /123/my-object-name/
    /123/something/ redirects to /123/my-object-name/
    
  3. 如果我将object name更改为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/,编辑对象名后,返回{}

我用了一个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/的URL,然后必须重定向到/123/my-object-name

我怎样才能让它像Stackoverflow一样工作?


Tags: tonamepyselfviewidurlobject
2条回答

鉴于你反复的评论——“。。。模式如何知道要返回哪一个slug“,似乎您无法理解它是如何工作的。我会试着为你分解这个过程。在

首先,您将编写两个指向一个视图的url模式。记住给两个模式不同的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/发出请求时,它将匹配第一个url模式。在
  2. 当向/123/my-object-name/发出请求时,它将匹配第二个模式。在
  3. 当使用错误的slug发出请求时,比如-/123/some-wrong-slug/,它也将匹配第二个模式。别担心,你会在你的视野里找到错误的鼻涕虫。在

但这三个请求都将由一个视图处理。在

其次,在模型中定义一个名为slugproperty。您将使用它来生成和访问对象的slug。在

^{pr2}$

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

# views.py

def my_view(request, object_id, slug=None):
    # first get the object
    my_object = get_object_or_404(MyModel, id=object_id)

    # Now we will check if the slug in url is same 
    # as my_object's slug or not
    if slug != my_object.slug:
        # either slug is wrong or None
        return redirect(my_object.get_absolute_url())

    # this is processed if slugs match
    # so do whatever you want
    return render(request, 'my-template.html', {'my_object': my_object})

我希望这说明了如何实现类似于url行为的StackOverflow。在

# 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)

相关问题 更多 >

    热门问题