Skip to main content
 首页 » 编程设计

python之Django ORM之如何在具有多对多字段的两个条件下加入

2024年11月24日31傻小

我有两个模型:

class Subscription(models.Model): 
    name = models.CharField(max_length=100, unique=True) 
 
class Email(models.Model): 
    email = models.EmailField(unique=True) 
    subscriptions = models.ManyToManyField(Subscription) 
    is_confirmed = models.BooleanField(default=False) 

我需要通过django orm模拟sql查询

select * from newsletter_subscription ss 
left join newsletter_mail_subscriptions ms 
    on ss.id = ms.subscription_id and ms.mail_id = <mail_id>; 

所以我可以通过获取可用订阅的完整列表来查看邮件是否订阅了每个订阅

我需要类似 FilteredRelated 的东西:

q = Subscription.objects.annotate( 
    is_subscribe=FilteredRelation( 
        'mail_subscriptions', condition=Q(mail_subscriptions__mail_id=10) 
    ) 
) 

但是FilteredRelation不支持跨越关系字段的条件

请您参考如下方法:

对于 FilteredRelation 根本不需要这样做,您可以这样写:

Subscription.objects.filter( 
    <b>email__id=</b><i>mail_id</i> 
)

使用 mail_id 您希望过滤的 Email 对象的 ID。

Django 将构造一个如下所示的查询:

SELECT subscription.id, subscription.name 
FROM subscription 
INNER JOIN email_subscriptions 
    ON subscription.id = email_subscriptions.subscription_id 
WHERE email_subscriptions.email_id = <i>email_id</i>

无论如何都不需要使用 LEFT OUTER JOIN,因为您检查 mail_id 是否是特定的 id,因此 code>INNER JOIN 将产生相同的集合。

请注意,您使用了 ManyToManyField,并且 Django 在两个实体之间创建了一个表,但您无法访问该表,除非您指定一个带有 through [Django-doc] 的模型。参数。

您还可以使用 is_subscribed 注释 Subscription,例如:

from django.db.models import Exists, OuterRef 
 
Subscription.objects.annotate( 
    is_subscribed=Exists( 
        Email.subscriptions.through.objects.filter( 
            subscription_id=OuterRef('id'), 
            mail_id=<i>email_id</i> 
        ) 
    ) 
)

这会产生如下查询:

SELECT subscription.*, 
    <b>EXISTS(</b> 
        SELECT U0.id, U0.email_id, U0.subscription_id 
        FROM email_subscriptions U0 
        WHERE U0.subscription_id = subscription.id AND U0.mail_id = <i>email_id</i> 
    <b>) AS is_subscribed</b> 
FROM subscription