【问题标题】:Django ORM: Override related_name of Field in Child ClassDjango ORM:覆盖子类中字段的related_name
【发布时间】:2017-03-20 09:23:29
【问题描述】:

我得到了这个例外:

django.core.exceptions.FieldError:

“SpecialPlugin”类中的本地字段“ticket”与基类“BasePlugin”中名称相似的字段发生冲突

这是我的模型:

class BasePlugin(models.Model):
    ticket = models.OneToOneField('foobar.ticket', primary_key=True, 
                                  related_name='%(app_label)s_%(class)s')

    class Meta(IndexImplementation.Meta):
        abstract = True

    # .. Other stuff which should be available for SpecialPlugin 
    #    and other child classes.

class SpecialPlugin(BasePlugin):
    ticket = models.OneToOneField('foobar.ticket', primary_key=True, 
                                  related_name='special')

我只有found this note,但在我的例子中,父类是抽象的。我不确定它是否适用于此。

我想为子类SpecialPlugin 指定相关名称“特殊”,因为 BasePlugin 的相关名称 (%(app_label)s_%(class)s) 会破坏旧代码。

有没有办法让 SpecialPlugin.ticket 的related_name 为“特殊”?

【问题讨论】:

  • 只有当父类不是抽象时才会出现这个错误。我刚刚使用类似于您的简单模型进行了测试,并且效果很好。如果您查看引发异常的 Django 代码,它会检查父类是否是抽象的。这向我表明 BasePlugin 没有正确设置为抽象。 IndexImplementation.Meta 中有什么内容?
  • @solarissmoke IndexImplementation.Meta 是抽象的 = True。这就是为什么我不理解错误消息。

标签: django django-models


【解决方案1】:

更新:

使用 django 的 default_related_name 元选项可以完成类似的操作。


它可能看起来像一个丑陋的 hack,但您可以将函数调用设置为 related_name 参数而不是字符串。然后在子类/模型中覆盖该函数。

class BasePlugin(models.Model):

    @staticmethod
    def get_ticket_related_name():
        return '%(app_label)s_%(class)s'

    ticket = models.OneToOneField('foobar.ticket', primary_key=True, 
                                  related_name=get_ticket_related_name.__func__())

    class Meta(IndexImplementation.Meta):
        abstract = True


class SpecialPlugin(BasePlugin):
    @staticmethod
    def get_ticket_related_name():
        return 'special'

【讨论】:

  • 我的子类中的 get_ticket_related_name 永远不会被调用。
  • @kzh 你确定: 1. 你的基类是抽象的。 2. 更改子类中get_ticket_related_name 方法的外观/声明顺序是否修复?
  • 我会尽量安排顺序。谢谢。
  • @v1k45 我刚刚尝试了这个解决方案,但它似乎并没有被采纳。子类定义了静态方法“get_ticket_related_name”。但是基类的方法是被调用的方法。 (并且基类是抽象的)我能看到的唯一区别是 Base 的 Meta 使用的是 IndexImplementation,而我的不是。但我似乎找不到那个类是什么?
  • @albrnick 我不确定这一点,但我发现 django 中已经存在类似的东西。查找 default_related_name 元选项。 docs.djangoproject.com/en/2.0/ref/models/options/…
【解决方案2】:

看起来问题的核心在于模型字段Django model inheritance, overriding fields的覆盖

解决问题的简单方法是将 BasePlugin 解耦到没有 ticket 字段的类,然后创建一个包含 ticket 字段的子类

class BaseWithoutTicketPlugin(models.Model):
    # .. Other stuff which should be available for SpecialPlugin 
    #    and other child classes.
    class Meta(IndexImplementation.Meta):
        abstract = True

class BasePlugin(BaseWithoutTicketPlugin):
    ticket = models.OneToOneField('foobar.ticket', primary_key=True, 
                                  related_name='%(app_label)s_%(class)s')

    class Meta(BaseWithoutTicketPlugin.Meta):
        abstract = True


class SpecialPlugin(BaseWithoutTicketPlugin):
    ticket = models.OneToOneField('foobar.ticket', primary_key=True, 
                                  related_name='special')

想法是在需要自定义ticket 时使用BaseWithoutTicketPlugin,在不需要时使用BasePlugin

【讨论】:

  • BasePlugin 有其他需要的属性。我需要继承它。抱歉,我正在为这个问题剥离我的代码......也许被剥离了很多。我更新了问题。
  • @guettli 在我的建议中仍然没有看到问题,我更新了我的答案。
  • “其他东西”部分需要属性“票”。这意味着您需要将此部分移动到“BasePlugin”。完成此操作后,其他东西部分不再可用于 SpecialPlugin :-(
【解决方案3】:

在 Meta 类中使用默认的相关名称:

class SpecialPlugin(BasePlugin):
    class Meta:
        default_related_name = 'special_plugins'

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2023-03-15
    • 2020-07-26
    • 1970-01-01
    • 2016-10-11
    • 2015-11-05
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多