1
0
Fork 0
mirror of https://github.com/seanmorley15/AdventureLog.git synced 2025-07-28 01:09:37 +02:00

refactor(serializers): rename Adventure to Location and update related fields

This commit is contained in:
Sean Morley 2025-07-09 23:08:54 -04:00
parent c5639bed75
commit 0566c71caa

View file

@ -1,6 +1,6 @@
from django.utils import timezone from django.utils import timezone
import os import os
from .models import Adventure, AdventureImage, ChecklistItem, Collection, Note, Transportation, Checklist, Visit, Category, Attachment, Lodging from .models import Location, LocationImage, ChecklistItem, Collection, Note, Transportation, Checklist, Visit, Category, Attachment, Lodging
from rest_framework import serializers from rest_framework import serializers
from main.utils import CustomModelSerializer from main.utils import CustomModelSerializer
from users.serializers import CustomUserDetailsSerializer from users.serializers import CustomUserDetailsSerializer
@ -9,17 +9,17 @@ from geopy.distance import geodesic
from integrations.models import ImmichIntegration from integrations.models import ImmichIntegration
class AdventureImageSerializer(CustomModelSerializer): class LocationImageSerializer(CustomModelSerializer):
class Meta: class Meta:
model = AdventureImage model = LocationImage
fields = ['id', 'image', 'adventure', 'is_primary', 'user_id', 'immich_id'] fields = ['id', 'image', 'location', 'is_primary', 'user', 'immich_id']
read_only_fields = ['id', 'user_id'] read_only_fields = ['id', 'user']
def to_representation(self, instance): def to_representation(self, instance):
# If immich_id is set, check for user integration once # If immich_id is set, check for user integration once
integration = None integration = None
if instance.immich_id: if instance.immich_id:
integration = ImmichIntegration.objects.filter(user=instance.user_id).first() integration = ImmichIntegration.objects.filter(user=instance.user).first()
if not integration: if not integration:
return None # Skip if Immich image but no integration return None # Skip if Immich image but no integration
@ -42,8 +42,8 @@ class AttachmentSerializer(CustomModelSerializer):
extension = serializers.SerializerMethodField() extension = serializers.SerializerMethodField()
class Meta: class Meta:
model = Attachment model = Attachment
fields = ['id', 'file', 'adventure', 'extension', 'name', 'user_id'] fields = ['id', 'file', 'location', 'extension', 'name', 'user']
read_only_fields = ['id', 'user_id'] read_only_fields = ['id', 'user']
def get_extension(self, obj): def get_extension(self, obj):
return obj.file.name.split('.')[-1] return obj.file.name.split('.')[-1]
@ -59,11 +59,11 @@ class AttachmentSerializer(CustomModelSerializer):
return representation return representation
class CategorySerializer(serializers.ModelSerializer): class CategorySerializer(serializers.ModelSerializer):
num_adventures = serializers.SerializerMethodField() num_locations = serializers.SerializerMethodField()
class Meta: class Meta:
model = Category model = Category
fields = ['id', 'name', 'display_name', 'icon', 'num_adventures'] fields = ['id', 'name', 'display_name', 'icon', 'num_locations']
read_only_fields = ['id', 'num_adventures'] read_only_fields = ['id', 'num_locations']
def validate_name(self, value): def validate_name(self, value):
return value.lower() return value.lower()
@ -71,7 +71,7 @@ class CategorySerializer(serializers.ModelSerializer):
def create(self, validated_data): def create(self, validated_data):
user = self.context['request'].user user = self.context['request'].user
validated_data['name'] = validated_data['name'].lower() validated_data['name'] = validated_data['name'].lower()
return Category.objects.create(user_id=user, **validated_data) return Category.objects.create(user=user, **validated_data)
def update(self, instance, validated_data): def update(self, instance, validated_data):
for attr, value in validated_data.items(): for attr, value in validated_data.items():
@ -81,8 +81,8 @@ class CategorySerializer(serializers.ModelSerializer):
instance.save() instance.save()
return instance return instance
def get_num_adventures(self, obj): def get_num_locations(self, obj):
return Adventure.objects.filter(category=obj, user_id=obj.user_id).count() return Location.objects.filter(category=obj, user=obj.user).count()
class VisitSerializer(serializers.ModelSerializer): class VisitSerializer(serializers.ModelSerializer):
@ -91,13 +91,12 @@ class VisitSerializer(serializers.ModelSerializer):
fields = ['id', 'start_date', 'end_date', 'timezone', 'notes'] fields = ['id', 'start_date', 'end_date', 'timezone', 'notes']
read_only_fields = ['id'] read_only_fields = ['id']
class AdventureSerializer(CustomModelSerializer): class LocationSerializer(CustomModelSerializer):
images = serializers.SerializerMethodField() images = serializers.SerializerMethodField()
visits = VisitSerializer(many=True, read_only=False, required=False) visits = VisitSerializer(many=True, read_only=False, required=False)
attachments = AttachmentSerializer(many=True, read_only=True) attachments = AttachmentSerializer(many=True, read_only=True)
category = CategorySerializer(read_only=False, required=False) category = CategorySerializer(read_only=False, required=False)
is_visited = serializers.SerializerMethodField() is_visited = serializers.SerializerMethodField()
user = serializers.SerializerMethodField()
country = CountrySerializer(read_only=True) country = CountrySerializer(read_only=True)
region = RegionSerializer(read_only=True) region = RegionSerializer(read_only=True)
city = CitySerializer(read_only=True) city = CitySerializer(read_only=True)
@ -108,16 +107,22 @@ class AdventureSerializer(CustomModelSerializer):
) )
class Meta: class Meta:
model = Adventure model = Location
fields = [ fields = [
'id', 'user_id', 'name', 'description', 'rating', 'activity_types', 'location', 'id', 'name', 'description', 'rating', 'tags', 'location',
'is_public', 'collections', 'created_at', 'updated_at', 'images', 'link', 'longitude', 'is_public', 'collections', 'created_at', 'updated_at', 'images', 'link', 'longitude',
'latitude', 'visits', 'is_visited', 'category', 'attachments', 'user', 'city', 'country', 'region' 'latitude', 'visits', 'is_visited', 'category', 'attachments', 'user', 'city', 'country', 'region'
] ]
read_only_fields = ['id', 'created_at', 'updated_at', 'user_id', 'is_visited', 'user'] read_only_fields = ['id', 'created_at', 'updated_at', 'user', 'is_visited']
# Makes it so the whole user object is returned in the serializer instead of just the user uuid
def to_representation(self, instance):
representation = super().to_representation(instance)
representation['user'] = CustomUserDetailsSerializer(instance.user, context=self.context).data
return representation
def get_images(self, obj): def get_images(self, obj):
serializer = AdventureImageSerializer(obj.images.all(), many=True, context=self.context) serializer = LocationImageSerializer(obj.images.all(), many=True, context=self.context)
# Filter out None values from the serialized data # Filter out None values from the serialized data
return [image for image in serializer.data if image is not None] return [image for image in serializer.data if image is not None]
@ -128,7 +133,7 @@ class AdventureSerializer(CustomModelSerializer):
user = self.context['request'].user user = self.context['request'].user
for collection in collections: for collection in collections:
if collection.user_id != user and not collection.shared_with.filter(id=user.id).exists(): if collection.user != user and not collection.shared_with.filter(id=user.id).exists():
raise serializers.ValidationError( raise serializers.ValidationError(
f"Collection '{collection.name}' does not belong to the current user." f"Collection '{collection.name}' does not belong to the current user."
) )
@ -140,7 +145,7 @@ class AdventureSerializer(CustomModelSerializer):
if category_data: if category_data:
user = self.context['request'].user user = self.context['request'].user
name = category_data.get('name', '').lower() name = category_data.get('name', '').lower()
existing_category = Category.objects.filter(user_id=user, name=name).first() existing_category = Category.objects.filter(user=user, name=name).first()
if existing_category: if existing_category:
return existing_category return existing_category
category_data['name'] = name category_data['name'] = name
@ -162,7 +167,7 @@ class AdventureSerializer(CustomModelSerializer):
icon = category_data.icon icon = category_data.icon
category, created = Category.objects.get_or_create( category, created = Category.objects.get_or_create(
user_id=user, user=user,
name=name, name=name,
defaults={ defaults={
'display_name': display_name, 'display_name': display_name,
@ -171,10 +176,6 @@ class AdventureSerializer(CustomModelSerializer):
) )
return category return category
def get_user(self, obj):
user = obj.user_id
return CustomUserDetailsSerializer(user).data
def get_is_visited(self, obj): def get_is_visited(self, obj):
return obj.is_visited_status() return obj.is_visited_status()
@ -184,24 +185,24 @@ class AdventureSerializer(CustomModelSerializer):
collections_data = validated_data.pop('collections', []) collections_data = validated_data.pop('collections', [])
print(category_data) print(category_data)
adventure = Adventure.objects.create(**validated_data) location = Location.objects.create(**validated_data)
# Handle visits # Handle visits
for visit_data in visits_data: for visit_data in visits_data:
Visit.objects.create(adventure=adventure, **visit_data) Visit.objects.create(location=location, **visit_data)
# Handle category # Handle category
if category_data: if category_data:
category = self.get_or_create_category(category_data) category = self.get_or_create_category(category_data)
adventure.category = category location.category = category
# Handle collections - set after adventure is saved # Handle collections - set after location is saved
if collections_data: if collections_data:
adventure.collections.set(collections_data) location.collections.set(collections_data)
adventure.save() location.save()
return adventure return location
def update(self, instance, validated_data): def update(self, instance, validated_data):
has_visits = 'visits' in validated_data has_visits = 'visits' in validated_data
@ -214,9 +215,9 @@ class AdventureSerializer(CustomModelSerializer):
for attr, value in validated_data.items(): for attr, value in validated_data.items():
setattr(instance, attr, value) setattr(instance, attr, value)
# Handle category - ONLY allow the adventure owner to change categories # Handle category - ONLY allow the location owner to change categories
user = self.context['request'].user user = self.context['request'].user
if category_data and instance.user_id == user: if category_data and instance.user == user:
# Only the owner can set categories # Only the owner can set categories
category = self.get_or_create_category(category_data) category = self.get_or_create_category(category_data)
instance.category = category instance.category = category
@ -241,13 +242,13 @@ class AdventureSerializer(CustomModelSerializer):
visit.save() visit.save()
updated_visit_ids.add(visit_id) updated_visit_ids.add(visit_id)
else: else:
new_visit = Visit.objects.create(adventure=instance, **visit_data) new_visit = Visit.objects.create(location=instance, **visit_data)
updated_visit_ids.add(new_visit.id) updated_visit_ids.add(new_visit.id)
visits_to_delete = current_visit_ids - updated_visit_ids visits_to_delete = current_visit_ids - updated_visit_ids
instance.visits.filter(id__in=visits_to_delete).delete() instance.visits.filter(id__in=visits_to_delete).delete()
# call save on the adventure to update the updated_at field and trigger any geocoding # call save on the location to update the updated_at field and trigger any geocoding
instance.save() instance.save()
return instance return instance
@ -258,13 +259,13 @@ class TransportationSerializer(CustomModelSerializer):
class Meta: class Meta:
model = Transportation model = Transportation
fields = [ fields = [
'id', 'user_id', 'type', 'name', 'description', 'rating', 'id', 'user', 'type', 'name', 'description', 'rating',
'link', 'date', 'flight_number', 'from_location', 'to_location', 'link', 'date', 'flight_number', 'from_location', 'to_location',
'is_public', 'collection', 'created_at', 'updated_at', 'end_date', 'is_public', 'collection', 'created_at', 'updated_at', 'end_date',
'origin_latitude', 'origin_longitude', 'destination_latitude', 'destination_longitude', 'origin_latitude', 'origin_longitude', 'destination_latitude', 'destination_longitude',
'start_timezone', 'end_timezone', 'distance' # ✅ Add distance here 'start_timezone', 'end_timezone', 'distance'
] ]
read_only_fields = ['id', 'created_at', 'updated_at', 'user_id', 'distance'] read_only_fields = ['id', 'created_at', 'updated_at', 'user', 'distance']
def get_distance(self, obj): def get_distance(self, obj):
if ( if (
@ -284,29 +285,29 @@ class LodgingSerializer(CustomModelSerializer):
class Meta: class Meta:
model = Lodging model = Lodging
fields = [ fields = [
'id', 'user_id', 'name', 'description', 'rating', 'link', 'check_in', 'check_out', 'id', 'user', 'name', 'description', 'rating', 'link', 'check_in', 'check_out',
'reservation_number', 'price', 'latitude', 'longitude', 'location', 'is_public', 'reservation_number', 'price', 'latitude', 'longitude', 'location', 'is_public',
'collection', 'created_at', 'updated_at', 'type', 'timezone' 'collection', 'created_at', 'updated_at', 'type', 'timezone'
] ]
read_only_fields = ['id', 'created_at', 'updated_at', 'user_id'] read_only_fields = ['id', 'created_at', 'updated_at', 'user']
class NoteSerializer(CustomModelSerializer): class NoteSerializer(CustomModelSerializer):
class Meta: class Meta:
model = Note model = Note
fields = [ fields = [
'id', 'user_id', 'name', 'content', 'date', 'links', 'id', 'user', 'name', 'content', 'date', 'links',
'is_public', 'collection', 'created_at', 'updated_at' 'is_public', 'collection', 'created_at', 'updated_at'
] ]
read_only_fields = ['id', 'created_at', 'updated_at', 'user_id'] read_only_fields = ['id', 'created_at', 'updated_at', 'user']
class ChecklistItemSerializer(CustomModelSerializer): class ChecklistItemSerializer(CustomModelSerializer):
class Meta: class Meta:
model = ChecklistItem model = ChecklistItem
fields = [ fields = [
'id', 'user_id', 'name', 'is_checked', 'checklist', 'created_at', 'updated_at' 'id', 'user', 'name', 'is_checked', 'checklist', 'created_at', 'updated_at'
] ]
read_only_fields = ['id', 'created_at', 'updated_at', 'user_id', 'checklist'] read_only_fields = ['id', 'created_at', 'updated_at', 'user', 'checklist']
class ChecklistSerializer(CustomModelSerializer): class ChecklistSerializer(CustomModelSerializer):
items = ChecklistItemSerializer(many=True, source='checklistitem_set') items = ChecklistItemSerializer(many=True, source='checklistitem_set')
@ -314,21 +315,21 @@ class ChecklistSerializer(CustomModelSerializer):
class Meta: class Meta:
model = Checklist model = Checklist
fields = [ fields = [
'id', 'user_id', 'name', 'date', 'is_public', 'collection', 'created_at', 'updated_at', 'items' 'id', 'user', 'name', 'date', 'is_public', 'collection', 'created_at', 'updated_at', 'items'
] ]
read_only_fields = ['id', 'created_at', 'updated_at', 'user_id'] read_only_fields = ['id', 'created_at', 'updated_at', 'user']
def create(self, validated_data): def create(self, validated_data):
items_data = validated_data.pop('checklistitem_set') items_data = validated_data.pop('checklistitem_set')
checklist = Checklist.objects.create(**validated_data) checklist = Checklist.objects.create(**validated_data)
for item_data in items_data: for item_data in items_data:
# Remove user_id from item_data to avoid constraint issues # Remove user from item_data to avoid constraint issues
item_data.pop('user_id', None) item_data.pop('user', None)
# Set user_id from the parent checklist # Set user from the parent checklist
ChecklistItem.objects.create( ChecklistItem.objects.create(
checklist=checklist, checklist=checklist,
user_id=checklist.user_id, user=checklist.user,
**item_data **item_data
) )
return checklist return checklist
@ -348,8 +349,8 @@ class ChecklistSerializer(CustomModelSerializer):
# Update or create items # Update or create items
updated_item_ids = set() updated_item_ids = set()
for item_data in items_data: for item_data in items_data:
# Remove user_id from item_data to avoid constraint issues # Remove user from item_data to avoid constraint issues
item_data.pop('user_id', None) item_data.pop('user', None)
item_id = item_data.get('id') item_id = item_data.get('id')
if item_id: if item_id:
@ -363,14 +364,14 @@ class ChecklistSerializer(CustomModelSerializer):
# If ID is provided but doesn't exist, create new item # If ID is provided but doesn't exist, create new item
ChecklistItem.objects.create( ChecklistItem.objects.create(
checklist=instance, checklist=instance,
user_id=instance.user_id, user=instance.user,
**item_data **item_data
) )
else: else:
# If no ID is provided, create new item # If no ID is provided, create new item
ChecklistItem.objects.create( ChecklistItem.objects.create(
checklist=instance, checklist=instance,
user_id=instance.user_id, user=instance.user,
**item_data **item_data
) )
@ -391,7 +392,7 @@ class ChecklistSerializer(CustomModelSerializer):
return data return data
class CollectionSerializer(CustomModelSerializer): class CollectionSerializer(CustomModelSerializer):
adventures = AdventureSerializer(many=True, read_only=True) locations = LocationSerializer(many=True, read_only=True)
transportations = TransportationSerializer(many=True, read_only=True, source='transportation_set') transportations = TransportationSerializer(many=True, read_only=True, source='transportation_set')
notes = NoteSerializer(many=True, read_only=True, source='note_set') notes = NoteSerializer(many=True, read_only=True, source='note_set')
checklists = ChecklistSerializer(many=True, read_only=True, source='checklist_set') checklists = ChecklistSerializer(many=True, read_only=True, source='checklist_set')
@ -399,8 +400,8 @@ class CollectionSerializer(CustomModelSerializer):
class Meta: class Meta:
model = Collection model = Collection
fields = ['id', 'description', 'user_id', 'name', 'is_public', 'adventures', 'created_at', 'start_date', 'end_date', 'transportations', 'notes', 'updated_at', 'checklists', 'is_archived', 'shared_with', 'link', 'lodging'] fields = ['id', 'description', 'user', 'name', 'is_public', 'locations', 'created_at', 'start_date', 'end_date', 'transportations', 'notes', 'updated_at', 'checklists', 'is_archived', 'shared_with', 'link', 'lodging']
read_only_fields = ['id', 'created_at', 'updated_at', 'user_id'] read_only_fields = ['id', 'created_at', 'updated_at', 'user']
def to_representation(self, instance): def to_representation(self, instance):
representation = super().to_representation(instance) representation = super().to_representation(instance)