首页 > 解决方案 > Django 可以为此进行复杂的选择查询吗?

问题描述

我有以下模型用于存储两个用户之间的双向关系。记录总是插入到较小的用户 id 是 user_a 而较大的用户 id 是 user_b 的位置。

有没有办法根据参考用户 id 是否大于或小于另一个用户 id 来检索属于参考用户的所有记录和状态的正确值(如果 user_a 则对 relationship_type 应用负转换)?

也许有两个单独的查询,一个引用用户 = user_a,另一个引用用户 = user_b,然后是连接?

class Relationship(models.Model):
    RELATIONSHIP_CHOICES = (
        (0, 'Blocked'),
        (1, 'Allowed'),
        (-2, 'Pending_A'),
        (2, 'Pending_B'),
        (-3, 'Blocked_A'),
        (3, 'Blocked_B'),
    )
    user_a = models.ForeignKey(CustomUser, on_delete=models.SET_NULL, related_name='user_a',null=True)
    user_b = models.ForeignKey(CustomUser, on_delete=models.SET_NULL, related_name='user_b',null=True)
    relationship_type = models.SmallIntegerField(choices=RELATIONSHIP_CHOICES, default=0)

我正在尝试实现的 SQL 查询:

(SELECT user_b as user_select, -relationship_type as type_select WHERE user_a='reference_user') UNION (SELECT user_a as user_select, relationship_type as type_select WHERE user_b='reference_user')

标签: djangodjango-models

解决方案


鉴于您有 user 的 id user_id,您可以使用以下内容进行过滤:

from django.db.models import Q

Relationship.objects.filter(Q(user_a_id=user_id) | Q(user_b_id=user_id))

如果你有一个CustomUserobject user,它几乎是一样的:

from django.db.models import Q

Relationship.objects.filter(Q(user_a=user) | Q(user_b=user))

如果您希望获取Relationship给定类型的 s,我们可以执行以下操作:

from django.db.models import Q

rel_type = 2  # example rel_type

Relationship.objects.filter(
    Q(user_a=user, relationship_type=rel_type) |
    Q(user_b=user, relationship_type=-rel_type)
)

因此,在这里我们检索Relationship具有user_a给定用户和的对象relationship_type=2,或具有给定用户和的对象。Relationshipuser_brelationship_type=-2

我们可以注释查询集,然后采用联合,例如:

qs1 = Relationship.objects.filter(
    user_a=user, relationship_type=rel_type
).annotate(
    user_select=F('user_b'),
    rel_type=F('relationship_type')
)

qs2 = Relationship.objects.filter(
    user_a=user, relationship_type=rel_type
).annotate(
    user_select=F('user_a'),
    rel_type=-F('relationship_type')
)

qs = qs1.union(qs2)

虽然我不知道这是否是一个好主意:注释不是“可写的”(所以你不能更新这些)。

实现某种“代理对象”可能会更好,它可以交换user_auser_b,并否定relationship类型,因此能够像真实Relationship对象一样行事。


推荐阅读