GitHub notifications alike app for Django providing comprehensive activity tracking and notification features.
—
Django template tags for displaying notification counts, status indicators, and live-updating notification interfaces with JavaScript integration for real-time updates.
Django template tags for displaying notification information and generating JavaScript for live updates.
@register.simple_tag(takes_context=True)
def notifications_unread(context):
"""
Get unread notification count for current user (cached).
Args:
context: Django template context containing request and user
Returns:
int: Number of unread notifications for authenticated user, 0 for anonymous
Template usage:
{% notifications_unread %}
{% notifications_unread as count %}
"""
@register.filter
def has_notification(user):
"""
Check if user has any unread notifications.
Args:
user: Django User object
Returns:
bool: True if user has unread notifications, False otherwise
Template usage:
{{ user|has_notification }}
{% if user|has_notification %}...{% endif %}
"""
@register.simple_tag
def register_notify_callbacks(
badge_class='live_notify_badge',
menu_class='live_notify_list',
refresh_period=15,
callbacks='',
api_name='list',
fetch=5,
nonce=None,
mark_as_read=False
):
"""
Generate JavaScript code for live notification functionality.
Args:
badge_class (str): CSS class for notification badge elements
menu_class (str): CSS class for notification list elements
refresh_period (int): Update interval in seconds
callbacks (str): Comma-separated callback function names
api_name (str): API endpoint type ('list' or 'count')
fetch (int): Number of notifications to fetch
nonce (str, optional): CSP nonce for script tag
mark_as_read (bool): Auto-mark notifications as read when fetched
Returns:
str: HTML script tag with JavaScript configuration
Template usage:
{% register_notify_callbacks %}
{% register_notify_callbacks refresh_period=30 fetch=10 %}
"""
@register.simple_tag(takes_context=True)
def live_notify_badge(context, badge_class='live_notify_badge'):
"""
Render live notification count badge.
Args:
context: Django template context
badge_class (str): CSS class for badge element
Returns:
str: HTML span element with current unread count
Template usage:
{% live_notify_badge %}
{% live_notify_badge "my-badge-class" %}
"""
@register.simple_tag
def live_notify_list(list_class='live_notify_list'):
"""
Render empty notification list container for JavaScript population.
Args:
list_class (str): CSS class for list container
Returns:
str: Empty HTML ul element for notifications
Template usage:
{% live_notify_list %}
{% live_notify_list "my-list-class" %}
"""def get_cached_notification_unread_count(user):
"""
Get cached unread notification count for user.
Args:
user: Django User object
Returns:
int: Cached unread notification count
Note:
Uses Django cache with CACHE_TIMEOUT setting for cache duration
"""<!-- Load the template tags -->
{% load notifications_tags %}
<!-- Simple unread count display -->
<div class="notifications">
Notifications ({% notifications_unread %})
</div>
<!-- Store count in variable -->
{% notifications_unread as unread_count %}
{% if unread_count > 0 %}
<div class="notification-alert">
You have {{ unread_count }} unread notification{{ unread_count|pluralize }}
</div>
{% endif %}
<!-- Check if user has notifications -->
{% if user|has_notification %}
<span class="notification-indicator">●</span>
{% endif %}{% load notifications_tags %}
<nav class="navbar">
<ul class="nav-items">
<li class="nav-item">
<a href="{% url 'notifications:unread' %}" class="nav-link">
<i class="icon-bell"></i>
Notifications
{% notifications_unread as count %}
{% if count > 0 %}
<span class="badge badge-danger">{{ count }}</span>
{% endif %}
</a>
</li>
</ul>
</nav>
<!-- Dropdown notification menu -->
<div class="notification-dropdown">
<button class="dropdown-toggle" data-toggle="dropdown">
<i class="icon-bell"></i>
{% if user|has_notification %}
<span class="notification-dot"></span>
{% endif %}
</button>
<div class="dropdown-menu">
<div class="dropdown-header">
Notifications
<span class="count">{% notifications_unread %}</span>
</div>
<div class="notification-list" id="notification-dropdown-list">
<!-- Will be populated by JavaScript -->
</div>
<div class="dropdown-footer">
<a href="{% url 'notifications:all' %}">View All</a>
<a href="{% url 'notifications:mark_all_as_read' %}">Mark All as Read</a>
</div>
</div>
</div>{% load notifications_tags %}
<!DOCTYPE html>
<html>
<head>
<title>My App</title>
<style>
.live_notify_badge {
background: red;
color: white;
border-radius: 50%;
padding: 2px 6px;
font-size: 12px;
position: absolute;
top: -8px;
right: -8px;
}
.live_notify_list {
max-height: 300px;
overflow-y: auto;
border: 1px solid #ddd;
border-radius: 4px;
}
.notification-item {
padding: 10px;
border-bottom: 1px solid #eee;
cursor: pointer;
}
.notification-item:hover {
background-color: #f5f5f5;
}
</style>
</head>
<body>
<div class="header">
<div class="notification-container" style="position: relative;">
<i class="icon-bell"></i>
<!-- Live badge that updates automatically -->
{% live_notify_badge %}
</div>
</div>
<div class="notification-panel">
<h3>Recent Notifications</h3>
<!-- Live list that updates automatically -->
{% live_notify_list %}
</div>
<!-- Register JavaScript for live updates -->
{% register_notify_callbacks refresh_period=20 fetch=8 %}
<!-- Custom callback functions -->
<script>
function onNotificationUpdate(data) {
console.log('Notifications updated:', data);
// Custom handling when notifications are updated
if (data.unread_count > 0) {
document.title = `(${data.unread_count}) My App`;
} else {
document.title = 'My App';
}
}
function onNotificationClick(notification) {
console.log('Notification clicked:', notification);
// Handle notification click
window.location.href = '/notification/' + notification.id;
}
</script>
<!-- Register callbacks -->
{% register_notify_callbacks callbacks="onNotificationUpdate,onNotificationClick" %}
</body>
</html>{% load notifications_tags %}
<!-- Custom CSS classes and configuration -->
{% register_notify_callbacks
badge_class="my-notification-badge"
menu_class="my-notification-menu"
refresh_period=30
api_name="list"
fetch=15
mark_as_read=True
%}
<!-- Multiple notification areas -->
<div class="sidebar">
<h4>Quick Notifications</h4>
{% live_notify_list "sidebar-notifications" %}
</div>
<div class="main-content">
<div class="toolbar">
<span class="notification-icon">
🔔
{% live_notify_badge "toolbar-badge" %}
</span>
</div>
</div>
<!-- Configure different areas with different settings -->
<script>
// Custom configuration for sidebar
{% register_notify_callbacks
menu_class="sidebar-notifications"
fetch=5
refresh_period=60
%}
// Custom configuration for toolbar
{% register_notify_callbacks
badge_class="toolbar-badge"
api_name="count"
refresh_period=15
%}
</script>{% load notifications_tags %}
<!-- Generate nonce for CSP -->
{% csrf_token %}
<script nonce="{{ csp_nonce }}">
const csrfToken = document.querySelector('[name=csrfmiddlewaretoken]').value;
</script>
<!-- Pass nonce to template tag -->
{% register_notify_callbacks nonce=csp_nonce %}{% load notifications_tags %}
<!-- Only load live notifications for authenticated users -->
{% if user.is_authenticated %}
<div class="user-notifications">
{% if user|has_notification %}
<div class="notification-alert">
You have new notifications!
{% live_notify_badge %}
</div>
{% endif %}
{% live_notify_list %}
{% register_notify_callbacks %}
</div>
{% else %}
<div class="guest-message">
<a href="{% url 'login' %}">Login</a> to see notifications
</div>
{% endif %}{% load notifications_tags %}
<!-- Bootstrap navbar with notification dropdown -->
<nav class="navbar navbar-expand-lg navbar-dark bg-dark">
<div class="navbar-nav ms-auto">
<div class="nav-item dropdown">
<a class="nav-link dropdown-toggle position-relative" href="#" data-bs-toggle="dropdown">
<i class="fas fa-bell"></i>
{% live_notify_badge "position-absolute top-0 start-100 translate-middle badge rounded-pill bg-danger" %}
</a>
<div class="dropdown-menu dropdown-menu-end" style="width: 300px;">
<h6 class="dropdown-header">
Notifications
<span class="badge bg-primary ms-2">{% notifications_unread %}</span>
</h6>
<div class="dropdown-divider"></div>
<div class="notification-container" style="max-height: 400px; overflow-y: auto;">
{% live_notify_list "list-unstyled" %}
</div>
<div class="dropdown-divider"></div>
<div class="dropdown-item-text text-center">
<a href="{% url 'notifications:all' %}" class="btn btn-sm btn-outline-primary me-2">
View All
</a>
<a href="{% url 'notifications:mark_all_as_read' %}" class="btn btn-sm btn-outline-secondary">
Mark All Read
</a>
</div>
</div>
</div>
</div>
</nav>
<!-- Bootstrap styling for notifications -->
<style>
.list-unstyled .notification-item {
padding: 0.5rem 1rem;
border-bottom: 1px solid #dee2e6;
text-decoration: none;
color: inherit;
display: block;
}
.list-unstyled .notification-item:hover {
background-color: #f8f9fa;
}
.list-unstyled .notification-item:last-child {
border-bottom: none;
}
</style>
{% register_notify_callbacks
menu_class="list-unstyled"
badge_class="position-absolute top-0 start-100 translate-middle badge rounded-pill bg-danger"
refresh_period=25
fetch=10
%}{% load notifications_tags %}
<!-- Override default notification rendering -->
<script>
function customNotificationRenderer(notifications) {
const container = document.querySelector('.live_notify_list');
container.innerHTML = '';
notifications.forEach(notification => {
const item = document.createElement('div');
item.className = 'notification-item';
item.innerHTML = `
<div class="notification-avatar">
<img src="/static/img/avatar-placeholder.png" alt="Avatar">
</div>
<div class="notification-content">
<div class="notification-text">
<strong>${notification.actor}</strong> ${notification.verb}
${notification.target ? notification.target : ''}
</div>
<div class="notification-time">
${formatTimeAgo(notification.timestamp)}
</div>
</div>
<div class="notification-actions">
<button onclick="markAsRead(${notification.slug})" class="btn-sm">✓</button>
<button onclick="deleteNotification(${notification.slug})" class="btn-sm">✗</button>
</div>
`;
container.appendChild(item);
});
}
// Override the default render function
window.renderNotifications = customNotificationRenderer;
</script>
{% live_notify_list %}
{% register_notify_callbacks callbacks="customNotificationRenderer" %}Internal helper functions used by template tags for user context handling and caching.
def get_cached_notification_unread_count(user):
"""
Get cached unread notification count for user with configurable timeout.
Args:
user: Django User object
Returns:
int: Number of unread notifications (cached result)
Cache key: 'cache_notification_unread_count'
Cache timeout: Configured via CACHE_TIMEOUT setting
"""
def user_context(context):
"""
Extract authenticated user from template context.
Args:
context: Django template context dict
Returns:
User: Authenticated user object or None for anonymous/missing users
Handles:
- Missing user in context
- Anonymous users (both Django < 1.11 and >= 1.11 syntax)
- Authenticated users
"""Install with Tessl CLI
npx tessl i tessl/pypi-django-notifications-hq