Django’s built-in template tags are simply Python functions which means they can be used in places other than templates such as class-based views.
Here’s an example using pluralize:
I’ve spent the last few days implementing a Django SessionWizardView and ran into what I thought would be a simple problem – how to access the data of a previous form in order to initialize a later form (i.e. use data submitted on step 1 to initialize the step 3 form). Google didn’t turn up much so I turned to the documentation for a solution.
At first glance it seemed simple enough – use get_cleaned_data_for_step or get_all_cleaned_data. The problem with these, I quickly discovered, is that they call get_form which in turn calls get_form_initial (which is where I’m trying to use the submitted form data in the first place). So that was a dead end.
Next up was using get_form_step_data. This method is called before get_form_initial and has access to both the form’s data and cleaned_data properties. The issue with this technique, however, is that it would require the data to be stored in some way for use in get_form_initial (either via a session variable or an instance variable). While this works, it still seemed like there had to be a better way. After all, the wizard is storing this data somewhere already.
So after a bit of digging in the source, the solution was obvious – self.storage.get_step_data(step).
This method allows you to pull the data from any step and will return either None or a MultiValueDict.
One final thing to note is that because get_form_initial is called for every previous step (with that previous step as the step argument), it’s probably likely you’ll need to know what step you’re actually on. You can do this by using self.storage.current_step.
If anyone knows of a better way to do this, I’d love to know.
With the addition of generic views, we can easily do a redirect from urls.py. Simply pass the url to the RedirectView and that’s it.
So you’ve developed a secure section of your site and now you need to create an AJAX view… but that view needs to be locked down as well.
The RequireSignIn mixin in the previous post returns an HttpResponseRedirect which won’t work in this situation.
First, let’s lock down the view to require the user to be logged in when making an AJAX request.
Here’s what the mixin looks like:
You’ll notice that I’m making use of a method decorator – login_required_ajax. This decorator simply checks to see if the user is authenticated and if so, allows the request to continue. Otherwise it returns some json containing an error and the proper 401 http status which you could then use to ask the user to login.
Here’s the function:
Ok, now we’ve made sure the user is logged in. Let’s add a simple mixin (pulled from the Django docs), to return some json:
Finally, you can use these in your view like so…
By default some of Django’s class-based views support just a single form per view. This, of course, does not always play nicely with what you’re trying to accomplish. I’ve yet to come across a concrete example of how to accomplish this so after a bit of experimentation and some time spent diving into Django’s code, I’ve created a decent workaround. Please let me know in the comments if there’s a better way to do this.
For this example I’ll use a generic UpdateView and two ModelForms. The same technique should work with most of the class-based views as well as regular forms.
I’ll also mention that this handles the validation and submission of either form, but not both forms at the same time. The user is only submitting a single form, so you only need to handle that one.
There’s nothing special about the models or the templates, so let’s look at the view:
The first thing the view does is set the template, success_url and the two forms. Next we define the get_context_data method which adds either form to the context if it is missing.
UPDATE: There was an issue with the get_context_data method in which the form would not be populated with values from the model if a form was submitted with invalid data (and self.form_invalid was called). The current fix is to manually pass the data to the form using the initial argument.
The get_object method overrides the default behavior and gets a model instance based on a session variable. This isn’t necessary if you’re using the standard method of providing a pk or slug in the URL.
The next change is very important – override the default form_invalid method. The default method returns a single form in the context:
Since we have two forms, with different names, we need to switch to using **kwargs instead.
Finally, we override the post method to determine which form was submitted based on the name of the submit button (you could alternatively use a hidden field), validate the form and call form_valid or form_invalid. These methods will save the form and redirect to the success_url, or return to the view and show any errors, respectively.
And for the sake of clarity, the forms in your templates simply need a named submit button:
By default the generic DetailView expects you to provide a pk or slug in the URL. If it’s missing you’ll get a lovely little error:
Generic detail view UserView must be called with either an object pk or a slug
For the most part this is fine as you’ll usually provide an identifier in the URL. But what if you need to pull the identifier from someplace else such as the session?
To accomplish this, simply override the get_object method:
Also note that the model is no longer necessary as you’re explicitly calling it in get_object. The object is now available in the template using the model name, in this case user:
You can override this name by using context_object_name.
First, check if the request.GET dict contains a parameter named q:
Now ensure the variable has a value:
Separating your views into multiple files works much the same way as models except for one difference – importing the views in __init__.py isn’t necessary unless you want to type a little less.
Without importing them in __init__, you’ll need to do this:
But if you add them, you can do the following instead:
A typical use case is to provide the user a sign out button which redirects to another page such as the homepage. The following implements this using the generic RedirectView.
Class-based views were introduced in Django 1.3 and are very useful. This article discusses how to make use of them along with creating a mixin for requiring the user to be authenticated for a specific view.
I’ve tweaked the example a little bit, redirecting to the login page rather than a 404 and separating the mixin into a separate file.