2011-06-23 18 views
16

Estoy usando la vista genérica DetailView basada en clase de Django para buscar un objeto para visualizar. Bajo ciertas circunstancias, en lugar de mostrar el objeto, deseo retroceder y emitir un rediect HTTP en su lugar. No puedo ver cómo hago esto. Es para cuando un usuario golpea un objeto en mi aplicación, pero sin utilizar la URL canónica. Así, por ejemplo, en las direcciones URL StackOverflow tomar la forma:Redirigir desde Vista genérica DetailView en Django

http://stackoverflow.com/<content_type>/<pk>/<seo_friendly_slug> 

por ejemplo:

http://stackoverflow.com/questions/5661806/django-debug-toolbar-with-django-cms-and-django-1-3 

En realidad se puede escribir nada como la parte seo_friendly_slug y se le redirigirá a la URL canónica correcta para el objeto mirado a través del PK.

Deseo hacer lo mismo en mi DetailView. Recupere el objeto, verifique que sea la URL canónica y, si no, redirija a la URL get_absolute_url del elemento.

No puedo devolver un HttpResponseRedirect en get_object, ya que está esperando el objeto buscado. Parece que no puedo devolverlo desde get_context_data, ya que solo estoy esperando datos contextuales.

Tal vez solo necesito escribir una vista manual, pero me preguntaba si alguien sabía si era posible.

Gracias!

Ludo.

Respuesta

15

Esto no es un ajuste natural para DetailView. Para ello se debe redefinir el método get de BaseDetailView, que se parece:

class BaseDetailView(SingleObjectMixin, View): 
    def get(self, request, **kwargs): 
     self.object = self.get_object() 
     context = self.get_context_data(object=self.object) 
     return self.render_to_response(context) 

Así que en su clase que había necesidad de proporcionar un nuevo método get, que hizo el cheque URL entre ir a buscar el objeto y la creación de el contexto. Algo así como:

def get(self, request, **kwargs): 
    self.object = self.get_object() 
    if self.request.path != self.object.get_absolute_url(): 
     return HttpResponseRedirect(self.object.get_absolute_url()) 
    else: 
     context = self.get_context_data(object=self.object) 
     return self.render_to_response(context) 

Como se termina anulando así que gran parte de la funcionalidad que hace cuestionable si vale la pena utilizar realmente una visión genérica para esto, pero youknow.

+1

Puede llamar al método de obtención de los padres en el caso else aquí. Sería mucho más limpio. Vale totalmente la pena ya que las vistas genéricas basadas en clases están disponibles para ser ampliadas para la funcionalidad personalizada. – vimukthi

+1

Lo bueno de su enfoque es que no tenemos que preocuparnos si la implementación de BaseDetailView.get cambia, pero la desventaja es que tendríamos que hacer la recuperación de objetos dos veces para cada solicitud, que para mí no es vale la pena el potencial de rendimiento/escalabilidad. – Rolo

+0

Para evitar el doble golpe a 'get_object' puedes anularlo en tu propia clase o mixin y prefacio con un cheque' if hasattr (self, 'object', None) '; si la verificación tiene éxito, devuelve 'self.object'; de lo contrario, llama al' get_object' de parent. Exactamente como se implementó @Rakrakraut (https://stackoverflow.com/a/12858110). – interDist

10

El desarrollo de respuesta y los comentarios de Rolo, se me ocurrió la siguiente vista genérica para servir a este propósito:

from django import http 
from django.views import generic 


class CanonicalDetailView(generic.DetailView): 
    """ 
     A DetailView which redirects to the absolute_url, if necessary. 
    """ 
    def get_object(self, *args, **kwargs): 
     # Return any previously-cached object 
     if getattr(self, 'object', None): 
      return self.object 
     return super(CanonicalDetailView, self).get_object(*args, **kwargs) 

    def get(self, *args, **kwargs): 
     # Make sure to use the canonical URL 
     self.object = self.get_object() 
     obj_url = self.object.get_absolute_url() 
     if self.request.path != obj_url: 
      return http.HttpResponsePermanentRedirect(obj_url) 
     return super(CanonicalDetailView, self).get(*args, **kwargs); 

Esto se utiliza de la misma manera que en el DetailView normal, y debería funcionar para cualquier modelo que implementa get_absolute_url correctamente.

Cuestiones relacionadas