Hi I have a model which has 2 many to many fields in it. one is a standard m2m field which does not use any through tables whereas the other is a bit more complecated and has a through table. I am using the Django forms.modelform to display and save the forms. The code I have to save the form is
if form.is_valid():
f = form.save(commit=False)
f.modified_by = request.user
f.save()
form.save_m2m()
When i try to save the form I get the following error:
Cannot set values on a ManyToManyField which specifies an intermediary model.
I know this is happening when I do the form.save_m2m() because of the through table. What I'd liek to do is tell Django to ignore the m2m field with the through table but still save the m2m field without the through table. I can then go on to manually save the data for the through table field.
Thanks
If you have a model with multiple fields one is done with a through table and the other one is a regular many-to-many relation without a through table. You can still use save_m2m() to save the regular one. Simply add the through fields to your exclude list on your form.
Add inside your form class:
class Meta:
model = YourModel
exclude = ('m2mthroughfield',)
you can't save the m2m "without the through table"
the data you want to save is stored in the through table (and only in the through table)
Related
I am adding a new field named "user" to the "order" model. I did make migrations.
(Bonus question is: why in the sql3db column "user_id" was made, instead of "user"? But ok, I change form.fields['user'] to form.fields['user_id'], as the machine wants...)
I remove unneeded fields from the form in forms.py, and I try to add this fields in views.py (because I don't know how to send "user" to the forms.py, and I think it more secure like this).
And as I can understand - my new fields is absent for "order", when I use form.save().
This field can't be null by design.
Your form has no user field, hence that will not work. What you can do is alter the object wrapped in the form with:
if form.is_valid():
form.instance.user = request.user
form.instance.email = request.user.email
order = form.save()
My main problem is that my code never goes into the for loop though within the debugger I can see that hardware exists. The for loop gets just skipped and I can´t figure out why this is the case.
Models:
class Hardware(models.Model):
name = models.CharField(max_length=30)
description = models.TextField()
class Bundle(models.Model):
name = models.CharField(max_length=30)
description = models.TextField()
devices = models.ManyToManyField(Hardware)
class BundleForm(ModelForm):
class Meta:
model = Bundle
fields = ('name', 'description', 'devices')
labels = {
'name': _('Bundlename'),
'description': _('Beschreibung des Bundle'),
'devices': _('Hardware im Bundle')
}
Views:
elif request.method == 'POST' and 'newbundle' in request.POST:
form = BundleForm(request.POST)
if form.is_valid():
bundle = form.save(commit=False)
bundle.save()
for hardware in bundle.devices.all():
print(hardware)
messages.success(request, 'Erfolg! Die Daten wurden erfolgreich gespeichert.')
return redirect('/knowledgeeditor/bundle/', {'messages': messages})
Your question is not about iterating many-to-many fields, but saving them.
The modelforms documentation has this to say about using commit=False with a many-to-many field:
Another side effect of using commit=False is seen when your model has a many-to-many relation with another model. If your model has a many-to-many relation and you specify commit=False when you save a form, Django cannot immediately save the form data for the many-to-many relation. This is because it isn’t possible to save many-to-many data for an instance until the instance exists in the database.
To work around this problem, every time you save a form using commit=False, Django adds a save_m2m() method to your ModelForm subclass. After you’ve manually saved the instance produced by the form, you can invoke save_m2m() to save the many-to-many form data.
As noted there, you could use save_m2m() to save the field, but instead of doing that you should ask yourself why you are using commit=False here at all. There is no reason to do so, so you should omit that parameter and the subsequent separate save, and just let the form save itself in one go.
I am new to Django and I am using 1.7. I am looking for an example, on how to create a form that is populated with the current values which are stored. This way the user can see the forms current values and just modify the the ones they want. Then click save.
Assuming you're talking about values stored on a model, you can prepopulate a ModelForm using instance:
#######forms.py#############
class ArticleForm(ModelForm):
class Meta:
model = Article
fields = ['title']
#######views.py################
#create a normal ModelForm without any pre-populated values
blank_form = ArticleForm()
#create a ModelForm pre-populated with a specific object. first, get that object
article = Article.objects.get(pk=1)
form = ArticleForm(instance=article)
Taken from https://docs.djangoproject.com/en/1.7/topics/forms/modelforms/
I have a Django model with a Foreign key to a table that contains about 50,000 records. I am using the Django forms.ModelForm to create a form. The problem is I only need a small subset of the records from the table the Foreign key points to.
I am able to create that subset of choices in the init method. How can I prevent ModelForm from creating that initial set of choices?
I tried using the widgets parameter in the Meta method. But Django debug toolbar indicates the database is still being hit.
Thanks
The autogenerated ModelChoiceField will have its queryset initialized to the default. The widget is not where you are supposed to customize the queryset property.
Define the ModelChoiceField manually, initialize its queryset to be empty. Remember to name the ModelChoiceField the same as the one that would have been automatically generated, and remember to mention that field in the fields tuple. Now you can set the queryset from the constructor and avoid the database being hit twice.
If you are lucky (and you probably are, please test though), the queryset has not been evaluated during construction, and in that case, defining the ModelChoiceField manually is not required.
class YourModelForm(ModelForm):
your_fk_field_name = forms.ModelChoiceField(queryset=YourModel.objects.none())
class Meta:
model = YourModel
fields = ('your_fk_field_name', .......)
def __init__(self, *args, **kwargs):
super(YourModelForm, self).__init__(*args, **kwargs)
self.fields['your_fk_field_name'].queryset = ....
deals_formset_factory = modelformset_factory(Deal, form=DealCForm, extra=1)
attached_deals_formset = deals_formset_factory(request.POST, prefix='deals')
Since some fields of my Deal model are not shown in the form and hence can't be set by the user (but the M2M field is shown and can be set by the user), I can't just do a
for fm in attached_deals_formset:
if fm.has_changed():
fm.save()
since it would break.
So theoretically the idea in such situations is to do
deal = fm.save(commit=False)
...
deal.save()
but this doesn't save my M2M field inside deal. The Through table remains untouched. What is the best approach to solve this?
class Deal(models.Model):
deal_id = UUIDField()
....
sales_item = models.ManyToManyField(SalesItem)
I found the solution, there is no need to override the save method.
Another side effect of using commit=False is seen when your model has
a many-to-many relation with another model. If your model has a
many-to-many relation and you specify commit=False when you save a
form, Django cannot immediately save the form data for the
many-to-many relation. This is because it isn't possible to save
many-to-many data for an instance until the instance exists in the
database.
To work around this problem, every time you save a form using
commit=False, Django adds a save_m2m() method to your ModelForm
subclass. After you've manually saved the instance produced by the
form, you can invoke save_m2m() to save the many-to-many form data
Source
After deal.save() simply:
fm.save_m2m()