Loading docs/topics/class-based-views/generic-display.txt +16 −9 Original line number Diff line number Diff line Loading @@ -92,6 +92,15 @@ We'll be using these models:: def __unicode__(self): return self.name class Author(models.Model): salutation = models.CharField(max_length=10) name = models.CharField(max_length=200) email = models.EmailField() headshot = models.ImageField(upload_to='author_headshots') def __unicode__(self): return self.name class Book(models.Model): title = models.CharField(max_length=100) authors = models.ManyToManyField('Author') Loading Loading @@ -132,11 +141,11 @@ bit is just the lowercased version of the model's name. enabled in :setting:`TEMPLATE_LOADERS`, a template location could be: /path/to/project/books/templates/books/publisher_list.html .. highlightlang:: html+django This template will be rendered against a context containing a variable called ``object_list`` that contains all the publisher objects. A very simple template might look like the following:: might look like the following: .. code-block:: html+django {% extends "base.html" %} Loading @@ -159,8 +168,6 @@ consider some of the common ways you might customize and extend generic views. Making "friendly" template contexts ----------------------------------- .. highlightlang:: python You might have noticed that our sample publisher list template stores all the publishers in a variable named ``object_list``. While this works just fine, it isn't all that "friendly" to template authors: they have to "just know" that Loading Loading @@ -219,10 +226,10 @@ template, but you can override it to send more:: .. note:: Generally, get_context_data will merge the context data of all parent Generally, ``get_context_data`` will merge the context data of all parent classes with those of the current class. To preserve this behavior in your own classes where you want to alter the context, you should be sure to call get_context_data on the super class. When no two classes try to define the ``get_context_data`` on the super class. When no two classes try to define the same key, this will give the expected results. However if any class attempts to override a key after parent classes have set it (after the call to super), any children of that class will also need to explictly set it Loading Loading @@ -366,7 +373,7 @@ Performing extra work The last common pattern we'll look at involves doing some extra work before or after calling the generic view. Imagine we had a ``last_accessed`` field on our ``Author`` object that we were Imagine we had a ``last_accessed`` field on our ``Author`` model that we were using to keep track of the last time anybody looked at that author:: # models.py Loading @@ -375,7 +382,7 @@ using to keep track of the last time anybody looked at that author:: salutation = models.CharField(max_length=10) name = models.CharField(max_length=200) email = models.EmailField() headshot = models.ImageField(upload_to='/tmp') headshot = models.ImageField(upload_to='author_headshots') last_accessed = models.DateTimeField() The generic ``DetailView`` class, of course, wouldn't know anything about this Loading docs/topics/class-based-views/mixins.txt +46 −56 Original line number Diff line number Diff line Loading @@ -285,12 +285,17 @@ One way to do this is to combine :class:`ListView` with for the paginated list of books can hang off the publisher found as the single object. In order to do this, we need to have two different querysets: **Publisher queryset for use in get_object** We'll set that up directly when we call ``get_object()``. **Book queryset for use by ListView** We'll figure that out ourselves in ``get_queryset()`` so we can take into account the ``Publisher`` we're looking at. **``Publisher`` queryset for use in ``get_object``** We'll set the ``model`` attribute on the view and rely on the default implementation of ``get_object()`` to fetch the correct ``Publisher`` object. **``Book`` queryset for use by ``ListView``** The default implementation of ``get_queryset`` uses the ``model`` attribute to construct the queryset. This conflicts with our use of this attribute for ``get_object`` so we'll override that method and have it return the queryset of ``Book`` objects linked to the ``Publisher`` we're looking at. .. note:: Loading @@ -299,7 +304,7 @@ object. In order to do this, we need to have two different querysets: :class:`ListView` will put things in the context data under the value of ``context_object_name`` if it's set, we'll instead explictly ensure the Publisher is in the context data. :class:`ListView` ensure the ``Publisher`` is in the context data. :class:`ListView` will add in the suitable ``page_obj`` and ``paginator`` for us providing we remember to call ``super()``. Loading @@ -310,31 +315,36 @@ Now we can write a new ``PublisherDetail``:: from books.models import Publisher class PublisherDetail(SingleObjectMixin, ListView): model = Publisher # for SingleObjectMixin.get_object paginate_by = 2 template_name = "books/publisher_detail.html" def get(self, request, *args, **kwargs): self.object = self.get_object() return super(PublisherDetail, self).get(request, *args, **kwargs) def get_context_data(self, **kwargs): kwargs['publisher'] = self.object return super(PublisherDetail, self).get_context_data(**kwargs) context = super(PublisherDetail, self).get_context_data(**kwargs) context['publisher'] = self.object return context def get_queryset(self): self.object = self.get_object(Publisher.objects.all()) return self.object.book_set.all() Notice how we set ``self.object`` within ``get_queryset()`` so we can use it again later in ``get_context_data()``. If you don't set ``template_name``, the template will default to the normal Notice how we set ``self.object`` within ``get()`` so we can use it again later in ``get_context_data()`` and ``get_queryset()``. If you don't set ``template_name``, the template will default to the normal :class:`ListView` choice, which in this case would be ``"books/book_list.html"`` because it's a list of books; :class:`ListView` knows nothing about :class:`~django.views.generic.detail.SingleObjectMixin`, so it doesn't have any clue this view is anything to do with a Publisher. .. highlightlang:: html+django any clue this view is anything to do with a ``Publisher``. The ``paginate_by`` is deliberately small in the example so you don't have to create lots of books to see the pagination working! Here's the template you'd want to use:: template you'd want to use: .. code-block: html+django {% extends "base.html" %} Loading Loading @@ -426,8 +436,6 @@ code so that on ``POST`` the form gets called appropriately. both of the views implement ``get()``, and things would get much more confusing. .. highlightlang:: python Our new ``AuthorDetail`` looks like this:: # CAUTION: you almost certainly do not want to do this. Loading @@ -449,21 +457,18 @@ Our new ``AuthorDetail`` looks like this:: form_class = AuthorInterestForm def get_success_url(self): return reverse( 'author-detail', kwargs = {'pk': self.object.pk}, ) return reverse('author-detail', kwargs={'pk': self.object.pk}) def get_context_data(self, **kwargs): context = super(AuthorDetail, self).get_context_data(**kwargs) form_class = self.get_form_class() form = self.get_form(form_class) context = { 'form': form } context.update(kwargs) return super(AuthorDetail, self).get_context_data(**context) context['form'] = self.get_form(form_class) return context def post(self, request, *args, **kwargs): if not request.user.is_authenticated(): return HttpResponseForbidden() self.object = self.get_object() form_class = self.get_form_class() form = self.get_form(form_class) if form.is_valid(): Loading @@ -472,10 +477,8 @@ Our new ``AuthorDetail`` looks like this:: return self.form_invalid(form) def form_valid(self, form): if not self.request.user.is_authenticated(): return HttpResponseForbidden() self.object = self.get_object() # record the interest using the message in form.cleaned_data # Here, we would record the user's interest using the message # passed in form.cleaned_data['message'] return super(AuthorDetail, self).form_valid(form) ``get_success_url()`` is just providing somewhere to redirect to, Loading Loading @@ -528,15 +531,12 @@ write our own ``get_context_data()`` to make the message = forms.CharField() class AuthorDisplay(DetailView): queryset = Author.objects.all() model = Author def get_context_data(self, **kwargs): context = { 'form': AuthorInterestForm(), } context.update(kwargs) return super(AuthorDisplay, self).get_context_data(**context) context = super(AuthorDisplay, self).get_context_data(**kwargs) context['form'] = AuthorInterestForm() return context Then the ``AuthorInterest`` is a simple :class:`FormView`, but we have to bring in :class:`~django.views.generic.detail.SingleObjectMixin` so we Loading @@ -554,24 +554,14 @@ template as ``AuthorDisplay`` is using on ``GET``. form_class = AuthorInterestForm model = Author def get_context_data(self, **kwargs): context = { 'object': self.get_object(), } return super(AuthorInterest, self).get_context_data(**context) def get_success_url(self): return reverse( 'author-detail', kwargs = {'pk': self.object.pk}, ) def form_valid(self, form): if not self.request.user.is_authenticated(): def post(self, request, *args, **kwargs): if not request.user.is_authenticated(): return HttpResponseForbidden() self.object = self.get_object() # record the interest using the message in form.cleaned_data return super(AuthorInterest, self).form_valid(form) return super(AuthorInterest, self).post(request, *args, **kwargs) def get_success_url(self): return reverse('author-detail', kwargs={'pk': self.object.pk}) Finally we bring this together in a new ``AuthorDetail`` view. We already know that calling :meth:`~django.views.generic.base.View.as_view()` on Loading docs/topics/class-based-views/intro.txt +1 −1 File changed.Contains only whitespace changes. Show changes Loading
docs/topics/class-based-views/generic-display.txt +16 −9 Original line number Diff line number Diff line Loading @@ -92,6 +92,15 @@ We'll be using these models:: def __unicode__(self): return self.name class Author(models.Model): salutation = models.CharField(max_length=10) name = models.CharField(max_length=200) email = models.EmailField() headshot = models.ImageField(upload_to='author_headshots') def __unicode__(self): return self.name class Book(models.Model): title = models.CharField(max_length=100) authors = models.ManyToManyField('Author') Loading Loading @@ -132,11 +141,11 @@ bit is just the lowercased version of the model's name. enabled in :setting:`TEMPLATE_LOADERS`, a template location could be: /path/to/project/books/templates/books/publisher_list.html .. highlightlang:: html+django This template will be rendered against a context containing a variable called ``object_list`` that contains all the publisher objects. A very simple template might look like the following:: might look like the following: .. code-block:: html+django {% extends "base.html" %} Loading @@ -159,8 +168,6 @@ consider some of the common ways you might customize and extend generic views. Making "friendly" template contexts ----------------------------------- .. highlightlang:: python You might have noticed that our sample publisher list template stores all the publishers in a variable named ``object_list``. While this works just fine, it isn't all that "friendly" to template authors: they have to "just know" that Loading Loading @@ -219,10 +226,10 @@ template, but you can override it to send more:: .. note:: Generally, get_context_data will merge the context data of all parent Generally, ``get_context_data`` will merge the context data of all parent classes with those of the current class. To preserve this behavior in your own classes where you want to alter the context, you should be sure to call get_context_data on the super class. When no two classes try to define the ``get_context_data`` on the super class. When no two classes try to define the same key, this will give the expected results. However if any class attempts to override a key after parent classes have set it (after the call to super), any children of that class will also need to explictly set it Loading Loading @@ -366,7 +373,7 @@ Performing extra work The last common pattern we'll look at involves doing some extra work before or after calling the generic view. Imagine we had a ``last_accessed`` field on our ``Author`` object that we were Imagine we had a ``last_accessed`` field on our ``Author`` model that we were using to keep track of the last time anybody looked at that author:: # models.py Loading @@ -375,7 +382,7 @@ using to keep track of the last time anybody looked at that author:: salutation = models.CharField(max_length=10) name = models.CharField(max_length=200) email = models.EmailField() headshot = models.ImageField(upload_to='/tmp') headshot = models.ImageField(upload_to='author_headshots') last_accessed = models.DateTimeField() The generic ``DetailView`` class, of course, wouldn't know anything about this Loading
docs/topics/class-based-views/mixins.txt +46 −56 Original line number Diff line number Diff line Loading @@ -285,12 +285,17 @@ One way to do this is to combine :class:`ListView` with for the paginated list of books can hang off the publisher found as the single object. In order to do this, we need to have two different querysets: **Publisher queryset for use in get_object** We'll set that up directly when we call ``get_object()``. **Book queryset for use by ListView** We'll figure that out ourselves in ``get_queryset()`` so we can take into account the ``Publisher`` we're looking at. **``Publisher`` queryset for use in ``get_object``** We'll set the ``model`` attribute on the view and rely on the default implementation of ``get_object()`` to fetch the correct ``Publisher`` object. **``Book`` queryset for use by ``ListView``** The default implementation of ``get_queryset`` uses the ``model`` attribute to construct the queryset. This conflicts with our use of this attribute for ``get_object`` so we'll override that method and have it return the queryset of ``Book`` objects linked to the ``Publisher`` we're looking at. .. note:: Loading @@ -299,7 +304,7 @@ object. In order to do this, we need to have two different querysets: :class:`ListView` will put things in the context data under the value of ``context_object_name`` if it's set, we'll instead explictly ensure the Publisher is in the context data. :class:`ListView` ensure the ``Publisher`` is in the context data. :class:`ListView` will add in the suitable ``page_obj`` and ``paginator`` for us providing we remember to call ``super()``. Loading @@ -310,31 +315,36 @@ Now we can write a new ``PublisherDetail``:: from books.models import Publisher class PublisherDetail(SingleObjectMixin, ListView): model = Publisher # for SingleObjectMixin.get_object paginate_by = 2 template_name = "books/publisher_detail.html" def get(self, request, *args, **kwargs): self.object = self.get_object() return super(PublisherDetail, self).get(request, *args, **kwargs) def get_context_data(self, **kwargs): kwargs['publisher'] = self.object return super(PublisherDetail, self).get_context_data(**kwargs) context = super(PublisherDetail, self).get_context_data(**kwargs) context['publisher'] = self.object return context def get_queryset(self): self.object = self.get_object(Publisher.objects.all()) return self.object.book_set.all() Notice how we set ``self.object`` within ``get_queryset()`` so we can use it again later in ``get_context_data()``. If you don't set ``template_name``, the template will default to the normal Notice how we set ``self.object`` within ``get()`` so we can use it again later in ``get_context_data()`` and ``get_queryset()``. If you don't set ``template_name``, the template will default to the normal :class:`ListView` choice, which in this case would be ``"books/book_list.html"`` because it's a list of books; :class:`ListView` knows nothing about :class:`~django.views.generic.detail.SingleObjectMixin`, so it doesn't have any clue this view is anything to do with a Publisher. .. highlightlang:: html+django any clue this view is anything to do with a ``Publisher``. The ``paginate_by`` is deliberately small in the example so you don't have to create lots of books to see the pagination working! Here's the template you'd want to use:: template you'd want to use: .. code-block: html+django {% extends "base.html" %} Loading Loading @@ -426,8 +436,6 @@ code so that on ``POST`` the form gets called appropriately. both of the views implement ``get()``, and things would get much more confusing. .. highlightlang:: python Our new ``AuthorDetail`` looks like this:: # CAUTION: you almost certainly do not want to do this. Loading @@ -449,21 +457,18 @@ Our new ``AuthorDetail`` looks like this:: form_class = AuthorInterestForm def get_success_url(self): return reverse( 'author-detail', kwargs = {'pk': self.object.pk}, ) return reverse('author-detail', kwargs={'pk': self.object.pk}) def get_context_data(self, **kwargs): context = super(AuthorDetail, self).get_context_data(**kwargs) form_class = self.get_form_class() form = self.get_form(form_class) context = { 'form': form } context.update(kwargs) return super(AuthorDetail, self).get_context_data(**context) context['form'] = self.get_form(form_class) return context def post(self, request, *args, **kwargs): if not request.user.is_authenticated(): return HttpResponseForbidden() self.object = self.get_object() form_class = self.get_form_class() form = self.get_form(form_class) if form.is_valid(): Loading @@ -472,10 +477,8 @@ Our new ``AuthorDetail`` looks like this:: return self.form_invalid(form) def form_valid(self, form): if not self.request.user.is_authenticated(): return HttpResponseForbidden() self.object = self.get_object() # record the interest using the message in form.cleaned_data # Here, we would record the user's interest using the message # passed in form.cleaned_data['message'] return super(AuthorDetail, self).form_valid(form) ``get_success_url()`` is just providing somewhere to redirect to, Loading Loading @@ -528,15 +531,12 @@ write our own ``get_context_data()`` to make the message = forms.CharField() class AuthorDisplay(DetailView): queryset = Author.objects.all() model = Author def get_context_data(self, **kwargs): context = { 'form': AuthorInterestForm(), } context.update(kwargs) return super(AuthorDisplay, self).get_context_data(**context) context = super(AuthorDisplay, self).get_context_data(**kwargs) context['form'] = AuthorInterestForm() return context Then the ``AuthorInterest`` is a simple :class:`FormView`, but we have to bring in :class:`~django.views.generic.detail.SingleObjectMixin` so we Loading @@ -554,24 +554,14 @@ template as ``AuthorDisplay`` is using on ``GET``. form_class = AuthorInterestForm model = Author def get_context_data(self, **kwargs): context = { 'object': self.get_object(), } return super(AuthorInterest, self).get_context_data(**context) def get_success_url(self): return reverse( 'author-detail', kwargs = {'pk': self.object.pk}, ) def form_valid(self, form): if not self.request.user.is_authenticated(): def post(self, request, *args, **kwargs): if not request.user.is_authenticated(): return HttpResponseForbidden() self.object = self.get_object() # record the interest using the message in form.cleaned_data return super(AuthorInterest, self).form_valid(form) return super(AuthorInterest, self).post(request, *args, **kwargs) def get_success_url(self): return reverse('author-detail', kwargs={'pk': self.object.pk}) Finally we bring this together in a new ``AuthorDetail`` view. We already know that calling :meth:`~django.views.generic.base.View.as_view()` on Loading
docs/topics/class-based-views/intro.txt +1 −1 File changed.Contains only whitespace changes. Show changes