I'm still a noob at mixins in general so I'm just trying to understand what happens in this code that uses Ajax to submit forms. I've been scouring the docs for a whole day to try to figure out what happens. The code seems to work but I just don't entirely understand why. I still have a lot of question marks on the whole process so if anyone can correct my thinking that would be amazing
The AjaxableResponseMixin extends the form_invalid() and form_valid() methods of the FormView to support Ajax requests
Why is the Ajax class referring to itself with super()?
How does the class know to extend FormView's methods?
If the request is not Ajax it returns response
What does the response object do/have? Does it show a template? Does it have context for the template?
CreatePostView is a child class of the two parent classes that are passed in (AjaxableResponseMixin, FormView)
Do the order of the classes in the params have an impact in general and more specifically when calling super()?
When CreatePostView calls form_valid() and form_invalid() is it overwriting the Ajax class? If it isn't, what class methods is it changing?
If the form is valid then a post gets created and super().form_valid() gets called which then redirects to the success url because thats what the FormView.form_valid() does
Again, why is the super() referring to the FormView?
Am I better off just doing a HttpResponseRedirect instead of using super().form_valid()
If the form is invalid it redirects to the url with the "create_post" name
How do I redirect to the create post page and keep the data in the form the user tried to submit?
views.py
class AjaxableResponseMixin(object):
"""
Mixin to add AJAX support to a form.
Must be used with an object-based FormView (e.g. CreateView)
"""
def form_invalid(self, form):
response = super(AjaxableResponseMixin, self).form_invalid(form)
if self.request.is_ajax():
return JsonResponse(form.errors, status=400)
else:
return response
def form_valid(self, form):
response = super(AjaxableResponseMixin, self).form_valid(form)
if self.request.is_ajax():
data = {
'pk': self.object.pk,
}
return JsonResponse(data)
else:
return response
class CreatePostView(AjaxableResponseMixin, FormView):
form_class = CreatePostForm
template_name = 'forum/create_post.html'
success_url = reverse_lazy('home')
def form_valid(self, form):
user = self.request.user
form.create_post(user_obj=user)
messages.success(self.request, 'Your post was published')
return super().form_valid(form)
def form_invalid(self, form):
messages.error(self.request, 'Your post could not be published. Please try again')
return HttpResponseRedirect(reverse('create_post'))
Thank you so much to anybody who answers.
Most of your question relates to python multiple inheritance and MRO (method resolution order). Look it up, there are plenty of other resources that explain it in detail. But to help with your specific case, you have defined the inheritance in this order:
CreatePostView --> AjaxableResponseMixin ...> FormView
This is the order in which subclassed methods will be called. I've made the arrows different because the first is a subclass - parent class relationship, the second one isn't (CreatePostView is a subclass of both FormView and AjaxableResponseMixin)
I'll explain what happens with form_valid: So if you call the form_valid() method on a CreatePostView, its form_valid() method is called. This runs all the code of that method.
The last line of that method is return super().form_valid(form) telling python to call the parent's class method. With MRO, that is the AjaxableResponseMixin.form_valid method. But the first line is response = super().form_valid(...), which is FormView.form_valid().
So here, because super() is at the beginning, you effectively tell python to first go to the next in the MRO chain.
AjaxableResponseMixin does nothing with the super() response if the request is an ajax request (it returns JSON with the object pk as data), but if it's not an ajax request, it just returns the FormView response. In this case it's a redirect to the success_url, because that's what FormView does in case of success. You can see that here.
Now you can do the same exercise with form_invalid(). In fact, it should just return super().form_invalid(form). It should never redirect, because you want it to render the same form on the same page (in the normal case) or just report the form errors in the ajax case.
Notes:
If your method does not call super(), which is entirely legal, the parent's class method will never be called.
The order of the classes and the order of the calls is important. With Django CBVs, you will most often first call super() to take care of the basics (the default behaviour) and then add your own stuff. In your form_valid, you do it the other way round because you first want to save the object, otherwise the JSON cannot contain the object's pk. And actually, if used incorrectly, this mixin will crash on that line.
A mixin like AjaxableResponseMixin doesn't contain any python code forcing it to be "mixed" with a FormView, but it has to, because it calls super().form_valid(). In fact many IDEs warn you with that call. There's no way to tell python that AjaxableResponseMixin MUST be mixed with a FormView, but it should be part of the documentation so other developers know how to use it. The same holds for the warning that the object must be saved before calling AjaxableResponseMixin.form_valid.
Related
I've seen this Q&A:
Django DeleteView without confirmation template
and this one:
Django CSRF token won't show
but that doesn't address the built-in intended functionality of the DeleteViewm CBV when issued a GET. From the docs (emphasis mine):
https://docs.djangoproject.com/en/2.1/ref/class-based-views/generic-editing/#django.views.generic.edit.DeleteView
"If this view is fetched via GET, it will display a confirmation page that should contain a form that POSTs to the same URL."
The problem is that as I understand it, the rendered template in response to a GET will not included the RequestContext necessary to include the {% csrf_token %} in the mentioned POST form. I worked around it for the time being by overriding the get() method so that it uses render() to return the page, since it automatically includes the appropriate context.
How do I maximally leverage the DeleteView? What am I doing wrong that I need to implement the following code in my view?
def get(self, request, *args, **kwargs):
self.object = self.get_object()
context = self.get_context_data(object=self.object)
return render(self.request,'mainapp/template_confirm_delete.html')
I have 10 Django Class Based Views and I want to display them read-only to the user.
I want the whole form to be read-only, not only some values. Submitting the form should be disabled on the client (HTML) and a second time on the server (POST not allowed).
Is there a MixIn or an other simple solution?
Here's a mixin that does two simple things:
Sets html attributes for all fields in form for disabled andreadonly.
Overrides the form_valid method of your CBV so that no model saving ever happens; instead, the template is rendered (just as if there was no submitted data). The user, this way, does not cause any action if they submitted the form.
Form field errors may appear next to disabled fields if you are rendering the full form in your template; solve this by either erasing the form's error dictionary or by rendering each field individually without errors.
from django.views.generic.edit import FormMixin, ModelFormMixin
class ReadOnlyModelFormMixin(ModelFormMixin):
def get_form(self, form_class=None):
form = super(ReadOnlyModelFormMixin, self).get_form()
for field in form.fields:
# Set html attributes as needed for all fields
form.fields[field].widget.attrs['readonly'] = 'readonly'
form.fields[field].widget.attrs['disabled'] = 'disabled'
return form
def form_valid(self, form):
"""
Called when form is submitted and form.is_valid()
"""
return self.form_invalid(form)
Extending this concept for a non-model FormView is pretty simple; inherit from class FormMixin instead. :)
To disallow POST requests in general for class-based views you could use the following mixin:
class DisallowPostMixin(object):
def post(self, request, *args, **kwargs):
return self.http_method_not_allowed(self, request, *args, **kwargs)
If you also want to disable certain form fields etc. you could add the get_form method from Ian Price's answer.
You can hack it through middleware. On request - check view name and request method (if post - redirect), on response - add input attrs in response.content. But mixin - best solution.
I'm building a job application form. A logged-in user is permitted to apply to the job only once (there's only one job). At the moment, a user is able to directly access the job application (FormView), by typing in its specific URL, and an error message is thrown AFTER the user submits the form. To achieve this, I'm doing a couple of things:
(1) Wrapping the job application view in login_required()
(2) Using the form_valid() method to check whether or not a user has already submitted an application to this specific job via:
def form_valid(self, form):
form = form.save(commit=False)
form.user = self.request.user
if Application.objects.filter(user=user_id):
messages.error(self.request, 'Sorry, our records show that you have already applied to this job.')
return redirect('/')
But I'd rather not permit them to reach the page at all. Instead, I want to check whether or not a user has already applied (during the request), and redirect them away from the form, if they have already applied. I have limited access to logged-in users that pass a test in the past, using something like:
def job_application_view(request):
active_user = request.user
if Application.objects.filter(user=active_user):
return HttpResponse("Some response.")
However, I can't seem to figure out how to access request via the FormView Class-Based View. I'm sure I'm missing something simple. Perhaps another method of FormView I'm missing?
You can still use decorators on class-based views, but they are slightly more difficult to apply than function-based views.
class ApplicationView(FormView):
# ...
#method_decorator(user_passes_test(job_application_view))
def dispatch(self, *args, **kwargs):
return super(ApplicationView, self).dispatch(*args, **kwargs)
Answering specific parts of your post...
I have limited access to logged-in users that pass a test in the past
With class-based views, you need to decorate the url or decorate the dispatch method with any decorators you are interested in applying.
However, I can't seem to figure out how to access request via the FormView Class-Based View. I'm sure I'm missing something simple. Perhaps another method of FormView I'm missing?
You can access the request with self.request
I am trying to design a Django application that facilitates the lending and borrowing of musical instruments between musicians.
I have one template page that includes the form to post an instrument for lending or borrowing, and another template that includes the form for searching for available listings.
The difference between the two views other than the templates that are rendered (slightly different designs and buttons) is the name of the form they add to the context (i.e. PostForm() and SearchForm())
Basically, I have two views with almost completely the same code. This is bad practice usually.
Is there any way I can consolidate the two views into a "super-view" of sorts so that changes to one view are automatically made across both? I want to avoid duplicate code wherever possible.
This is very easy to do with Class Based Views (CBV).
For example, you may use django.views.generic.FormView, as follows in your views.py:
from django.views import generic
class ClassyFormView(generics.FormView): # Of course name the view whatever you like
# Note that I'm not setting any of the specific attributes here
# As I am planning on overriding this view for each form's specifics
# This is where you may override methods of the FormView
def get_context_data(self, *args, **kwargs):
""" This method allows you to edit the context passed to the template """
context = super(ClassyFormView, self).get_context_data(*args, **kwargs) # Get context from FormView
# Add variables to the context data
context['message'] = "Hello World!"
return context
def form_valid(self, form):
"""
This method is called once the form has been instantiated with
POST data and has been validated
"""
# Do whatever you want after the form is validated
print(form.cleaned_data['some_field'])
def form_invalid(self, form):
# Do something if the form is invalid
pass
You can then override your custom class, to maintain the specific things it does over FormView, but use the correct form class:
class SearchFormView(ClassyFormView):
form_class = SearchForm
class PostFormView(ClassyFormView):
form_class = PostForm
Note that you can (and probably will) also set fields such as prefix, success_url, and template_name, as well as override tons of other methods that may be useful!
Note that if your forms are ModelForms, then you will probably want to use one of the model specific generic form views, such as CreateView or UpdateView. Using these will allow you to access the object that the form is acting on. So, after setting the correct model in the class, your form_valid method may look like this:
def form_valid(self, form):
self.object = form.save(commit=False)
# Do stuff here to the object before you save it
# Such as altering or setting fields
self.object.some_field = "I'm a string"
self.object.save()
I can't explain everything about CBV, or even the class based form views here, so make sure to look at further documentation:
Django Class-Based-View Inspector is a really awesome site that not many people seem to know about! Usually easier than diving into the source code.
Relevant Django docs:
CBVs
Generic editing views
Decided to add a few more details that may be helpful.
The generic views specify defaults for their class attributes, but unless you're being really generic, it's usually a good idea to override them. Some explanation on the specific attributes:
prefix specifies a prefix for the form. Useful in cases where using multiple forms.
Defaults to None.
If you require more advanced logic, you can set the prefix by returning it in the get_prefix() method.
success_url specifies where the form will redirect to on success. For the model form views, this will default to the model's get_absolute_url()
Can be set by returning success url in get_success_url() method
template_name specifies the name of the template that the view will display upon a get request
Can be set by returning template name in get_template_name() method.
Configuring URLs for CBV is easy, too. Use the as_view() method as follows:
url(r'^some_url/', SearchFormView.as_view(), name="some-url")
I get an error in my class AuthorCreateForm when I submit my form.
NameError
self is not defined
How do I use a CreateForm?
I have created a class in my Author.py file
from django.views.generic import TemplateView, ListView, CreateView
from books.models import Author, Publisher, Book
from books.forms import AuthorForm
class AuthorCreateView(CreateView):
objAuthorForm = AuthorForm(self.request.POST)
if(objAuthorForm.save()):
success = "Form saved!"
else:
error = "There was an error!"
and I have a html template which submits to /Author/Create
and I have the following line in my urls.py
('^authors/create/$', Author.AuthorCreateView.as_view()),
I render the form at this URL
('^authors/new/$', TemplateView.as_view(template_name="author_new.html")),
I find the class based views confusing, does anyone have a good tutorial on how to use it for CRUD operations?
Thanks
What you have is a python error -- self is not defined. self is generally what refers to the class instance itself on class methods.
Anyways, I agree, it's brand spanking new and not as documented. I think looking at the source is absolutely key at this point.
To get comfortable with class based views, I'd start by subclassing django.views.generic.base.View, which implements only a few methods, namely attempting to call a function on the class based on the request method (post, get, head, - look at source).
For example, here's the first step to replace view functions with the new view classes:
class MyClassBasedView(View):
def get(self, request):
# behave exactly like old style views
# except this is called only on get request
return http.HttpResponse("Get")
def post(self, request):
return http.HttpResponse("Post")
(r'^foobar/$', MyClassBasedView.as_view())
Back to your specific question:
All TemplateView.as_view() does is render the template - CreateView is a combination of several other classes that handle ModelForms and template rendering (TemplateView).
So, for a very basic example, look to the docs for what class mixins are used by CreateView.
We see it implements TemplateResponseMixin, ModelFormMixin, and ProcessFormView, each containing a list of methods for those classes.
The most basic CreateView
At the most basic level, provide CreateView's ModelFormMixin with the model or custom ModelForm class as documented here.
Your CreateView class would look something like the following
class AuthorCreateView(CreateView):
form_class = AuthorForm
template_name = 'author_new.html'
success_url = 'success'
With those 3 core attributes set, call it in your URLs.
('^authors/create/$', Author.AuthorCreateView.as_view()),
Render the page and you'll see your ModelForm passed to the template as form, handling the form validation step (passing in request.POST / re-render if invalid), as well as calling form.save() and redirecting to the success_url.
Start overriding the class methods
To customize behavior, start overriding the methods documented for the mixins.
Remember that you simply need to return an HttpResponse from one of these methods just like any regular view function.
Example overriding form_invalid documented in ModelFormMixin:
class AuthorCreateView(CreateView):
form_class = AuthorForm
template_name = 'author_new.html'
success_url = 'success'
def form_invalid(self, form):
return http.HttpResponse("form is invalid.. this is just an HttpResponse object")
This per-method overriding starts becoming extremely useful as your forms grow more advanced and ultimately lets you build huge forms with a handful of lines of code, overriding only what is necessary.
Say you want to pass your form custom parameters such as the request object (very common if you need access to the user in the form): you merely need to override get_form_kwargs.
class MyFormView(FormView):
def get_form_kwargs(self):
# pass "user" keyword argument with the current user to your form
kwargs = super(MyFormView, self).get_form_kwargs()
kwargs['user'] = self.request.user
return kwargs
Class based views are a shining example of smart class usage. It gave me a great intro towards building my own mixins for views and python classes in general. It is saving countless hours.
Wow this got long. To think it started as a mere URL to the docs comment :)