Permalink
Join GitHub today
GitHub is home to over 28 million developers working together to host and review code, manage projects, and build software together.
Sign up| from django.core.exceptions import ImproperlyConfigured | |
| from django.db import models | |
| from django.http import Http404 | |
| from django.utils.translation import gettext as _ | |
| from django.views.generic.base import ContextMixin, TemplateResponseMixin, View | |
| class SingleObjectMixin(ContextMixin): | |
| """ | |
| Provide the ability to retrieve a single object for further manipulation. | |
| """ | |
| model = None | |
| queryset = None | |
| slug_field = 'slug' | |
| context_object_name = None | |
| slug_url_kwarg = 'slug' | |
| pk_url_kwarg = 'pk' | |
| query_pk_and_slug = False | |
| def get_object(self, queryset=None): | |
| """ | |
| Return the object the view is displaying. | |
| Require `self.queryset` and a `pk` or `slug` argument in the URLconf. | |
| 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(self.pk_url_kwarg) | |
| slug = self.kwargs.get(self.slug_url_kwarg) | |
| if pk is not None: | |
| queryset = queryset.filter(pk=pk) | |
| # Next, try looking up by slug. | |
| if slug is not None and (pk is None or self.query_pk_and_slug): | |
| slug_field = self.get_slug_field() | |
| queryset = queryset.filter(**{slug_field: slug}) | |
| # If none of those are defined, it's an error. | |
| if pk is None and slug is None: | |
| raise AttributeError( | |
| "Generic detail view %s must be called with either an object " | |
| "pk or a slug in the URLconf." % self.__class__.__name__ | |
| ) | |
| try: | |
| # Get the single item from the filtered queryset | |
| obj = queryset.get() | |
| except queryset.model.DoesNotExist: | |
| raise Http404(_("No %(verbose_name)s found matching the query") % | |
| {'verbose_name': queryset.model._meta.verbose_name}) | |
| return obj | |
| def get_queryset(self): | |
| """ | |
| Return the `QuerySet` that will be used to look up the object. | |
| This method is called by the default implementation of get_object() and | |
| 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( | |
| "%(cls)s is missing a QuerySet. Define " | |
| "%(cls)s.model, %(cls)s.queryset, or override " | |
| "%(cls)s.get_queryset()." % { | |
| 'cls': self.__class__.__name__ | |
| } | |
| ) | |
| return self.queryset.all() | |
| 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 isinstance(obj, models.Model): | |
| return obj._meta.model_name | |
| else: | |
| return None | |
| def get_context_data(self, **kwargs): | |
| """Insert the single object into the context dict.""" | |
| context = {} | |
| if self.object: | |
| context['object'] = self.object | |
| context_object_name = self.get_context_object_name(self.object) | |
| if context_object_name: | |
| context[context_object_name] = self.object | |
| context.update(kwargs) | |
| return super().get_context_data(**context) | |
| class BaseDetailView(SingleObjectMixin, View): | |
| """A base view for displaying a single object.""" | |
| def get(self, request, *args, **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. May not be | |
| called if render_to_response() is overridden. Return the following list: | |
| * the value of ``template_name`` on the view (if provided) | |
| * the contents of the ``template_name_field`` field on the | |
| object instance that the view is operating upon (if available) | |
| * ``<app_label>/<model_name><template_name_suffix>.html`` | |
| """ | |
| try: | |
| names = super().get_template_names() | |
| except ImproperlyConfigured: | |
| # If template_name isn't specified, it's not a problem -- | |
| # we just start with an empty list. | |
| 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 isinstance(self.object, models.Model): | |
| object_meta = self.object._meta | |
| names.append("%s/%s%s.html" % ( | |
| object_meta.app_label, | |
| object_meta.model_name, | |
| self.template_name_suffix | |
| )) | |
| elif getattr(self, 'model', None) is not None and issubclass(self.model, models.Model): | |
| names.append("%s/%s%s.html" % ( | |
| self.model._meta.app_label, | |
| self.model._meta.model_name, | |
| self.template_name_suffix | |
| )) | |
| # If we still haven't managed to find any template names, we should | |
| # re-raise the ImproperlyConfigured to alert the user. | |
| if not names: | |
| raise | |
| 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()`. | |
| """ |