【问题标题】:Django: Cannot create model with a Field that maps to different modelsDjango:无法使用映射到不同模型的字段创建模型
【发布时间】:2022-01-25 15:30:44
【问题描述】:

在我的应用程序中,我有一个名为 Supplier 的模型和多个模型,如 PartFuelInk 等,它们都是某种形式的资源供应商可能会提供。

我希望能够获取给定供应商的所有资源,并认为我可以通过制作具有两个字段的 SupplierResource 模型来有效地实现这一点:supplierresourceresource 是外键到任一“资源”表中的对象)。

但这似乎不起作用,因为我无法创建映射到不同模型的外键。

以下是示例模型:

class Supplier(models.Model):
    delivery_time = models.CharField(max_length=255, blank=True, null=True)
    minimum_order_quantity = models.FloatField(blank=True, null=True)
    billing_address = models.ForeignKey(...)
    shipping_address = models.ForeignKey(...)
    # ...


class Resource(models.Model):
    code = models.CharField(max_length=255, unique=True, blank=True, null=True)
    supplier = models.ForeignKey(
        Company,
        related_name="supplier_part",
        on_delete=models.SET_NULL,
        null=True,
        blank=True,
    )
    country = models.CharField(max_length=255, blank=True, null=True)

    class Meta:
        abstract = True

class Part(Resource):
    some_attr = models.CharField(max_length=255, blank=True, null=True)


class Fuel(Resource):
    some_other_attr = models.CharField(max_length=255, blank=True, null=True)

我可以遍历Resource 的所有子模型以收集给定供应商的资源,但这会非常慢。

我宁愿不使用多态性来避免不必要的复杂性(除非事实证明这是绝对必要的)。

【问题讨论】:

  • 您不必遍历Resource 的所有子代。您可以迭代所有 Resource 并查看它们是 Part 还是 Fuel 类型。无论如何,this 在你走上通用外键的道路之前是一本不错的读物
  • 请问您绝对需要每个资源作为单独模型的背后原因是什么。我认为这种结构在问题的基础和根源上没有帮助。新资源会发生什么?每次都必须修改代码并添加新模型?如果我了解背景,也许用一个非常简单的解决方案来支持你会更容易。
  • 资源彼此之间非常不同,并且具有许多不同的属性。因此,它们必须不同。资源必须被视为产品。你有更好的解决方案/模型结构吗?

标签: django django-models database-design


【解决方案1】:

您可以考虑使用django-polymorphic

如果你这样做,只需像这样让 Resource 从 PolymorphicModel 继承:

from polymorphic.models import PolymorphicModel

class Supplier(models.Model):
    delivery_time = models.CharField(max_length=255, blank=True, null=True)
    minimum_order_quantity = models.FloatField(blank=True, null=True)


class Resource(PolymorphicModel):
    code = models.CharField(max_length=255, unique=True, blank=True, null=True)
    country = models.CharField(max_length=255, blank=True, null=True)


class Part(Resource):
    some_attr = models.CharField(max_length=255, blank=True, null=True)


class Fuel(Resource):
    some_other_attr = models.CharField(max_length=255, blank=True, null=True)

然后您可以创建如下所示的 SupplierResource 模型:

class SupplierResource(models.Model):
    supplier = models.ForeignKey(Supplier, on_delete=models.CASCADE)
    resource = models.ForeignKey(Resource, on_delete=models.CASCADE)

或者,供应商和资源之间的多对多关系:

class Supplier(models.Model):
    delivery_time = models.CharField(max_length=255, blank=True, null=True)
    minimum_order_quantity = models.FloatField(blank=True, null=True)
    resources = models.ManyToManyField(Resource, related_name="suppliers")

【讨论】:

    【解决方案2】:

    正如 C14L 所述,您可以使用 GenericForeignKey 来创建与另一个模型的关系。然而 GenericForeignKeys 有自己的问题。他们不会创建反向关系。所以你不能做,例如[sr.supplier for sr in Part.supplier_resources],但是它可以让你摆脱你的Resource父模型。我相信你对 Resource 父模型的看法是正确的。我建议在 资源模型,你添加一个字段一个字段叫child_model_name

    class Resource(models.Model):
        child_model_name = models.TextField()
        some_attr = models.CharField(max_length=255, blank=True, null=True)
    

    child_model_name = 'part' 等等..

    那么在你的 SupplierResource 模型中你有

    class SupplierResource(models.Model):
        supplier = models.ForeignKey(Supplier, ..., related_name="supplier_resources")
        resource = models.ForeignKey(Resource, ..., related_name="supplier_resources")
    
    

    django 正在为您创建一个名为Resource 的表,其中包含资源的所有字段。它还会为您创建一个名为 Part 的表,其中包含特定于与 Resource 具有 OneToOne 关系的部分的字段。

    现在你可以这样做了吗:

    parts = Part.objects.filter(supplier_rescources__supplier=supplier)

    或者在一个列表中获取不同类型的资源(零件、燃料等)。

    resources = Resource.objects.filter(supplier_resources__supplier=supplier)
    
    [getattr(resource, resource.child_model_name) for resource in resources]
    

    【讨论】:

    • 我忘记提及的一件事是我的资源模型是抽象的。我刚刚更新了它。
    【解决方案3】:

    你能使用通用外键吗?他们使用content_typeobject_id 将同一个字段映射到不同的模型:

    https://docs.djangoproject.com/en/4.0/ref/contrib/contenttypes/#generic-relations

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 2022-08-03
      • 1970-01-01
      • 1970-01-01
      • 2011-05-07
      • 2018-10-22
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多