proposed doc additions on GCBV structure customization - akulakov/gcbv-docs GitHub Wiki
When customizing or combining GCBVs, most methods may be overridden in a straightforward way.
get() and post() methods
A few methods need special attention because of method interdependencies. Specifically, get() and post() methods act as central control methods that set up attributes, call other methods and configure them through arguments. Therefore, you may choose to bypass some of the methods via get() and/or post().
get_context_data() method
Another key method is get_context_data() because it sets up the view context and in some cases uses the attributes set up in get()/post().
get_context_data() is a method present in the following mixins: SingleObjectMixin, MultipleObjectMixin and ContextMixin but NOT in FormMixin, because Form Views simply need to add an instance of the form to context, which can be easily done in the get() method, while SingleObjectMixin and MultipleObjectMixin have more complex context logic.
This works fine in many cases but poses a problem when you try to mix FormMixin with another GCBV and you wish to use the 2nd CBV's get() method. SingleObjectMixin and MultipleObjectMixin both need to set up instance variables in get() for their respective get_context_data() methods.
In case of SingleObjectMixin and downstream classes, self.object
is created in get() and is available for
use in all subsequent methods called, therefore only one database hit is made.
If the form was added to context in get_context_data(), it would make GCBVs a lot more combinable because you could inherit from either DetailView and FormMixin OR ListView and FormMixin and things would "just work".
You can accomplish this by creating a ContextFormMixin and related form processing views as shown in examples below. These classes can be used as drop-in replacements for respective GCBVs and they'll take care of the context for you.
If you ever need to customize or combine GCBVs, it's important to understand the way get_context_data() methods work. Let's say we have this inheritance structure:
class MyView(DetailView, ListView):
def get_context_data(self, **kwargs):
context = {'a':1, 'b':2}
context.update(kwargs)
return super(MyView, self).get_context_data(**context)
def get(self, request, *args, **kwargs):
return self.render_to_response(self.get_context_data(a=5))
Note that after get_context_data() inserts its own values, it uses kwargs
to update the context -- therefore,
initial values take precedence. In this example, a will be equal to 5 in the context because it is set earlier
than get_context_data() call; b will be equal to 2 even if DetailView or ListView set it to a different value.
Methods get()/post() and the attributes they set up:
self.object
-
DetailView and UpdateView: get() creates self.object - set to value returned by get_object() - which is the object being viewed / updated.
-
CreateView: get() and post() initialize self.object to None
-
UpdateView and CreateView: when form is valid, post() will set self.object to value returned by form.save().
self.object is required (i.e. will raise AttributeError if missing) in the following methods:
- get_context_data(): used to add object to context
- get_template_names(): used to look up template_name_field on the object or failing that to generate template name from object’s model name.
self.object_list
- ListView: get() - self.object_list is set to the value returned by get_queryset() - which is the list of objects to show;
self.object_list is required (i.e. will raise AttributeError if missing) in the following methods:
- get_context_data(): used to add list of objects to context (optionally with pagination objects)
- get_template_names(): used to generate template name from object’s model name.
Form processing flow:
In get()/post(), Form object is created and added to context via argument.
In Update/CreateView, additionally, self.object is created in get()/post().
In post(), form is validated and is sent to form_valid() or form_invalid().
Note that form_valid() and form_invalid() actions are completely different: form_valid() processes bound form and redirects to ‘success url’, while form_invalid() action is similar to get() — it sets up context using get_context_data() and renders context to response with bound form in context to show the form errors.
Combining Mixin and/or View GCBV classes
The most important difference from the POV of mixing is that Mixin classes do not provide get() and post() methods. If you wish to use get() or post() from one GCBV and other methods from another, you should use the View class from the former and Mixin from the latter, e.g. SingleObjectMixin and a ListView.
If you wish to override get()/post() methods with your own code, you still need to use one of the View classes because they provide the as_view() and other needed methods. In such cases it may be clearer to inherit from two view classes, e.g. from DetailView and ListView because it indicates intent not to use one of their ‘view’ functionality in preference to the other. This approach is used in some of the examples.