I'm starting to learn django and started watching tutorials on how to create forms and i've seen a lot of places where the form is created like this.
def create(request):
if request.POST:
form = ArticleForm(request.POST)
if form.is_valid:
form.save()
else:
form = ArticleForm()
args = {}
args.update(csrf(request))
args['form'] = form
return render_to_response('create_article.html', args)
Now, assuming that I created a model called Article and then created an ArticleForm from that model, what exactly is going on here (in the code I provided above)? I understand the if form.is_valid: form.save() part, and according to what I read, request should always be the first parameter, but can someone explain what request as a parameter does and what the first two lines of the function are doing? And what exactly is going on in the else statement and after the else statement (the args part)?
EDIT: Also, suppose the Article model has a field called name = models.CharField(max_length=20), is there a way for me to get / access what the user entered for that particular section of the form? Suppose I want to get the name and see if the name already exists in my database, would there be a way for me to do that?
request.POST among other things (like the CSRF token value) contains all the data the user has entered in the form.
if request.POST
checks if the user actually validated the form, otherwise there is no POST data in the request.
form = ArticleForm(request.POST)
looks strange at first but when the user validates the form, the same page is loaded but the POST data is processed in the django form for data validation (like checking if a required field was left blank, etc…) in order to display errors in the form.
If there is no error (form.is_valid()) then the view program continues.
I hope you are familiar with HTTP methods like GET and POST.
request object represents a single request by any user agent. So it can be a request that's sent from browser from you when you browse a particular page or from a crawler from a search engine. Read more about request here
request.POST is an attribute of this request object, it's a QueryDict (much similar to a normal Python dict). It contains the HTTP POST parameters that are sent to your view.
In short in your example:
def create(request):
if request.POST: # check if the request is POST request and it contains any parameter
form = ArticleForm(request.POST) # then pass all those parameters to the form
if form.is_valid: # process the form to check if it's valid
form.save() # save the data if it's valid
else:
form = ArticleForm() # if not valid data, initialize an new / empty form
args = {} # create a dict to pass to the template
args.update(csrf(request)) # add the CSRF token
args['form'] = form # add the 'form' above to the 'args' dict
return render_to_response('create_article.html', args) # pass that dict to template
Not so sure why you have this example, normally I would do the last part like this:
def create(request):
.... your code ....
else:
form = ArticleForm()
return render(request, 'create_article.html', { form: form })
Hope it helps.
There are some mistakes in the code and it seems it's copy-pasted from a SO question. I would recommend going through the excellent Django documentation, especially the Django tutorial.
Your example should rather look like this example from the Django docs.
Here are some comments:
def create(request):
if request.POST:
form = ArticleForm(request.POST)
if form.is_valid:
form.save()
# after successful POST
# we want to redirect to a different page here
else:
form = ArticleForm()
args = {}
# you really don't need the following necessarily
# just use `{% csrf_token %}` inside the form in your template
args.update(csrf(request))
args['form'] = form
# using just `render` like in the example linked to above is more modern
return render_to_response('create_article.html', args)
Related
I'm working on a django project where during registration, a user can submit a code to get special discounts. The validation of the discount codes is already working nicely, but I'm missing one beautifying aspect: After the user submits an invalid code I want to empty out the input field; i.e.:
def validate_code(value):
# check code for validity or raise ValidationError
class CodeForm(forms.Form):
code = forms.CharField(validators=[validate_code])
# in my views.py
def code_verification_view(request):
if request.method == 'POST':
form = CodeForm(request.POST)
if form.is_valid():
# proceed with given code
else:
# modify form to return an empty html input for the code field
# this is where I'm stuck
form.fields['code'].value = ''
# ... render the form in a template
The end result should be a form with an empty input field, but the validation errors showing. The behavior should be similar to how password input fields are emptied if the form verification fails.
EDIT: I solved the problem, but in a very hacky way:
see https://stackoverflow.com/a/46564834/8572938
I'd appreciate a proper solution that does not rely on accessing protected members of the form.
the key is to reset form variable
form = CodeForm(None)
in your code
def code_verification_view(request):
if request.method == 'POST':
form = CodeForm(request.POST)
if form.is_valid():
# proceed with given code
else:
form = CodeForm(None)
Just render your template, if your form is not valid, it will show error, In case if it is valid process your data
def code_verification_view(request):
if request.method == 'POST':
form = CodeForm(request.POST)
if form.is_valid():
// process your data
else:
form.data['field'] = None
return render(request, template_name, {'form': form})
Make a field validation in your form definition:
class CodeForm(forms.Form):
code = forms.CharField(validators=[validate_code])
def clean_code(self):
code = self.cleaned_data(code)
error = # some of your process
if error:
self.fields['code'] = None
raise forms.ValidationError('...')
else:
return code
And remove the else part in your view, instead you want to do something else. If you just want to display the form with error, the raise forms.ValidationError will do it.
You can in django form add a clean_<field_name> to control each field as you like.
More info here
I found a way that works, but it's quite dirty:
old_form = CodeForm(request.POST)
form = CodeForm()
if old_form.is_valid():
# ...
else:
form._errors = old_form._errors
# pass form into the rendering context
This way, I get a clean form with the preserved errors.
While it does the job, it is clearly an ugly hack.
I'm trying to learn Django and have come up with a situation I can't figure out. I have the following code:
def contact_add(request):
if request.method == 'POST':
form = ContactManageForm(request.POST)
if form.is_valid():
if form.has_changed(): # <-- ALWAYS RETURNS TRUE!
form.clean()
...
elif 'id' in request.GET: # Request to show an existing contact
new_contact_dynamic = contacts.models.PersonDynamic.objects.get(person_static = request.GET['id'],
current_record_fg = True)
form = ContactManageForm(new_contact_dynamic.__dict__, initial=new_contact_dynamic.__dict__)
else: # This must be to add a new contact
form = ContactAddForm()
return render(request, 'contact_manage.html', {'form': form})
So, if I'm sent an ID number, I read a record and display it on the screen. My template gives the user a 'submit changes' button. My problem, as noted above, is that Django always shows that the form has changed, even if the user hasn't changed any data on the screen (i.e. he just hit the submit changes button without changing anything).
So, am I doing something obviously wrong in my code that's creating this situation? Am I misinterpreting how the form.has_changed() method works?
It's my assumption that when I use the initial=parameter after a GET request, Django is storing that data somewhere and knows the context when the user then hits the 'submit data' button, is this wrong?
Yes you need to initialize your Form with initial data.
In your view the GET and POST requests have no common context. You may want to use sessions for that.
But in this case, it is not necessary. You can retrieve the instance on each request:
def contact_add(request):
if 'id' in request.GET:
new_contact_dynamic = contacts.models.PersonDynamic.objects.get(
person_static = request.GET['id'],
current_record_fg = True
)
if request.method == 'POST':
form = ContactManageForm(request.POST, initial=new_contact_dynamic.__dict__)
...
else: # Show an existing contact
form = ContactManageForm(initial=new_contact_dynamic.__dict__)
else:
form = ContactAddForm()
return render(request, 'contact_manage.html', {'form': form})
This question may seem obvious, but I have been thrown for a loop the past few days. The vast majority of tutorials and documentation I find on django-forms shows them as their own solitary views.
Take this example from django 1.6 official docs as a typical example, 'Using a form in a view':
from django.shortcuts import render
from django.http import HttpResponseRedirect
def contact(request):
if request.method == 'POST': # If the form has been submitted...
form = ContactForm(request.POST) # A form bound to the POST data
if form.is_valid(): # All validation rules pass
# Process the data in form.cleaned_data
# ...
return HttpResponseRedirect('/thanks/') # Redirect after POST
else:
form = ContactForm() # An unbound form
return render(request, 'contact.html', {
'form': form,
})
The problem I have is that I almost never dedicate a whole page or view just to one form. I can't really {% include %} the form view due to context variables. I also can't {% extend %} it without bringing in way to many items or reworking all of my templates and views.
Is it a good practice to combine form logic along with other references and variables in the same view? I would just like to confirm that yes this is normal and acceptable or that no I am doing things unacceptably. Off-hand there looks like some non DRY boilerplate if I do this in each view that needs a form.
There also appears to be some debate in the blogs & tutorials for form logic. Its hard to tell whether modifying a CBV or using some good ol' FBV is preferred. I just don't want to get any bad habits while I am still new and learning.
This is my currently "working" code for my view:
def home_page(request):
all_sliders = Slider.objects.all()
all_marketing = Marketing.objects.all()
all_features = Feature.objects.all()
skill_set = Skills.objects.all()
#clunky way of adding the form...yes? no?
errors = []
if request.method == 'POST':
form = ContactForm(request.POST)
if not request.POST.get('subject', ''):
errors.append('Enter a subject.')
if not request.POST.get('message', ''):
errors.append('Enter a message.')
if request.POST.get('email') and '#' not in request.POST['email']:
errors.append('Enter a valid e-mail address.')
if not errors:
send_mail(
request.POST['subject'],
request.POST['message'],
request.POST.get('email', 'noreply#mysite.com'),
# email address where message is sent.
['email#mysite.com'],
)
return HttpResponseRedirect('frontpage/thanks/')
else:
form = ContactForm()
context = {'sliders': all_sliders, 'marketing': all_marketing,
'feature': all_features, 'skills': skill_set,
#this is tied to my form logic
'form': form, 'errors': errors,
}
return render(request, 'frontpage/home.html', context)
In closing, I don't want to use an add-on library. I am learning django and want to learn it well before using libraries for basic things like forms.
There is nothing wrong with providing extra context/whatnot to a view with a form. In fact, it's pretty easy to do with CBVs. The docs for the Generic FormView provide a good example of a contact form, and you can extend it to add your extra context by overriding the get_context_data. What you should not do is form validation in the view. Leave that to your form class in forms.py. Here's an example pieced together from the docs:
# forms.py
class ContactForm(forms.Form):
subject = forms.CharField()
message = forms.CharField(widget=forms.Textarea)
email = forms.CharField()
# Whatever fields you had.
def clean(self):
"""This is where you should be checking for required
fields and making sure the submitted data is safe."""
pass
def send_email(self):
# send email using the self.cleaned_data dictionary
send_mail(
self.cleaned_data['subject'],
self.cleaned_data['message'],
self.cleaned_data.get('email', 'noreply#mysite.com'),
['email#mysite.com']
)
# views.py
class HomePageFormView(FormView):
template_name = 'frontpage/home.html'
form_class = ContactForm
success_url = 'frontpage/thanks/'
def get_context_data(self, **kwargs):
context = super(HomePageFormView, self).get_context_data(**kwargs)
context['sliders'] = Slider.objects.all()
context['marketing'] = Marketing.objects.all()
context['feature'] = Feature.objects.all()
context['skills'] = Skills.objects.all()
return context
def form_valid(self, form):
# This method is called when valid form data has been POSTed.
# It should return an HttpResponse.
form.send_email()
return super(HomePageFormView, self).form_valid(form)
Is it a good practice to combine form logic along with other
references and variables in the same view? I would just like to
confirm that yes this is normal and acceptable or that no I am doing
things unacceptably.
It is perfectly acceptable to add other things to the context in your views (otherwise, how would you render things?).
The problem comes when you are talking about form logic, and here is where there are some issues with your code/approach.
There is a convention in django (which you are free to violate), is that forms go in a forms.py file that is part of your application.
You can perfectly declare all your form classes in your views.py, there is nothing wrong with this approach but once you start collaborating with others, or start combining public django apps into your code, it is best to use a convention. After all, a software development framework is nothing but a bunch of conventions and some helpers all bundled together nicely.
However, a more serious problem with your logic is that you are not using form validation - and this you must absolutely stop right now.
Forms are - at their core - a way to validate dictionaries, and they are one of the most powerful features of the django framework. They allow you to validate any dictionary and are used anywhere you are working with models or data.
The code you have written is almost exactly what the form validation does - it checks if required fields are missing in the form (or, think of another way - required keys are None in the dictionary) and then adds error messages and marks the form (or dictionary) as invalid.
The basic logic of using forms is like this:
def someview(request):
form = SomeForm() # This creates an unbound (empty) form
if request.method == 'POST':
form = SomeForm(request.POST, request.FILES) # Bind the form to
# POST data
if form.is_valid():
# perform validation
# do something with form's data
data = form.cleaned_data['somefield']
# Or, if its a model form (a form that is tied to a
# model), save the model since the form is validated
obj = form.save()
# All post requests should redirect
return redirect('index')
else:
# The form was not valid, return the form
# to the view, except this time it will
# contain helpful error messages
return render(request, 'form.html', {'form': form})
else:
# Return an empty form to the view
# for the user to fill in, as this is a GET not POST
# request
return render(request, 'form.html', {'form': form})
You can always customize the validation rules for a form either on a field-by-field basis, or on the overall data in the form. This is discussed in the documentation on form and field validation.
Off-hand there looks like some non DRY boilerplate if I do this in
each view that needs a form.
The new CBV have solved this problem by taking care of the repeated logic (one of the benefits of inheritance and classes). The code I pasted above can be minimized to the following when using FormView:
from django.core.urlresolvers import reverse_lazy
from django.views.generic.edit import FormView
class SomeView(FormView):
template_name = 'form.html'
form_class = SomeForm
success_url = reverse_lazy('index')
def form_valid(self, form):
data = form.cleaned_data['somefield']
return super(SomeView, self).form_valid(form)
There also appears to be some debate in the blogs & tutorials for form
logic. Its hard to tell whether modifying a CBV or using some good ol'
FBV is preferred. I just don't want to get any bad habits while I am
still new and learning.
There is nothing wrong with using FBV - they are still perfectly valid django. The benefit you get with CBV is that common functionality is only written once. My advice is to use CBV when you have common logic that you want to modify on a per-view basis. Forms is a good example, displaying models, pagination, rendering simple templates, downloading data (for example, you can have one base view that converts objects to Excel, and then inherit from here in any view that needs to provide a download feature) are good candidates for CBV.
Hy there,
i just read here that posted data can't be sent by a redirect,
and then found that people are generally not happy with using redirects. And my question is why?
My situation
I have a Django app which starts by rendering a page which gives the choice of using Registered or Anonymous usage.
def start(request):
request.session.flush()
return render_to_response('start.html')
If Registered usage is chosen a simple form is rendered to insert the registration data, and it loops to itself until form is valid after which 'auth/' takes place.
def registration(request):
if request.method == "POST":
form = RegistrationForm(request.POST)
if form.is_valid():
return HttpResponseRedirect('auth/')
else:
return render_to_response('registration.html',
{'form':form})
form = RegistrationForm()
return render_to_response('registration.html',
{'form':form})
If anonymous usage is chosen, the registration page is skipped, and is redirected to 'auth/', the same Django view function like in upper code:
return HttpResponseRedirect('auth/')
What i was hoping to achieve with auth is for it to just set the sessions, write the data to a database and then redirect the next page which is identical to Registered and Anonymous users (only the sessions would differ).
def auth(request):
ipdb.set_trace()
if request.method == "POST":
request.session['user_type'] = 'REG'
request.session['email'] = request.POST['email']
request.session['first_name'] = request.POST['first_name']
RegisteredUser.objects.create(first_name = request.POST['first_name'],
email = request.POST['email'],
company = request.POST['company'])
else:
request.session['user_type'] = 'ANONIM'
return HttpResponseRedirect('next_page')
Of course, when the debugger starts, request.method always returns GET.
My reasons
There is a lots of talking about separating the view from the logic, and i don't find it very readable or loosely coupled when i have a Django view function (if I drop the 'auth/', 'next_page' would have to take it's functionality) which has to
Check for request.post
If there if the reason for it is form validation of the current page, or if it's from the previous page, in which case write that data to a base
Do additional checks to set the sessions for registered or anonymous user
which I would have to do since using separated functions just for logic that redirects instead of rendering doesn't seem as the generally accepted way.
Can someone please shed some light on things?
UPDATE WITH SOLUTION
Thanks to the answers i figured out the pattern on how to do this. The code for registration is now:
def registration(request):
if request.method == "POST":
form = RegistrationForm(request.POST)
if form.is_valid():
request.session['user_type'] = 'REG'
request.session['email'] = request.POST['email']
request.session['first_name'] = request.POST['first_name']
RegisteredUser.objects.create(first_name = request.POST['first_name'],
email = request.POST['email'],
company = request.POST['company'])
return HttpResponseRedirect('param_select/')
else:
return render_to_response('registration.html',
{'form':form},
context_instance = RequestContext(request))
form = RegistrationForm()
return render_to_response('registration.html',
{'form':form},
context_instance = RequestContext(request))
From now on, I'm using the "Loop into yourself with POST data, and then perform the logic" method.
To demystify the name, thats what the new registration(request) does.
First it renders the form to input the data
Than it is recalled once the data is submitted because of link to itself
form action="" method="post"
This is repeated until correct form data isn't submitted after which the logic is called (in this case, writing to a base and setting the sessions).
It ends with a httpRedirect to a different page leaving the registration completely handled by registration(request).
By default you can assume that user is anonymous and set request.session['user_type'] = 'ANONIM' in your start() view.
Whatever you are doing in auth() you can do it in registration() along with request.session['user_type'] = 'REG'. With this you can use the validated form to create RegisteredUser and flag errors as well if required.
And once POST processing is valid and complete it can redirect to your next_page.
If anonymous user is chosen, start will redirect to next_page rather than auth/
Still new to python and django, though learning ;-)
I have a view that is intended to display a form with contact information. After succesfully processing the form and saving/creating the object, I want to display the same view again (to add another contact) but with a message added saying the previous contact information was successfully saved.
From Django return redirect() with parameters I learned that the way to redirect to a view with a passed parameter is to simply call the view again and display the response.
My view starts as follows:
def addabentry(request, entrytype, messages=[]):
""" Presents form to create a company listing, then enters company into database"""
After postback and successfully saving the data, I call the view again as follows:
messages = ["%s %s has been added." % (entrytype, entry.name)]
response = addabentry(request, entrytype=entrytype, messages=messages)
return HttpResponse(response)
However, the form on the second go-round seems to be bound with the previous data, presumably because the POST parameter is still in the request object I pass to the view.
Is there a way to unbind the form for the second time around? Or, as is more likely, is there a better way of doing what I want? Do I need to use request.sessions as mentioned in the referenced SO question?
Thanks for your help!
W.
You need messages framework.
i think you may be making things a lot more complicated than they need to.
from the basic form example in the docs
def contact(request):
message = ''
if request.method == 'POST': # If the form has been submitted...
form = ContactForm(request.POST) # A form bound to the POST data
if form.is_valid(): # All validation rules pass
# Process the data in form.cleaned_data
# ...
# instead of redirecting here, create a new blank form, and add a message
form = ContactForm()
message = "contact successfully created"
else:
form = ContactForm() # An unbound form
return render_to_response('contact.html', {
'form': form,
'message': message,
})