单独注释 Django 查询集中的每个项目

发布于 2025-01-20 17:01:56 字数 5245 浏览 2 评论 0 原文

目标

我试图获取一个查询集,告诉我用户是否订阅了给定的邮件列表,并能够使用 mailing_list.is_subbed 访问该 bool

理想情况下,我们将有一个查询集,其中每个项目都有一个带注释的字段“is_subbed”为 TrueFalse

上下文

对于上下文,此视图将提供一个带有复选框的表单,这些复选框根据用户的状态进行选中/取消选中。 该页面可通过包含令牌的 url 以隐身模式访问,该令牌包含 1) 用户的电子邮件和 2) 邮件发送记录的 ID(其中包含发送到的邮件列表等详细信息,详细信息如下

在当前状态下, is_subbed 函数仅在第一个项目上调用一次,并且生成的 bool 被注释到每个项目,我希望它为查询集中的每个项目运行。 我怎样才能做到这一点?目前,如果第一个项目在输入到 is_subbed 后返回 True,则每个复选框都会被标记,因为每个项目上的带注释字段 is_subbed 都设置为 True 。

代码

这是我当前的工作: 摘要:

  1. 视图
  2. 在所述视图片段中使用的
  3. 函数和模型的实现,我用它来访问 jinja2

views.py

class PressSubscriptionManagementView(TemplateView):
    template_name = "mailing_list/press_subscription_management.html"

    def is_subbed(self, user: User, mailing_list: MailingList) -> bool:
        """
        Check if the user is subbed to the mailing list
        """
        return user_current_state(user, mailing_list).event_type == "sub"

    def get_context_data(self, **kwargs) -> dict:
        context = super().get_context_data(**kwargs)
        email, send_record_id = token_valid(kwargs["token"])
        if email and send_record_id:
            context["user"] = User.objects.get(email=email)
            # In the current state, is_subbed is only called once on the first
            # item in the list. If this call returns True, every checkbox is
            # checked. None otherwise.
            context["press_subscription_list"] = \
                    def get_context_data(self, **kwargs) -> dict:
        context = super().get_context_data(**kwargs)
        email, send_record_id = token_valid(kwargs["token"])
        if email and send_record_id:
            context["user"] = User.objects.get(email=email)
            # In the current state, is_subbed is only called once on the first
            # item in the list. If this call returns True, every checkbox is
            # checked. None otherwise.
            context["press_subscription_list"] = \
                MailingList.objects.filter(
                    mailing_list_type="PR"
                    ).order_by("-id"
                    ).annotate(  # noqa
                        is_subbed=(
                            ExpressionWrapper(
                                Value(
                                    self.is_subbed(
                                        context["user"],
                                        F('mailing_list__id')
                                    ),
                                    output_field=BooleanField()
                                    ),
                                output_field=BooleanField()
                            )
                        )
                    )

        return context

中的结果user_current_state 的实现:

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)

MailingList 和 MailingListEvent 的实现:

class MailingListEvent(models.Model):

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

    user = models.ForeignKey(User, on_delete=models.CASCADE)
    mailing_list = models.ForeignKey(MailingList,
                                     on_delete=models.CASCADE)


class MailingList(models.Model):
    # This is a user-visible name that can change.
    mailing_list_name = models.CharField(max_length=80, blank=False)
    # This is the unique name that can't change.  It just makes code a
    # bit more readable than referring to the pk id.
    mailing_list_token = models.CharField(max_length=80, blank=False,
                                          unique=True)

SendRecord 的实现:

class TopicBlogEmailSendRecord(models.Model):
    slug = models.SlugField(max_length=90, allow_unicode=True, blank=True)
    mailinglist = models.ForeignKey(MailingList, on_delete=models.PROTECT)
    recipient = models.ForeignKey(User, on_delete=models.PROTECT)

最后是 模板,我像这样访问值:

{% for mailing_list in press_subscription_list %}
    [...]
    <input type="checkbox" name="{{ mailing_list.mailing_list_name }}"
    value="{{ mailing_list.id }}" id="mailing_list_id_{{ mailing_list.id }}"
    {% if mailing_list.is_subbed %} checked {% endif %} />
    [...]
{% endfor %}

Objective

I'm trying to get a queryset that tells me if a user is subscribed to a given mailing list and being able to access that bool using mailing_list.is_subbed

Ideally we would have a queryset where each item has a annotated field "is_subbed" which is either True or False.

Context

For context, this view is going to serve a form with checkboxes that are checked/unchecked depending on the status of the user.
The page is accessible in incognito mode through a url that holds a token which contains 1) The email of the user and 2) The ID of the mail send record (which holds details like the mailing list it's been sent to, details below)

Question

In the current state, the is_subbed function is called only once on the first item, and the resulting bool is annotated to every item, I'd like it to run for each item in the queryset.
How can I do that ? For now if the first item returns True once fed to is_subbed, every checkbox is marked because the annotated field is_subbed is set to True on each item.

Code

Here is my current work :
Summary :

  1. The view
  2. Implementation of function and models used in said view
  3. Snippet I use to access my results in jinja2

views.py

class PressSubscriptionManagementView(TemplateView):
    template_name = "mailing_list/press_subscription_management.html"

    def is_subbed(self, user: User, mailing_list: MailingList) -> bool:
        """
        Check if the user is subbed to the mailing list
        """
        return user_current_state(user, mailing_list).event_type == "sub"

    def get_context_data(self, **kwargs) -> dict:
        context = super().get_context_data(**kwargs)
        email, send_record_id = token_valid(kwargs["token"])
        if email and send_record_id:
            context["user"] = User.objects.get(email=email)
            # In the current state, is_subbed is only called once on the first
            # item in the list. If this call returns True, every checkbox is
            # checked. None otherwise.
            context["press_subscription_list"] = \
                    def get_context_data(self, **kwargs) -> dict:
        context = super().get_context_data(**kwargs)
        email, send_record_id = token_valid(kwargs["token"])
        if email and send_record_id:
            context["user"] = User.objects.get(email=email)
            # In the current state, is_subbed is only called once on the first
            # item in the list. If this call returns True, every checkbox is
            # checked. None otherwise.
            context["press_subscription_list"] = \
                MailingList.objects.filter(
                    mailing_list_type="PR"
                    ).order_by("-id"
                    ).annotate(  # noqa
                        is_subbed=(
                            ExpressionWrapper(
                                Value(
                                    self.is_subbed(
                                        context["user"],
                                        F('mailing_list__id')
                                    ),
                                    output_field=BooleanField()
                                    ),
                                output_field=BooleanField()
                            )
                        )
                    )

        return context

implementation of user_current_state :

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)

Implementation of MailingList and MailingListEvent :

class MailingListEvent(models.Model):

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

    user = models.ForeignKey(User, on_delete=models.CASCADE)
    mailing_list = models.ForeignKey(MailingList,
                                     on_delete=models.CASCADE)


class MailingList(models.Model):
    # This is a user-visible name that can change.
    mailing_list_name = models.CharField(max_length=80, blank=False)
    # This is the unique name that can't change.  It just makes code a
    # bit more readable than referring to the pk id.
    mailing_list_token = models.CharField(max_length=80, blank=False,
                                          unique=True)

And Finally the implementation of SendRecord :

class TopicBlogEmailSendRecord(models.Model):
    slug = models.SlugField(max_length=90, allow_unicode=True, blank=True)
    mailinglist = models.ForeignKey(MailingList, on_delete=models.PROTECT)
    recipient = models.ForeignKey(User, on_delete=models.PROTECT)

In the template, I access the value like this :

{% for mailing_list in press_subscription_list %}
    [...]
    <input type="checkbox" name="{{ mailing_list.mailing_list_name }}"
    value="{{ mailing_list.id }}" id="mailing_list_id_{{ mailing_list.id }}"
    {% if mailing_list.is_subbed %} checked {% endif %} />
    [...]
{% endfor %}

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

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

发布评论

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

评论(1

江城子 2025-01-27 17:01:56

您可以使用 subquery ,与a 有条件的表达

例如,类似的内容:

def get_context_data(self, **kwargs) -> dict:
    ...
    # subquery mailing list events for each of the user's mailing lists
    events = MailingListEvent.objects.filter(
        user=context['user'], mailing_list=models.OuterRef('pk')
    ).order_by('-event_timestamp')
    # annotate using subquery
    annotated_mailing_lists = MailingList.objects.annotate(
        latest_event_type=models.Subquery(events.values('event_type')[:1]),
        is_subbed=models.Case(
            models.When(latest_event_type='sub', then=True),
            default=False,
            output_field=models.BooleanField(),
        )
    )
    context["press_subscription_list"] = annotated_mailing_lists.filter(...)...

注意:

  • 这是您的 mailinglistevent 模型定义 event_type event_timestamp fields(从示例中丢失)。
  • 为了清楚起见,我使用何时(最新_EVENT_TYPE ='sub',... ,但是您可能应该使用 mailinglistevent.eventtype.subscribe 而不是'sub'sub'<< /代码>。

Instead of your is_subbed() method, you could use a Subquery, combined with a conditional expression.

For example, something like this:

def get_context_data(self, **kwargs) -> dict:
    ...
    # subquery mailing list events for each of the user's mailing lists
    events = MailingListEvent.objects.filter(
        user=context['user'], mailing_list=models.OuterRef('pk')
    ).order_by('-event_timestamp')
    # annotate using subquery
    annotated_mailing_lists = MailingList.objects.annotate(
        latest_event_type=models.Subquery(events.values('event_type')[:1]),
        is_subbed=models.Case(
            models.When(latest_event_type='sub', then=True),
            default=False,
            output_field=models.BooleanField(),
        )
    )
    context["press_subscription_list"] = annotated_mailing_lists.filter(...)...

Notes:

  • This assumes your MailingListEvent model defines event_type and event_timestamp fields (which are missing from your example).
  • For clarity, I used When(latest_event_type='sub', ..., but you should probably use MailingListEvent.EventType.SUBSCRIBE instead of 'sub'.
~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文