Django中的复杂反向查询

时间:2011-10-15 03:17:51

标签: django generics foreign-keys reverse

简而言之:我的模型 B - > A< -C ,我想过滤 Bs ,其中至少存在一个 C ,满足某些任意条件且与 A相关,因为 B 。一些复杂因素(见下文)的帮助也很受欢迎。

详细说明:

我正在尝试创建一个通用模型来限制用户访问其他模型中的行。这是一个(简化)示例:

class CanRead(models.Model):
    user = models.ForeignKey(User)
    content_type = models.ForeignKey(ContentType)
    object_id = models.PositiveIntegerField()
    content_object = generic.GenericForeignKey('content_type', 'object_id')

class Direct(models.Model):
    ...

class Indirect(models.Model):
    direct = models.ForeignKey(Direct)
    ...

class Indirect2(models.Model):
    indirect = models.ForeignKey(Indirect)
    ...

CanRead 关联到每个模型中的每一行(空间成本太高)是不可行的,因此只有一些模型需要具有该关联(例如直接以上)。在这种情况下,我会看到用户是否可以访问 Direct

Direct.objects.filter(Q(canread__user=current_user), rest_of_query)

(不幸的是,这个查询不起作用 - 至少在1.2.5中 - 因为通用的fk;对此的任何帮助都会受到赞赏,但有一些解决方法,真正的问题是接下来的内容)

其他人的可访问性将取决于他们与其他模特的关系。因此,如果可以访问直接,则用户可以访问间接,如果 indirect__direct Indirect2 将是,等

我的问题是,我该怎么做这个查询?我很想写一些类似的东西:

Indirect.objects.filter(Q(canread__content_object=F('direct'), canread__user=current_user), rest_of_query)

Indirect2.objects.filter(Q(canread__content_object=F('indirect__direct'), canread__user=current_user), rest_of_query)

但这不起作用(Django期望 CanRead 间接之间的关系 - 这是不存在的 - 以使反向查询起作用)。如果我在SQL中直接编写它,我会做类似的事情:

SELECT *
  FROM indirect i
    JOIN direct d ON i.direct = d.id
    JOIN canread c ON c.object_id = d.id
  WHERE
    c.content_type = <<content type for Direct>> AND
    c.user = <<current user>> AND
    <<rest_of_query>>

但我无法将该查询转换为Django。可能吗?如果没有,那么最不突兀的做法是什么(使用尽可能少的原始SQL)?

谢谢你的时间!

注意:提到的解决方法是不使用通用fk ...... :(我可以丢弃 CanRead 模型,并且有很多 CanReadDirect CanReadDirect2 < / strong>, CanReadDirect3 等。这是一个轻微的麻烦,但不会太多阻碍我的项目。

2 个答案:

答案 0 :(得分:3)

对于你给出的简单案例,解决方案很简单:

B.objects.filter(a__c__isnull=False)

对于实际查询,这是我的尝试:

Indirect.objects.filter(direct__id__in=
    zip(*CanRead.objects.filter(
           content_type=ContentType.objects.get_for_model(Direct)
        ).values_list('id'))[0])

但这种方式非常慢:您从一个查询集中提取ID,然后使用

进行查询
where id in (1, 2, 3, ... 10000)

哪个非常慢。我们在项目中加入通用外键时遇到了类似问题,并决定在模型管理器中使用原始查询。

class DirectManager(Manager):
    def can_edit(self, user):
        return self.raw(...)

我还建议在Django 1.3中查看per-row permissions框架。

答案 1 :(得分:1)

访问控制模型并不那么简单...... 使用众所周知的访问控制模型,例如: DAC / MAC 要么 RBAC 还有一个名为django-rbac的项目。