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 theUsermodel [Django-doc] directly. For more information you can see the referencing theUsermodel section of the documentation.
Note: Django's
DateTimeField[Django-doc] has aauto_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 inModelForms by default.