Search code examples
djangohttp-redirectslug

Redirect url if slug does not match


I want to use the same url model as SO. The lookup is done through the id number but if the wrong slug is in the link than a redirect is thrown to the correct page.

example: questions/4787731/whatever will redirect to questions/4787731/canonical-links-and-301-redirect-if-url-doesnt-match-slug. My current code does the lookup through the id but does not redirect in case of a bad slug. example: entities/12786676/whatever should redirect to /entities/12786676/yellow-and-green-tree/. I do get the content of the page correctly but I want also the url to be redirected to avoid bad links. Here is the code:

views:

class EntityRedirectDetailView(RedirectView):
    def get(self, request, *args, **kwargs):
        pk = self.kwargs.get('pk', None)
        slug=self.kwargs.get('slug', None)
        entity = EntitiesNew.objects.get(pk=pk)
        self.url = '/entities/%s/%s' % (entity.pk, entity.slug)
        return super(EntityRedirectDetailView, self).get(request, *args, **kwargs)

urls:

urlpatterns = [
    url(r'^$', ListView.as_view(context_object_name = 'entities_list', queryset=EntitiesNew.objects.order_by('-id_number')[:500]), name='entities'),  
    url(r'^(?P<pk>\d+)/$', views.EntityRedirectDetailView.as_view(), name='entities-redirect'),  
    url(r'^(?P<pk>\d+)(?:/(?P<slug>[\w\d-]+))?/$', DetailView.as_view( model=EntitiesNew, context_object_name="entity"), name="entities-detail"),
]

thanks


Solution

  • Ok I managed to do what I wanted by overriding the get method in my EntityDetailView(Detailview) as per Paulo Almeida's suggestion. Here is the final code.

    urls.py:

    url(r'^(?P<pk>\d+)/$', views.EntityRedirectDetailView.as_view(), name='entities-redirect'), 
        url(r'^(?P<pk>\d+)/(?P<slug>[-\w\d]+)/$', views.EntityDetailView.as_view(), name="entities-detail"),
    

    views.py

    class EntityRedirectDetailView(RedirectView):
        def get(self, request, *args, **kwargs):
            pk = self.kwargs.get('pk', None)
            slug=self.kwargs.get('slug', None)
            entity = EntitiesNew.objects.get(pk=pk)
            self.url = '/entities/%s/%s' % (entity.pk, entity.slug)
            return super(EntityRedirectDetailView, self).get(request, *args, **kwargs)
    
    class EntityDetailView(generic.DetailView):
        """
            A DetailView which redirects to the absolute_url, if necessary.
        """
        template_name = 'entities/entitiesnew_detail.html'
        queryset = EntitiesNew.objects.get_queryset()
        def get_object(self, *args, **kwargs):
            # Return any previously-cached object
            pk = self.kwargs.get('pk', None)
            entity=EntitiesNew.objects.get(pk=pk)
            if getattr(self, 'entity', None):
                return self.entity
            return super(EntityDetailView, self).get_object(*args, **kwargs)
        def get(self, *args, **kwargs):
            # Make sure to use the canonical URL
            self.entity = self.get_object()
            obj_url = self.entity.get_absolute_url()
            if self.request.path != obj_url:
                return http.HttpResponsePermanentRedirect(obj_url)
            return super(EntityDetailView, self).get(*args, **kwargs);
    

    in models.py I have the get_absoulte_url:

    def get_absolute_url(self):
            return reverse('entities:entities-detail', kwargs={'pk':self.pk, 'slug':self.slug})
    

    and in the template: {% url 'entities:entities-detail' entitiesnew.pk entitiesnew.slug %}