当在模型中为关系字段添加了related_name
参数后,您可以使用该参数指定的名称来引用反向关系。下面是一个简单的例子来说明如何引用反向关系。
假设您有以下两个模型:
from django.db import models
class Author(models.Model):
name = models.CharField(max_length=100)
class Book(models.Model):
title = models.CharField(max_length=100)
author = models.ForeignKey(Author, on_delete=models.CASCADE, related_name='books')
在上面的例子中,Book
模型有一个外键字段author
,它关联到Author
模型。通过添加related_name='books'
参数,您为Book
模型指定了反向关系名称为books
。
现在,您可以使用该反向关系名称在Author
模型的实例中访问与其相关的Book
对象集合。例如:
author = Author.objects.get(pk=1)
books = author.books.all()
在上面的代码中,我们从数据库中获取了Author
模型的一个实例,并通过books
反向关系名称访问了与该作者相关的所有书籍对象。
通过使用related_name
参数,您可以根据需要指定反向关系的名称,以便在模型之间进行导航和访问相关对象。
下面是一个“使用反向关系名称(related_name)解决由“多对多”中间关系表的反向关系名引起的冲突问题。
关于什么是中间关系表,为什么要有中间关系表,以及由中间关系表的反向关系名引起的冲突问题的详细解释,请参考我的另一篇表博文:
通过一个实际例子说明Django中的数据库操作方法法ForeignKey()-外键的用法【数据表“一对多”关系】,并详解“中间关系表”、反向关系(related_name)、反向关系名冲突的概念
E:\Python_project\P_001\myshop-test\myshop_background_2\users\models.py 中的代码如下:
from datetime import datetime
from django.db import models
from django.contrib.auth.models import AbstractUser
class MyUser(AbstractUser):
SEX = (
(0, '男'),
(1, '女'),
)
LEVEL = (
(1, '寂寞卡会员'),
(2, '钻石卡会员'),
(3, '金卡会员'),
(4, '银卡会员'),
)
STATUS = (
(0, '正常'),
(1, '异常'),
)
truename = models.CharField('真实姓名', blank=True, max_length=50)
mobile = models.CharField('手机号码', max_length=11, default="")
sex = models.IntegerField(default=0, choices=SEX)
birthday = models.DateField(blank=True, null=True)
user_img = models.ImageField("头像", upload_to="user_img", default="")
level = models.IntegerField(default=4, choices=LEVEL)
status = models.IntegerField(default=0, choices=STATUS)
create_time = models.DateTimeField(default=datetime.now, verbose_name='创建时间')
update_time = models.DateTimeField(default=datetime.now, verbose_name="更新时间")
def __str__(self):
return self.username
class Meta(AbstractUser.Meta):
permissions = (
['check_myuser', '审核用户信息'],
)
执行数据库迁移命令 manage.py makemigrations 时报错如下:
SystemCheckError: System check identified some issues:
ERRORS:
auth.User.groups: (fields.E304) Reverse accessor for 'auth.User.groups' clashes with reverse accessor for 'users.MyUser.groups'.
HINT: Add or change a related_name argument to the definition for 'auth.User.groups' or 'users.MyUser.groups'.
auth.User.user_permissions: (fields.E304) Reverse accessor for 'auth.User.user_permissions' clashes with reverse accessor for 'users.MyUser.user_permissions'.
HINT: Add or change a related_name argument to the definition for 'auth.User.user_permissions' or 'users.MyUser.user_permissions'.
users.MyUser.groups: (fields.E304) Reverse accessor for 'users.MyUser.groups' clashes with reverse accessor for 'auth.User.groups'.
HINT: Add or change a related_name argument to the definition for 'users.MyUser.groups' or 'auth.User.groups'.
users.MyUser.user_permissions: (fields.E304) Reverse accessor for 'users.MyUser.user_permissions' clashes with reverse accessor for 'auth.User.user_permissions'.
HINT: Add or change a related_name argument to the definition for 'users.MyUser.user_permissions' or 'auth.User.user_permissions'.
错误提示指出了关于groups
和user_permissions
字段的冲突。
具体来说,有两个冲突:
①是auth.User的groups字段与users.MyUser中的groups中产生了冲突;
②是auth.User.的user_permissions字段与users.MyUser中的user_permissions产生了冲突。
提问:auth.User是什么时候引入的?
答案在下面这段代码里:
INSTALLED_APPS = [
'django.contrib.admin',
'django.contrib.auth',
'django.contrib.contenttypes',
'django.contrib.sessions',
'django.contrib.messages',
'django.contrib.staticfiles',
# 'basic',
'users',
]
注意下面代码中的语句:
'django.contrib.auth',
提问:明明自己的模块中没有定义groups
和user_permissions
字段啊?
答:定义了的,因为自己定义的MyUser
模型继承自AbstractUser
,而AbstractUser
又继续了系统自带的User
模型的这些字段。
解决方法:
解决方法一:
不在setting.py中去注册应用 ‘django.contrib.auth’,但是如果要使用Django自带的后台系统,不建议这么做,原因如下:
在Django中,默认情况下,django.contrib.auth
应用是用于身份验证和用户管理的关键应用程序,它提供了Django自带的后台管理系统的用户认证和权限管理功能。如果您想要使用Django自带的后台管理系统,通常是需要包括django.contrib.auth
应用的。
在settings.py
中的INSTALLED_APPS
设置中包括django.contrib.auth
应用可以确保后台管理系统正常工作,并具备用户认证和权限管理的功能。这样,您可以使用内置的用户模型、用户组、权限等功能。
如果您不想使用Django自带的后台管理系统,或者您已经有了自定义的用户管理系统,可以考虑从INSTALLED_APPS
中移除django.contrib.auth
应用。这样可以减少不必要的代码和数据库表,并使得您的项目更加轻量化。
但是请注意,如果您移除了django.contrib.auth
应用,您将失去许多与用户认证和权限相关的功能,包括但不限于以下内容:
- 用户认证和登录功能
- 用户注册和密码重置功能
- 用户权限和权限管理功能
- 用户组管理功能
- 后台管理系统的用户认证和权限管理功能
所以,在决定是否移除django.contrib.auth
应用之前,请确保您已经有了替代的用户认证和权限管理方案,并且考虑到相关功能的实现和维护成本。
解决方法二:
重命名自定义模型中的groups
和user_permissions
字段**:如果希望在自定义的模型中保留groups
和user_permissions
字段,但避免与内置的auth.User
模型发生冲突您可以为这两个字段添加related_name
参数并指定不同的名称。例如:
from django.db import models
from django.contrib.auth.models import AbstractUser, Group, Permission
class MyUser(AbstractUser):
# ...
groups = models.ManyToManyField(
Group,
verbose_name='groups',
blank=True,
help_text='The groups this user belongs to.',
related_name='user_groups' # 设置不同的 related_name
)
user_permissions = models.ManyToManyField(
Permission,
verbose_name='user permissions',
blank=True,
help_text='Specific permissions for this user.',
related_name='user_permissions' # 设置不同的 related_name
)
# ...
通过为自定义模型中的groups
和user_permissions
字段添加related_name
参数,并将其设置为与内置auth.User
模型的字段不同的名称,可以避免冲突。
请注意,修改模型后,您需要再次运行makemigrations
和migrate
命令来应用更改:
python manage.py makemigrations
python manage.py migrate
修改完成后再执行命令 makemigrations ,就没有报错了,如下图所示: