GitHub notifications alike app for Django providing comprehensive activity tracking and notification features.
—
The foundational notification models, queryset methods, and signal handling that power the entire django-notifications-hq system. This includes the main Notification model with fields for actor, verb, target, timestamps, and read status.
The main notification model that extends AbstractNotification with additional humanization methods for displaying time information.
class Notification(AbstractNotification):
def naturalday(self):
"""
Return human-readable day format for notification timestamp.
Returns:
str: 'today', 'yesterday', or date string
"""
def naturaltime(self):
"""
Return human-readable relative time for notification timestamp.
Returns:
str: '2 hours ago', 'just now', etc.
"""
class Meta(AbstractNotification.Meta):
abstract = False
swappable = swappable_setting('notifications', 'Notification')The base notification model containing all core functionality for activity tracking with actor-verb-object patterns.
class AbstractNotification(models.Model):
"""
Activity model describing actor acting out a verb on optional target.
Based on Activity Streams specification.
Format patterns:
- <actor> <verb> <time>
- <actor> <verb> <target> <time>
- <actor> <verb> <action_object> <target> <time>
"""
# Core fields
level = models.CharField(max_length=20, choices=LEVELS, default='info')
recipient = models.ForeignKey(settings.AUTH_USER_MODEL, on_delete=models.CASCADE)
unread = models.BooleanField(default=True, db_index=True)
verb = models.CharField(max_length=255)
description = models.TextField(blank=True, null=True)
timestamp = models.DateTimeField(default=timezone.now, db_index=True)
public = models.BooleanField(default=True, db_index=True)
deleted = models.BooleanField(default=False, db_index=True)
emailed = models.BooleanField(default=False, db_index=True)
data = JSONField(blank=True, null=True)
# Generic foreign key fields for actor
actor_content_type = models.ForeignKey(ContentType, on_delete=models.CASCADE)
actor_object_id = models.CharField(max_length=255)
actor = GenericForeignKey('actor_content_type', 'actor_object_id')
# Generic foreign key fields for target (optional)
target_content_type = models.ForeignKey(ContentType, on_delete=models.CASCADE, blank=True, null=True)
target_object_id = models.CharField(max_length=255, blank=True, null=True)
target = GenericForeignKey('target_content_type', 'target_object_id')
# Generic foreign key fields for action object (optional)
action_object_content_type = models.ForeignKey(ContentType, on_delete=models.CASCADE, blank=True, null=True)
action_object_object_id = models.CharField(max_length=255, blank=True, null=True)
action_object = GenericForeignKey('action_object_content_type', 'action_object_object_id')
objects = NotificationQuerySet.as_manager()
def __str__(self):
"""
String representation following activity stream patterns.
Returns:
str: Formatted notification string with actor, verb, objects, and time
"""
def timesince(self, now=None):
"""
Time since notification creation using Django's timesince utility.
Args:
now (datetime, optional): Reference time for calculation
Returns:
str: Relative time string
"""
@property
def slug(self):
"""
URL-safe slug for notification identification.
Returns:
int: Notification ID converted to URL slug
"""
def mark_as_read(self):
"""Mark notification as read if currently unread."""
def mark_as_unread(self):
"""Mark notification as unread if currently read."""
def actor_object_url(self):
"""
Generate admin URL for the actor object.
Returns:
str: HTML link to admin change page or plain ID
"""
def action_object_url(self):
"""
Generate admin URL for the action object.
Returns:
str: HTML link to admin change page or plain ID
"""
def target_object_url(self):
"""
Generate admin URL for the target object.
Returns:
str: HTML link to admin change page or plain ID
"""
class Meta:
abstract = True
ordering = ('-timestamp',)
index_together = ('recipient', 'unread')
verbose_name = 'Notification'
verbose_name_plural = 'Notifications'Custom queryset providing notification-specific filtering and bulk operations for efficient database queries and notification management.
class NotificationQuerySet(models.query.QuerySet):
"""Custom queryset with notification-specific methods."""
def unsent(self):
"""
Filter notifications that haven't been emailed.
Returns:
QuerySet: Notifications with emailed=False
"""
def sent(self):
"""
Filter notifications that have been emailed.
Returns:
QuerySet: Notifications with emailed=True
"""
def unread(self, include_deleted=False):
"""
Filter unread notifications.
Args:
include_deleted (bool): Whether to include soft-deleted notifications
Returns:
QuerySet: Unread notifications
"""
def read(self, include_deleted=False):
"""
Filter read notifications.
Args:
include_deleted (bool): Whether to include soft-deleted notifications
Returns:
QuerySet: Read notifications
"""
def mark_all_as_read(self, recipient=None):
"""
Mark all notifications in queryset as read.
Args:
recipient (User, optional): Filter by specific recipient
Returns:
int: Number of notifications updated
"""
def mark_all_as_unread(self, recipient=None):
"""
Mark all notifications in queryset as unread.
Args:
recipient (User, optional): Filter by specific recipient
Returns:
int: Number of notifications updated
"""
def deleted(self):
"""
Filter soft-deleted notifications.
Returns:
QuerySet: Notifications with deleted=True
Raises:
ImproperlyConfigured: If SOFT_DELETE setting is False
"""
def active(self):
"""
Filter active (non-deleted) notifications.
Returns:
QuerySet: Notifications with deleted=False
Raises:
ImproperlyConfigured: If SOFT_DELETE setting is False
"""
def mark_all_as_deleted(self, recipient=None):
"""
Soft delete all notifications in queryset.
Args:
recipient (User, optional): Filter by specific recipient
Returns:
int: Number of notifications updated
Raises:
ImproperlyConfigured: If SOFT_DELETE setting is False
"""
def mark_all_as_active(self, recipient=None):
"""
Restore all notifications in queryset from soft deletion.
Args:
recipient (User, optional): Filter by specific recipient
Returns:
int: Number of notifications updated
Raises:
ImproperlyConfigured: If SOFT_DELETE setting is False
"""
def mark_as_unsent(self, recipient=None):
"""
Mark notifications as not emailed.
Args:
recipient (User, optional): Filter by specific recipient
Returns:
int: Number of notifications updated
"""
def mark_as_sent(self, recipient=None):
"""
Mark notifications as emailed.
Args:
recipient (User, optional): Filter by specific recipient
Returns:
int: Number of notifications updated
"""LEVELS = Choices('success', 'info', 'warning', 'error')from django.contrib.auth.models import User
from notifications.models import Notification
# Get user notifications
user = User.objects.get(username='jane')
# Query unread notifications
unread = user.notifications.unread()
# Query read notifications
read = user.notifications.read()
# Mark all as read
user.notifications.mark_all_as_read()
# Query by level
info_notifications = user.notifications.filter(level='info')
# Check if notification exists
has_unread = user.notifications.unread().exists()
# Get notification count
unread_count = user.notifications.unread().count()
# Access notification details
for notification in user.notifications.all()[:5]:
print(f"{notification.actor} {notification.verb}")
print(f"Time: {notification.naturaltime()}")
print(f"Description: {notification.description}")
if notification.target:
print(f"Target: {notification.target}")# Get specific notification
notification = Notification.objects.get(id=123)
# Mark as read/unread
notification.mark_as_read()
notification.mark_as_unread()
# Access related objects
actor = notification.actor
target = notification.target
action_object = notification.action_object
# Get formatted time
time_ago = notification.timesince()
natural_day = notification.naturalday()
# Check properties
is_unread = notification.unread
is_public = notification.public
level = notification.levelInstall with Tessl CLI
npx tessl i tessl/pypi-django-notifications-hq