8

I am trying to implement a URL scheme similar to stack overflow's in django/python.

E.g. the pk is stored in the URL along with a slug of the title so for this question (id #4787731) the URL is

https://stackoverflow.com/questions/4787731/canonical-links-and-301-redirect-if-url-doesnt-match-slug

If I later change the title (or just put in some random crud into the url) then the site will still know which question I am after (by the ID) and will 301 redirect to the correct URL - e.g. try.

https://stackoverflow.com/questions/4787731/canonical-links-MODIFIED-URL

So

  • What is the best way to include canonical links in my pages such as

    <link rel="canonical" href="https://stackoverflow.com/questions/4787731/canonical-links-and-301-redirect-if-url-doesnt-match-slug">

(can I use get_absolute_url)

  • What is the best way to recognize that the current URL doesn't match the canonical link and issue a 301?

Note - This question is similar but only addresses the case of generating the slug on the fly or statically.

Community
  • 1
  • 1
Ryan
  • 23,871
  • 24
  • 86
  • 132
  • what would be the use of a canonical tag if you're planning to do a 301 anyway? – Wolph Jan 24 '11 at 23:06
  • Good question, that's exactly what SO do, cover all bases I suspect. – Ryan Jan 25 '11 at 00:05
  • Related: [Redirect from Generic View DetailView in Django](https://stackoverflow.com/questions/6456586/redirect-from-generic-view-detailview-in-django) – User Oct 07 '17 at 12:53

2 Answers2

6

1: I don't think there's a point in using the canonical tag if there are 301s anyways.

Let's just imagine a scenario where you change the URL from /q/111/hello-world to /q/111/foobar. The engines won't assume the two are equal unless they visit the original url with the canonical tag on it pointing to /q/111/foobar (which it wont, because it's now a 301, severing any proof of a relationship between the pages).

2: I'd do it the straight forward way. Define a non unique slug field and compare vs the captured URL in your detail view.

# models
class MyModel(models.Model):
    # ...
    non_unique_slug = models.SlugField()

    def get_absolute_url(self):
        return "/questions/%s/%s" % (self.id, self.non_unique_slug)


# urls
    r'^questions/(?P<id>\d+)/(?P<slug>[\w-]+)/$' 

# views
def my_view(request, id, slug):
    page = Page.objects.get(id=id)
    if not slug == page.slug:
        return http.HttpResponsePermanentRedirect(page.get_absolute_url())

    # render page
    return direct_to_template(request, "foobar.html", {'page': page})
Yuji 'Tomita' Tomita
  • 115,817
  • 29
  • 282
  • 245
1

I followed Yuji's helpful instructions but found that you'll need to use the HttpResponsePermanentRedirect object to get a permanent 301 instead of the temporary 302.

Gringo Suave
  • 29,931
  • 6
  • 88
  • 75