Can not add values from forms to Django models - django

I made a form and there I had a multiple-choice field called artists which I got from my database and while adding a song a user can select multiple artists and save the song.
The artists are a ManyToManyField in Django models.
models.py
class Artists(models.Model):
""" Fields for storing Artists Data """
artist_name = models.CharField(max_length = 50, blank = False)
dob = models.DateField()
bio = models.TextField(max_length = 150)
def __str__(self):
return self.artist_name
class Songs(models.Model):
""" Fields for storing song data """
song_name = models.CharField(max_length = 30, blank = False)
genre = models.CharField(max_length = 30, blank = False)
artist = models.ManyToManyField(Artists)
release_date = models.DateField()
forms.py
class Song_input(forms.Form):
queryset = Artists.objects.only('artist_name')
OPTIONS = []
for i in queryset:
s = []
s = [i, i]
OPTIONS.append(s)
artist_name = forms.MultipleChoiceField(widget=forms.CheckboxSelectMultiple,choices=OPTIONS)
song_name = forms.CharField()
genre = forms.CharField()
release_date = forms.DateField(widget=DateInput)
Now I want to get all the values selected from the form and save to my database. Here the artist_name may have multiple values.
I have tried using the add() and create() methods but can not figure out how to add all the data where one field (artist_name) having multiple data to my database.

I strongly advise to make use of a ModelForm [Django-doc]. Especially since you make use of ManyToManyFields, which are more cumbersome to save yourself.
# app/forms.py
from django import forms
class SongForm(forms.ModelForm):
class Meta:
model = Songs
fields = '__all__'
widgets = {
'artist': forms.CheckboxSelectMultiple,
'release_date': forms.DateInput
}
There is thus no need to specify the fields yourself, you can change the widgets by adding these to the widgets dictionary [Django-doc] of the Meta subclass.
In your view, you can then both render and sae objects with that form:
# app/views.py
from app.forms import SongForm
def add_song(request):
if request.method == 'POST':
form = SongForm(request.POST, request.FILES)
if form.is_valid():
form.save()
return redirect('name-of-some-view')
else:
form = SongForm()
return render(request, 'some-template.html', {'form': form})
The form.save() will save the object in the database.
In the template, you can then render the template:
<form method="post" action="{% url 'name-of-add_song-view' %}">
{% csrf_token %}
{{ form }}
<input type="submit" value="Submit">
</form>

Related

Django form with inputs populated from database

I have a simple Django model structure with Products and Orders, linked by an intermediary table that determines the quantity of each product within a given order:
models.py:
class Product(models.Model):
name = models.CharField()
class Order(models.Model):
id = models.IntegerField()
products = models.ManyToManyField(Product, through='OrderProduct')
class OrderProduct(models.Model):
order=models.ForeignKey(Order, on_delete=models.CASCADE)
product = models.ForeignKey(Product, on_delete=models.CASCADE)
quantity = models.PositiveSmallIntegerField()
I'd like to design a form in my forms.py file that 1) pulls a list of every product in the database; 2) gives the user the option to adjust the quantity of each product ordered (e.g., in a CharField input), and 3) creates new Order and OrderProduct objects that store a record of the quantity of each Product ordered by the user.
However, I can't figure out a good way to create a form that will automatically pull all Products from the database and add fields to represent the quantity ordered of the given Product. Ideally the result would look something like this, but coded automatically:
forms.py:
class OrderForm(forms.Form):
product_1_quantity = forms.IntegerField()
product_2_quantity = forms.IntegerField()
product_3_quantity = forms.IntegerField()
....etc., for each product. What's the best way to do this? Does it involve a 'for' loop that creates a new field for each product found in the database?
I think Django Formsets might be useful for you. You can create sets of the same form over and over depending based on number you decide, hence your products. Check it out here . You can do stuff such as in the below example.
class ArticleForm(forms.Form):
title = forms.CharField()
from django.forms import formset_factory
ArticleFormSet = formset_factory(ArticleForm, extra=2)
You can also pre-populate it with data such as,
formset = ArticleFormSet(initial=[
{'title': 'Django is now open source'}
])
Hope this helps!
Elaborating on the answer below from user3140312 — Django's formsets provided the solution!
models.py:
class Product(models.Model):
name = models.CharField()
class Order(models.Model):
id = models.IntegerField()
products = models.ManyToManyField(Product, through='OrderProduct')
class OrderProduct(models.Model):
order=models.ForeignKey(Order, on_delete=models.CASCADE)
product = models.ForeignKey(Product, on_delete=models.CASCADE)
quantity = models.PositiveSmallIntegerField()
forms.py:
class ProductForm(forms.Form):
product_id = forms.IntegerField(widget=forms.HiddenInput)
quantity = forms.IntegerField()
views.py:
from .forms import ProductForm
from django.forms import formset_factory
from .models import Product
# Show a form asking for the user's product quantities
def product_form(request):
# If this form is being processed...
if request.method == 'POST':
ProductFormSet = formset_factory(ProductForm)
# Grab the form from the POST variable
formset = ProductFormSet(request.POST)
# If the form is valid...
if formset.is_valid():
# Do whatever you want with the results
# If the form is not being processed, create a new ProductFormSet and render it
else:
product_list = []
for product in Product.objects.all():
product_list.append({'product_id': product.id, 'quantity': 0})
ProductFormSet = formset_factory(ProductForm, extra=0)
formset = ProductFormSet(initial=product_list)
return render(request, 'template.html', {'formset': formset})
template.html:
<form action="{% url '' %}" method="post">
{% csrf_token %}
{{ formset.as_table }}
{{ formset.management_form }}
<input type="submit" value="Submit" />
</form>
You can use ModelChoiceField to show all the products without do a for loop:
class OrderForm(forms.Form):
product = forms.ModelChoiceField(queryset=Product.objects.all()) # with queryset you can show a checkbox with the query
quantify = forms.IntegerField()
Now your views should take care of save your data. If for example you need a button "Add new" you can use ajax otherwise you can use formset

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

Django form with fields from two different models

I need to display one form, with multiple fields from 2 different models.
Form will contain only part of fields from models, and layout will be made using the crispy forms.
My models:
class Company(BaseModel):
title = models.CharField(_('Company'), max_length=128)
domain = models.CharField(_('Domain'), max_length=128)
class Account(BaseModel):
company = models.ForeignKey(Company)
user = models.OneToOneField(User)
role = models.CharField(_('Role'), choices=ROLES, default='member', max_length=32)
Fields which I want to show in form:
company title, user first name, user last name, user email
Is it even possible? How can I do this?
The other answers on this page involve tossing away the benefits of model forms and possibly needing to duplicate some of the functionality you get for free.
The real key is to remember that one html form != one django form. You can have multiple forms wrapped in a single html form tag.
So you can just create two model forms and render them both in your template. Django will handle working out which POST parameters belong to each unless some field names clash - in which case give each form a unique prefix when you instantiate it.
Forms:
class CompanyForm(forms.ModelForm):
class Meta:
fields = [...]
model = Company
class AccountForm(forms.ModelForm):
class Meta:
fields = [...]
model = Account
View:
if request.method == 'POST':
company_form = CompanyForm(request.POST)
account_form = AccountForm(request.POST)
if company_form.is_valid() and account_form.is_valid():
company_form.save()
account_form.save()
return HttpResponseRedirect('/success')
else:
context = {
'company_form': company_form,
'account_form': account_form,
}
else:
context = {
'company_form': CompanyForm(),
'account_form': AccountForm(),
}
return TemplateResponse(request, 'your_template.html', context)
Template:
<form action="." method="POST">
{% csrf_token %}
{{ company_form.as_p }}
{{ account_form.as_p }}
<button type="submit">
</form>
In your forms.py
from django import forms
class YourForm(forms.Form):
title = forms.CharField()
first_name = forms.CharField()
last_name = ...
In your views.py
from forms import YourForm
from django import views
from models import Company, Account
class YourFormView(views.FormView)
template_name = 'some_template.html'
form_class = YourForm
success_url = '/thanks/'
def form_valid(self, form):
title = form.cleaned_data['title']
...
# do your processing here using Company and Account
# i.e. company = Company.objects.create(title=title, ...)
# account = Account.objects.get_or_create(
# user=..., company=company ...)
# ... more processing
#
# Call company.save() and account.save() after adding
# your processed details to the relevant instances
# and return a HttpResponseRedirect(self.success_url)
def is_valid(self):
# don't forget to validate your fields if need be here
As usual the docs are pretty helpful.
https://docs.djangoproject.com/en/1.7/topics/forms/

Django error in forms.FileField()

I have a problem with my app.
A pet store app.
I created 2 forms . The First form allow users to create their own store and save the data into the models which I did successfully and the second form allows users to add their own pets to the pet store.
The first form was successful because it got validated correctly but my second form didn't succeed the validation because in PetForm , I have a field called image = forms.FileField() where users can upload a picture of their pet and the validation fail because the picture does not save anywhere.
I tried to put in an argument into image = forms.FileField(upload_to="images/")
but I received an error
__init__() got an unexpected keyword argument 'upload_to'
I'm reading the documentation at the moment and it state ' When you I use FileField in a form, you must also remember to bind the file data to the form.'.
I'm having trouble understanding binding the file data .
Can someone please help me !
My forms.py
from django import forms
from pet.models import Store
from pet.models import Pet
class StoreForm(forms.ModelForm):
name = forms.CharField(max_length =20,widget =forms.Textarea)
number = forms.CharField(max_length =20,widget =forms.Textarea)
address = forms.CharField(max_length = 20,widget = forms.Textarea)
class Meta:
model = Store
fields = ('name','number','address')
class PetForm(forms.ModelForm):
animal =forms.CharField(max_length = 20,widget = forms.Textarea)
description =forms.CharField(max_length =20, widget = forms.Textarea)
owner = forms.ModelChoiceField(queryset=Store.objects.all())
image = forms.FileField()
class Meta:
model = Pet
fields = ('animal','description','owner','image')
My models.py
from django.db import models
class Store(models.Model):
name = models.CharField(max_length = 20)
number = models.BigIntegerField()
address =models.CharField(max_length = 20)
def __unicode__(self):
return self.name
class Pet(models.Model):
animal = models.CharField(max_length =20)
description = models.TextField()
owner = models.ForeignKey(Store)
image = models.FileField(upload_to="images/",blank=True,null=True)
def __unicode__(self):
return self.animal
This is parts of my views.py
import from django.core.files.uploadedfile import SimpleUploadedFile
def fan(request): # this is the function that saves my form into my models.
form = PetForm(request.POST or None)
if form.is_valid():
dad = form.save(commit=False)
dad.save()
if 'cat' in request.POST:
cat = request.POST['next']
else:
cat = reverse('world:index')
return HttpResponseRedirect(cat)
return render_to_response(
'fan.html',
{'form':PetForm()},
context_instance = RequestContext(request)
)
and My fan.html
<form method="POST" "action">{% csrf_token %}
<ul>
{{ form.as_ul }}
</ul>
<input type = "submit" value= "Add Pets to Store" />
</form>
Because your override your Pet model image. Delete the image in your form.
class PetForm(forms.ModelForm):
animal =forms.CharField(max_length = 20,widget = forms.Textarea)
description =forms.CharField(max_length =20, widget = forms.Textarea)
owner = forms.ModelChoiceField(queryset=Store.objects.all())
class Meta:
model = Pet
fields = ('animal','description','owner','image')
//It's not necessary to defined again model field in the form. Once you call the model
//in the form it's understood what you want to show. You can only defined the model
//field again if you want something to add or embed in that field. Like for example you
//want to change the charfield in to textarea or you defined a queryset like you did
//above. Erase your max_length because you are already defined that in the model.
When you upload images don't forget to add "multipart/form-data" in the form
<form method="POST" enctype="multipart/form-data" "action" >
{% csrf_token %}
<ul>
{{ form.as_ul }}
</ul>
<input type = "submit" value= "Add Pets to Store" />
</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.