【问题标题】:Django, treat OneToOne related field as my own fieldDjango,把OneToOne相关领域当成自己的领域
【发布时间】:2016-05-26 07:51:24
【问题描述】:

我实际上是在尝试提出自己的继承方案,因为 Django 的继承不符合我的需要。

我希望父表(类)包含通用数据字段。
子类将在单独的表中拥有自己的附加数据。

class ProductBase(models.Model):

    common = models.IntegerField()

    def get_price(self):
        return some_price


class FooProduct(ProductBase):

    # no field because I'm proxy

    class Meta:
        proxy = True

    def get_price(self):
        return price_using_different_logic


class FooExtra(models.Model):

    base = models.OneToOneField(ProductBase, primary_key=True)
    phone = models.CharField(max_length=10)

我的问题是,是否可以将 Foo 视为拥有 FooExtra 的字段?

我想做一些事情,比如关注..

foo = FooProduct.objects.create()

foo.phone = "3333"  # as django does with its multiple inheritance
foo.save()
FooProduct.objects.filter(phone="3333")  

我想列出不同种类的产品(数据)

  1. 我需要把它们一起列出来,所以抽象的Base继承就出来了

  2. 从列表中,我想将每个模型视为多态模型,在迭代 ProductBase.objects.all() 时,product.get_price() 将使用适当的类方法。 (如果不需要,则无需加入)

  3. 当且仅当我需要时,我才检索额外的表数据(通过类似.select_related('fooextra')

Django-polymorphic 接近我想要的,但它的作用相当模糊,所以我不敢使用它,我认为它失败了#3。

【问题讨论】:

  • Django 的默认模型继承对你不起作用的原因是什么?
  • @Ben:我已经编辑了问题来解决这个问题。

标签: python django django-inheritance


【解决方案1】:

如果我理解得很好,您希望继承并且希望特定于子类的字段位于单独的表上。 据我所知,您不需要代理类来实现这一点,您可以按照https://docs.djangoproject.com/en/1.9/topics/db/models/#multi-table-inheritance 手册中的说明实现多表继承,例如:

class Base(models.Model):
    common = models.IntegerField()


class Foo(Base):
    phone = models.CharField(max_length=10)

正如上面链接中所解释的,这将自动创建一对一的关系。当然,您可以像上面的示例一样执行foo.phone = "3333"(其中foo 的类型为Foo)。巧妙的是,您还可以访问 foo.common,而在您的示例中,它应该是 foo.base.common

【讨论】:

    【解决方案2】:

    您似乎不希望有任何不同于 Django 标准继承的东西。

    class ProductBase(models.Model):
        common1 = models.IntegerField()
        common2 = models.IntegerField()
    
    class FooProduct(ProductBase):
        fooextra = models.IntegerField()
    
    class BarProduct(ProductBase):
        barextra = models.IntegerField()
    

    如果您创建每个实例:

    foo1 = FooProduct(common1=1, common2=1, fooextra=1)
    foo2 = FooProduct(common1=1, common2=1, fooextra=2)
    bar1 = BarProduct(common1=1, common2=1, barextra=1)
    bar2 = BarProduct(common1=1, common2=1, barextra=2)
    

    您可以遍历所有产品:

    for product in ProductBase.objects.all():
        print product.common1, product.common2
    

    从实际上是FooProductProductBase 对象,您可以通过以下方式获取自定义字段:

    product.foo.fooextra
    

    从实际上是BarProductProductBase 对象,您可以通过以下方式获取自定义字段:

    product.bar.barextra
    

    您仍然可以进行查询:

    foo = FooProduct.objects.get(fooextra=1)
    bar = BarProduct.objects.get(barextra=2)
    

    您可以直接访问这些对象上的公共字段:

    foo.common1
    bar.common2 
    

    如果您需要对查询等进行更多控制,您可以使用 django-model-utils 中的 InheritanceManager - 这也应该解决第 3 点:ProductBase.objects.filter(...).select_subclasses() 将为您提供 FooProductBarProduct 对象而不是 @ 987654337@ 个对象。

    【讨论】:

    • 不错的答案,你比我快 1 分钟 :-)
    • 我认为它失败了#2,polymorphic,如果 base 有 def price(self) 并且 foo 有它自己的版本,ProductBase.objects.all(): 不会使用多态价格。
    • 如果你从模型工具包中调用 select_subclasses 会这样
    • @ben:这会导致加入并违反#3,我不想加入多态行为(除非我需要)
    • 您确定它在需要而不是最初时不会这样做吗?我没有测试所以我不知道。此外,除非您对此进行了测试并且性能对您来说太慢,否则我不确定该要求背后的原因是什么。