I have a Django Model with ManyToManyField
in it. I need to require user to select at least one M2M value in this field.
I tried to set blank=False
to M2M field but it didn’t help.
class Skill(models.Model): name = models.CharField(max_length=200) class PersonSkills(models.Model): person = models.ForeignKey('Person') skill = models.ForeignKey('Skill') class Person(models.Model): name = models.CharField(max_length=200) skills = models.ManyToManyField('Skill', through='PersonSkills') p = Person(name='Bob') p.save() # success, but I expect that this should throw ValidationError, because I didn't select at least one Skill for this person
I can solve this situation with custom Form
definition or with override save()
method for Person
model.
Is it possible to prevent create Person
without at least one Skill
selected, with set ManyToManyField
options? Or I need to create custom logic to handle this situation? Thanks.
I use Django 1.7
and Python 3.4
Update 1. How to create ModelForm
to control M2M? Because in cleaned_data
I have only fields that I pass for Person
form, and haven’t data that I pass as M2M fields. I try to create object in Admin Site and control that Skills
selected. I enter Skill
‘s via inline
.
# admin.py class PersonSkillsInline(admin.TabularInline): model = Person.skills.through extra = 2 class PersonAdmin(admin.ModelAdmin): inlines = [PersonSkillsInline] admin.site.register(Person, PersonAdmin)
Advertisement
Answer
On a database level… no, that’s not possible. Any enforcement of this will have to come from your application logic.
The reason is that every m2m relation has a record with a foreign key to both sides of the m2m relation. SQL cannot enforce the existence of the referencing side of a relationship, only of the referenced side of a relationship.
Furthermore, you can’t enforce it in your model either, because the Person
has to be created and saved before you can assign any many-to-many relations.
Your only options are to enforce it in the form or the view.
In an InlineModelAdmin
this can easily be done by specifying min_num
(1.7+):
class PersonSkillsInline(admin.TabularInline): model = Person.skills.through min_num = 1 extra = 2