Отношения "многие ко многим" в Django

Я пытаюсь создать веб-приложение, в котором пользователи могут участвовать в некоторых группах (каждый пользователь может быть частью нескольких групп), и я хочу иметь возможность делать запросы типа

group.users_set()

и

user.groups_set()

Я хочу видеть все группы, в которых участвует пользователь, на странице администратора каждого пользователя и наоборот. Моя последняя попытка была такой:

class CustomUser(AbstractUser):
    groups = models.ManyToManyField('Group', through='Participation')

class Group(models.Model):
    group_name = models.CharField(max_length=200)    
    group_password = models.CharField(max_length=200)
    customusers = models.ManyToManyField('CustomUser', through='Participation')

class Participation(models.Model):
    customuser = models.ForeignKey(CustomUser, on_delete=models.CASCADE)
    group = models.ForeignKey(Group, on_delete=models.CASCADE) 

но я получаю

django.core.management.base.SystemCheckError: SystemCheckError: System check identified some issues:

ERRORS:
<class 'userreg.admin.CustomUserAdmin'>: (admin.E013) The value of 'fieldsets[2][1]["fields"]' cannot include the ManyToManyField 'groups', because that field manually specifies a relationship model.

Раньше, только

users = models.ManyToManyField(CustomUser)

в классе "Группа" и без класса "Участие" я смог добиться половины своей цели - увидеть список зарегистрированных пользователей на странице администратора. Что я делаю неправильно?

Вы указываете ManyToManyField в одной из двух моделей, поэтому, например:

class CustomUser(AbstractUser):
    groups = models.ManyToManyField(
        'Group',
        related_name='users'
        through='Participation'
    )

class Group(models.Model):
    name = models.CharField(max_length=200)
    password = models.CharField(max_length=200)

class Participation(models.Model):
    customuser = models.ForeignKey(CustomUser, on_delete=models.CASCADE)
    group = models.ForeignKey(Group, on_delete=models.CASCADE)

Параметр related_name=… [Django-doc] задает имя отношения в обратном порядке, то есть от Group к CustomUser, и вы можете переименовать его в users, как это сделано здесь. Если вы не укажете его, по умолчанию будет modelname_set с modelname именем модели (здесь CustomUser) в нижнем регистре (так customuser).

Поскольку ваша through=… модель [Django-doc] имеет только два ForeignKey к двум моделям, вам не нужно создавать одну, и вы можете упростить это до:

class CustomUser(AbstractUser):
    groups = models.ManyToManyField(
        'Group',
        related_name='users'
    )

class Group(models.Model):
    name = models.CharField(max_length=200)
    password = models.CharField(max_length=200)

Ответ, предоставленный @Willem Van Onsem, был очень полезен, но я решил свою проблему, сделав это в models.py

class CustomUser(AbstractUser):
    groups = models.ManyToManyField('Group')

class Group(models.Model):
    name = models.CharField(max_length=200)
    password = models.CharField(max_length=200)
    users = models.ManyToManyField(CustomUsers)

и это в admin.py:

from django.contrib import admin
from .models import Group, CustomUser
from django.contrib.auth.admin import UserAdmin

class GroupAdmin(admin.ModelAdmin):
    fieldsets = [
        (None,               {'fields': ['group_name', 'group_password']}),
        ('Users', {'fields': ['users']}),
    ]

class CustomUserAdmin(UserAdmin):
    fieldsets = [
        (None,               {'fields': ['username', 'password']}),
        ('Groups', {'fields': ['groups']}),
    ]
    

admin.site.register(CustomUser, CustomUserAdmin)

admin.site.register(Group, GroupAdmin)
Вернуться на верх