mirror of
https://github.com/pacnpal/thrilltrack-explorer.git
synced 2025-12-21 09:31:13 -05:00
Add email templates for user notifications and account management
- Created a base email template (base.html) for consistent styling across all emails. - Added moderation approval email template (moderation_approved.html) to notify users of approved submissions. - Added moderation rejection email template (moderation_rejected.html) to inform users of required changes for their submissions. - Created password reset email template (password_reset.html) for users requesting to reset their passwords. - Developed a welcome email template (welcome.html) to greet new users and provide account details and tips for using ThrillWiki.
This commit is contained in:
252
django/apps/entities/signals.py
Normal file
252
django/apps/entities/signals.py
Normal file
@@ -0,0 +1,252 @@
|
||||
"""
|
||||
Signal handlers for automatic search vector updates.
|
||||
|
||||
These signals ensure search vectors stay synchronized with model changes,
|
||||
eliminating the need for manual re-indexing.
|
||||
|
||||
Signal handlers are only active when using PostgreSQL with PostGIS backend.
|
||||
"""
|
||||
from django.db.models.signals import post_save, pre_save
|
||||
from django.dispatch import receiver
|
||||
from django.conf import settings
|
||||
from django.contrib.postgres.search import SearchVector
|
||||
|
||||
from apps.entities.models import Company, RideModel, Park, Ride
|
||||
|
||||
# Only register signals if using PostgreSQL with PostGIS
|
||||
_using_postgis = 'postgis' in settings.DATABASES['default']['ENGINE']
|
||||
|
||||
|
||||
if _using_postgis:
|
||||
|
||||
# ==========================================
|
||||
# Company Signals
|
||||
# ==========================================
|
||||
|
||||
@receiver(post_save, sender=Company)
|
||||
def update_company_search_vector(sender, instance, created, **kwargs):
|
||||
"""
|
||||
Update search vector when company is created or updated.
|
||||
|
||||
Search vector includes:
|
||||
- name (weight A)
|
||||
- description (weight B)
|
||||
"""
|
||||
# Update the company's own search vector
|
||||
Company.objects.filter(pk=instance.pk).update(
|
||||
search_vector=(
|
||||
SearchVector('name', weight='A', config='english') +
|
||||
SearchVector('description', weight='B', config='english')
|
||||
)
|
||||
)
|
||||
|
||||
|
||||
@receiver(pre_save, sender=Company)
|
||||
def check_company_name_change(sender, instance, **kwargs):
|
||||
"""
|
||||
Track if company name is changing to trigger cascading updates.
|
||||
|
||||
Stores the old name on the instance for use in post_save signal.
|
||||
"""
|
||||
if instance.pk:
|
||||
try:
|
||||
old_instance = Company.objects.get(pk=instance.pk)
|
||||
instance._old_name = old_instance.name
|
||||
except Company.DoesNotExist:
|
||||
instance._old_name = None
|
||||
else:
|
||||
instance._old_name = None
|
||||
|
||||
|
||||
@receiver(post_save, sender=Company)
|
||||
def cascade_company_name_updates(sender, instance, created, **kwargs):
|
||||
"""
|
||||
When company name changes, update search vectors for related objects.
|
||||
|
||||
Updates:
|
||||
- All RideModels from this manufacturer
|
||||
- All Rides from this manufacturer
|
||||
"""
|
||||
# Skip if this is a new company or name hasn't changed
|
||||
if created or not hasattr(instance, '_old_name'):
|
||||
return
|
||||
|
||||
old_name = getattr(instance, '_old_name', None)
|
||||
if old_name == instance.name:
|
||||
return
|
||||
|
||||
# Update all RideModels from this manufacturer
|
||||
ride_models = RideModel.objects.filter(manufacturer=instance)
|
||||
for ride_model in ride_models:
|
||||
RideModel.objects.filter(pk=ride_model.pk).update(
|
||||
search_vector=(
|
||||
SearchVector('name', weight='A', config='english') +
|
||||
SearchVector('manufacturer__name', weight='A', config='english') +
|
||||
SearchVector('description', weight='B', config='english')
|
||||
)
|
||||
)
|
||||
|
||||
# Update all Rides from this manufacturer
|
||||
rides = Ride.objects.filter(manufacturer=instance)
|
||||
for ride in rides:
|
||||
Ride.objects.filter(pk=ride.pk).update(
|
||||
search_vector=(
|
||||
SearchVector('name', weight='A', config='english') +
|
||||
SearchVector('park__name', weight='A', config='english') +
|
||||
SearchVector('manufacturer__name', weight='B', config='english') +
|
||||
SearchVector('description', weight='B', config='english')
|
||||
)
|
||||
)
|
||||
|
||||
|
||||
# ==========================================
|
||||
# Park Signals
|
||||
# ==========================================
|
||||
|
||||
@receiver(post_save, sender=Park)
|
||||
def update_park_search_vector(sender, instance, created, **kwargs):
|
||||
"""
|
||||
Update search vector when park is created or updated.
|
||||
|
||||
Search vector includes:
|
||||
- name (weight A)
|
||||
- description (weight B)
|
||||
"""
|
||||
# Update the park's own search vector
|
||||
Park.objects.filter(pk=instance.pk).update(
|
||||
search_vector=(
|
||||
SearchVector('name', weight='A', config='english') +
|
||||
SearchVector('description', weight='B', config='english')
|
||||
)
|
||||
)
|
||||
|
||||
|
||||
@receiver(pre_save, sender=Park)
|
||||
def check_park_name_change(sender, instance, **kwargs):
|
||||
"""
|
||||
Track if park name is changing to trigger cascading updates.
|
||||
|
||||
Stores the old name on the instance for use in post_save signal.
|
||||
"""
|
||||
if instance.pk:
|
||||
try:
|
||||
old_instance = Park.objects.get(pk=instance.pk)
|
||||
instance._old_name = old_instance.name
|
||||
except Park.DoesNotExist:
|
||||
instance._old_name = None
|
||||
else:
|
||||
instance._old_name = None
|
||||
|
||||
|
||||
@receiver(post_save, sender=Park)
|
||||
def cascade_park_name_updates(sender, instance, created, **kwargs):
|
||||
"""
|
||||
When park name changes, update search vectors for related rides.
|
||||
|
||||
Updates:
|
||||
- All Rides in this park
|
||||
"""
|
||||
# Skip if this is a new park or name hasn't changed
|
||||
if created or not hasattr(instance, '_old_name'):
|
||||
return
|
||||
|
||||
old_name = getattr(instance, '_old_name', None)
|
||||
if old_name == instance.name:
|
||||
return
|
||||
|
||||
# Update all Rides in this park
|
||||
rides = Ride.objects.filter(park=instance)
|
||||
for ride in rides:
|
||||
Ride.objects.filter(pk=ride.pk).update(
|
||||
search_vector=(
|
||||
SearchVector('name', weight='A', config='english') +
|
||||
SearchVector('park__name', weight='A', config='english') +
|
||||
SearchVector('manufacturer__name', weight='B', config='english') +
|
||||
SearchVector('description', weight='B', config='english')
|
||||
)
|
||||
)
|
||||
|
||||
|
||||
# ==========================================
|
||||
# RideModel Signals
|
||||
# ==========================================
|
||||
|
||||
@receiver(post_save, sender=RideModel)
|
||||
def update_ride_model_search_vector(sender, instance, created, **kwargs):
|
||||
"""
|
||||
Update search vector when ride model is created or updated.
|
||||
|
||||
Search vector includes:
|
||||
- name (weight A)
|
||||
- manufacturer__name (weight A)
|
||||
- description (weight B)
|
||||
"""
|
||||
RideModel.objects.filter(pk=instance.pk).update(
|
||||
search_vector=(
|
||||
SearchVector('name', weight='A', config='english') +
|
||||
SearchVector('manufacturer__name', weight='A', config='english') +
|
||||
SearchVector('description', weight='B', config='english')
|
||||
)
|
||||
)
|
||||
|
||||
|
||||
@receiver(pre_save, sender=RideModel)
|
||||
def check_ride_model_manufacturer_change(sender, instance, **kwargs):
|
||||
"""
|
||||
Track if ride model manufacturer is changing.
|
||||
|
||||
Stores the old manufacturer on the instance for use in post_save signal.
|
||||
"""
|
||||
if instance.pk:
|
||||
try:
|
||||
old_instance = RideModel.objects.get(pk=instance.pk)
|
||||
instance._old_manufacturer = old_instance.manufacturer
|
||||
except RideModel.DoesNotExist:
|
||||
instance._old_manufacturer = None
|
||||
else:
|
||||
instance._old_manufacturer = None
|
||||
|
||||
|
||||
# ==========================================
|
||||
# Ride Signals
|
||||
# ==========================================
|
||||
|
||||
@receiver(post_save, sender=Ride)
|
||||
def update_ride_search_vector(sender, instance, created, **kwargs):
|
||||
"""
|
||||
Update search vector when ride is created or updated.
|
||||
|
||||
Search vector includes:
|
||||
- name (weight A)
|
||||
- park__name (weight A)
|
||||
- manufacturer__name (weight B)
|
||||
- description (weight B)
|
||||
"""
|
||||
Ride.objects.filter(pk=instance.pk).update(
|
||||
search_vector=(
|
||||
SearchVector('name', weight='A', config='english') +
|
||||
SearchVector('park__name', weight='A', config='english') +
|
||||
SearchVector('manufacturer__name', weight='B', config='english') +
|
||||
SearchVector('description', weight='B', config='english')
|
||||
)
|
||||
)
|
||||
|
||||
|
||||
@receiver(pre_save, sender=Ride)
|
||||
def check_ride_relationships_change(sender, instance, **kwargs):
|
||||
"""
|
||||
Track if ride park or manufacturer are changing.
|
||||
|
||||
Stores old values on the instance for use in post_save signal.
|
||||
"""
|
||||
if instance.pk:
|
||||
try:
|
||||
old_instance = Ride.objects.get(pk=instance.pk)
|
||||
instance._old_park = old_instance.park
|
||||
instance._old_manufacturer = old_instance.manufacturer
|
||||
except Ride.DoesNotExist:
|
||||
instance._old_park = None
|
||||
instance._old_manufacturer = None
|
||||
else:
|
||||
instance._old_park = None
|
||||
instance._old_manufacturer = None
|
||||
Reference in New Issue
Block a user