Django ORM Запрос для получения номера пользователя, находящегося в данный момент в подписке

Я использую журнал событий, который отслеживает подписки и отписки на заданные списки рассылки.

Моя цель - один раз обратиться к базе данных (sqlite), чтобы получить количество подписанных пользователей, мне не нужны объекты, только число.

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)

Пока я нашел только это решение для работы :

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)

Я знаю, что в Django есть метод .count(), но преобразование в список все равно попадает в базу данных.

Может ли кто-нибудь предложить запрос, который вернет количество пользователей, подписанных на данный момент, учитывая эту модель?

Вы можете аннотировать Users последними MailingListEvent для этого MailingList, используя Subquery выражение [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, то это не будет засчитано как подписка.


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.

Вернуться на верх