| import re |
| |
| from django.core.exceptions import ImproperlyConfigured, ObjectDoesNotExist |
| from django.http import Http404 |
| from django.views.generic.base import TemplateResponseMixin, View |
| |
| |
| class SingleObjectMixin(object): |
| """ |
| Provides the ability to retrieve a single object for further manipulation. |
| """ |
| model = None |
| queryset = None |
| slug_field = 'slug' |
| context_object_name = None |
| |
| def get_object(self, queryset=None): |
| """ |
| Returns the object the view is displaying. |
| |
| By default this requires `self.queryset` and a `pk` or `slug` argument |
| in the URLconf, but subclasses can override this to return any object. |
| """ |
| # Use a custom queryset if provided; this is required for subclasses |
| # like DateDetailView |
| if queryset is None: |
| queryset = self.get_queryset() |
| |
| # Next, try looking up by primary key. |
| pk = self.kwargs.get('pk', None) |
| slug = self.kwargs.get('slug', None) |
| if pk is not None: |
| queryset = queryset.filter(pk=pk) |
| |
| # Next, try looking up by slug. |
| elif slug is not None: |
| slug_field = self.get_slug_field() |
| queryset = queryset.filter(**{slug_field: slug}) |
| |
| # If none of those are defined, it's an error. |
| else: |
| raise AttributeError(u"Generic detail view %s must be called with " |
| u"either an object id or a slug." |
| % self.__class__.__name__) |
| |
| try: |
| obj = queryset.get() |
| except ObjectDoesNotExist: |
| raise Http404(u"No %s found matching the query" % |
| (queryset.model._meta.verbose_name)) |
| return obj |
| |
| def get_queryset(self): |
| """ |
| Get the queryset to look an object up against. May not be called if |
| `get_object` is overridden. |
| """ |
| if self.queryset is None: |
| if self.model: |
| return self.model._default_manager.all() |
| else: |
| raise ImproperlyConfigured(u"%(cls)s is missing a queryset. Define " |
| u"%(cls)s.model, %(cls)s.queryset, or override " |
| u"%(cls)s.get_object()." % { |
| 'cls': self.__class__.__name__ |
| }) |
| return self.queryset._clone() |
| |
| def get_slug_field(self): |
| """ |
| Get the name of a slug field to be used to look up by slug. |
| """ |
| return self.slug_field |
| |
| def get_context_object_name(self, obj): |
| """ |
| Get the name to use for the object. |
| """ |
| if self.context_object_name: |
| return self.context_object_name |
| elif hasattr(obj, '_meta'): |
| return re.sub('[^a-zA-Z0-9]+', '_', |
| obj._meta.verbose_name.lower()) |
| else: |
| return None |
| |
| def get_context_data(self, **kwargs): |
| context = kwargs |
| context_object_name = self.get_context_object_name(self.object) |
| if context_object_name: |
| context[context_object_name] = self.object |
| return context |
| |
| |
| 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) |
| |
| |
| class SingleObjectTemplateResponseMixin(TemplateResponseMixin): |
| template_name_field = None |
| template_name_suffix = '_detail' |
| |
| def get_template_names(self): |
| """ |
| Return a list of template names to be used for the request. Must return |
| a list. May not be called if get_template is overridden. |
| """ |
| names = super(SingleObjectTemplateResponseMixin, self).get_template_names() |
| |
| # If self.template_name_field is set, grab the value of the field |
| # of that name from the object; this is the most specific template |
| # name, if given. |
| if self.object and self.template_name_field: |
| name = getattr(self.object, self.template_name_field, None) |
| if name: |
| names.insert(0, name) |
| |
| # The least-specific option is the default <app>/<model>_detail.html; |
| # only use this if the object in question is a model. |
| if hasattr(self.object, '_meta'): |
| names.append("%s/%s%s.html" % ( |
| self.object._meta.app_label, |
| self.object._meta.object_name.lower(), |
| self.template_name_suffix |
| )) |
| elif hasattr(self, 'model') and hasattr(self.model, '_meta'): |
| names.append("%s/%s%s.html" % ( |
| self.model._meta.app_label, |
| self.model._meta.object_name.lower(), |
| self.template_name_suffix |
| )) |
| return names |
| |
| |
| class DetailView(SingleObjectTemplateResponseMixin, BaseDetailView): |
| """ |
| Render a "detail" view of an object. |
| |
| By default this is a model instance looked up from `self.queryset`, but the |
| view will support display of *any* object by overriding `self.get_object()`. |
| """ |