如何使用Django过滤一对一的通用关系?

时间:2019-05-09 16:19:51

标签: python django

我有一个审核模型:

class ItemModeration(models.Model):

    class Meta:
        indexes = [
            models.Index(fields=['object_id', 'content_type']),
        ]
        unique_together = ('content_type', 'object_id')

    content_type = models.ForeignKey(ContentType, on_delete=models.CASCADE)
    object_id = models.PositiveIntegerField()
    item = GenericForeignKey('content_type', 'object_id')

    published = models.BooleanField(default=False)
    ...

动态附加审核对象的描述符:

class ItemModerationDescriptor(object):
    def __init__(self, **default_kwargs):
        self.default_kwargs = default_kwargs

    def __get__(self, instance, owner):
        ctype = ContentType.objects.get_for_model(instance.__class__)
        try:
            moderation = ItemModeration.objects.get(content_type__pk=ctype.id,
                                                    object_id=instance.pk)
        except ItemModeration.DoesNotExist:
            moderation = ItemModeration(item=instance,**self.default_kwargs)
            moderation.save()
        return moderation

还有我要审核的模型:

class Product(models.Model):
    user = models.ForeignKey(
        User,
        null=True,
        on_delete=models.SET_NULL)
    created = models.DateTimeField(
        auto_now_add=True,
        blank=True, null=True,
    )
    modified = models.DateTimeField(
        auto_now=True,
        blank=True, null=True,
    )
    name = models.CharField(
        max_length=PRODUCT_NAME_MAX_LENGTH,
        blank=True, null=True,
    )
    moderation = ItemModerationDescriptor()

现在我可以轻松看到产品的“发布”状态:

p=Product(name='my super product')
p.save()
print(p.moderation.published)
-> False

通用关系很有用,因为无论类型是什么,我都将能够搜索对象以进行调节:它可能是产品,图像,注释。

 to_moderate_qs = ItemModeration.objects.filter(published=False)

现在,如何获得已发布产品的过滤列表? 我想做这样的事情

published_products_qs = Product.objects.filter(moderation__published=True, name__icontains='sony')

但是,当然,它不会起作用,因为moderation属性不是Django模型字段。

我如何有效地做到这一点?我正在考虑适当的JOIN,但是我不知道如何在不使用原始SQL的情况下使用django做到这一点。

2 个答案:

答案 0 :(得分:1)

Django为此提供了一个很好的内置答案:GenericRelation。只需在Product模型上定义通用关系,然后将其用作常规相关字段即可,而不是描述符:

from django.contrib.contenttypes.fields import GenericRelation

class Product(models.Model):
    ...
    moderation = GenericRelation(ItemModeration)

然后像通常使用相关模型一样处理创建,并且过滤应完全按照您的规定进行。要用作当前系统,在创建新的save时必须插入一个钩子或ItemModeration方法来创建相关的Product对象,但这与其他相关对象没有什么不同django模型。如果您确实想保留描述符类,显然可以为GenericRelation使用辅助模型字段。

您还可以添加related_query_name以仅根据ItemModeration内容类型来过滤Product对象。

警告,如果您这样做使用GenericRelation,请注意它具有固定的级联删除行为。因此,如果您不希望在删除ItemModeration时删除Product对象,请小心添加pre_delete钩子或等效钩子!

更新

我无意中忽略了问题的OneToOne方面,因为GenericForeignKey是一对多关系,但是可以通过巧妙使用QuerySet来实现类似的功能。的确,您不能将product.moderation作为单个对象进行访问。但是,例如,以下查询在过滤的产品列表上进行迭代,并提取其名称,用户名和相关ModerationItem的发布日期:

Product.objects.filter(...).values_list(
    'name', 'user__username', 'moderation__published'
)

答案 1 :(得分:0)

您必须使用String s = "hi how are you doing today jane"; ArrayList<String> al = new ArrayList<>(); String[] splitted = s.split("\\s+"); int n = splitted.length; for(int i=0; i<n; i++) { al.add(splitted[i]); } for(int i=0; i<n-1; i=i+2) { System.out.print(al.get(i+1)+" "+al.get(i)+" "); } if((n%2) != 0) { System.out.print(al.get(n - 1)); } 来按特定模型类型查询表。

像这样:

content_type

有关该主题的更多详细信息,请检查contenttypes doc