You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

Django抽象模型中能否声明两个指向同表的ForeignKey?

在Django ORM抽象模型中声明两个指向同一张表的ForeignKey的解决方案

你想在抽象模型TrackableDateModel中同时添加create_user和update_user两个指向User表的ForeignKey,但添加update_user后执行makemigrations时出现反向访问器冲突报错:

ERRORS:
DjModule.A.create_user: (fields.E304) Reverse accessor 'User.create_user' for 'DjModule.A.create_user' clashes with reverse accessor for DjModule.B.create_user'.
HINT: Add or change a related_name argument to the definition for 'DjModule.A.Export4Payment.create_user' or 'DjModule.B.create_user'.

原代码如下:

class TrackableDateModel(models.Model):
    """Abstract model to Track the creation/updated date for a model."""
    create_date = models.DateTimeField(auto_now_add=True, help_text="Creation time", verbose_name="Created", null=True, blank=True)
    create_user = models.ForeignKey(User, related_name="create_user", on_delete=models.CASCADE, help_text="Who created", verbose_name="Author", null=True, blank=True)
    update_date = models.DateTimeField(auto_now=True, help_text="Time of change", verbose_name="Changed", null=True, blank=True)

    class Meta:
        abstract = True


class A(TrackableDateModel):
    ....

class B(TrackableDateModel):
    .....

尝试添加的字段:

update_user = models.ForeignKey(User, related_name="update_user", on_delete=models.CASCADE, help_text="Who modified", verbose_name="Author of change", null=True, blank=True)

错误原因

所有继承该抽象模型的子类(比如A、B)会共享相同的related_name值,导致User模型上生成的反向访问器名称重复,引发冲突。

解决办法

使用Django提供的%(class)s占位符,让每个子类的related_name自动带上自身类名,避免重复。修改后的抽象模型代码如下:

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

class TrackableDateModel(models.Model):
    """Abstract model to Track the creation/updated date for a model."""
    create_date = models.DateTimeField(auto_now_add=True, help_text="Creation time", verbose_name="Created", null=True, blank=True)
    create_user = models.ForeignKey(
        User, 
        related_name="created_%(class)s", 
        on_delete=models.CASCADE, 
        help_text="Who created", 
        verbose_name="Author", 
        null=True, 
        blank=True
    )
    update_date = models.DateTimeField(auto_now=True, help_text="Time of change", verbose_name="Changed", null=True, blank=True)
    update_user = models.ForeignKey(
        User, 
        related_name="updated_%(class)s", 
        on_delete=models.CASCADE, 
        help_text="Who modified", 
        verbose_name="Author of change", 
        null=True, 
        blank=True
    )

    class Meta:
        abstract = True


class A(TrackableDateModel):
    # 子类自定义字段
    pass

class B(TrackableDateModel):
    # 子类自定义字段
    pass

效果说明

  • 对于A类,create_user对应的反向访问器变为User.created_a.all(),update_user对应的反向访问器变为User.updated_a.all()
  • 对于B类,反向访问器则是User.created_b.all()和User.updated_b.all()
  • 这样就不会出现反向访问器名称冲突的问题,执行makemigrations和migrate都能正常完成

内容的提问来源于stack exchange,提问作者Victor

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.06.26 19:30:15