pythondjangodjango-modelsdjango-rest-frameworknested-set-model

Django - DRF ManyToMany and ForeignKey


I am building an api for entertainment management.

The user can create empty project or projects with show, tour and festival. User can also create show and specify project id.

A project can contain:

  1. Show 0 to *
  2. Tour 0 to *
  3. Festival 0 to *

A Tour can contain:

  1. Show 0 to 1

A Festival can contain:

  1. Show 0 to *

A Show, Tour and Festival necessarily part of a Project.

Here are my models:

class Project(models.Model):
    """Models for Projects"""
    name = models.CharField(max_length=255, unique=True)
    shows = models.ManyToManyField('Show', blank=True)
    festivals = models.ManyToManyField('Festival', blank=True)
    tours = models.ManyToManyField('Tour', blank=True)
    author = models.ForeignKey(
        settings.AUTH_USER_MODEL,
        on_delete=models.CASCADE,
    )
    date_created = models.DateTimeField(auto_now_add=True)
    last_update = models.DateTimeField(auto_now=True)

    def __str__(self):
        return self.name


class Show(models.Model):
    """Models for Shows"""
    name = models.CharField(max_length=255, unique=True)
    start_date = models.DateTimeField(default=timezone.now, blank=True)
    end_date = models.DateTimeField(default=timezone.now, blank=True)
    tours = models.ManyToManyField('Tour', blank=True)
    festivals = models.ManyToManyField('Festival', blank=True)
    projects = models.ForeignKey(Project, on_delete=models.CASCADE, default=None)
    author = models.ForeignKey(
        settings.AUTH_USER_MODEL,
        on_delete=models.CASCADE,
    )
    date_created = models.DateTimeField(auto_now_add=True)
    last_update = models.DateTimeField(auto_now=True)

    def __str__(self):
        return self.name


class Tour(models.Model):
    """Models for Tour."""
    name = models.CharField(max_length=255, unique=True)
    start_date = models.DateTimeField(default=timezone.now, blank=True)
    end_date = models.DateTimeField(default=timezone.now, blank=True)
    production = models.CharField(max_length=255, blank=True)
    shows = models.ForeignKey(Show, on_delete=models.CASCADE, blank=True, null=True)
    projects = models.ForeignKey(Project, on_delete=models.CASCADE, default=None)
    author = models.ForeignKey(
        settings.AUTH_USER_MODEL,
        on_delete=models.CASCADE,
    )
    date_created = models.DateTimeField(auto_now_add=True)
    last_update = models.DateTimeField(auto_now=True)

    def __str__(self):
        return self.name


class Festival(models.Model):
    """Models for Festival."""
    name = models.CharField(max_length=255, unique=True)
    start_date = models.DateTimeField(default=timezone.now, blank=True)
    end_date = models.DateTimeField(default=timezone.now, blank=True)
    address_1 = models.CharField(max_length=128, blank=True)
    address_2 = models.CharField(max_length=128, blank=True)
    city = models.CharField(max_length=64, default="Paris")
    state = models.CharField(max_length=25, default='France')
    zip_code = models.CharField(max_length=8, default="92000")
    gps = models.CharField(max_length=255, blank=True)
    technical_rider = models.FileField(null=True, upload_to=riders_file_path)
    shows = models.ForeignKey(Show, on_delete=models.CASCADE, blank=True, null=True)
    projects = models.ForeignKey(Project, on_delete=models.CASCADE, default=None)
    author = models.ForeignKey(
        settings.AUTH_USER_MODEL,
        on_delete=models.CASCADE,
    )
    date_created = models.DateTimeField(auto_now_add=True)
    last_update = models.DateTimeField(auto_now=True)

    def __str__(self):
        return self.name

everything works fine but I have a doubt about the double relationship m2m / foreignkey between :

class Project(models.Model):
    """Models for Projects"""
    shows = models.ManyToManyField('Show', blank=True)

AND

class Show(models.Model):
    """Models for Shows"""
    projects = models.ForeignKey(Project, on_delete=models.CASCADE, default=None)

Solution

  • Your implementation is incorrect. In Django when you specify relationships you only do so one one side of them. You seem confused about what type of relationships these models should have.

    Can a project have multiple shows? It seems so. Can a show be part of multiple projects?

    If the answer to the second question is yes then you should use a many-to-many relationship. If not, then this should be a one-to-many relationship.

    M2M Example: Functionality-wise it doesn't matter where you place the ManyToManyField. It creates a symmetric relationship. But you should put it in the Model where it makes the most 'sense'. That's the model from which you'll be able to access and update the relationship info from the Django admin

    # m2m example 
    class Project(models.Model):
        shows = models.ManyToManyField(Show, related_name="projects")
    
    class Show(models.Model):
        ...   
    

    1toM Example:

    # one-to-many example
    class Project(models.Model):
        ...
    
    class Show(models.Model):
        ...
        project = models.ForeignKey(Project, on_delete=models.CASCADE, related_name="shows")
    

    These two implementations can't (and definitely shouldn't) be mixed up as they define different types of relationships. If you struggle to understand the difference between them i recommend you to checkout this video (not the whole thing obviously). Once you have established which type of relationship you need, it's going to be a child's play to implement your Django models and to understand what you can do with them.