Skip to content
Advertisement

Django 1.7: how to make ManyToManyField required?

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
User contributions licensed under: CC BY-SA
7 People found this is helpful
Advertisement