Django - Saving a form form multiple model - django

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.

Related

Basic logics with Django views and foreignkey

I am new to Django and struggling with a basic problem, yet cannot find a solution online.
I have these models:
class Suggestion(models.Model):
author = models.ForeignKey('auth.User')
title = models.CharField(max_length=200)
description = models.TextField()
created_date = models.DateTimeField(default=timezone.now)
def __str__(self):
return self.title
class Vote(models.Model):
suggestion = models.ForeignKey(Suggestion)
voter = models.ForeignKey('auth.User')
vote_count = models.IntegerField(default=1)
and I'm trying create a view that would add a Vote to a given Suggestion, capturing the User who voted. I've seen some seem to do this with a form or with a regular function, so not sure what's the best practice here?
EDIT
Here is my attempt (not working). Any help/advice appreciated.
#forms.py
class VoteForm(forms.ModelForm):
class Meta:
model = Vote
fields = ()
#models.py
class Vote(models.Model):
suggestion = models.ForeignKey(Suggestion)
voter = models.ForeignKey('auth.User')
vote_count = models.BooleanField()
#views.py
def add_vote(request, pk):
if request.method == 'POST':
form = VoteForm(request.POST)
suggestion = get_object_or_404(Suggestion, pk=pk)
if form.is_valid():
vote = form.save(commit=False)
vote.voter = request.user
vote.vote_count = True
vote.save()
return render(request, 'suggestion/suggestion_detail.html', {'suggestion': suggestion})
#vote_form.html
<form method="post">
{% csrf_token %}
{{ form }}
<button type="submit">Vote</button>
</form>
You should create a form for the vote and include it on the Suggestion view. The form can have it's own html -- vote_form.html. Then include it on the suggestion html page with
{% include '[name of directory]/vote_form.html' %}
As for the vote count, it shouldn't be an integer field unless you want users to cast multiple votes. If you just want someone to be able to vote once per suggestion, you should make the vote_count a boolean field (either true or false). Then you can assign true to a vote and false to a non-vote.
I've managed to do what I wanted in this way:
#vote_form.html
<form action="{% url 'add_vote' suggestion.id %}" method="post">
{% csrf_token %}
<input type="submit" value="I want to vote">
</form>
#urls.py
urlpatterns = [
url(r'^suggestion/(?P<pk>\d+)/$', views.SuggestionDetail.as_view(), name="suggestion_detail"),
url(r'^suggestion/(?P<pk>\d+)/vote/$', views.add_vote, name='add_vote'),
]
#models.py
class Vote(models.Model):
suggestion = models.ForeignKey(Suggestion)
voter = models.ForeignKey('auth.User')
vote_count = models.BooleanField()
#views.py
def add_vote(request, pk):
suggestion = get_object_or_404(Suggestion, pk=pk)
vote = Vote(
suggestion = suggestion,
voter = request.user,
vote_count = True)
has_user_voted = Vote.objects.filter(voter=request.user, suggestion=suggestion).count()
if has_user_voted < 1:
vote.save()
else:
messages.error(request, 'It seems you have already voted, only one vote is allowed')
return HttpResponseRedirect(reverse('suggestion_detail', args=(suggestion.id,)))

Uploading image in template

I created two models:
class Country(models.Model):
name = models.CharField(max_length=50)
class City(models.Model):
name = models.CharField(max_length=50)
country = models.ManyToManyField(Country)
image = models.ImageField('New photos', upload_to='img/newphotos', blank=True)
I want add new cities through template so i created:
views.py
def newcity(request):
if request.method == "POST":
form = CityForm(request.POST)
if form.is_valid():
city = form.save(commit=False)
city.save()
form.save_m2m()
return redirect('city.views.detailcity', pk=city.pk)
else:
form = CityForm()
return render(request, 'city/editcity.html', {'form': form})
forms.py:
class CityForm(forms.ModelForm):
class Meta:
model = City
fields = ('name', 'country', 'image',)
Everything is ok but when I add image there is nothing happens - image is chosen but when I click save button new city is added without image (in admin panel it works). What must I add to my code? And how can i make possibility to add to one city few images? When i will add first image there should appear button to add second etc. Now I have place only for one.
Add request.FILES in your views
form = CityForm(request.POST, request.FILES)
and make sure you have enctype="multipart/form-data" and method="post" in your template
<form method="post" enctype="multipart/form-data">{% csrf_token %}
</form>
https://docs.djangoproject.com/en/dev/ref/request-response/#django.http.HttpRequest.FILES

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',)

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

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>

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.