Django form - Calling is_valid() in a template - django

I'm using Django 1.5. I have a template with multiple forms: one driver has many cars (inline forms). I want to display a hint indicating which section contains errors.
The template:
{% if driver_form.is_bound and not driver_form.is_valid %}
Please correct errors in the Driver Data.
{% endif %}
{% if car_form.is_bound and not car_form.is_valid %}
Please correct errors in the Car Data.
{% endif %}
The problem is that if the first form is invalid, the second message pops up as well, even though the second form is valid. I also noticed that if I put {{ car_form.is_valid }} three times in a row, the first time it is empty, the next time (and following) it is True.
The original view:
if request.method == 'POST':
driver_form = DriverModelForm(request.POST)
car_form_set = CarInlineFormSet(request.POST) # wrong: no instance passed
if driver_form.is_valid():
driver = driver_form.save(commit=False)
car_form_set = CarInlineFormSet(request.POST, instance=driver)
if car_form_set.is_valid():
driver.save()
car_form_set.save()
else:
driver_form = DriverModelForm()
car_form_set = CarInlineFormset()
return render(request, 'template.html', {
'driver_form': driver_form,
'car_form_set': car_form_set
})
UPDATE:
It seems that for simplicity's sake I left out the details that actually caused the problem. Lesson learned.
There was a Javascript setting values on page load, while it only needed to be done on a dropdown change. It was hiding the erroneous values and causing the form to be valid next time around.
There is a dependency: driver_form is a model form, and car_form_set is an inline form set based on the instance that the driver_form is adding.
The problem was indeed in the view, as seddonym suggested: the car_form_set was not being initialized with an instance if the driver_form was invalid. So is_valid() was neither True nor False. Conclusion: using is_valid() in the template works just fine.
The fixed view:
driver = Driver()
if request.method == 'POST':
driver_form = DriverModelForm(request.POST, instance=driver)
if driver_form.is_valid():
driver = driver_form.save(commit=False)
car_form_set = CarInlineFormSet(request.POST, instance=driver)
if car_form_set.is_valid():
driver.save()
car_form_set.save()
else:
driver_form = DriverModelForm(instance=driver)
car_form_set = CarInlineFormset(instance=driver)
return render(request, 'template.html', {
'driver_form': driver_form,
'car_form_set': car_form_set
})

You don't need to call form.is_valid, that will already have been called by the view. Instead, check for errors:
{% if first_form.errors %}
Please correct errors in the First Form.
{% endif %}
{% if second_form.errors %}
Please correct errors in the Second Form.
{% endif %}

Related

Why is my ModelForm not showing the right settings?

I have two Model Forms, one for notification types and one for privacy settings. Both show the defaults and not what is saved in the database. How can I make the form show the choice that's stored in the database instead of the defaults? Example: Say the user chose a Direct Message privacy of "Friends and Followers". When they visit the privacy options page again after having saved the changes, it shows "Open" instead of reflecting what is in the database. I tried assigning the values saved in the database directly to the form when rendering the page when the request is GET but that didn't do anything, even though the debug print shows that the value of form.dm_privacy is indeed what's in the database, it still shows "Open".
Both forms work fine, the changes get saved and everything. I don't understand why it's not reflecting the changes though, is this just something that Model Forms do and not something I can change?
privacy_options.html
{% extends "accbase.html" %}
{% block content %}
<h1>Privacy Options</h1>
<form method="post">
{% csrf_token %}
{{form.as_p}}
<button type="submit">Save</button>
</form>
{% endblock %}
views.py
#login_required
def privacy_options(request):
"""
Holds all privacy options such as.. open/closed DMs, who can see profile/posts on profile
"""
if request.method == "POST":
form = PrivacyOptionForm(request.POST)
if form.is_valid():
print("Before saving:",request.user.dm_privacy, request.user.profile_privacy, request.user.included_in_find_friends)
user = request.user.username
request.user.dm_privacy = form.cleaned_data['dm_privacy']
request.user.profile_privacy = form.cleaned_data['profile_privacy']
request.user.included_in_find_friends = form.cleaned_data['included_in_find_friends']
request.user.save()
print("After save:",request.user.dm_privacy, request.user.profile_privacy, request.user.included_in_find_friends)
return render(request, 'acc_manage/acc_nav.html', {'username':user})
else:
form = PrivacyOptionForm()
form.included_in_find_friends = request.user.included_in_find_friends
form.profile_privacy = request.user.profile_privacy
form.dm_privacy = request.user.dm_privacy
print("\n\n",form.dm_privacy, "\n\n")
return render(request, 'acc_manage/privacy_options.html', {'form': form})
Fixed it by changing form = PrivacyOptionForm() to form = PrivacyOptionForm(instance=request.user)

Validating changed queryset not working - Django

I want to do the following:
A user sees all eventrecords he previously created on one page and can edit them.
The problem I have is that if he edits one or more of them and inputs an invalid choice no validation errors are shown. Instead, nothing happens (if I have if changed_events.is_valid() in the code) or I get "ValueError at /coding/assignment/3/
The EventRecord could not be changed because the data didn't validate." If the user puts in valid data, saving works just fine.
I would like to show validation errors on the page, the way it is already working when creating new entries.
Following my code:
The view (I'm not posting my whole view, as it's fairly complex and everything else is working fine. These are the parts responsible for what's not working):
##### Show already created events on the page
current_article = paginator.page(current_page).object_list[0]
EventFormSet = modelformset_factory(EventRecord, can_delete=True, exclude=('coder','article','url','last_updated'), extra=0)
event_queryset = EventRecord.objects.filter(article__id=current_article.id).filter(coder=request.user.id)
coded_events = EventFormSet(queryset=event_queryset, prefix="event_form")
elif 'save_changes' in request.POST:
formset = CodingFormSet(prefix="coding_form")
changed_events = EventFormSet(request.POST, prefix="event_form")
# if changed_events.is_valid():
instances = changed_events.save()
try:
history_record = ArticleHistory.objects.filter(article__id=paginator.page(current_page).object_list[0].id).filter(coder=request.user.id)[0]
history_record.last_updated = datetime.datetime.now()
history_record.save()
except:
history_form = ArticleHistoryForm()
article_history = history_form.save(commit=False)
article_history.article = paginator.page(current_page).object_list[0]
article_history.coder = request.user
article_history.last_updated = datetime.datetime.now()
article_history.save()
redirect_to = "?page=%s" % current_page
return HttpResponseRedirect(redirect_to)
The template:
{% for eventrecord in coded_events %}
{{ eventrecord.id }}
<div class="form-container">
{% if eventrecord.non_field_errors %}
{{form.non_field_errors}}
{%endif%}
{% for field in eventrecord %}
{% if field.errors %}
<li>{{ field.errors|striptags }}</li>
{% endif %}
{% endfor %}
Has anyone an idea what I'm doing wrong?
Clearly Django is recognizing that something is going wrong, but why doesn't it show it in the template but creates an error-page? Why does nothing happen when I include the is_valid()?
I really don't know what I'm supposed to do, any help is greatly appreciated!
Oh man, this was stupid:
Right after posting my question I saw the answer, after working days on it.
Here's what went wrong:
The queryset was passed to the template as coded_events.
During the save_changes operation, I renamed it to changed_events, which then couldn't be passed back to the template in case of validation errors.
After fixing that, everything works fine.

Django: how do i edit/update a model?

Argh. Hey all, i have a muy simple django question:
And argh, i'm sorry, i've read and read, and I am sure this is covered somewhere super-obviously, but i haven't found it!
How do i edit/update a model using django? Like, the data values? Not the model fields?
Here is my code! (I'm using a home-brew version of stuff!)
Here is the urls:
url(r'^editStory/(?P<id>\d+)$',
StoryModelView.as_view(
context_object_name='form',
template_name ='stories/editStory.html',
success_template= 'stories/editStorySuccess.html'
),
{},
'editStory'
),
Here is the view:
def get(self,request,id=None):
form = self.getForm(request,id)
return self.renderValidations(form)
def getForm(self, request,id):
if id:
return self.getModelById(request,id)
return StoryForm()
def getModelById(self,request,id):
theStory = get_object_or_404(Story, pk=id)
if theStory.user != request.user:
raise HttpResponseForbidden()
return StoryForm(theStory)
def renderValidations(self,form):
if self.context_object_name:
contextName = self.context_object_name
else:
contextName = 'form'
if self.template_name:
return render_to_response(self.template_name,{contextName:form})
else :
return render_to_response('stories/addStory.html',{contextName:form})
def getPostForm(self,request,id):
if id:
theStory = self.idHelper(request,id)
return StoryForm(request.POST,theStory)
return StoryForm(request.POST)
def processForm(self,form,request):
theStory = form.save(commit=False)
theStory.user = request.user
return theStory
Here is the template code:
{% block content %}
<h3>Edit story</h3>
<form action="" method="post">
{% csrf_token %}
{% for field in form %}
<div class="fieldWrapper">
{{ field.errors }}
{{ field.label_tag }} {{ field }}
</div>
{% endfor %}
<input type="submit" value="Submit"/>
</form>
{% endblock %}
try as i might, i either:
get an error
get nothing
i get an error with the code as-displayed, caused by this line
{% for field in form %}
and with the error of:
Caught AttributeError while rendering: 'Story' object has no attribute 'get'
or i get nothing - no data at all - if i change my "getModelById" method to read:
def getModelById(self,request,id):
theStory = get_object_or_404(StoryForm, pk=id)
if theStory.user != request.user:
raise HttpResponseForbidden()
return theStory
StoryForm is one of those "ModelForm" things, and its model is "Story".
SO! The question:
how do i fix this code to make it work? What have i done wrong?
You don't show what your class is inheriting from, but it seems like you're just using a standard single object display view. Instead, you should use one of the editing mixins that are provided for this purpose.
Without knowing what your model looks like, are you looking for something along the lines of
s = Story.objects.get(some criteria)
s.user = <some user>
s.save()
?
Sorry, I find your question a little vague.
Upon rereading, one thing jumped out at me:
You can't do a query (get, filter, or any variation on these) on a model-- you have to do it on an object manager, like objects.
So, as above, in your case, Story.objects.get_object_or_404 should solve your error.

Django: Multiple date fields in forms that allow blank entry issue

I have a model in Django that allows blanks for two date fields:
class ReleaseStream(models.Model):
name = models.CharField(max_length=200,db_column='name')
version = models.CharField(max_length=20,blank=True,db_column='version')
target_date = models.DateTimeField(null=True,blank=True,db_column='target_date')
actual_date = models.DateTimeField(null=True,blank=True,db_column='actual_date')
description = models.TextField(db_column='description')
...and a form definition:
class ReleaseStreamForm(ModelForm):
class Meta:
model = ReleaseStream
When the form comes up, I can fill in a value for the "target_date", and not for the "actual_date" fields, and when the form.save() fires it appears to write the value supplied for "target_date" into both fields. I have looked at the post data going into the code that does the form.save() and it definitely has a value for "target_date" and a '' for "actual_date", so I don't think that there is something weird with the form itself, variable names in the POST, etc.
Now, if I supply a non-blank value for "actual_date", the form.save() does the right thing - both the "target_date" and "actual_date" have the correct values written in. Am I doing something wrong, or is this potentially a bug in django?
Here is the template (sorry for the blank comment below:)
{% extends "base.html" %}
{% block title %}{{ form_title }}{% endblock %}
{% block subtitle %}{% endblock %}
{% block content %}
<form action={{ action_url }} method="post">
<table>
{{ form.as_table }}
</table>
<input type="submit" value="Submit" />
</form>
{% endblock %}
And the code that handles the form:
def edit_release_stream(request,req_release_stream_id=None):
form_title = 'Edit release stream'
if request.method == 'POST':
if req_release_stream_id!=None:
release_stream_entry=ReleaseStream.objects.get(pk=req_release_stream_id)
form = ReleaseStreamForm(request.POST,instance=release_stream_entry)
else:
form = ReleaseStreamForm(request.POST)
if form.is_valid():
form.save()
return HttpResponseRedirect('/releases/')
elif req_release_stream_id!=None:
release_stream_entry=ReleaseStream.objects.get(pk=req_release_stream_id)
form = ReleaseStreamForm(instance=release_stream_entry)
else:
form_title = 'Add new release stream'
form = ReleaseStreamForm()
return render_to_response('dashboard/tableform.html', {
'action_url': request.get_full_path(),
'form_title': form_title,
'form': form,
})
... And the post data coming in:
<QueryDict: {u'name': [u'NewRelease'], u'target_date': [u'2011-06-15 00:00'], u'version': [u'4.5.1'], u'actual_date': [u''], u'description': [u'']}>
You can see that it has a valid POST var of "actual_date", with an empty string. This post yields a form.save() that stores the string provided above for "target_date" for both "target_date" and "actual_date".
If I then run a post with differing values for the two dates - here is the post:
<QueryDict: {u'name': [u'NewRelease'], u'target_date': [u'2011-06-15 00:00'], u'version': [u'4.5.1'], u'actual_date': [u'2011-07-15 00:00'], u'description': [u'']}>
In this case, with two distinct, non-empty strings, it writes the correct value shown in the POST above into each of the fields in the db.
I don't believe it to be a bug in Django, or somebody would have seen this problem a long time ago. Can you show us the template that renders the form? Also, if you can show the contents of the request.POST, that'd also be useful.
I'm guessing that your template code is incorrect somehow. The only other problem I can think of would be custom validation in your form (if there is any). Is that the whole ModelForm definition that you've supplied?

django forms error not shown

I'm trying to create a form. Here is my form class:
class RegisterForm(forms.Form):
login=forms.CharField(min_length=5,max_length=15)
password=forms.CharField(min_length=5,max_length=15,widget=forms.PasswordInput)
passwordConfirmation=forms.CharField(min_length=5,max_length=15,label="Re enter password",widget=forms.PasswordInput)
email=forms.EmailField(min_length=5,max_length=20)
question=forms.CharField(min_length=8,max_length=20,label="Security question")
answer=forms.CharField(min_length=5,max_length=20,widget=forms.PasswordInput)
answerConfirmation=forms.CharField(min_length=5,max_length=20,label="Re enter answer",widget=forms.PasswordInput)'
And now i have tamplate as follow:
{% if form.login.errors %}
{{ form.login.errors }}
{% endif %}
{{ form.login }}<label for="login">Enter desired login</label><br />
And so on i just only change form.name etc. to one from the forms class.
And when i filled form incorrect i don't get any error or nothing just blank form. Where I made a mistake? Thx for help
Edit:
Sorry i forget to show my function here is it
def register(request):
if request.method == 'POST':
form=RegisterForm(request.POST)
if form.is_valid():
return HttpResponseRedirect('/thanks/register')
else:
form = RegisterForm(auto_id=False)
return render_to_response('misc/register.html',locals(),context_instance=RequestContext(request))
else:
form=RegisterForm(auto_id=False)
return render_to_response('misc/register.html',locals(),context_instance=RequestContext(request))
form = RegisterForm(auto_id=False)
On this line you are creating new blank form and all validation errors are lost. Comment it out.
You've redeclared the form in the first else clause, so you've overwritten the errors. Drop that else clause completely, bring the very last line back one indent, so it catches the case when the firm is not valid.