mirror of
https://github.com/seanmorley15/AdventureLog.git
synced 2025-07-25 07:49:37 +02:00
commit
cfe14b3708
21 changed files with 1117 additions and 197 deletions
|
@ -1,7 +1,7 @@
|
|||
import os
|
||||
from django.contrib import admin
|
||||
from django.utils.html import mark_safe
|
||||
from .models import Adventure, Checklist, ChecklistItem, Collection, Transportation, Note
|
||||
from .models import Adventure, Checklist, ChecklistItem, Collection, Transportation, Note, AdventureImage
|
||||
from worldtravel.models import Country, Region, VisitedRegion
|
||||
|
||||
|
||||
|
@ -57,6 +57,20 @@ class CustomUserAdmin(UserAdmin):
|
|||
else:
|
||||
return
|
||||
|
||||
class AdventureImageAdmin(admin.ModelAdmin):
|
||||
list_display = ('user_id', 'image_display')
|
||||
|
||||
def image_display(self, obj):
|
||||
if obj.image: # Ensure this field matches your model's image field
|
||||
public_url = os.environ.get('PUBLIC_URL', 'http://127.0.0.1:8000').rstrip('/')
|
||||
public_url = public_url.replace("'", "")
|
||||
return mark_safe(f'<img src="{public_url}/media/{obj.image.name}" width="100px" height="100px"')
|
||||
else:
|
||||
return
|
||||
|
||||
image_display.short_description = 'Image Preview'
|
||||
|
||||
|
||||
class CollectionAdmin(admin.ModelAdmin):
|
||||
def adventure_count(self, obj):
|
||||
return obj.adventure_set.count()
|
||||
|
@ -78,6 +92,7 @@ admin.site.register(Transportation)
|
|||
admin.site.register(Note)
|
||||
admin.site.register(Checklist)
|
||||
admin.site.register(ChecklistItem)
|
||||
admin.site.register(AdventureImage, AdventureImageAdmin)
|
||||
|
||||
admin.site.site_header = 'AdventureLog Admin'
|
||||
admin.site.site_title = 'AdventureLog Admin Site'
|
||||
|
|
19
backend/server/adventures/migrations/0001_adventure_image.py
Normal file
19
backend/server/adventures/migrations/0001_adventure_image.py
Normal file
|
@ -0,0 +1,19 @@
|
|||
# Generated by Django 5.0.8 on 2024-08-15 23:20
|
||||
|
||||
import django_resized.forms
|
||||
from django.db import migrations
|
||||
|
||||
|
||||
class Migration(migrations.Migration):
|
||||
|
||||
dependencies = [
|
||||
('adventures', 'migrate_images'),
|
||||
]
|
||||
|
||||
operations = [
|
||||
migrations.AddField(
|
||||
model_name='adventure',
|
||||
name='image',
|
||||
field=django_resized.forms.ResizedImageField(blank=True, crop=None, force_format='WEBP', keep_meta=True, null=True, quality=75, scale=None, size=[1920, 1080], upload_to='images/'),
|
||||
),
|
||||
]
|
27
backend/server/adventures/migrations/0002_adventureimage.py
Normal file
27
backend/server/adventures/migrations/0002_adventureimage.py
Normal file
|
@ -0,0 +1,27 @@
|
|||
# Generated by Django 5.0.8 on 2024-08-15 23:17
|
||||
|
||||
import django.db.models.deletion
|
||||
import django_resized.forms
|
||||
import uuid
|
||||
from django.conf import settings
|
||||
from django.db import migrations, models
|
||||
|
||||
|
||||
class Migration(migrations.Migration):
|
||||
|
||||
dependencies = [
|
||||
('adventures', '0001_initial'),
|
||||
migrations.swappable_dependency(settings.AUTH_USER_MODEL),
|
||||
]
|
||||
|
||||
operations = [
|
||||
migrations.CreateModel(
|
||||
name='AdventureImage',
|
||||
fields=[
|
||||
('id', models.UUIDField(default=uuid.uuid4, editable=False, primary_key=True, serialize=False, unique=True)),
|
||||
('image', django_resized.forms.ResizedImageField(crop=None, force_format='WEBP', keep_meta=True, quality=75, scale=None, size=[1920, 1080], upload_to='images/')),
|
||||
('adventure', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to='adventures.adventure')),
|
||||
('user_id', models.ForeignKey(default=1, on_delete=django.db.models.deletion.CASCADE, to=settings.AUTH_USER_MODEL)),
|
||||
],
|
||||
),
|
||||
]
|
|
@ -0,0 +1,19 @@
|
|||
# Generated by Django 5.0.8 on 2024-08-15 23:31
|
||||
|
||||
import django.db.models.deletion
|
||||
from django.db import migrations, models
|
||||
|
||||
|
||||
class Migration(migrations.Migration):
|
||||
|
||||
dependencies = [
|
||||
('adventures', '0001_adventure_image'),
|
||||
]
|
||||
|
||||
operations = [
|
||||
migrations.AlterField(
|
||||
model_name='adventureimage',
|
||||
name='adventure',
|
||||
field=models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, related_name='images', to='adventures.adventure'),
|
||||
),
|
||||
]
|
29
backend/server/adventures/migrations/migrate_images.py
Normal file
29
backend/server/adventures/migrations/migrate_images.py
Normal file
|
@ -0,0 +1,29 @@
|
|||
from django.db import migrations
|
||||
|
||||
def move_images_to_new_model(apps, schema_editor):
|
||||
Adventure = apps.get_model('adventures', 'Adventure')
|
||||
AdventureImage = apps.get_model('adventures', 'AdventureImage')
|
||||
|
||||
for adventure in Adventure.objects.all():
|
||||
if adventure.image:
|
||||
AdventureImage.objects.create(
|
||||
adventure=adventure,
|
||||
image=adventure.image,
|
||||
user_id=adventure.user_id,
|
||||
)
|
||||
|
||||
|
||||
class Migration(migrations.Migration):
|
||||
|
||||
dependencies = [
|
||||
('adventures', '0001_initial'),
|
||||
('adventures', '0002_adventureimage'),
|
||||
]
|
||||
|
||||
operations = [
|
||||
migrations.RunPython(move_images_to_new_model),
|
||||
migrations.RemoveField(
|
||||
model_name='Adventure',
|
||||
name='image',
|
||||
),
|
||||
]
|
|
@ -182,3 +182,13 @@ class ChecklistItem(models.Model):
|
|||
|
||||
def __str__(self):
|
||||
return self.name
|
||||
|
||||
class AdventureImage(models.Model):
|
||||
id = models.UUIDField(default=uuid.uuid4, editable=False, unique=True, primary_key=True)
|
||||
user_id = models.ForeignKey(
|
||||
User, on_delete=models.CASCADE, default=default_user_id)
|
||||
image = ResizedImageField(force_format="WEBP", quality=75, upload_to='images/')
|
||||
adventure = models.ForeignKey(Adventure, related_name='images', on_delete=models.CASCADE)
|
||||
|
||||
def __str__(self):
|
||||
return self.image.url
|
|
@ -1,29 +1,39 @@
|
|||
import os
|
||||
from .models import Adventure, ChecklistItem, Collection, Note, Transportation, Checklist
|
||||
from .models import Adventure, AdventureImage, ChecklistItem, Collection, Note, Transportation, Checklist
|
||||
from rest_framework import serializers
|
||||
|
||||
class AdventureSerializer(serializers.ModelSerializer):
|
||||
class AdventureImageSerializer(serializers.ModelSerializer):
|
||||
class Meta:
|
||||
model = AdventureImage
|
||||
fields = ['id', 'image', 'adventure']
|
||||
read_only_fields = ['id']
|
||||
|
||||
def to_representation(self, instance):
|
||||
representation = super().to_representation(instance)
|
||||
|
||||
# Build the full URL for the image
|
||||
request = self.context.get('request')
|
||||
if request and instance.image:
|
||||
public_url = request.build_absolute_uri(instance.image.url)
|
||||
else:
|
||||
public_url = f"{os.environ.get('PUBLIC_URL', 'http://127.0.0.1:8000').rstrip('/')}/media/{instance.image.name}"
|
||||
|
||||
representation['image'] = public_url
|
||||
return representation
|
||||
|
||||
|
||||
|
||||
class AdventureSerializer(serializers.ModelSerializer):
|
||||
images = AdventureImageSerializer(many=True, read_only=True)
|
||||
class Meta:
|
||||
model = Adventure
|
||||
fields = '__all__'
|
||||
fields = ['id', 'user_id', 'name', 'description', 'rating', 'activity_types', 'location', 'date', 'is_public', 'collection', 'created_at', 'updated_at', 'images', 'link', 'type', 'longitude', 'latitude']
|
||||
read_only_fields = ['id', 'created_at', 'updated_at', 'user_id']
|
||||
|
||||
def to_representation(self, instance):
|
||||
representation = super().to_representation(instance)
|
||||
if instance.image:
|
||||
public_url = os.environ.get('PUBLIC_URL', 'http://127.0.0.1:8000').rstrip('/')
|
||||
#print(public_url)
|
||||
# remove any ' from the url
|
||||
public_url = public_url.replace("'", "")
|
||||
representation['image'] = f"{public_url}/media/{instance.image.name}"
|
||||
return representation
|
||||
|
||||
def validate_activity_types(self, value):
|
||||
if value:
|
||||
return [activity.lower() for activity in value]
|
||||
return value
|
||||
|
||||
class TransportationSerializer(serializers.ModelSerializer):
|
||||
|
||||
class Meta:
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
from django.urls import include, path
|
||||
from rest_framework.routers import DefaultRouter
|
||||
from .views import AdventureViewSet, ChecklistViewSet, CollectionViewSet, NoteViewSet, StatsViewSet, GenerateDescription, ActivityTypesView, TransportationViewSet
|
||||
from .views import AdventureViewSet, ChecklistViewSet, CollectionViewSet, NoteViewSet, StatsViewSet, GenerateDescription, ActivityTypesView, TransportationViewSet, AdventureImageViewSet
|
||||
|
||||
router = DefaultRouter()
|
||||
router.register(r'adventures', AdventureViewSet, basename='adventures')
|
||||
|
@ -11,6 +11,7 @@ router.register(r'activity-types', ActivityTypesView, basename='activity-types')
|
|||
router.register(r'transportations', TransportationViewSet, basename='transportations')
|
||||
router.register(r'notes', NoteViewSet, basename='notes')
|
||||
router.register(r'checklists', ChecklistViewSet, basename='checklists')
|
||||
router.register(r'images', AdventureImageViewSet, basename='images')
|
||||
|
||||
|
||||
urlpatterns = [
|
||||
|
|
|
@ -1,12 +1,13 @@
|
|||
import uuid
|
||||
import requests
|
||||
from django.db import transaction
|
||||
from rest_framework.decorators import action
|
||||
from rest_framework import viewsets
|
||||
from django.db.models.functions import Lower
|
||||
from rest_framework.response import Response
|
||||
from .models import Adventure, Checklist, Collection, Transportation, Note
|
||||
from .models import Adventure, Checklist, Collection, Transportation, Note, AdventureImage
|
||||
from worldtravel.models import VisitedRegion, Region, Country
|
||||
from .serializers import AdventureSerializer, CollectionSerializer, NoteSerializer, TransportationSerializer, ChecklistSerializer
|
||||
from .serializers import AdventureImageSerializer, AdventureSerializer, CollectionSerializer, NoteSerializer, TransportationSerializer, ChecklistSerializer
|
||||
from rest_framework.permissions import IsAuthenticated
|
||||
from django.db.models import Q, Prefetch
|
||||
from .permissions import IsOwnerOrReadOnly, IsPublicReadOnly
|
||||
|
@ -528,5 +529,97 @@ class ChecklistViewSet(viewsets.ModelViewSet):
|
|||
user = self.request.user
|
||||
return Checklist.objects.filter(user_id=user)
|
||||
|
||||
def perform_create(self, serializer):
|
||||
serializer.save(user_id=self.request.user)
|
||||
|
||||
class AdventureImageViewSet(viewsets.ModelViewSet):
|
||||
serializer_class = AdventureImageSerializer
|
||||
permission_classes = [IsAuthenticated]
|
||||
|
||||
def dispatch(self, request, *args, **kwargs):
|
||||
print(f"Method: {request.method}")
|
||||
return super().dispatch(request, *args, **kwargs)
|
||||
|
||||
@action(detail=True, methods=['post'])
|
||||
def image_delete(self, request, *args, **kwargs):
|
||||
return self.destroy(request, *args, **kwargs)
|
||||
|
||||
|
||||
def create(self, request, *args, **kwargs):
|
||||
if not request.user.is_authenticated:
|
||||
return Response({"error": "User is not authenticated"}, status=status.HTTP_401_UNAUTHORIZED)
|
||||
adventure_id = request.data.get('adventure')
|
||||
try:
|
||||
adventure = Adventure.objects.get(id=adventure_id)
|
||||
except Adventure.DoesNotExist:
|
||||
return Response({"error": "Adventure not found"}, status=status.HTTP_404_NOT_FOUND)
|
||||
|
||||
if adventure.user_id != request.user:
|
||||
return Response({"error": "User does not own this adventure"}, status=status.HTTP_403_FORBIDDEN)
|
||||
|
||||
return super().create(request, *args, **kwargs)
|
||||
|
||||
def update(self, request, *args, **kwargs):
|
||||
if not request.user.is_authenticated:
|
||||
return Response({"error": "User is not authenticated"}, status=status.HTTP_401_UNAUTHORIZED)
|
||||
|
||||
adventure_id = request.data.get('adventure')
|
||||
try:
|
||||
adventure = Adventure.objects.get(id=adventure_id)
|
||||
except Adventure.DoesNotExist:
|
||||
return Response({"error": "Adventure not found"}, status=status.HTTP_404_NOT_FOUND)
|
||||
|
||||
if adventure.user_id != request.user:
|
||||
return Response({"error": "User does not own this adventure"}, status=status.HTTP_403_FORBIDDEN)
|
||||
|
||||
return super().update(request, *args, **kwargs)
|
||||
|
||||
def perform_destroy(self, instance):
|
||||
print("perform_destroy")
|
||||
return super().perform_destroy(instance)
|
||||
|
||||
def destroy(self, request, *args, **kwargs):
|
||||
print("destroy")
|
||||
if not request.user.is_authenticated:
|
||||
return Response({"error": "User is not authenticated"}, status=status.HTTP_401_UNAUTHORIZED)
|
||||
|
||||
instance = self.get_object()
|
||||
adventure = instance.adventure
|
||||
if adventure.user_id != request.user:
|
||||
return Response({"error": "User does not own this adventure"}, status=status.HTTP_403_FORBIDDEN)
|
||||
|
||||
return super().destroy(request, *args, **kwargs)
|
||||
|
||||
def partial_update(self, request, *args, **kwargs):
|
||||
if not request.user.is_authenticated:
|
||||
return Response({"error": "User is not authenticated"}, status=status.HTTP_401_UNAUTHORIZED)
|
||||
|
||||
instance = self.get_object()
|
||||
adventure = instance.adventure
|
||||
if adventure.user_id != request.user:
|
||||
return Response({"error": "User does not own this adventure"}, status=status.HTTP_403_FORBIDDEN)
|
||||
|
||||
return super().partial_update(request, *args, **kwargs)
|
||||
|
||||
@action(detail=False, methods=['GET'], url_path='(?P<adventure_id>[0-9a-f-]+)')
|
||||
def adventure_images(self, request, adventure_id=None, *args, **kwargs):
|
||||
if not request.user.is_authenticated:
|
||||
return Response({"error": "User is not authenticated"}, status=status.HTTP_401_UNAUTHORIZED)
|
||||
|
||||
try:
|
||||
adventure_uuid = uuid.UUID(adventure_id)
|
||||
except ValueError:
|
||||
return Response({"error": "Invalid adventure ID"}, status=status.HTTP_400_BAD_REQUEST)
|
||||
|
||||
queryset = AdventureImage.objects.filter(
|
||||
Q(adventure__id=adventure_uuid) & Q(user_id=request.user)
|
||||
)
|
||||
|
||||
serializer = self.get_serializer(queryset, many=True, context={'request': request})
|
||||
return Response(serializer.data)
|
||||
|
||||
def get_queryset(self):
|
||||
return AdventureImage.objects.filter(user_id=self.request.user)
|
||||
|
||||
def perform_create(self, serializer):
|
||||
serializer.save(user_id=self.request.user)
|
Loading…
Add table
Add a link
Reference in a new issue