Django ORM:从单个模型反向关联查询多个模型

3
我在查询反向关系时遇到了麻烦,我学了很多关于select_relatedprefetch_related的知识,但我仍然没能实现这个功能。
首先看一下我的模型:
from django.db import models
import uuid

class Person(models.Model):
    alias = models.UUIDField(primary_key=True,default=uuid.uuid4, editable=False, unique=True)
    name = models.CharField(max_length=20)

class Appointment(models.Model):
    patient = models.ForeignKey(Person, related_name="patient_for_appointment", on_delete=models.CASCADE)
    data = models.CharField(max_length=20)

class Sales(models.Model):
    customer = models.ForeignKey(Person, related_name="customer_for_sales", on_delete=models.CASCADE)
    amount = models.FloatField()

class Prescription(models.Model):
    patient = models.ForeignKey(Person, related_name="Patient_for_prescription", on_delete=models.CASCADE)
    details = models.CharField(max_length=100)

我正在尝试过滤“Person”模型,以检查这个人是否有任何“处方”,“销售”和“预约”,我想要用一个单一的查询来获取所有这些信息。将使用人的“别名”(主键)来过滤它。
我可以像下面这样分别进行过滤:
patient_alias = '53fsdfsdf-fdsfds-df-fdf'
queryset = Appointment.objects.filter(
     patient__alias=patient_alias
 )

但我不想这样做,因为它会有性能问题。我不想用单独的查询方式。
我只想查询Person模型,以检查一个人是否有预约、处方或销售。
例如:Person.objects.filter(alias='某人的别名') 请问有谁可以帮助我实现这个功能?
非常感谢!
1个回答

0

我们可以选择所有的Person,除了那些没有AppointmentSalesPrescription的人:

Person.objects.exclude(
    patient_for_appointment=None,
    customer_for_sales=None,
    Patient_for_prescription=None
)

这将生成一个类似于以下查询的查询语句:
SELECT person.alias, person.name
FROM person
WHERE NOT (
    person.alias IN (
        SELECT U0.alias
        FROM person U0
        LEFT OUTER JOIN prescription U1 ON U0.alias = U1.patient_id
        WHERE U1.id IS NULL
    )
    AND person.alias IN (
        SELECT U0.alias
        FROM person U0
        LEFT OUTER JOIN sales U1 ON U0.alias = U1.customer_id
        WHERE U1.id IS NULL AND U0.alias = person.alias
    )
    AND person.alias IN (
        SELECT U0.alias
        FROM person U0
        LEFT OUTER JOIN appointment U1
        ON U0.alias = U1.patient_id
        WHERE U1.id IS NULL AND U0.alias = person.alias
    )
)

或者我们可以使用联合体,例如:

Person.objects.filter(
    patient_for_appointment__isnull=False
).union(
    Person.objects.filter(customer_for_sales__isnull=False),
    Person.objects.filter(Patient_for_prescription__isnull=False)
)

这将导致一个类似于以下查询的查询:
(
    SELECT person.alias, person.name
    FROM person
    INNER JOIN appointment ON person.alias = appointment.patient_id
    WHERE appointment.id IS NOT NULL
) UNION (
    SELECT person.alias, person.name FROM person
    INNER JOIN sales ON person.alias = sales.customer_id
    WHERE sales.id IS NOT NULL
) UNION (
    SELECT person.alias, person.name
    FROM person
    INNER JOIN prescription ON person.alias = prescription.patient_id
    WHERE prescription.id IS NOT NULL
)

@mamuncode:看一下https://docs.djangoproject.com/en/dev/ref/models/querysets/#prefetch-related。它明确指出:“另一方面,*prefetch_related*会为每个关系执行单独的查找,并在Python中进行“连接”。这使得它可以预取多对多和多对一对象。”所以,在这里,您实际上将做到您在问题文本中想要避免的事情:向数据库进行**多次**往返:P - Willem Van Onsem
是的,我想避免在数据库中进行多次往返。你给出的解决方案不行,因为你的查询只会显示一个结果。我需要四个结果在一个查询中同时显示。 - user11995591
如果这个人有约会、处方、销售等,则应返回相应信息。如果没有约会,则应返回“无”,如果存在约会,则应返回类似以下的约会详情。 - user11995591
1
@mamuncode:你可以添加额外的.prefetch_related(..)部分,例如Person.objects.filter( patient_for_appointment__isnull=False ).union( Person.objects.filter(customer_for_sales__isnull=False), Person.objects.filter(Patient_for_prescription__isnull=False) ).prefetch_related('patient_for_appointment', 'patient_for_prescription', 'customer_for_sales')。但这样会产生额外的查询。但是在这里并不是坏事,实际上,在一对多关系中查询并在一个查询中获取它可能会导致网络拥塞重复。 - Willem Van Onsem
1
那么,您将获得一个查询集,其中包含已经拥有“约会”/“销售”/“处方”的“人员”,以及这些内容的获取方式。通过在一个查询中执行此操作,您将创建一个表,该表是三个(!)连接的结果,因此如果您的表很大,它很容易导致数百万个(重复的)行。 - Willem Van Onsem
显示剩余7条评论

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接