【问题标题】:Django: Mark as Read "Notifications"Django:标记为已读“通知”
【发布时间】:2017-09-15 21:31:17
【问题描述】:

我正在做一个学校项目。现在任何用户都可以提出问题。

为了在任何用户提出问题时通知所有用户,我创建了一个新应用程序并在提出问题时通过简单的“视图”通知他们。但这只是简单的通知。

一旦用户打开通知选项卡,我如何将它们标记为已读?就像在社交网络上一样!

【问题讨论】:

  • 如果您想在用户打开页面时考虑“已读”通知,那么您需要将您的逻辑放在对页面的调用中。从会话中查找用户,然后更新通知对象。

标签: python django


【解决方案1】:

我建议您使用ContentType 为任何模型制作动态通知。下面这个sn-p是一个如何实现通知系统的例子;

1.在你的models.py

from django.db import models
from django.contrib.auth.models import User
from django.contrib.contenttypes.models import ContentType
from django.contrib.contenttypes.fields import GenericForeignKey
from django.utils.translation import ugettext_lazy as _


class ContentTypeToGetModel(object):

    """
    requires fields:
        - content_type: FK(ContentType)
        - object_id: PositiveIntegerField()
    """

    def get_related_object(self):
        """
        return the related object of content_type.
        eg: <Question: Holisticly grow synergistic best practices>
        """
        # This should return an error: MultipleObjectsReturned
        # return self.content_type.get_object_for_this_type()
        # So, i handle it with this one:
        model_class = self.content_type.model_class()
        return model_class.objects.get(id=self.object_id)

    @property
    def _model_name(self):
        """
        return lowercase of model name.
        eg: `question`, `answer`
        """
        return self.get_related_object()._meta.model_name


class Notification(models.Model, ContentTypeToGetModel):
    # sender = models.ForeignKey(
    #    User, related_name='notification_sender')

    receiver = models.ForeignKey(
        User, related_name='notification_receiver')

    content_type = models.ForeignKey(
        ContentType, related_name='notifications', on_delete=models.CASCADE)

    object_id = models.PositiveIntegerField(_('Object id'))
    content_object = GenericForeignKey('content_type', 'object_id')

    created = models.DateTimeField(auto_now_add=True)
    modified = models.DateTimeField(auto_now=True)

    STATUS_CHOICES = (
        ('reply', _('a reply')),
        ('comment', _('a comment')),
        ('message', _('a message'))
    )
    status = models.CharField(
        _('Status'), max_length=20,
        choices=STATUS_CHOICES, default='comment')

    is_read = models.BooleanField(
        _('Is read?'), default=False)

    def __str__(self):
        title = _('%(receiver)s have a %(status)s in the %(model)s:%(id)s')
        return title % {'receiver': self.receiver.username, 'status': self.status,
                        'model': self._model_name, 'id': self.object_id}

    class Meta:
        verbose_name_plural = _('notifications')
        ordering = ['-created']

2。在你的views.py

from django.views.generic import (ListView, DetailView)
from yourapp.models import Notification


class NotificationListView(ListView):
    model = Notification
    context_object_name = 'notifications'
    paginate_by = 10
    template_name = 'yourapp/notifications.html'

    def get_queryset(self):
        notifications = self.model.objects.filter(receiver=self.request.user)

        # mark as reads if `user` is visit on this page.
        notifications.update(is_read=True)
        return notifications

3。在你的yourapp/notifications.html

{% extends "base.html" %}

{% for notif in notifications %}
  {{ notif }}

  {# for specific is like below #}
  {# `specific_model_name` eg: `comment`, `message`, `post` #}
  {% if notif._model_name == 'specific_model_name' %}
    {# do_stuff #}
  {% endif %}
{% endfor %}

那么,当我创建通知时? 例如:当其他用户在此帖子上向receiver 发送评论时。

from django.contrib.contenttypes.models import ContentType

def send_a_comment(request):
    if request.method == 'POST':
        form = SendCommentForm(request.POST)
        if form.is_valid():
            instance = form.save(commit=False)
            #instance.sender = request.user
            ...
            instance.save()

            receiver = User.objects.filter(email=instance.email).first()
            content_type = ContentType.objects.get(model='comment')

            notif = Notification.objects.create(
                receiver=receiver,
                #sender=request.user,
                content_type=content_type,
                object_id=instance.id,
                status='comment'
            )
            notif.save()

菜单怎么样?喜欢这个 stackoverflow、facebook、instagram 还是其他?

你可以用模板标签来处理它,例如:

# yourapp/templatetags/notification_tags.py

from django import template

from yourapp.models import Notification

register = template.Library()

@register.filter
def has_unread_notif(user):
    notifications = Notification.objects.filter(receiver=user, is_read=False)
    if notifications.exists():
        return True
    return False

还有navs.html 菜单:

{% load notification_tags %}

{% if request.user.is_authenticated %}
  <ul class="authenticated-menu">
    <li>
        <a href="/notifications/">
          {% if request.user|has_unread_notif %}
            <i class="globe red active icon"></i>
          {% else %}
            <i class="globe icon"></i>
          {% endif %}
        </a>
    </li>
  </ul>
{% endif %}

【讨论】:

    【解决方案2】:

    首先你需要 ManyToManyField 用于阅读帖子的用户模块。

    profile.py

    class Profile(models.Model):
    user = models.OneToOneField(User, on_delete=models.CASCADE)
    subscribed = models.ManyToManyField(User, related_name='subscribed', blank=True)
    readed = models.ManyToManyField("blogs.BlogPost", related_name='readed', blank=True)
    updated = models.DateTimeField(auto_now=True)
    created = models.DateTimeField(auto_now_add=True)
    
    def __str__(self):
        return str(self.user.username)
    
    class Meta:
        ordering = ("-created",)
    

    如果你有这样的博客模型:

    class BlogPost(models.Model):
    author = models.ForeignKey(Profile, on_delete=models.CASCADE)
    post_title = models.CharField("Post Title", max_length=150, unique=True)
    post_content = models.TextField("Content")
    created = models.DateTimeField(auto_now_add=True)
    
    def __str__(self):
        return self.post_title
    
    class Meta:
        ordering = ("-created",)
    

    您需要在博客文章视图中创建一个按钮功能。

    def mark_as_read_button(request):
    if request.method == "POST":
        my_profile = Profile.objects.get(user=request.user)
        post = request.POST.get("post_pk")
        obj = BlogPost.objects.get(pk=post)
    
        if obj in my_profile.readed.all():
            my_profile.readed.remove(obj)
        else:
            my_profile.readed.add(obj)
        return redirect(request.META.get("HTTP_REFERER"))
    return redirect("blogs:subscribed-blogs")
    

    然后你可以在模板文件中这样使用:

    {% extends 'base.html' %}
    
    {% block title %}Subscribed Blog Posts{% endblock %}
    
    {% block content %}
    {% for post in posts %}
        <h5 class="card-title">{{ post.post_title }}</h5>
        {% if post in readed %}
            <form action="{% url "blogs:mark_as_read" %}" method="POST">
                {% csrf_token %}
                <input type="hidden" name="post_pk" value={{ post.pk }}>
                <button type="submit" class="btn btn-danger btn-sm">Mark as unread</button>
            </form>
        {% else %}
            <form action="{% url "blogs:mark_as_read" %}" method="POST">
                {% csrf_token %}
                <input type="hidden" name="post_pk" value={{ post.pk }}>
                <button type="submit" class="btn btn-success btn-sm">Mark as read</button>
            </form>
        {% endif %}
        <p class="card-text">{{ post.created }}</p>
        <p class="card-body">{{ post.post_content }}</p>
        <hr>
    {% endfor %}
    {% endblock %}
    

    良好的编码。

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 2021-07-19
      • 1970-01-01
      • 2017-10-04
      • 2012-01-05
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2020-10-16
      相关资源
      最近更新 更多