Add unique users to ProjectMember model in Django

Viewed 32

I'm new to Django. Can anyone out there for the rescue.
In my current proect, I've couple of models inside a Django app: Project, ProjectMember. Here's their model and admin structure.

models.py

class Project(models.Model):
    name = models.CharField(max_length=100)
    start_date = models.DateField()
    end_date = models.DateField()
    description = models.TextField(max_length=1000)

class ProjectMember(models.Model):
    project = models.ForeignKey(Project, on_delete=models.SET_NULL, null=True)
    user = models.ForeignKey(User, on_delete=models.SET_NULL, null=True)

admin.py

from .models import Project, ProjectMember

class ProjectMemberInline(admin.TabularInline):
    model = ProjectMember
    extra = 0

class ProjectAdmin(admin.ModelAdmin):
    list_display = ('name', 'start_date', 'end_date')
    inlines = [ProjectMemberInline]
admin.site.register(Project, ProjectAdmin)

What I want to achieve is, every Project(i.e. model) should have unique users(ProjectMember) and every user can be associated with multiple projects. But with the current structure, system allows duplicate ProjectMember insertion in the admin. Please see the screenshot, I've add here 2 same users.

Any help is appreciated.

2 Answers

What you have effectively done is define the "through" table for your own M2M relationship here. You have 2 options:

  1. Redefine your tables to use an out of the box M2M relationship - https://docs.djangoproject.com/en/3.2/topics/db/examples/many_to_many/

  2. Add a unique_together constraint - https://docs.djangoproject.com/en/3.2/ref/models/options/#unique-together

There's nothing wrong with what you've done, it's just a question of whether you want to use what Django gives you, or roll your own, so the solution of choice is up to you.

Edit: As the other answer points out, uniqie_together is deprecated infavour of UniqueConstraint, but the concept is the same, and which one you use may depend on the version of Django you're on. It sounds like this is a new project, so probably a recent version, in which case UniqueConstraint...

We can work with Django's constraint framework such that we can add a UniqueConstraint [Django-doc] over the two ForeignKeys:

class ProjectMember(models.Model):
    project = models.ForeignKey(Project, on_delete=models.SET_NULL, null=True)
    user = models.ForeignKey(User, on_delete=models.SET_NULL, null=True)

    class Meta:
        constraints = [
            models.UniqueConstraint(
                fields=['project', 'user'],
                name='onceperproject'
            )
        ]

Most modern databases will enforce this, and thus prevent that you can add the same user twice per project, or the same project twice to a user.

Related