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

Merge pull request #239 from seanmorley15/new_images

New images
This commit is contained in:
Sean Morley 2024-08-17 22:43:40 -04:00 committed by GitHub
commit cfe14b3708
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
21 changed files with 1117 additions and 197 deletions

View file

@ -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'

View 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/'),
),
]

View 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)),
],
),
]

View file

@ -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'),
),
]

View 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',
),
]

View file

@ -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

View file

@ -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:

View file

@ -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 = [

View file

@ -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)