Skip to content
Open
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
# Generated manually for collection field on Transportation and Lodging

from django.db import migrations, models
import django.db.models.deletion


class Migration(migrations.Migration):

dependencies = [
('adventures', '0071_alter_collectionitineraryitem_unique_together_and_more'),
]

operations = [
migrations.AddField(
model_name='transportation',
name='collection',
field=models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.CASCADE, to='adventures.collection'),
),
migrations.AddField(
model_name='lodging',
name='collection',
field=models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.CASCADE, to='adventures.collection'),
),
# Note: note.collection and checklist.collection already exist in database
]
30 changes: 30 additions & 0 deletions backend/server/adventures/migrations/0073_collectiontemplate.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
# Generated manually for CollectionTemplate model

from django.conf import settings
from django.db import migrations, models
import django.db.models.deletion
import uuid


class Migration(migrations.Migration):

dependencies = [
migrations.swappable_dependency(settings.AUTH_USER_MODEL),
('adventures', '0072_transportation_collection_lodging_collection'),
]

operations = [
migrations.CreateModel(
name='CollectionTemplate',
fields=[
('id', models.UUIDField(default=uuid.uuid4, editable=False, primary_key=True, serialize=False, unique=True)),
('name', models.CharField(max_length=255)),
('description', models.TextField(blank=True, null=True)),
('template_data', models.JSONField(default=dict)),
('is_public', models.BooleanField(default=False)),
('created_at', models.DateTimeField(auto_now_add=True)),
('updated_at', models.DateTimeField(auto_now=True)),
('user', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, related_name='collection_templates', to=settings.AUTH_USER_MODEL)),
],
),
]
19 changes: 19 additions & 0 deletions backend/server/adventures/models.py
Original file line number Diff line number Diff line change
Expand Up @@ -707,6 +707,25 @@ def __str__(self):
self.collection.name} - {self.date} - {self.name or 'Unnamed Day'}"


class CollectionTemplate(models.Model):
"""Reusable template for creating new collections with pre-defined structure"""
id = models.UUIDField(default=uuid.uuid4, editable=False, unique=True, primary_key=True)
name = models.CharField(max_length=255)
description = models.TextField(blank=True, null=True)
template_data = models.JSONField(default=dict)
# Structure: {notes: [...], checklists: [...], transportations: [...], lodgings: [...]}
is_public = models.BooleanField(default=False)
user = models.ForeignKey(User, on_delete=models.CASCADE, related_name='collection_templates')
created_at = models.DateTimeField(auto_now_add=True)
updated_at = models.DateTimeField(auto_now=True)

class Meta:
ordering = ['-created_at']

def __str__(self):
return f"{self.name} ({'Public' if self.is_public else 'Private'})"


class CollectionItineraryItem(models.Model):
id = models.UUIDField(primary_key=True, default=uuid.uuid4, editable=False)

Expand Down
40 changes: 34 additions & 6 deletions backend/server/adventures/serializers.py
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
import os
from .models import Location, ContentImage, ChecklistItem, Collection, Note, Transportation, Checklist, Visit, Category, ContentAttachment, Lodging, CollectionInvite, Trail, Activity, CollectionItineraryItem, CollectionItineraryDay
from .models import Location, ContentImage, ChecklistItem, Collection, Note, Transportation, Checklist, Visit, Category, ContentAttachment, Lodging, CollectionInvite, Trail, Activity, CollectionItineraryItem, CollectionItineraryDay, CollectionTemplate
from rest_framework import serializers
from main.utils import CustomModelSerializer
from users.serializers import CustomUserDetailsSerializer
Expand Down Expand Up @@ -788,25 +788,37 @@ def get_transportations(self, obj):
# Only include transportations if not in nested context
if self.context.get('nested', False):
return []
return TransportationSerializer(obj.transportation_set.all(), many=True, context=self.context).data
try:
return TransportationSerializer(obj.transportation_set.all(), many=True, context=self.context).data
except Exception:
return [] # Handle missing column gracefully

def get_notes(self, obj):
# Only include notes if not in nested context
if self.context.get('nested', False):
return []
return NoteSerializer(obj.note_set.all(), many=True, context=self.context).data
try:
return NoteSerializer(obj.note_set.all(), many=True, context=self.context).data
except Exception:
return [] # Handle missing column gracefully

def get_checklists(self, obj):
# Only include checklists if not in nested context
if self.context.get('nested', False):
return []
return ChecklistSerializer(obj.checklist_set.all(), many=True, context=self.context).data
try:
return ChecklistSerializer(obj.checklist_set.all(), many=True, context=self.context).data
except Exception:
return [] # Handle missing column gracefully

def get_lodging(self, obj):
# Only include lodging if not in nested context
if self.context.get('nested', False):
return []
return LodgingSerializer(obj.lodging_set.all(), many=True, context=self.context).data
try:
return LodgingSerializer(obj.lodging_set.all(), many=True, context=self.context).data
except Exception:
return [] # Handle missing column gracefully

def get_status(self, obj):
"""Calculate the status of the collection based on dates"""
Expand Down Expand Up @@ -1058,9 +1070,25 @@ def get_item(self, obj):
"""Return id and type for the linked item"""
if not obj.item:
return None

return {
'id': str(obj.item.id),
'type': obj.content_type.model,
}


class CollectionTemplateSerializer(CustomModelSerializer):
class Meta:
model = CollectionTemplate
fields = [
'id', 'name', 'description', 'template_data', 'is_public',
'user', 'created_at', 'updated_at'
]
read_only_fields = ['id', 'created_at', 'updated_at', 'user']

def to_representation(self, instance):
representation = super().to_representation(instance)
# Convert user to UUID string for consistency
representation['user'] = str(instance.user.uuid)
return representation

1 change: 1 addition & 0 deletions backend/server/adventures/urls.py
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,7 @@
router.register(r'visits', VisitViewSet, basename='visits')
router.register(r'itineraries', ItineraryViewSet, basename='itineraries')
router.register(r'itinerary-days', ItineraryDayViewSet, basename='itinerary-days')
router.register(r'collection-templates', CollectionTemplateViewSet, basename='collection-templates')

urlpatterns = [
# Include the router under the 'api/' prefix
Expand Down
3 changes: 2 additions & 1 deletion backend/server/adventures/views/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -18,4 +18,5 @@
from .trail_view import *
from .activity_view import *
from .visit_view import *
from .itinerary_view import *
from .itinerary_view import *
from .template_view import *
Loading