简体   繁体   English

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

[英]How can I make Stackoverflow-style URLs in Django with a working reverse, that allow the slug to change?

How can I make an URL scheme that works just like Stackoverflow? 如何使URL方案像Stackoverflow一样工作?

This isn't the same question as this one , though it is similar. 这与这个问题不是同一个问题,尽管它是相似的。 The difference is that I want an URL scheme that implements all of Stackoverflow's cleverness, and allows reverse ing to generate fully slugged URLs. 不同之处在于我想要一个实现Stackoverflow所有智能的URL方案,并允许reverse生成完全被阻塞的URL。

Specifically, the Stackoverflow behaviour to mimic: 具体来说,Stackoverflow行为模仿:

  1. Lookup by id only, with slug only for SEO/readability purposes 仅通过id查找,仅用于搜索引擎优化/可读性目的
  2. Forwarding to correct slug when the incorrect slug, or no slug is given. 当不正确的塞子或没有塞子时,转发纠正塞子。 eg if I have an object 123 with a name My Object Name : 例如,如果我有一个名为My Object Name的对象123

     /123/ redirects to /123/my-object-name/ /123/something/ redirects to /123/my-object-name/ 
  3. If I change the object name to My New Object Name then the redirect target slug changes accordingly (this is Stackoverflow's behaviour, if you edit your question's title), eg: 如果我将对象名称更改为My New Object Name则重定向目标slug会相应更改(这是Stackoverflow的行为,如果您编辑问题的标题),例如:

     /123/my-object-name/ redirects to /123/my-new-object-name/ 
  4. Reverse working so that {% url 'my_view' 123 %} returns /123/my-object-name/ , and after editing the object name, returns /123/my-new-object-name/ 反向工作,以便{% url 'my_view' 123 %}返回/123/my-object-name/ ,并在编辑对象名后,返回/123/my-new-object-name/

I've hacked something whereby I use a models.py : 我已经破解了一些我使用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])

...and a urls.py pattern: ...和urls.py模式:

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

...and a views.py : ...和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())

...but this feels wrong, and means when I do a reverse or {% url 'my_view' 123 %} it returns a URL like /123/ that then has to redirect to /123/my-object-name . ...但这感觉不对,意味着当我做reverse{% url 'my_view' 123 %}它返回一个像/123/这样的网址,然后必须重定向到/123/my-object-name

How can I make this work just like Stackoverflow? 我怎样才能像Stackoverflow一样完成这项工作?

Given your recurring comment – "... how would the pattern know which slug to return" , it appears that you are having trouble understanding how it works. 鉴于你的反复评论 - “......模式将如何知道哪个slug返回” ,看起来你很难理解它是如何工作的。 I'll try to break down the process for you. 我会尝试为你分解这个过程。

First of all, you will write two url patters pointing to one view. 首先,您将编写指向一个视图的两个URL图案。 Remember to give both patterns different name . 请记住给两种模式赋予不同的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'),
...

Now, this is where it gets interesting: 现在,这是有趣的地方:

  1. Whenever a request is made to /123/ , it will match the first url pattern. 每当向/123/发出请求时,它将匹配第一个url模式。
  2. When a request is made to /123/my-object-name/ , it will match the second pattern. 当请求/123/my-object-name/ ,它将匹配第二个模式。
  3. When a request is made with a wrong slug, like - /123/some-wrong-slug/ , it will also match the second pattern. 当使用错误的slug进行请求时,例如 - /123/some-wrong-slug/ ,它也将匹配第二个模式。 Don't worry, you'll check for wrong slug in your view. 别担心,你会在视图中检查错误的slu ..

But all three requests will be handled by one view. 但是所有三个请求都将由一个视图处理。

Second, define a property called slug in your model. 其次,在模型中定义一个名为slugproperty You will use this to generate and access slugs of objects. 您将使用它来生成和访问对象的slugs。

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

And finally, the view which will handle the requests should look something like this: 最后,处理请求的视图应如下所示:

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

I hope this makes it clear how to implement a StackOverflow-like url behaviour. 我希望这清楚地说明了如何实现类似StackOverflow的 url行为。

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

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM