GenericForeignKey或ForeignKey

时间:2019-02-05 15:42:14

标签: python django

我目前必须对我的模型Order做出以下决定:我使用GenericForeignKey来引用模型DiscountModelAnotherDiscountModel。只能是其中之一,因此从GenericForeignKey的想法出发就可以理解。

但是,我在性能很重要的地方实施它。另一种方法是必须在我的模型中的ForeignKey个字段中:discount_modelanother_discount_model。其中之一将永远是空的。

我现在想知道在添加“其他折扣模型”之前您将走哪条路。您有什么见识可以与我分享吗?目前,GenericForeignKey对我来说似乎有点复杂,我将不得不在代码中更改几个部分。

除了Risadinha的评论外,我在这里分享我当前的模型结构:

class AbstractDiscount(TimeStampedModel):


    value = models.PositiveIntegerField(
        verbose_name=_("Value"),
        validators=[
            MinValueValidator(0),
        ],
        null=True,
        blank=True,
    )
    percentage = models.DecimalField(
        verbose_name=_("Percentage"),
        max_digits=5,
        decimal_places=4,
        validators=[
            MinValueValidator(0),
            MaxValueValidator(1),
        ],
        null=True,
        blank=True,
    )
    type = models.CharField(
        verbose_name=_("Type"),
        max_length=10,
        choices=TYPE_CHOICES,
    )
    redeemed_amount = models.PositiveIntegerField(
        verbose_name=_("Amount of redeems"),
        default=0,
    )

    class Meta:
        abstract = True


class Discount(AbstractDiscount):
    available_amount = models.PositiveIntegerField(
        verbose_name=_("Available amount"),
    )
    valid_from = models.DateTimeField(
        verbose_name=_("Valid from"),
        help_text=_("Choose local time of event location. Leave empty and discount will be valid right away."),
        null=True,
        blank=True,
    )
    valid_until = models.DateTimeField(
        verbose_name=_("Valid until"),
        help_text=_("Choose local time of event location. Leave empty to keep discount valid till the event."),
        null=True,
        blank=True,
    )
    comment = models.TextField(
        verbose_name=_("Comment"),
        help_text=_("Leave some notes for this discount code."),
        null=True,
        blank=True,
    )
    status = models.CharField(
        verbose_name=_("Status"),
        max_length=12,
        choices=STATUS_CHOICES,
        default=STATUS_ACTIVE,
    )
    code = models.CharField(
        verbose_name=_("Discount code"),
        max_length=20,
    )
    event = models.ForeignKey(
        Event,
        related_name='discounts',
        on_delete=models.CASCADE,
    )  # CASCADE = delete the discount if the event is deleted
    tickets = models.ManyToManyField(
        Ticket,
        related_name='discounts',
        blank=True,
        help_text=_("Leave empty to apply this discount to all tickets"),
        verbose_name=_("Tickets"),
    )

    class Meta:
        verbose_name = _("Discount")
        verbose_name_plural = _("Discounts")
        ordering = ['code']


class SocialDiscount(AbstractDiscount):
    event = models.OneToOneField(
        Event,
        related_name='social_ticketing_discount',
        on_delete=models.CASCADE,
    )  # CASCADE = delete the discount if the event is deleted
    tickets = models.ManyToManyField(
        Ticket,
        related_name='social_ticketing_discount',
        blank=True,
        help_text=_("Leave empty to apply this discount to all tickets"),
        verbose_name=_("Tickets"),
    )

    class Meta:
        verbose_name = _("SocialDiscount")
        verbose_name_plural = _("SocialDiscount")

1 个答案:

答案 0 :(得分:3)

仅出于考虑,对此没有通用的答案。该决定取决于您需要使用此解决方案实施的业务逻辑。

两列

order.discount = ForeignKey(Discount, null=True)
order.social_discount = ForeignKey(SocialDiscount, null=True)

签入后续代码时:

if order.discount:
    # do this based on Discount model
elif order.social_discount:
    # do that based on SocialDiscount model

这是支持两种截然不同的Discount行为的解决方案。

使用此:

  • 如果只有那两个,并且将来不再有
  • 如果您要对它们调用非常不同的字段和方法(它们周围有不同的业务逻辑)。

非抽象父母

# renamed from AbstractDiscount to ParentDiscount for obvious reasons
order.discount = ForeignKey(ParentDiscount, null=True)

后续代码:

if order.discount:
    # do things that apply to either discount
    if isinstance(order.discount, 'Discount'):
        # do things that only apply to Discount
    elif isinstance(order.discount, 'SocialDiscount'):
        # do things that only apply to SocialDiscount

使用此:

  • 如果将来将来有更多ParentDiscount的孩子,
  • 如果有适用于所有子代之间共享的任何类型的ParentDiscount的通用业务逻辑。

GenericForeignKey

查询GenericForeignKeys需要一些工作。正如@Andy所说,它不受直接支持,但是您当然可以一起在content_typeobject_id上进行查询。除非您只能依靠__in,否则object_id查找将无法进行。

它将无法以表格的形式立即使用。对于Django Admin,可能有一些解决方案,请参阅GenericForeignKey and Admin in Django

使用此:

  • 如果将来可能会有更多各种类型的折扣(问产品负责人,并确保这不仅仅是将来的日子)
  • 如果有适用于这些类型的通用业务逻辑,
  • 如果您不需要临时管理快速解决方案。
相关问题