GitHub notifications alike app for Django providing comprehensive activity tracking and notification features.
—
Pre-built Django views for displaying, managing, and interacting with notifications through web interfaces. Includes list views, mark-as-read functionality, and deletion with proper URL routing and login requirements.
Django class-based views for displaying notifications with pagination and authentication requirements.
class NotificationViewList(ListView):
"""Base list view for notifications with common configuration."""
template_name = 'notifications/list.html'
context_object_name = 'notifications'
paginate_by = settings.PAGINATE_BY # From configuration
@method_decorator(login_required)
def dispatch(self, request, *args, **kwargs): ...
class AllNotificationsList(NotificationViewList):
"""List view showing all user notifications."""
def get_queryset(self):
"""
Returns all notifications for authenticated user.
Respects SOFT_DELETE setting for filtering.
Returns:
QuerySet: User's notifications (active or all based on settings)
"""
class UnreadNotificationsList(NotificationViewList):
"""List view showing only unread notifications."""
def get_queryset(self):
"""
Returns only unread notifications for authenticated user.
Returns:
QuerySet: User's unread notifications
"""View functions for marking notifications as read with proper authentication and redirect handling.
@login_required
def mark_all_as_read(request):
"""
Mark all user notifications as read and redirect.
Args:
request: Django HTTP request object
Query Parameters:
next (str, optional): URL to redirect to after marking as read
Returns:
HttpResponseRedirect: Redirect to 'next' URL or notifications:unread
"""
@login_required
def mark_as_read(request, slug=None):
"""
Mark specific notification as read and redirect.
Args:
request: Django HTTP request object
slug (str): Notification ID slug
Query Parameters:
next (str, optional): URL to redirect to after marking as read
Returns:
HttpResponseRedirect: Redirect to 'next' URL or notifications:unread
Raises:
Http404: If notification not found or doesn't belong to user
"""
@login_required
def mark_as_unread(request, slug=None):
"""
Mark specific notification as unread and redirect.
Args:
request: Django HTTP request object
slug (str): Notification ID slug
Query Parameters:
next (str, optional): URL to redirect to after marking as unread
Returns:
HttpResponseRedirect: Redirect to 'next' URL or notifications:unread
Raises:
Http404: If notification not found or doesn't belong to user
"""View function for deleting notifications with support for both hard and soft deletion.
@login_required
def delete(request, slug=None):
"""
Delete notification (hard or soft based on settings) and redirect.
Args:
request: Django HTTP request object
slug (str): Notification ID slug
Query Parameters:
next (str, optional): URL to redirect to after deletion
Returns:
HttpResponseRedirect: Redirect to 'next' URL or notifications:all
Raises:
Http404: If notification not found or doesn't belong to user
Note:
Deletion behavior depends on SOFT_DELETE setting:
- If True: Sets deleted=True and saves
- If False: Permanently deletes from database
"""Pre-configured URL patterns for notification views with proper namespacing.
# In notifications/urls.py
app_name = 'notifications'
urlpatterns = [
# List views
re_path(r'^$', views.AllNotificationsList.as_view(), name='all'),
re_path(r'^unread/$', views.UnreadNotificationsList.as_view(), name='unread'),
# Mark as read/unread
re_path(r'^mark-all-as-read/$', views.mark_all_as_read, name='mark_all_as_read'),
re_path(r'^mark-as-read/(?P<slug>\d+)/$', views.mark_as_read, name='mark_as_read'),
re_path(r'^mark-as-unread/(?P<slug>\d+)/$', views.mark_as_unread, name='mark_as_unread'),
# Delete
re_path(r'^delete/(?P<slug>\d+)/$', views.delete, name='delete'),
# API endpoints (see API Endpoints documentation)
re_path(r'^api/unread_count/$', views.live_unread_notification_count, name='live_unread_notification_count'),
re_path(r'^api/all_count/$', views.live_all_notification_count, name='live_all_notification_count'),
re_path(r'^api/unread_list/$', views.live_unread_notification_list, name='live_unread_notification_list'),
re_path(r'^api/all_list/', views.live_all_notification_list, name='live_all_notification_list'),
]# In your main urls.py
from django.urls import path, include
import notifications.urls
urlpatterns = [
# Other URL patterns...
path('inbox/notifications/', include(notifications.urls, namespace='notifications')),
# More URL patterns...
]<!-- In your base template -->
<div class="notification-menu">
<a href="{% url 'notifications:unread' %}">
Unread Notifications
<span class="badge">{% notifications_unread %}</span>
</a>
<ul class="dropdown-menu">
<li><a href="{% url 'notifications:all' %}">All Notifications</a></li>
<li><a href="{% url 'notifications:unread' %}">Unread</a></li>
<li><a href="{% url 'notifications:mark_all_as_read' %}">Mark All as Read</a></li>
</ul>
</div>
<!-- In notification list template -->
{% for notification in notifications %}
<div class="notification {% if notification.unread %}unread{% endif %}">
<div class="notification-content">
{{ notification.description }}
<small>{{ notification.naturaltime }}</small>
</div>
<div class="notification-actions">
{% if notification.unread %}
<a href="{% url 'notifications:mark_as_read' notification.slug %}">Mark as Read</a>
{% else %}
<a href="{% url 'notifications:mark_as_unread' notification.slug %}">Mark as Unread</a>
{% endif %}
<a href="{% url 'notifications:delete' notification.slug %}"
onclick="return confirm('Delete this notification?')">Delete</a>
</div>
</div>
{% endfor %}
<!-- Pagination -->
{% if is_paginated %}
<div class="pagination">
{% if page_obj.has_previous %}
<a href="?page={{ page_obj.previous_page_number }}">« Previous</a>
{% endif %}
<span class="current">
Page {{ page_obj.number }} of {{ page_obj.paginator.num_pages }}
</span>
{% if page_obj.has_next %}
<a href="?page={{ page_obj.next_page_number }}">Next »</a>
{% endif %}
</div>
{% endif %}Create your own notification templates by placing them in your templates directory:
<!-- templates/notifications/list.html -->
{% extends "base.html" %}
{% load notifications_tags %}
{% block title %}Notifications{% endblock %}
{% block content %}
<div class="notifications-container">
<h1>
Notifications
{% if view.get_queryset|length > 0 %}
<span class="count">({{ view.get_queryset|length }})</span>
{% endif %}
</h1>
{% if notifications %}
<div class="notification-actions">
<a href="{% url 'notifications:mark_all_as_read' %}" class="btn btn-primary">
Mark All as Read
</a>
</div>
<div class="notifications-list">
{% for notification in notifications %}
{% include "notifications/notice.html" with notification=notification %}
{% endfor %}
</div>
<!-- Pagination controls -->
{% if is_paginated %}
<!-- Pagination HTML here -->
{% endif %}
{% else %}
<div class="no-notifications">
<p>No notifications found.</p>
<a href="{% url 'notifications:all' %}">View All Notifications</a>
</div>
{% endif %}
</div>
{% endblock %}<!-- For AJAX-based mark as read -->
<script>
function markAsRead(notificationSlug) {
fetch(`/inbox/notifications/mark-as-read/${notificationSlug}/`, {
method: 'POST',
headers: {
'X-CSRFToken': document.querySelector('[name=csrfmiddlewaretoken]').value,
},
})
.then(response => {
if (response.ok) {
// Update UI to show notification as read
document.querySelector(`[data-notification="${notificationSlug}"]`)
.classList.remove('unread');
}
})
.catch(error => console.error('Error:', error));
}
function deleteNotification(notificationSlug) {
if (confirm('Are you sure you want to delete this notification?')) {
fetch(`/inbox/notifications/delete/${notificationSlug}/`, {
method: 'POST',
headers: {
'X-CSRFToken': document.querySelector('[name=csrfmiddlewaretoken]').value,
},
})
.then(response => {
if (response.ok) {
// Remove notification from DOM
document.querySelector(`[data-notification="${notificationSlug}"]`)
.remove();
}
})
.catch(error => console.error('Error:', error));
}
}
</script>All view functions support the next query parameter for custom redirects:
<!-- Redirect back to current page after action -->
<a href="{% url 'notifications:mark_as_read' notification.slug %}?next={{ request.get_full_path|urlencode }}">
Mark as Read
</a>
<!-- Redirect to specific page -->
<a href="{% url 'notifications:delete' notification.slug %}?next={% url 'dashboard' %}">
Delete
</a>All views require user authentication. For non-authenticated users, they will be redirected to the login page:
# The @login_required decorator handles this automatically
# Users will be redirected to settings.LOGIN_URLInstall with Tessl CLI
npx tessl i tessl/pypi-django-notifications-hq