Motivation
I'm currently creating a to-do-list app in django for practice. What I'm trying to do now is to give the "user" the option to submit multiple to-do items at once. To do so, I display the form multiple times and then retrieve the items from each form individually.
Attempt & Error
Here's the form in question:
class AddItemForm(forms.Form):
name = forms.CharField(max_length=60, label='Item Name')
priority = forms.IntegerField(required=False,
widget=forms.Select(choices=Item.PRIORITY))
due_date = forms.DateTimeField(required=False, label='Due Date')
However, when I try to create a form using keyword arguments (the lines of interest are in the for loop):
def add_item(request):
if request.method == 'POST':
r = request.POST
names = r.getlist('name')
priorities = r.getlist('priority')
due_dates = r.getlist('due_date')
for i in xrange(len(names)):
form = AddItemForm(
name=names[i],
priority=priorities[i],
due_date=due_dates[i],
)
if form.is_valid():
item = form.cleaned_data
Item.objects.create(**item)
return HttpResponseRedirect('/todo')
form = AddItemForm()
try:
num_items = xrange(int(request.GET.get('n', 1)))
except ValueError:
num_items = xrange(1)
return render(request, 'add_item.html',
{'form': form, 'num_items': num_items})
I get the following error message:
Exception Type: TypeError
Exception Value:
__init__() got an unexpected keyword argument 'priority'
I don't understand what's going on since I do have priority as a field in AddItemForm.
HTML
Here's the template html if it helps:
<!DOCTYPE html>
<html>
<head> <title>Add item</title> </head>
<body>
<form method="post">{% csrf_token %}
{% for i in num_items %}
<div>{{ form }}</div>
{% endfor %}
<input type="submit">
</form>
<br><br>
<form action="/todo" method="get">
<input type="submit" value="Go back to To-Do List">
</form>
</body>
</html>
Well, that's not how forms work. You're not supposed to process the POST arguments first, and you can't pass data for individual fields as arguments like that. You're simply supposed to pass request.POST into the form instantiation as-is.
The way to do a set of multiple identical forms is to use a formset. You can then pass the POST data straight into the formset instantiation, and get validated forms out.
Note that since your form is being used to create model instances, you may want to consider using a modelform (and a model formset, on the same page).
Django the form.init() accepts an 'initial' keyword argument, you could have set the initial values of the form in the following way:
form = AddItemForm(initial = {
'name':names[i],
'priority':priorities[i],
'due_date':due_dates[i],
}
)
Related
Consider this :
{% for user in users.query.all() %}
<tr>
<form method='POST' action="">
<td>{{form.username}}</td>
<td>{{form.description}}</td>
<td>{{form.submit(value="Update")}}</td>
</form>
</tr>
{% endfor %}
For each user this will create a small form that I can update, I want to populate these forms with current database data
What I tried to do in the routes file:
#app.route("/Users")
def listUsers():
users = Users
form = UserForm()
if request.method == 'GET':
for user in users.query.all():
form.username.data = user.username
form.description.data = user.description
return render_template('Users.html', users=users, form=form)
This results in having the data of the last user populating all of the forms, how can I go about fixing this ?
I was thinking of assigning an id to each form that matchs the user, but how would I be able to send a dynamic number of forms ?
It took me a while, but I got a work around, just gonna post it if anyone else has the same issue:
I used javascript ... created a function and called it within the for loop which populated the fields for me
function populateForm(username,description){
var form = document.getElementById('form id here');
form.nextElementSibling.value = username;
form.nextElementSibling.nextElementSibling.textContent = description;
}
note that I used value for input field and textContent for textfield, then inside the for loop i added a script tag
<script>
populateForm('{{user.username}}','{{user,description}}');
</script>
I'm trying to get validation running on a django form used to retrieve a list of objects in a ListView View. Despite having read django docs and many other questions here, I can't find out what's wrong in this simple test code:
form.html
<form action="list.html" method="get">
{{ form }}
<input type="submit" value="Submit">
</form>
list.html
<ul>
{% for area in object_list %}
<li>{{ area.name }}</li>
{% endfor %}
</ul>
forms.py
from django import forms
class SearchArea(forms.Form):
area = forms.CharField(label='Area code', max_length=6)
def clean_area(self):
area = self.cleaned_data['area'].upper()
if '2' in area:
raise forms.ValidationError("Error!")
return area
views.py
class HomePageView(FormView):
template_name = 'form.html'
form_class = SearchArea
class AreaListView(ListView):
template_name = 'list.html'
model = AreaCentral
def get_queryset(self):
q = self.request.GET.get('area')
return AreaCentral.objects.filter(area__istartswith=q)
When I try to submit something like "2e" I would expect a validation error, instead the form is submitted. Moreover I can see in the GET parameters that 'area' is not even converted to uppercase ('2E' instead of '2e').
The default a FormView will only process the form on POST; the GET is for initially displaying the empty form. So you need to use method="post" in your template form element.
Your action attribute is also suspect; it needs to point to the URL of the form view. If that actually is the URL, note it's not usual to use extensions like ".html" in Django URLs, and I would recommend not doing so.
This is my first django project and I'm struggling to finish it.
I've been working to function that editing post. When user clicks button, it send no(int)for that article, and get information related to no and display on page. User can edit that post in the same form and when user click submit, it redirect to home.html
However, the function I made keep sending me an error message that it takes 2 arguments even though I did not use any function that takes 2 arguments.
Here is views.py
#login_required
def edit_article(request, article_no):
article = Article.objects.filter(pk=article_no)
form = ArticleForm(request.POST, instance=request.article)
if form.is_valid():
form.save()
messages.add_message(request, messages.SUCCESS, _('Article correctly saved.'))
# If the save was successful, redirect to another page
redirect_url = reverse('blog/home.html')
return HttpResponseRedirect(redirect_url)
else:
form = ArticleForm(instance=request.article)
return (request, {'form': form}, context)
This is form in detail.html where send no value to edit_article.html
<form action="{% url 'blog:edit_article' %}" method="post" style="display: inline;">
{% csrf_token %}
<input type="hidden" name="no" value="{{ item.no }}" />
<button type="submit">edit></button>
</form>
The article_no arg does not magically find its way into the function call via the POST submit. You need to provide it to the url tag:
{% url 'blog:edit_article' item.no %}
This assumes, of course, that you have a url pattern with an appropriate named group associated with this view/view name.
If You are talking about this function, it does recieve more than one Arg, it recieves the No you are talking about, and the request object
def edit_article(request, article_no):
...
If your view needs arguments you must give the arguments in the url templatetag, like this :
{% url 'accounts:detail_account' username = username %}
i have an custom form , whenever i fetch the form values to save in the database than it display an error ( applicationform() got an unexpected keyword argument 'job_title' ) and the values are not save in the table.
views.py :-
def applicationvalue(request):
if request.method == 'POST':
getjobtitle = request.POST['jobtitle']
getintable = applicationform(job_title=getjobtitle)
getintable.save()
print getjobtitle
return HttpResponse(getintable)
else:
return render_to_response('registration/applicationform.html')
my form is :-
<form method="POST" action="#" class="form-horizontal" id="applicationform" name="appform">
<input type="text" id="u_jobtitle" class="input-xlarge" name="jobtitle" value=" " />
<button class="btn btn-gebo" type="submit" name="usubmit">Save changes</button>
whenever i fetch the values from form to save the values in table field " job_title " than it will display an error :-
applicationform() got an unexpected keyword argument 'job_title'
Change input field name to job_title in your html
<input name="job_title" type="text" id="u_jobtitle" class="input-xlarge" value=" " />
-------------^ changed
and then in view do
def applicationvalue(request):
if request.method == 'POST':
#Dont need this
#getjobtitle = request.POST['jobtitle']
#---------------------------Use request.POST
getintable = applicationform(request.POST)
getintable.save()
print getjobtitle
return HttpResponse(getintable)
else:
return render_to_response('registration/applicationform.html')
It will be better if you use same form to render html instead of hand coding it.
The applicationform constructor should take the request.POST as argument.
But it seems to me that you are not using django forms in the "right" way. I think that your view doesn't follow the django philosophy for using form.
In your case, you should have a model:
from django.db import models
class Application(models.Model):
job_title = models.CharField(max_length=100)
Based on this model, you can declare a ModelForm:
from django import forms
from .models import ApplicationModel
class ApplicationForm(forms.ModelForm):
class Meta:
model = ApplicationModel
fields = ('job_title',)
Then you can use this form in your view
def applicationvalue(request):
if request.method == 'POST':
form = ApplicationForm(request.POST)
if form.is_valid():
#This is called when the form fields are ok and we can create the object
application_object = form.save()
return HttpResponse("Some HTML code") # or HttResponseRedirect("/any_url")
else:
form = ApplicationForm()
#This called when we need to display the form: get or error in form fields
return render_to_response('registration/applicationform.html', {'form': form})
finally you should have a registration/applicationform.html template with something like:
{% extends "base.html" %}
{% block content %}
<form action="" method="post">{% csrf_token %}
<table>
{{form.as_table}}
</table>
<input type="submit" value="Add">
</form>
{% endblock %}
I hope it helps
I'm familar with using templates to collect the data, but on displaying is there a smart way that Django will display the fields and populate them with the right values. I can do it manually of course, but the model knows the field type. I didn't see any documentation on this. For example I collect data from the template with:
<strong>Company Name</strong>
<font color="red">{{ form.companyname.errors }}</font>
{{ form.companyname }}
where form is my company model containing all the fields. How would I go about ensuring that I could use this type of methodology such that Django would render the text fields and populate with the current values. For example is there a way to send in values in the following way:
myid = int(self.request.get('id'))
myrecord = Company.get_by_id(myid)
category_list = CompanyCategory.all()
path = os.path.join(os.path.dirname(__file__), 'editcompany.html')
self.response.out.write(template.render(path, {'form': myrecord, 'category_list': category_list}))
Can I do the same this with records and will the template populate with values sent in? Thanks
It sounds like you may be confused about the difference and proper usage of Form vs ModelForm
Regardless of which type of form you use, the templating side of forms stays the same:
Note: all of the values in your form (as long as its bound to POST or has an instance) will be prepopulated at render.
<form class="well" action="{% url member-profile %}" method="POST" enctype="multipart/form-data">{% csrf_token %}
<fieldset>
{{ form.non_field_errors }}
{{ form.display_name.label_tag }}
<span class="help-block">{{ form.display_name.help_text }}</span>
{{ form.display_name }}
<span class="error">{{ form.display_name.errors }}</span>
{{ form.biography.label_tag }}
<span class="help-block">{{ form.biography.help_text }}</span>
{{ form.biography }}
<span class="error">{{ form.biography.errors }}</span>
<input type="submit" class="button primary" value="Save" />
</fieldset>
</form>
if you want to be populating a form from a record (or submit a form as a record) its probably best to use ModelForm
EX a profile form that doesn't display the User FK dropdown:
class ProfileForm(forms.ModelForm):
"""Profile form"""
class Meta:
model = Profile
exclude = ('user',)
The View:
def profile(request):
"""Manage Account"""
if request.user.is_anonymous() :
# user isn't logged in
messages.info(request, _(u'You are not logged in!'))
return redirect('member-login')
# get the currently logged in user's profile
profile = request.user.profile
# check to see if this request is a post
if request.method == "POST":
# Bind the post to the form w/ profile as initial
form = ProfileForm(request.POST, instance=profile)
if form.is_valid() :
# if the form is valid
form.save()
messages.success(request, _(u'Success! You have updated your profile.'))
else :
# if the form is invalid
messages.error(request, _(u'Error! Correct all errors in the form below and resubmit.'))
else:
# set the initial form values to the current user's profile's values
form = ProfileForm(instance=profile)
return render(
request,
'membership/manage/profile.html',
{
'form': form,
}
)
notice that the outer else initializes the form with an instance: form = ProfileForm(instance=profile) and that the form submit initializes the form with post, BUT still binds to instance form = ProfileForm(request.POST, instance=profile)
If you're looking at forms, it would seem like a good idea to start with Django's forms framework, specifically forms for models.