Pedro
Pedro

Reputation: 41

Many-to-many relationships in Django

I'm currently doing a personal project to have a better understanding of how the Django REST framework works. Essentially, I'm trying to build a web application that allows users to share files, kind of a simplified version of Google Drive/Dropbox.

However, I'm having some trouble with many to many relationships in my models. Essentially, I want each user to have its own folders, in each folder they would have zero or more files. A user could then share (read only access or allow write/delete) an entire folder or only individual files.

I'm trying to model this relationships as follows:

from django.db import models
from django.contrib.auth.models import User


class Folder(models.Model):
    title = models.CharField(max_length=50)
    description = models.TextField()
    user = models.ForeignKey(User, on_delete=models.CASCADE)
    users_can_edit = models.ManyToManyField(User)
    users_can_see = models.ManyToManyField(User)



class File(models.Model):
    title = models.CharField(max_length=50)
    description = models.TextField()
    folder = models.ForeignKey(Folder, on_delete=models.CASCADE)
    created_on = models.DateTimeField(auto_now=True, editable=False)
    users_can_edit = models.ManyToManyField(User)
    users_can_see = models.ManyToManyField(User)

However, I get this error that I wasn't able to fix when I try to run the server:

ERRORS:
api.File.users_can_edit: (fields.E304) Reverse accessor for 'api.File.users_can_edit' clashes with reverse accessor for 'api.File.users_can_see'.
        HINT: Add or change a related_name argument to the definition for 'api.File.users_can_edit' or 'api.File.users_can_see'.
api.File.users_can_see: (fields.E304) Reverse accessor for 'api.File.users_can_see' clashes with reverse accessor for 'api.File.users_can_edit'.
        HINT: Add or change a related_name argument to the definition for 'api.File.users_can_see' or 'api.File.users_can_edit'.
api.Folder.user: (fields.E304) Reverse accessor for 'api.Folder.user' clashes with reverse accessor for 'api.Folder.users_can_edit'.
        HINT: Add or change a related_name argument to the definition for 'api.Folder.user' or 'api.Folder.users_can_edit'.
api.Folder.user: (fields.E304) Reverse accessor for 'api.Folder.user' clashes with reverse accessor for 'api.Folder.users_can_see'.
        HINT: Add or change a related_name argument to the definition for 'api.Folder.user' or 'api.Folder.users_can_see'.
api.Folder.users_can_edit: (fields.E304) Reverse accessor for 'api.Folder.users_can_edit' clashes with reverse accessor for 'api.Folder.user'.
        HINT: Add or change a related_name argument to the definition for 'api.Folder.users_can_edit' or 'api.Folder.user'.
api.Folder.users_can_edit: (fields.E304) Reverse accessor for 'api.Folder.users_can_edit' clashes with reverse accessor for 'api.Folder.users_can_see'.
        HINT: Add or change a related_name argument to the definition for 'api.Folder.users_can_edit' or 'api.Folder.users_can_see'.
api.Folder.users_can_see: (fields.E304) Reverse accessor for 'api.Folder.users_can_see' clashes with reverse accessor for 'api.Folder.user'.
        HINT: Add or change a related_name argument to the definition for 'api.Folder.users_can_see' or 'api.Folder.user'.
api.Folder.users_can_see: (fields.E304) Reverse accessor for 'api.Folder.users_can_see' clashes with reverse accessor for 'api.Folder.users_can_edit'.
        HINT: Add or change a related_name argument to the definition for 'api.Folder.users_can_see' or 'api.Folder.users_can_edit'.

I'm still a beginner and only know the basic of SQL. This being said, is this an approach appropriate or is there any other way you would model these relationships?

Upvotes: 2

Views: 210

Answers (1)

willeM_ Van Onsem
willeM_ Van Onsem

Reputation: 476659

You have two ManyToManyFields to the same model, so the related_name=…s [Django-doc] are the same, and will clash. You should specify the related_name=…s, so:

from django.db import models
from django.conf import settings

class Folder(models.Model):
    title = models.CharField(max_length=50)
    description = models.TextField()
    user = models.ForeignKey(settings.AUTH_USER_MODEL, on_delete=models.CASCADE)
    users_can_edit = models.ManyToManyField(
        settings.AUTH_USER_MODEL,
        related_name='folders_that_can_be_edited'
    )
    users_can_see = models.ManyToManyField(
        settings.AUTH_USER_MODEL,
        related_name='folders_that_can_be_seen'
    )

class File(models.Model):
    title = models.CharField(max_length=50)
    description = models.TextField()
    folder = models.ForeignKey(Folder, on_delete=models.CASCADE)
    created_on = models.DateTimeField(auto_now=True, editable=False)
    users_can_edit = models.ManyToManyField(
        settings.AUTH_USER_MODEL,
        related_name='files_that_can_be_edited'
    )
    users_can_see = models.ManyToManyField(
        settings.AUTH_USER_MODEL,
        related_name='files_that_can_be_seen'
    )

But it might be better to define a single ManyToManyField with a through=… model [Django-doc] that then encodes the permissions of that user-file combination, this will require less tables, and makes filtering, etc. more effective.


Note: It is normally better to make use of the settings.AUTH_USER_MODEL [Django-doc] to refer to the user model, than to use the User model [Django-doc] directly. For more information you can see the referencing the User model section of the documentation.

Upvotes: 2

Related Questions