How to create and process a form with multiple same fields? - django

I'm really stuck with this. To show my problem I created a new Django project and started from scratch, focusing only on one single form.
What I'm trying to do is to create a form with several fields of the same name. I tried using modelformset_factory to achieve this but it looks to me like it's not what I really need.
Below is my code (also on dpaste) which currently works fine with one single field called name. How can I create and process a form which would have several name fields? Could somebody point me in the right direction?
# models.py
class Category(models.Model):
name = models.CharField(max_length=30, unique=True)
user = models.ForeignKey(User, blank=True, null=True)
class Meta:
verbose_name_plural = "Ingredience Categories"
def __unicode__(self):
return self.name
# forms.py
class CategoryForm(ModelForm):
class Meta:
model = Category
fields = ('name',)
# views.py
def home(request):
if request.method == 'POST':
catform = CategoryForm(request.POST)
catformInstance = catform.save(commit = False)
catformInstance.save()
return HttpResponseRedirect('')
else:
catform = CategoryForm()
context = {'catform': catform}
return render_to_response('home.html', context, context_instance=RequestContext(request))
# home.html template
<h3>Insert new Category</h3>
<form action="/" method="post" id="ingr-cat-form">{% csrf_token %}
{{ catform.as_p }}
<input type="submit" name="ingrCatForm" value="Save" />
</form>
UPDATE: to clarify, I want to allow user to insert several categories within one form. I think I'm getting close, here is my new version of views.py but it still stores just one category (the last one in the list):
def home(request):
if request.method == 'POST':
catform = CategoryForm(request.POST)
names = request.POST.getlist('name')
catformInstance = catform.save(commit = False)
for name in names:
catformInstance.name = name
catformInstance.save()
return HttpResponseRedirect('')
else:
catform = CategoryForm()
context = {'catform': catform}
return render_to_response('home.html', context, context_instance=RequestContext(request))

You cannot have fields with the same name (on the same Model). If you only need to change the html label in the html form, use
class Category(models.Model):
name = models.CharField(max_length=30, unique=True)
name2 = models.CharField(max_length=30, unique=True, verbose_name="name")
user = models.ForeignKey(User, blank=True, null=True)
or
class CategoryForm(ModelForm):
def __init__(self , *args, **kwargs):
super(CategoryForm, self).__init__(*args, **kwargs)
self.fields['name2'].label = "name"

Here is a working solution. Thanks to #YardenST for pointing me in the right direction. I managed to solve my initial problem by following this tutorial.
# models.py
class Category(models.Model):
name = models.CharField(max_length=30, unique=True)
user = models.ForeignKey(User, blank=True, null=True)
class Meta:
verbose_name_plural = "Ingredience Categories"
def __unicode__(self):
return self.name
# forms.py
class CategoryForm(ModelForm):
class Meta:
model = Category
fields = ('name',)
# views.py
def home(request):
if request.method == 'POST':
catforms = [CategoryForm(request.POST, prefix=str(x), instance=Category()) for x in range(0,3)]
if all([cf.is_valid() for cf in catforms]):
for cf in catforms:
catformInstance = cf.save(commit = False)
catformInstance.save()
return HttpResponseRedirect('')
else:
catform = [CategoryForm(prefix=str(x), instance=Category()) for x in range(0,3)]
context = {'catform': catform}
return render_to_response('home.html', context, context_instance=RequestContext(request))
# home.html template
<h3>Insert new Category</h3>
<form action="/" method="post" id="ingr-cat-form">{% csrf_token %}
{% for catform_instance in catform %} {{ catform_instance.as_p }} {% endfor %}
<input type="submit" name="ingrCatForm" value="Save" />
</form>

Related

Django & Form with SelectMultiple: null value in column x of relation y violates not-null constraint

I am facing the following error when trying to save the project after filling the form where i am choosing multiple tags for this project:
null value in column "tag_id" of relation "project_tags" violates not-null constraint
DETAIL: Failing row contains (10263, 10262, null). --> project_tags(id, project_id, tag_id)
The db model is simple: I have projects, and each project has tag(s). All is defined by the following models:
class Project(models.Model):
id = models.AutoField(primary_key=True)
client = models.ForeignKey(User, on_delete=models.CASCADE)
start_date = models.DateField()
end_date = models.DateField()
class Meta:
managed = False
db_table = 'project'
def get_absolute_url(self):
return reverse('project-detail', kwargs={'pk': self.pk})
class ProjectTag(models.Model):
id = models.IntegerField(primary_key=True)
name = models.CharField(max_length=30)
class Meta:
managed = False
db_table = 'project_tag'
def __str__(self):
return self.name
class ProjectTags(models.Model):
id = models.AutoField(primary_key=True)
project = models.ForeignKey(Project, on_delete=models.CASCADE)
tag = models.ManyToManyField(ProjectTag)
class Meta:
managed = False
db_table = 'project_tags'
View is following:
#login_required
def add_project(request):
submitted = False
if request.method == "POST":
form1 = ProjectForm(request.POST)
form2 = ProjectTagsForm(request.POST)
if form1.is_valid() and form2.is_valid():
form1_part = form1.save(commit=False)
form1_part.client = request.user
form1_part.save()
project_id = Project.objects.get(id=form1_part.id)
form2_part = form2.save(commit=False)
form2_part.project = project_id
form2_part.save()
return HttpResponseRedirect('/project/new?submitted=True')
else:
form1 = ProjectForm
form2 = ProjectTagsForm
if 'submitted' in request.GET:
submitted = True
context = {'form1':form1
,'form2':form2
,'submitted':submitted
}
return render(request, 'home_page/add_project.html',context)
WIth following forms that are both in one template:
class ProjectForm(forms.ModelForm):
class Meta:
model = Project
fields = ('start_date', 'end_date')
def __init__(self, *args, **kwargs):
super(ProjectForm, self).__init__(*args, **kwargs)
class ProjectTagsForm(forms.ModelForm):
class Meta:
model = ProjectTags
fields = ('tag',)
widgets = {
'tag': forms.SelectMultiple(attrs={'class': 'form-control','size': 12})
}
{% if submitted %}
Your project was submitted successfully!
{% else %}
<form action="" method=POST>
{% csrf_token %}
{{ form1.as_p }}
{{ form2 }}
<input type="submit" value="submit me" class="btn btn-secondary">
I understand the problem that after submitting the form it is not able to convert the selected tag names into tag_ids to fill the records (id,project_id,tag_id) and insert them to db table (as the table doesnt allow nulls as tag_id), but I have no idea how to fix, especially when it is practically copy paste of this Codemy video and its script where it worked https://youtu.be/x99hW1N8Nug

Edit UserProfile information in Django

Problem description: UserProfile form doesn't save any data.
I am creating a new User and automatically create a UserProfile object for him (so I'm extending UserProfile), so I can go to admin page and fill all the fields . But when I'm trying to do it from client side, my form just doesn't catch the data.
Also the strangest moment is that I can change username and email using UserChangeForm, so I'm trying to do the same for UserProfileObject.
models.py:
class UserProfile(models.Model):
user = models.OneToOneField(User)
image = models.ImageField(upload_to='profile_image', blank=True)
title = models.CharField(max_length=100, default = '')
first_name = models.CharField(max_length=200, default = '')
last_name = models.CharField(max_length=200, default = '')
subject = models.ManyToManyField('Subject', related_name='tutor_type', default = '', help_text="Select a subject")
AREA_STATUS = (
('Jerusalem', 'Jerusalem'),
('Tel Aviv', 'Tel Aviv'),
('Haifa', 'Haifa'),
('Eilat', 'Eilat')
)
area = models.CharField(max_length=200, choices=AREA_STATUS, blank=True, default='', help_text='Tutor area')
# Foreign Key used because tutor can only have one area, but area can have multiple tutors
# Author as a string rather than object because it hasn't been declared yet in file.
description = models.TextField(max_length=4000, help_text="Enter a brief description about yourself")
charge = models.IntegerField(default = '0')
# ManyToManyField used because Subject can contain many tutors. Tutors can cover many subjects.
# Subject declared as an object because it has already been defined.
LANGUAGE_CHOICES = (
('English','English'),
('Hebrew','Hebrew'),
('Russian','Russian'),
('French','French'),
('Arabic','Arabic'),
)
language = models.CharField('Language', choices = LANGUAGE_CHOICES, max_length=50, null=True)
def __str__(self):
return self.user.username
def display_subject(self):
"""
Creates a string for the subject. This is required to display subject in Admin.
"""
return ', '.join([ subject.name for subject in self.subject.all()[:3] ])
display_subject.short_description = 'Subject'
def create_profile(sender, **kwargs):
if kwargs['created']:
user_profile = UserProfile.objects.create(user=kwargs['instance'])
post_save.connect(create_profile, sender = User)
forms.py::
class EditProfileForm(UserChangeForm):
class Meta:
model = User
fields = (
'username',
'email',
'password'
)
class EditExtendedProfileForm(UserChangeForm):
class Meta:
model = UserProfile
fields = '__all__'
exclude = ('user',)
views.py:
def edit_profile(request):
if request.method == 'POST':
form = EditProfileForm(request.POST, instance=request.user)
if form.is_valid():
form.save()
return redirect(reverse('accounts:view_profile'))
else:
form = EditProfileForm(instance=request.user)
args = {'form': form}
return render(request, 'accounts/edit_profile.html', args)
def edit_extended_profile(request):
if request.method == "POST":
form = EditExtendedProfileForm(request.POST, instance=request.user)
if form.is_valid():
form.save()
return redirect(reverse('accounts:view_profile'))
else:
return redirect(reverse('accounts:edit_extended_profile'))
else:
form = EditExtendedProfileForm(instance = request.user)
args = {'form':form}
return render(request, 'accounts/edit_extended_profile.html', args)
edit_extended_profile.html:
{% extends "base.html" %}
{% block head %}
<title>Profile</title>
{% endblock %}
{% block body %}
<div class = "container">
<form method="POST">
{% csrf_token %}
{{form.as_p}}
<button type = "submit" class = "btn btn-success">Submit</button>
</form>
</div>
{% endblock %}
and it is the same template as for edit_profile view.
No traceback, no errors. Any help will be appreciated. Thanks in advance.

Django - Saving a form form multiple model

I am new in Django, and trying to create a simple blog platform out of it. It's where user can create/edit a blog post on the front end.
Here is my models.py
class Blog(models.Model):
title = models.CharField(max_length=200)
content = models.TextField()
author = models.ForeignKey(User, null=True, blank=True)
......
class Categories(models.Model):
blog = models.ForeignKey(Blog)
name = models.CharField(max_length=200)
slug = models.CharField(max_length=200)
......
And I created a form in the front end by :
#forms.py
class AddBlogForm(forms.ModelForm):
class Meta:
model = Blog
class AddCategoryForm(forms.ModelForm):
class Meta:
model = Categories
#views.py
def AddBlogPost(request):
if request.POST:
lf= AddBlogForm(request.POST, request.FILES,prefix="Lform")
cf = AddCategoryForm(request.POST, prefix="Cform")
if lf.is_valid() and cf.is_valid():
addblog = lf.save(commit=False)
addblog.save()
obj_id = addblog.id
addcategory = cf.save(commit=False)
addcatgory.blog_id = obj_id
addcatgory.save()
return HttpResponseRedirect(reverse('edit', args=(obj_id,)))
else:
blog= AddBlogForm(prefix="Lform")
category = AddCategoryForm(prefix="Cform")
return render_to_response("forms.html", locals(),context_instance=RequestContext(request))
#template
<form action="" enctype="multipart/form-data" method="POST">{% csrf_token %}
{{blog.as_p}}
{{category.as_p}}
<input type="submit">
</form>
This working actually, but I feel it is too redundant create and saving a form with two classes.It's there anyway to simplified this?
You can use django forms to save data from a form to multiple models. For example:
forms.py
class AddBlogCategory(forms.Form):
title= form.CharField(max_length=100)
content= form.CharField(max_length=255)
...............
...............
views.py:
def AddBlogPost(request):
if request.POST:
form= AddBlogCategory(request.POST)
if form.is_valid():
addblog= Blog() #import model class
addblog.title= form.cleaned_data['title']
addblog.content= form.cleaned_data['content']
addblog.author= request.user
.....
addblog.save()
obj_id= addblog.id
addcat=Categories()
addcat.blog= addblog
....
addcat.save()
return HttpResponseRedirect(reverse('edit', args=(obj_id,)))
else:
return render_to_response("forms.html", {form:'form'})
Its an untested code, but you can have an idea how to implement it.

ModelForm has empty fields list

Sorry for my poor english...
I've got a Model called Habitation :
class Habitation(models.Model):
propr = models.ForeignKey(Client, related_name="proprietaire")
locat = models.ForeignKey(Client, related_name="locataire", null=True, blank=True)
etage = models.CharField(max_length=2, blank=True)
numero = models.CharField(max_length=3, blank=True)
ad1 = models.CharField(max_length=64)
ad2 = models.CharField(max_length=64, blank=True)
cp = models.CharField(max_length=5)
ville = models.CharField(max_length=32)
def get_appareils(self):
return Appareil.objects.filter(habitation=self)
def selflink(self):
if self.id:
return 'Editer' % str(self.id)
else:
return 'Indéfini'
selflink.allow_tags = True
def __unicode__(self):
return u'%s - %s %s' % (self.ad1, self.cp, self.ville)
With his edit view :
def edit(request, habitation_id):
habitation = Habitation.objects.get(pk=habitation_id)
if request.POST:
form = HabitationForm(request.POST, instance=habitation)
if form.is_valid():
form.save()
return redirect('clients')
else:
form = HabitationForm(instance=habitation)
print form.fields
return render_to_response('habitations/edit.html', {
'habitation_id': habitation_id,
'form': form,
}, context_instance=RequestContext(request))
and his template :
<table>
<form action="/habitations/edit/{{ habitation_id }}/" method="post">
{{ form }}
{% csrf_token %}
{{ form.as_table }}
</form>
</table>
Form:
from django import forms
from client import models
class HabitationForm(forms.ModelForm):
class meta:
model = models.Habitation
fields = ('propr', 'locat', 'etage', 'numero', 'ad1', 'ad2', 'cp', 'ville',)
My view (or my ModelForm) doesn't retrive any field, so no more form field.
Is anybody has any suggestion ?
The meta class name in form should be Meta not meta.
Update your form to
from django import forms
from client import models
class HabitationForm(forms.ModelForm):
class Meta: #<---- define with capital M
model = models.Habitation
fields = ('propr', 'locat', 'tegae', 'numero', 'ad1', 'ad2', 'cp', 'ville',)

Storing form data in the database

I can't figure out how to store a simple form in the database. I think I'm quite close but there is probably something wrong in my views.py. Here is my code, any ideas what I'm doing wrong? (also on dpaste)
# models.py
class IngredienceCategory(models.Model):
name = models.CharField(max_length=30, unique=True)
user = models.ForeignKey(User, null=True, blank=True)
class Meta:
verbose_name_plural = "Ingredience Categories"
def __unicode__(self):
return self.name
# forms.py
class CategoryForm(forms.Form):
name = forms.CharField(max_length=30)
# views.py
#login_required
def newCategory(request):
if request.method == 'POST':
username = request.user.username
cform = CategoryForm(request.POST)
if cform.is_valid():
formInstance = cform.save(commit = False)
formInstance.user = username
formInstance.name = cform.cleaned_data['name']
formInstance = IngredienceCategory.objects.filter(name=formInstance.name, user=formInstance.user)
formInstance.save()
# return HttpResponseRedirect('new-category/')
else:
form = CategoryForm()
context = {'form': form}
return render_to_response('new-category.html', context, context_instance=RequestContext(request))
# new-category.html
<h3>Insert New Category</h3>
<form action="/" method="post" id="food-form">{% csrf_token %}
{{ form.as_p }}
<input type="submit" name="foodForm" value="Save" />
</form>
The line below is not useful at it current position. That command will perform a database query and assign the result as a queryset, before you have saved the form data.
formInstance = IngredienceCategory.objects.filter(name=formInstance.name, user=formInstance.user)
This should work:
With cform as a normal Form:
if cform.is_valid():
formInstance = IngredienceCategory(user=request.user, cform.cleaned_data['name'])
formInstance.save()
If cform had been a ModelForm you could do:
if cform.is_valid():
formInstance = cform.save(commit=False)
formInstance.user = request.user
formInstance.save()
I do recommend you to check out ModelForms since it will build the cleaning functionality based on your model.
You should inherit from ModelForm
from django.forms import ModelForm
class CategoryForm(ModelForm):
class Meta:
model = IngredienceCategory
Refer to https://docs.djangoproject.com/en/dev/topics/forms/modelforms/ for how to render form and save it to database.