Django ORM 查询获取当前订阅用户的数量

发布于 2025-01-19 11:27:15 字数 2504 浏览 3 评论 0 原文

我使用一个事件日志,该日志跟踪订阅和取消订阅以给定的邮件列表。

我的目标是一次(SQLITE)点击数据库以获取订阅用户的数量,我不需要对象,而是一个数字。

到目前为止

class MailingListEvent(models.Model):
    """Events on mailing lists.

    This represents subscribes, unsubscribes, and bounces.  We'd like
    to understand what happens and when, not just the current state of
    the system.

    """
    class EventType(models.TextChoices):
        SUBSCRIBE = 'sub', 'inscription'
        UNSUBSCRIBE = 'unsub', 'désinscription'
        BOUNCE = 'bounce', 'bounce'

    user = models.ForeignKey(User, on_delete=models.CASCADE)
    mailing_list = models.ForeignKey(MailingList,
                                     on_delete=models.CASCADE)
    event_timestamp = models.DateTimeField(default=django.utils.timezone.now)
    event_type = models.CharField(max_length=6, choices=EventType.choices)

,我只找到了可以使用的解决方案:

def user_subscribe_count(mailing_list):
    """Return the number of users currently subscribed to the mailing list.

    We want to know how many users are currently subscribed.  Note
    that individual users might subscribe and unsubscribe multiple
    times.  Other (future) events could happen as well.
    """
    user_list = MailingListEvent.objects.filter(mailing_list=mailing_list).values_list('user',flat=True).distinct()
    users_subscribed = list()
    for user in user_list:
        user_state = user_current_state(User.objects.get(pk=user),mailing_list)
        if user_state.event_type == MailingListEvent.EventType.SUBSCRIBE:
           users_subscribed.append(user_state)
    return len(users_subscribed)

def user_current_state(user, mailing_list):
    """Return user's most current state on the provided mailing list

    Return the most recent event associated with this user in this
    mailing list.

    """
    try:
        the_user = MailingListEvent.objects.filter(
            Q(event_type=MailingListEvent.EventType.SUBSCRIBE) |
            Q(event_type=MailingListEvent.EventType.UNSUBSCRIBE),
            user=user, mailing_list=mailing_list).latest(
                'event_timestamp')
        return the_user
    except MailingListEvent.DoesNotExist:
        return MailingListEvent(
            user=user, mailing_list=mailing_list,
            event_type=MailingListEvent.EventType.UNSUBSCRIBE)

我知道Django上有一个.count()方法,但是无论如何,转换为列表已经列出了数据库。

有人可以提出一个查询,该查询可以返回当前订阅此模型的用户数量吗?

I use an Event log that tracks subscriptions and unsubscriptions to given mailing lists.

My objective is to hit the database one time (sqlite) to get the number of subscribed users, I don't need the objects, just a number.

models.py

class MailingListEvent(models.Model):
    """Events on mailing lists.

    This represents subscribes, unsubscribes, and bounces.  We'd like
    to understand what happens and when, not just the current state of
    the system.

    """
    class EventType(models.TextChoices):
        SUBSCRIBE = 'sub', 'inscription'
        UNSUBSCRIBE = 'unsub', 'désinscription'
        BOUNCE = 'bounce', 'bounce'

    user = models.ForeignKey(User, on_delete=models.CASCADE)
    mailing_list = models.ForeignKey(MailingList,
                                     on_delete=models.CASCADE)
    event_timestamp = models.DateTimeField(default=django.utils.timezone.now)
    event_type = models.CharField(max_length=6, choices=EventType.choices)

So far I found only this solution to work with :

def user_subscribe_count(mailing_list):
    """Return the number of users currently subscribed to the mailing list.

    We want to know how many users are currently subscribed.  Note
    that individual users might subscribe and unsubscribe multiple
    times.  Other (future) events could happen as well.
    """
    user_list = MailingListEvent.objects.filter(mailing_list=mailing_list).values_list('user',flat=True).distinct()
    users_subscribed = list()
    for user in user_list:
        user_state = user_current_state(User.objects.get(pk=user),mailing_list)
        if user_state.event_type == MailingListEvent.EventType.SUBSCRIBE:
           users_subscribed.append(user_state)
    return len(users_subscribed)

def user_current_state(user, mailing_list):
    """Return user's most current state on the provided mailing list

    Return the most recent event associated with this user in this
    mailing list.

    """
    try:
        the_user = MailingListEvent.objects.filter(
            Q(event_type=MailingListEvent.EventType.SUBSCRIBE) |
            Q(event_type=MailingListEvent.EventType.UNSUBSCRIBE),
            user=user, mailing_list=mailing_list).latest(
                'event_timestamp')
        return the_user
    except MailingListEvent.DoesNotExist:
        return MailingListEvent(
            user=user, mailing_list=mailing_list,
            event_type=MailingListEvent.EventType.UNSUBSCRIBE)

I know there is a .count() method on Django, but converting to list already hits the database anyway.

Can someone suggest a query that would return the number of users currently subscribed given this model please ?

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(1

指尖上得阳光 2025-01-26 11:27:15

您可以使用 MailingList 添加最新的 MailingListEvent 来注释 User。 com/en/dev/ref/models/expressions/#subquery-expressions" rel="nofollow noreferrer">子查询表达式[Django-doc]

from django.db.models import OuterRef, Subquery

User.objects.alias(
    latest_action=Subquery(
        MailingListEvent.objects.filter(
            mailing_list=mailing_list,
            user=OuterRef('pk')
        ).order_by('-event_timestamp').values('event_type')[:1]
    )
).filter(latest_action='sub').count()

如果最近的操作是BOUNCE,那么这将被计为订阅用户。


注意:通常最好使用 settings.AUTH_USER_MODEL[Django-doc] 来引用用户模型,而不是使用 直接用户模型[Django-doc]。有关更多信息,您可以参阅引用文档的User模型部分


注意:Django的DateTimeField [Django-doc]
有一个 auto_now_add=... 参数[Django-doc]
使用时间戳。这将自动分配当前日期时间
创建对象时,将其标记为不可编辑(editable=False),例如
默认情况下,它不会出现在 ModelForm 中。

You can annotate the Users with the latest MailingListEvent for that MailingList by using a Subquery expression [Django-doc]:

from django.db.models import OuterRef, Subquery

User.objects.alias(
    latest_action=Subquery(
        MailingListEvent.objects.filter(
            mailing_list=mailing_list,
            user=OuterRef('pk')
        ).order_by('-event_timestamp').values('event_type')[:1]
    )
).filter(latest_action='sub').count()

If the latest action was BOUNCE, then this will not be counted as a subscribed user.


Note: It is normally better to make use of the settings.AUTH_USER_MODEL [Django-doc] to refer to the user model, than to use the User model [Django-doc] directly. For more information you can see the referencing the User model section of the documentation.


Note: Django's DateTimeField [Django-doc]
has a auto_now_add=… parameter [Django-doc]
to work with timestamps. This will automatically assign the current datetime
when creating the object, and mark it as non-editable (editable=False), such
that it does not appear in ModelForms by default.

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文