How to save a ManyToMany relationship from a ModelForm in Django - django

I'm not sure to save my ManyToMany relationship. I found my exact problem in this thread: Django embedded ManyToMany form, except instead of Sales and Products models, I have models that make up a movie.
I tried the solution, but I receive a syntax error. I don't understand how Django should link the EquipmentModel, LightModel, and ActorModel to the ManyToMany relationship in MovieModel. So far (before trying the other thread's solution), the CharFields that are displayed on the form for LightModel, EquipmentModel, and ActorModel are not linked to the ManyToManyField in MovieModel. So when I save the forms and try to access a particular Movie's actors, all I see is a blank list. The solution from the other thread seems to make sense since it tries to link the models to the ManyToMany relationship in MovieModel, but I don't understand how Django knows which MovieModel to add to (how does it get the correct movieID?).
On a side note, is there a way to check for duplicate movies when the user presses the 'Submit' button on the form? I want to avoid creating duplicates.
views.py:
def add_movie(request, movieID=""):
if request.method == "POST":
form = MovieModelForm(request.POST)
eform = EquipmentModelForm(request.POST)
lform = LightModelForm(request.POST)
aform = ActorModelForm(request.POST)
print 'checking form'
print request.POST.items()
if form.is_valid() and eform.is_valid() and lform.is_valid() and aform.is_valid():
print 'form is valid'
movie_to_add = form.save()
e = eform.save()
l = lform.save()
a = aform.save()
movie_to_add.actors.add(a)
movie_to_add.lights.add(l)
movie_to_add.equipments.add(e)
# return HttpResponseRedirect('/data')
else:
# code for create forms ....
return render_to_response('add_movie.html', {'form':form, 'eform':eform,'lform':lform, 'aform':aform,}, context_instance=RequestContext(request))
Other code that may help:
forms.py
class LightModelForm(forms.ModelForm):
class Meta:
model = LightModel
class ActorModelForm(forms.ModelForm):
class Meta:
model = ActorModel
class EquipmentModelForm(forms.ModelForm):
class Meta:
model = EquipmentModel
class MovieModelForm(forms.ModelForm):
class Meta:
model = MovieModel
fields = ("title", "rank")
models.py
class EquipmentModel(models.Model):
equip = models.CharField(max_length=20)
class ActorModel(models.Model):
actor = models.CharField(max_length=20)
class LightModel(models.Model):
light = models.CharField(max_length=20)
class MovieModel(models.Model):
rank = models.DecimalField(max_digits=5000, decimal_places=3)
title = models.CharField(max_length=20)
equipments = models.ManyToManyField(EquipmentModel, blank=True, null=True)
actors = models.ManyToManyField(ActorModel, blank=True, null=True)
lights = models.ManyToManyField(LightModel, blank=True, null=True)
def __str__(self):
return self.title
Edit: removed unnecessary init and fields thanks to DTing
Edit2: Fixed!

There is a whole lot of stuff going wrong here in addition to what spulec said.
Your models.py look okay.
class EquipmentModel(models.Model):
equip = models.CharField(max_length=20)
class ActorModel(models.Model):
actor = models.CharField(max_length=20)
class LightModel(models.Model):
light = models.CharField(max_length=20)
class MovieModel(models.Model):
rank = models.DecimalField(max_digits=5000, decimal_places=3)
title = models.CharField(max_length=20)
equipments = models.ManyToManyField(EquipmentModel, blank=True, null=True)
actors = models.ManyToManyField(ActorModel, blank=True, null=True)
lights = models.ManyToManyField(LightModel, blank=True, null=True)
def __str__(self):
return self.title
You don't need to override the __init__ method on forms if you are not changing anything on init. You also don't need to be explicit about the fields if you want to include them all.
class LightModelForm(forms.ModelForm):
class Meta:
model = LightModel
class ActorModelForm(forms.ModelForm):
class Meta:
model = ActorModel
class EquipmentModelForm(forms.ModelForm):
class Meta:
model = EquipmentModel
class MovieModelForm(forms.ModelForm):
class Meta:
model = MovieModel
fields = ("title", "rank")
your view doesn't really make sense unless for every movie you are trying to add you also want to:
add a new movie to the db using the submitted post data
create one actor object and add to db
create one light object and add to db
create one equipment object and add to db
take those three objects and add them to another movie's m2m relationships.
This other movie is some movie that you pulled from the urlconf and passed to your view, not the one you just created.
This all seems a little strange.
what i think you want to do is create all the equipment, actors and lights objects so they are in your db already, and use the default m2m formfield widget to select them when adding a movie.
so:
forms.py
class MovieModelForm(forms.ModelForm):
class Meta:
model = MovieModel
urls.py:
url(r'^add_movie/$', add_movie)
views.py:
def add_movie(request):
if request.method=='POST':
form = MovieModelForm(request.POST)
if form.is_valid():
form.save()
return HttpResponse('success')
else:
form = MovieModelForm()
context = {'form':form }
return render_to_response('some_template.html', context,context_instance=RequestContext(request))
you could combine adding actors, lights, and equipment into the same form but that's a bit much for me to write out right now.
As far as modifying your original code to add those lights, actors, and equipment to the movie you just created, you could do this:
if form.is_valid() and eform.is_valid() and lform.is_valid() and aform.is_valid():
new_movie = form.save()
e = eform.save()
l = lform.save()
a = aform.save()
new_movie.actors.add(a)
new_movie.lights.add(l)
new_movie.equipments.add(e)

Change it to:
movie_to_add = get_object_or_404(MovieModel, id=movieID)

Related

How to save multiple django form in one page with one submit button

i'm new when developing django. I have some question after days by days finding the answer. Here the go.
What i want is making ajax form that handle 2 model form, with 1 submit button
I can already saved the data in each form, but got problem in foreign key field got None instead
Here we go my model:
class ModelA(models.Model):
name = models.CharField(max_length=100, blank=True)
info = models.CharField(max_length=200, blank=True)
def __str__(self):
return self.name
class ModelB(models.Model):
xmodel = models.ForeignKey(to=ModelA, on_delete=models.CASCADE, related_name='modelX', blank=True, null=True)
no_1 = models.CharField(max_length=150, blank=True)
no_2 = models.CharField(max_length=150, blank=True)
Form Class:
class ModelAForm(forms.ModelForm):
class Meta:
model = ModelA
fields = '__all__'
class ModelBForm(forms.ModelForm):
class Meta:
model = ModelB
exclude = ('xmodel',)
View class:
def AddData(request):
tpl = 'add.html'
if request.method == 'POST':
mdla = ModelAForm(request.POST)
mdlb = ModelBForm(request.POST)
if mdla.is_valid():
obj = mdla.save()
if mdlb.is_valid():
mdlb.save(commit=False)
mdlb.xmodel=obj
mdlb.save()
else:
mdla = ModelAForm()
mdlb = ModelbForm()
In TPL i'm using ajax to send, it can save model a and model b, but in modelb xmodel (foreign key) it got None when check in adminpanel.
Which i do wrong, how to make it happen when handling the forms?
For people that got something like me, using 2 form.
i've solved my problem with this logic.
Create object manually
Initiatite form with instance new object
save
for above problem:
def AddData(request):
tpl = 'add.html'
if request.method == 'POST':
mdla = ModelAForm(request.POST)
if mdla.is_valid():
obj = mdla.save()
newObj = ModelB.objects.create(xmodel=obj)
mdlb = ModelBForm(request.POST, instance=newObj)
if mdlb.is_valid():
mdlb.save()
else:
mdla = ModalAForm()
mdlb = ModalBForm
This is solution i get, if anyone could do better please tell me. Thankx

Django - directly using the Model class to avoid writing a Form class explicitely

Currently prototyping for a Django app. I have code that defines a model and a form, which I then use to have users enter new instances in the DB. For instance:
models.py
class Produits(models.Model):
no_prod = models.CharField(max_length=70)
descfr = models.CharField(max_length=70)
cost = models.DecimalField(max_digits=10, decimal_places=2)
prix1 = models.DecimalField(max_digits=10, decimal_places=2)
prix2 = models.DecimalField(max_digits=10, decimal_places=2, default=None)
forms.py
class CreateProduct(forms.Form):
no_prod = forms.CharField(max_length=70)
descfr = forms.CharField(max_length=70)
cost = forms.DecimalField(max_digits=10, decimal_places=2)
prix1 = forms.DecimalField(max_digits=10, decimal_places=2)
prix2 = forms.DecimalField(max_digits=10, decimal_places=2)
views.py
def create_product(request):
if request.method == "POST":
form = CreateProduct(request.POST)
if form.is_valid():
# create a product
p = Produits(**form.cleaned_data)
p.save()
return HttpResponseRedirect('/soum/produits') # let's say go back to product list or something
else:
form = CreateProduct()
return render(request, 'soum/produitsform.html', {'form': form})
That works all right.
However, the form & the model fields are exactly the same. In fact in the application a number of classes in the models may map one to one in this way. Is there a clever way in Django simplify the code above, so that "by default" I could just create a form that directly uses a model, and then only make explicit forms for those classes that don't map 1 to 1?
Yes, you should use ModelForm:
class ProductForm(forms.ModelForm):
class Meta:
model = Produits
Et le tour est joué : )
Edit: from here you can choose a subselection of fields from your model, that you only want to make available in your form:
class ProductForm(forms.ModelForm):
class Meta:
model = Produits
fields = ['descfr', 'prix1']

Django: Form and many2many relationship using through relationshiop

I am trying to sort out a specific problem that involve "many2many" relationship using through specification.
I've already tried to use inline_factory but I was not able to sort out the problem.
I have these tables
class Person(models.Model):
id = models.AutoField(primary_key=True)
fullname = models.CharField(max_length=200)
nickname = models.CharField(max_length=45, blank=True)
class Meta:
db_table = 'people'
class Role(models.Model):
role = models.CharField(max_length=200)
class Meta:
verbose_name_plural = 'roles'
db_table = 'roles'
class Study(models.Model):
id = models.AutoField(primary_key=True)
title = models.CharField(max_length=255)
description = models.CharField(max_length=1000)
members = models.ManyToManyField(Person, through='Studies2People')
class Meta:
db_table = 'studies'
class Studies2People(models.Model):
person = models.ForeignKey(Person)
role = models.ForeignKey(Role)
study = models.ForeignKey(Study)
class Meta:
verbose_name_plural = 'studies2people'
db_table = 'studies2people'
unique_together = (('person', 'role', 'study'),)
#forms.py
from .models import Study, Person, Role, Studies2People
class RegisterStudyForm(ModelForm):
class Meta:
model = Study
fields = '__all__'
#View.py
class StudyCreateView(CreateView):
template_name = 'managements/register_study.html'
model = Study
form_class = RegisterStudyForm
success_url = 'success/'
def get(self, request, *args, **kwargs):
self.object = None
form_class = self.get_form_class()
form = self.get_form(form_class)
return self.render_to_response(self.get_context_data(form=form))
The code above creates a form like:
Study.Title
Study.description
List of People
I want to create a form to fill in all fields that involve Studies2People Something like this:
Study.Title
Study.description
Combo(people.list)
Combo(Role.list)
Maybe I should start from Studies2People but I don't know how to show the "inline" forms involved.
Thanks in advance
C.
waiting someone that is able to explain with some examples the relationship m2m with through (model & view), I sorted out my problem in a different way.
I've created three forms.
1 Model Form (study)
2 Form (forms with ModelChoiceField(queryset=TableX.objects.all())
Created a classView to manage the get and post action.(validation form too)
In the post procedure I used "transaction" to avoid "fake" data.
I hope that someone will post an example with complex m2m relationships.
Regards
Cinzia

Django multi-form save

I already tried nearly everything I could find regarding this, but I am pretty sure I am just one small suggestion away from solving my issue.
I am trying to save to forms that I generated using the forms method of Django at the same time. These forms have a ForeignKey relationship.
My model:
class Publisher(models.Model):
company = models.CharField(max_length=255)
address1 = models.CharField(max_length=255)
address2 = models.CharField(max_length=255)
city = models.CharField(max_length=255)
zipcode = models.CharField(max_length=10)
email = models.EmailField(max_length=255)
firstname = models.CharField(max_length=255)
lastname = models.CharField(max_length=255)
tc = models.BooleanField()
timestamp = models.DateTimeField(auto_now=True)
def __unicode__(self):
return self.company
class PublisherQuestions(models.Model):
referal = models.TextField()
updates = models.BooleanField()
publisher = models.ForeignKey(Publisher)
preferredCommunication = models.ForeignKey(PublisherCommunication)
def __unicode__(self):
return self.publisher
class PublisherForm(ModelForm):
class Meta:
model = Publisher
class PublisherQuestionsForm(ModelForm):
class Meta:
model = PublisherQuestions
exclude = ('publisher')
and my view:
def register(request):
if request.method == 'POST':
form = PublisherForm(data = request.POST)
formQuestions = PublisherQuestionsForm(data = request.POST)
if form.is_valid() and formQuestions.is_valid():
publisher = form.save()
formQuestions.publisher = publisher
formQuestions.save()
return HttpResponseRedirect('/thanks/')
So, what I try to do, is to save the second form "formQuestions" with the foreign key publisher against the publisher_id from the PublisherForm.
Unfortunately MySQL / Django is giving me the following error.
Column 'publisher_id' cannot be null
This might be a complete newbie question and yes there has been many people asking nearly the same, but none of the solutions worked for me.
Thanks for your help, as always appreciated!
You can try the following to check whether you get the same error or not:
publisher = form.save()
questions = formQuestions.save(commit=False)
questions.publisher = publisher
questions.save()
With commit=False you get the Model without saving it to the database.
https://docs.djangoproject.com/en/dev/topics/forms/modelforms/#the-save-method
In fact it is the Model you want to manipulate, adding the Publisher and not the form.
If you need to access the data from the form I think you should use for example this:
https://docs.djangoproject.com/en/dev/ref/forms/api/#accessing-clean-data
How I understood it is that you have a ModelForm and not a Model, so if you want to manipulate the Model you first need to create it, or at least it is the cleanest way of manipulating Model data. Otherwise you can manipulate the Form data but it is another story, you want to choose the raw data or the clean data etc.

Store the current user in a field of a django model

I'm trying to find the way of store the user who creates an object and I want to do it automatically. I've found some things but nothing works for me. Here is the code:
Models.py:
class Track(models.Model):
...
usuari = models.ForeignKey(User)
Forms.py:
class TrackForm(forms.ModelForm):
class Meta:
model = Track
Views.py
def pujar_track(request):
if request.method=='POST':
formulari = TrackForm(request.POST, request.FILES)
if formulari.is_valid():
formulari.save()
return HttpResponseRedirect('/inici')
else:
formulari = TrackForm()
return render(request,'principal/trackForm.html',
{'formulari':formulari})
I've seen about put:
usuari = models.ForeignKey(User, blank=True, editable=False) but
But I don't know when can I set the user in the field.
Thanks in advice!
You can hide usuari from your TrackForm, so the user can't select it:
class TrackForm(forms.ModelForm):
class Meta:
model = Track
exclude = ('usuari',)
And then, replace your formulari.save() with:
track = formulari.save(commit=False)
track.user = request.user
track.save()
This is a common use case and detailed in the Django docs.