继承带外键的抽象类时出错

4 投票
1 回答
1919 浏览
提问于 2025-04-16 08:44

我在Django中写了下面的代码

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

class AuditColumns(models.Model):
    created_at=models.DateField("Created at")
    created_by=models.ForeignKey(User, db_column="created_by", related_name="poll_user_created_by")
    updated_at=models.DateTimeField("Updated at")
    updated_by=models.ForeignKey(User, db_column="updated_by", null=True, related_name="poll_user_updated_by")
    class Meta:
        abstract = True


class Poll(AuditColumns):
    question=models.CharField(max_length=300)
    start_poll_at=models.DateTimeField(null=True)
    end_poll_at=models.DateTimeField(null=True)
    is_active=models.BooleanField(default=True)


class Choice(AuditColumns):
    choice=models.CharField(max_length=200)

当我执行这段代码时,出现了以下错误

mo@debian:~/PycharmProjects/KlamTam$ ./manage.py sql polls
Error: One or more models did not validate:
polls.poll: Accessor for field 'created_by' clashes with related field 'User.poll_user_created_by'. Add a related_name argument to the definition for 'created_by'.
polls.poll: Reverse query name for field 'created_by' clashes with related field 'User.poll_user_created_by'. Add a related_name argument to the definition for 'created_by'.
polls.poll: Accessor for field 'updated_by' clashes with related field 'User.poll_user_updated_by'. Add a related_name argument to the definition for 'updated_by'.
polls.poll: Reverse query name for field 'updated_by' clashes with related field 'User.poll_user_updated_by'. Add a related_name argument to the definition for 'updated_by'.
polls.choice: Accessor for field 'created_by' clashes with related field 'User.poll_user_created_by'. Add a related_name argument to the definition for 'created_by'.
polls.choice: Reverse query name for field 'created_by' clashes with related field 'User.poll_user_created_by'. Add a related_name argument to the definition for 'created_by'.
polls.choice: Accessor for field 'updated_by' clashes with related field 'User.poll_user_updated_by'. Add a related_name argument to the definition for 'updated_by'.
polls.choice: Reverse query name for field 'updated_by' clashes with related field 'User.poll_user_updated_by'. Add a related_name argument to the definition for 'updated_by'.

这个问题和设置一个独特的related_name有关,有没有办法动态生成related_name,以确保它们总是独一无二的?当然,了解related_name的作用会更好,如果可以的话,能否避免设置它们呢?

祝好,

1 个回答

8

Django的ORM(对象关系映射)提供了一些相关名称,帮助你轻松找到所有与当前模型相关的模型。比如说,如果你有一个从Foo到Bar的关联,那么Bar的实例默认会有一个叫做foo_set的属性,这个属性会给你一个列表,里面包含所有与这个特定Bar关联的Foos。

你可以指定自己的related_name,这样可以让你的代码更容易理解,使用更自然或更合适的名称。

不过,正如你所看到的,Django的ORM要求related_name的值必须是唯一的。因为你的基类中有外键(FK),它会自动为所有子模型创建这些外键,并且使用相同的名称,这显然是行不通的。

好消息是,文档中的这条说明提供了一个解决方案,适用于Django 1.2及以上版本

如果因为某种原因你必须使用Django 1.1或更早的版本,这里有一个替代的解决方案:在抽象模型中不要使用外键,而是用一个字段来存储被关联模型的主键,并相应地添加访问器。例如:

class AuditBase(models.Model):
    created_at = models.DateTimeField("Created at", auto_now_add=True)
    created_by = models.IntegerField(required=True)
    updated_at = models.DateTimeField("Updated at", auto_now=True)
    updated_by = models.IntegerField(required=True)

    class Meta:
        abstract = True

@property
def creator(self):
    return User.objects.get(id=self.created_by) 

@property
def last_editor(self):
    return User.objects.get(id=self.updated_by)

def save(self, *args, **kwargs):
    #track the creator/last editor via an optional kwarg
    active_user = self.kwargs.get('user')
    if active_user:
       self.updated_by = active_user
    if active_user and not self.created_by:
       self.created_by = active_user

    return super(AuditBase, self).save(*args, **kwargs)

撰写回答