在Django中使用GenericForeignKey进行复杂的过滤

时间:2013-03-25 07:09:05

标签: django generics filter

我有点困难,想出这个。我有两个自定义用户模型(例如:买方,卖方),它扩展了一个自定义用户模型(CustomUser),它包含所有常用字段(包括我们需要过滤东西的email)并且是{ {1}}模型。

现在,我们正在使用第三方电子邮件服务向/向我们的应用程序发送和接收电子邮件。第三方服务通知我们一个事件。比如说,如果电子邮件被退回或失败,他们会在abstract网址上向我们发送POST请求,在对请求进行身份验证后,我们会更新记录并记下哪个电子邮件失败。

我们有一个额外的电子邮件模型(SentMessage),我们保存已发送的邮件。这个模型看起来像这样。

callback

如您所见,上述模型中的class SentMessage(models.Model): subject = models.CharField(max_length=100) body = models.TextField() sender = models.ForeignKey(models.InternalUser) content_type = models.ForeignKey(ContentType) object_id = models.PositiveIntegerField() recipient = generic.GenericForeignKey('content_type', 'object_id') bounced = models.BooleanField(default=False) 字段为recipient,可以绑定到任何其他模型(在我们的案例中为买方或卖方)。如果消息是在跳出事件等处进行的,我们将更新记录的模型。我希望使用第三方服务提供的电子邮件地址过滤收件人。所以基本上流程就像是。

- >使用给定的电子邮件地址过滤收件人。(收件人可以是买方或卖方) - >使用上述内容,根据事件类型过滤掉已被退回或失败的GenericForeignKey

我在这里第一点陷入困境。如何过滤SentMessage模型或Buyer模型中存在的对象。我甚至无法执行以下操作,因为Seller类是一个抽象类,CustomUserBuyer都继承该类:

Seller

从给定的电子邮件地址中查找(过滤)recipient = models.CustomUser.objects.get(email=bounced_email) content_typeobject_id的最佳方法是什么?

2 个答案:

答案 0 :(得分:1)

这是您需要前往https://docs.djangoproject.com/en/dev/ref/contrib/contenttypes/

的地方

首先获取content_type的类,然后使用object_id

获取对象
recipient = models.CustomUser.objects.get(email='guido@example.com')
messages = SentMessage.objects.filter(content_object=recipient, email=bounced_email)

答案 1 :(得分:0)

我倾向于同意n3storm一个抽象模型可能不适合这种情况,但我会假设你有理由使用它。

在这种情况下,您的具体模型中的GenericRelation可能会起作用,因为GenericRelation充当GenericForeignKey的“反向关系”。 (https://docs.djangoproject.com/en/dev/ref/contrib/contenttypes/#django.contrib.contenttypes.generic.GenericRelation

E.g。类似的东西可能会起作用:

class CustomUser(models.Model):
    # ... your common fields

    class Meta:
        abstract = True

class Buyer(CustomUser):
    # ... buyer-specific fields
    sent_messages = GenericRelation(SentMessage)

class Seller(CustomUser):
    # ... seller-specific fields
    sent_messages = GenericRelation(SentMessage)


recipient_list = [x for x in Buyer.objects.filter(sent_messages=bounced_email)] + 
                 [x for x in Seller.objects.filter(sent_messages=bounced_email)]
# The above list can contain at most one element since a SentMessage will reference at most one object.

关于验证的一些评论:除非你对SentMessage.content_type FK进行了一些验证,否则不可能 GFK引用的另一个模型不是BuyerSeller 。这可以通过在limit_choices_to FK中使用content_type来防止,但您必须确保调用full_clean模型实例的SentMessage方法来应用验证({{3 }})。 Django Admin正确使用limit_choices_to来阻止输入无效的FK值。

另外,我认为默认情况下,GFK不会强制存在您引用的object_id(至少从1.4开始,这就是我正在使用的)。这意味着最终可能会出现上述示例中的recipient_list为空的情况。

警告:向模型添加GenericRelation意味着删除该模型的实例也会级联删除GFK链接的条目。在这种情况下,这意味着删除Buyer将删除与SentMessage相关的所有Buyer。有关如何解决此功能/限制的详细信息,请参阅https://docs.djangoproject.com/en/dev/ref/models/instances/#validating-objects