mirror of
https://github.com/pacnpal/thrillwiki_django_no_react.git
synced 2025-12-20 11:51:10 -05:00
feat: Implement avatar upload system with Cloudflare integration
- Added migration to transition avatar data from CloudflareImageField to ForeignKey structure in UserProfile. - Fixed UserProfileEvent avatar field to align with new avatar structure. - Created serializers for social authentication, including connected and available providers. - Developed request logging middleware for comprehensive request/response logging. - Updated moderation and parks migrations to remove outdated triggers and adjust foreign key relationships. - Enhanced rides migrations to ensure proper handling of image uploads and triggers. - Introduced a test script for the 3-step avatar upload process, ensuring functionality with Cloudflare. - Documented the fix for avatar upload issues, detailing root cause, implementation, and verification steps. - Implemented automatic deletion of Cloudflare images upon avatar, park, and ride photo changes or removals.
This commit is contained in:
@@ -10,7 +10,6 @@ from datetime import timedelta
|
||||
from django.utils import timezone
|
||||
from apps.core.history import TrackedModel
|
||||
import pghistory
|
||||
from cloudflare_images.field import CloudflareImagesField
|
||||
|
||||
|
||||
def generate_random_id(model_class, id_field):
|
||||
@@ -160,7 +159,12 @@ class UserProfile(models.Model):
|
||||
blank=True,
|
||||
help_text="Legacy display name field - use User.display_name instead",
|
||||
)
|
||||
avatar = CloudflareImagesField(blank=True, null=True)
|
||||
avatar = models.ForeignKey(
|
||||
'django_cloudflareimages_toolkit.CloudflareImage',
|
||||
on_delete=models.SET_NULL,
|
||||
null=True,
|
||||
blank=True
|
||||
)
|
||||
pronouns = models.CharField(max_length=50, blank=True)
|
||||
|
||||
bio = models.TextField(max_length=500, blank=True)
|
||||
@@ -181,12 +185,26 @@ class UserProfile(models.Model):
|
||||
"""
|
||||
Return the avatar URL or generate a default letter-based avatar URL
|
||||
"""
|
||||
if self.avatar:
|
||||
# Return Cloudflare Images URL with avatar variant
|
||||
base_url = self.avatar.url
|
||||
if '/public' in base_url:
|
||||
return base_url.replace('/public', '/avatar')
|
||||
return base_url
|
||||
if self.avatar and self.avatar.is_uploaded:
|
||||
# Try to get avatar variant first, fallback to public
|
||||
avatar_url = self.avatar.get_url('avatar')
|
||||
if avatar_url:
|
||||
return avatar_url
|
||||
|
||||
# Fallback to public variant
|
||||
public_url = self.avatar.get_url('public')
|
||||
if public_url:
|
||||
return public_url
|
||||
|
||||
# Last fallback - try any available variant
|
||||
if self.avatar.variants:
|
||||
if isinstance(self.avatar.variants, list) and self.avatar.variants:
|
||||
return self.avatar.variants[0]
|
||||
elif isinstance(self.avatar.variants, dict):
|
||||
# Return first available variant
|
||||
for variant_url in self.avatar.variants.values():
|
||||
if variant_url:
|
||||
return variant_url
|
||||
|
||||
# Generate default letter-based avatar using first letter of username
|
||||
first_letter = self.user.username[0].upper() if self.user.username else "U"
|
||||
@@ -197,21 +215,32 @@ class UserProfile(models.Model):
|
||||
"""
|
||||
Return avatar variants for different use cases
|
||||
"""
|
||||
if self.avatar:
|
||||
base_url = self.avatar.url
|
||||
if '/public' in base_url:
|
||||
return {
|
||||
"thumbnail": base_url.replace('/public', '/thumbnail'),
|
||||
"avatar": base_url.replace('/public', '/avatar'),
|
||||
"large": base_url.replace('/public', '/large'),
|
||||
}
|
||||
else:
|
||||
# If no variant in URL, return the same URL for all variants
|
||||
return {
|
||||
"thumbnail": base_url,
|
||||
"avatar": base_url,
|
||||
"large": base_url,
|
||||
}
|
||||
if self.avatar and self.avatar.is_uploaded:
|
||||
variants = {}
|
||||
|
||||
# Try to get specific variants
|
||||
thumbnail_url = self.avatar.get_url('thumbnail')
|
||||
avatar_url = self.avatar.get_url('avatar')
|
||||
large_url = self.avatar.get_url('large')
|
||||
public_url = self.avatar.get_url('public')
|
||||
|
||||
# Use specific variants if available, otherwise fallback to public or first available
|
||||
fallback_url = public_url
|
||||
if not fallback_url and self.avatar.variants:
|
||||
if isinstance(self.avatar.variants, list) and self.avatar.variants:
|
||||
fallback_url = self.avatar.variants[0]
|
||||
elif isinstance(self.avatar.variants, dict):
|
||||
fallback_url = next(iter(self.avatar.variants.values()), None)
|
||||
|
||||
variants = {
|
||||
"thumbnail": thumbnail_url or fallback_url,
|
||||
"avatar": avatar_url or fallback_url,
|
||||
"large": large_url or fallback_url,
|
||||
}
|
||||
|
||||
# Only return variants if we have at least one valid URL
|
||||
if any(variants.values()):
|
||||
return variants
|
||||
|
||||
# For default avatars, return the same URL for all variants
|
||||
default_url = self.get_avatar_url()
|
||||
|
||||
Reference in New Issue
Block a user