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

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?

Related

WTForms - Pass argument to form then create fields if variable exists

Depending on the arguments I pass to the form I want to return different form fields.
class TestForm(FlaskForm):
"""Test Form."""
if one:
field1 = StringField('Field 1')
if two:
field2 = StringField("Field 2")
if three:
field3 = StringField("Field 3")
submit = SubmitField("Add Service")
def __init__(self, one=None, two=None, three=None, *args, **kwargs):
super().__init__(*args, **kwargs)
self.one = one
self.two = two
self.three = three
I am not able to see the arguments when doing the if statements.
I am aware of the option to have logic in html when rendering the form, however due the nature of the project have opted to use quick_form on the html side.
Here is the html code I am using.
{% import 'bootstrap/wtf.html' as wtf %}
<h3 >Add Service</h3>
<div class="row">
<div class="col-md-4">
{{ wtf.quick_form(form) }}
</div>
</div>
Instead of creating this logic in your form class. I would recommend to create all the fields you need and then dynamically choose which to show the user using jinja2 in your html file.
Here's an example.
{% for fields in fields_list %}
{% if field == '1' %}
{{ form.field1.label(class="form-control-label") }}
{{ form.field1(class="form-control form-control-lg") }}
{% endif %}
{% if field == '2' %}
{{ form.field2.label(class="form-control-label") }}
{{ form.field2(class="form-control form-control-lg") }}
{% endif %}
{% if field == '3' %}
{{ form.field3.label(class="form-control-label") }}
{{ form.field3(class="form-control form-control-lg") }}
{% endif %}
{% endfor %}
And then when you render or redirect to your .html from your routes code, don't forget to send
the proper arguments, such as
# create your fields list, which do I want to show?
# Make it a list of integers
fields_list = [1, 2, 3]
return render_template('insert-name.html', fields_list , form=form)
If my answer helped you, please consider accepting my answer.
I am new to this site and trying to build up some reputation :)
Thank you! Happy Coding!
If someone ever comes accross the same question, here is a quick solution I came upon some times ago.
It is in part inspired from the accepted answer here.
In your forms.py or wherever you declare your TestForm class, put the class declaration inside a function that takes your parameter as an argument and returns the class object as output.
The argument will now be accessible within the class itself, allowing for any test you may want to perform.
Here's a working example based on your original question (I just added a default value to get at least one StringField in case the parameter is ommited):
def create_test_form(var='one'):
class TestForm(FlaskForm):
"""Test Form."""
if var == 'one':
field1 = StringField('Field 1')
if var == 'two':
field2 = StringField("Field 2")
if var == 'three':
field3 = StringField("Field 3")
submit = SubmitField("Add Service")
return TestForm()
Then simply create the form in your routes like so:
form = create_test_form('two')
Finally pass it to your HTML to render the form with quick_form like you did.
This example will render a form with a single StringField named "Field 2" and a "Add Service" submit button.

Add form returns None as record id, causing URL to error

I'm using the code found here (SO.com) to use the same template to both add and edit a record, but when I add a new record and click Submit, I get a 404 on the URL http://192.168.1.3:5678/app/student/edit/None/, and I'm not exactly sure why.
Here is the relevant portion of my urls.py:
url(r'^app/lesson/new/$', 'edit_lesson', {}, 'lesson_new'),
url(r'^app/lesson/edit/(?P<id>\d+)/$', 'edit_lesson', {}, 'lesson_edit'),
Here is the relevant portion of my views.py:
def edit_lesson(request, id=None, template_name='lesson_edit_template.html'):
if id:
t = "Edit"
lesson = get_object_or_404(Lesson, pk=id)
stu = get_object_or_404(Student, pk=sid)
if stu.teacher != request.user:
raise HttpResponseForbidden()
else:
t = "Add"
lesson = Lesson()
if request.POST:
form = LessonForm(request.POST, instance=lesson)
if form.is_valid():
form.save()
# If the save was successful, redirect to another page
return view_lessons(request)
else:
form = LessonForm(instance=lesson)
return render_to_response(template_name, {
'form': form,
't': t,
'lesson': lesson,
}, context_instance=RequestContext(request))
And finally, here is my template:
<h1>{{ t }} Lesson</h1>
<form action="/app/lesson/edit/{{ lesson.id }}/" method="post"> {% csrf_token %}
{{ form.as_p }}
<input type="submit" value="Submit" />
</form>
I'm certain that I'm missing something really easy, but I can't seem to put my finger on it. I'm using Django 1.3.1 if that makes any difference.
Thanks,
MC
There's no need to specify any URL in the form's action attribute. Just do
<form action="" method="post">
and it will POST back to the URL that you originally used to access it, which is what you want.
In add case {{ lesson.id }} is None, because lesson is unsaved Lesson() instance, without pk, so your form is being fired to nonexistent URL.
I recommend separating create and edit views and processing them in different ways (or even inherit generic views - with new class-based generic views it's easy and pleasant).
Also, use {% url %} template tag everywhere instead of hard-coded urls.

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- populating an "update/edit" form with an autofield and foreignkey

I'm new to Django and I'm creating an app to create and display employee data for my company.
Currently the model, new employee form, employee table display, login/logout, all works. I am working on editing the current listings.
I have hover on row links to pass the pk (employeeid) over the url and the form is populating correctly- except the manytomanyfields are not populating, and the pk is incrementing, resulting in a duplicate entry (other than any data changes made).
I will only put in sample of the code because the model/form has 35 total fields which makes for very long code the way i did the form fields manually (to achieve a prettier format).
#view.py #SEE EDIT BELOW FOR CORRECT METHOD
#login_required
def employee_details(request, empid): #empid passed through URL/link
obj_list = Employee.objects.all()
e = Employee.objects.filter(pk=int(empid)).values()[0]
form = EmployeeForm(e)
context_instance=RequestContext(request) #I seem to always need this for {%extend "base.html" %} to work correctly
return render_to_response('employee_create.html', locals(), context_instance,)
#URLconf
(r'^employee/(?P<empid>\d+)/$', employee_details),
# snippets of employee_create.html. The same template used for create and update/edit, may be a source of problems, they do have different views- just render to same template to stay DRY, but could add an additional layer of extend for differences needed between the new and edit requests EDIT: added a 3rd layer of templates to solve this "problem". not shown in code here- easy enough to add another child template
{% extends "base.html" %}
{% block title %}New Entry{% endblock %}
{% block content %}
<div id="employeeform">
{% if form.errors %}
<p style="color: red;">
Please correct the error{{ form.errors|pluralize }} below.
</p>
{% endif %}
<form action="/newemp/" method="post" class="employeeform">{% csrf_token %} #SEE EDIT
<div class="left_field">
{{ form.employeeid.value }}
{{ form.currentemployee.errors }}
<label for="currentemployee" >Current Employee?</label>
{{ form.currentemployee }}<br/><br/>
{{ form.employer.errors }}
<label for="employer" class="fixedwidth">Employer:</label>
{{ form.employer }}<br/>
{{ form.last_name.errors }}
<label for="last_name" class="fixedwidth">Last Name:</label>
{{ form.last_name }}<br/>
{{ form.facility.errors }} #ManyToMany
<label for="facility" class="fixedwidth">Facility:</label>
{{ form.facility }}<br/><br/>
</div>
<div id="submit"><br/>
<input type="submit" value="Submit">
</div>
</form>
#models.py
class Employee(models.Model):
employeeid = models.AutoField(primary_key=True, verbose_name='Employee ID #')
currentemployee = models.BooleanField(null=False, blank=True, verbose_name='Current Employee?')
employer = models.CharField(max_length=30)
last_name = models.CharField(max_length=30)
facility = models.ForeignKey(Facility, null=True, blank=True)
base.html just has a header on top, a menu on the left and a big empty div where the forms, employee tables, etc all extend into.
screenshot2
So, how do I need to change my view and/or the in the template to update an entry, rather than creating a new one? (
And how do I populate the correct foriegnkeys? (the drop down boxes have the right options available, but the "-----" is selected even though the original database entry contains the right information.
Let me know if i need to include some more files/code
I have more pics too but i cant link more or insert them as a new user :< I'll just have to contribute and help out other people! :D
EDIT:
I've been working on this more and haven't gotten too far. I still can't get the drop-down fields to select the values saved in the database (SQLite3).
But the main issue I'm trying to figure out is how to save as an update, rather than a new entry. save(force_update=True) is not working with the default ModelForm save parameters.
views.py
def employee_details(request, empid):
context_instance=RequestContext(request)
obj_list = Employee.objects.all()
if request.method == 'POST':
e = Employee.objects.get(pk=int(empid))
form = EmployeeForm(request.POST, instance=e)
if form.is_valid():
form.save()
return HttpResponseRedirect('/emp_submited/')
else:
e = Employee.objects.get(pk=int(empid))
form = EmployeeForm(instance=e)
return render_to_response('employee_details.html', {'form': form}, context_instance,)
also changed template form action to "" (from /newemp/ which was the correct location for my new employee tempalte, but not the update.
Thanks to this similar question.
updating a form in djnago is simple:
steps:
1. extract the previous data of the form and populate the edit form with this these details to show to user
2. get the new data from the edit form and store it into the database
step1:
getting the previous data
views.py
def edit_user_post(request, topic_id):
if request.method == 'POST':
form = UserPostForm(request.POST)
if form.is_valid():
#let user here be foreign key for the PostTopicModel
user = User.objects.get(username = request.user.username)
#now set the user for the form like: user = user
#get the other form values and post them
#eg:topic_heading = form.cleaned_data('topic_heading')
#save the details into db
#redirect
else:
#get the current post details
post_details = UserPostModel.objcets.get(id = topic_id)
data = {'topic_heading':topic.topic_heading,'topic_detail':topic.topic_detail,'topic_link':topic.topic_link,'tags':topic.tags}
#populate the edit form with previous details:
form = UserPostForm(initial = data)
return render(request,'link_to_template',{'form':form})

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.