1
0
Fork 0
mirror of https://github.com/seanmorley15/AdventureLog.git synced 2025-08-02 19:55:18 +02:00

feat: add VisitedCity model and serializer, update admin interface, and enhance city visit tracking functionality

This commit is contained in:
Sean Morley 2025-01-09 12:38:29 -05:00
parent 44810e6343
commit 80cec30fda
15 changed files with 344 additions and 234 deletions

View file

@ -2,7 +2,7 @@ import os
from django.contrib import admin
from django.utils.html import mark_safe
from .models import Adventure, Checklist, ChecklistItem, Collection, Transportation, Note, AdventureImage, Visit, Category
from worldtravel.models import Country, Region, VisitedRegion, City
from worldtravel.models import Country, Region, VisitedRegion, City, VisitedCity
from allauth.account.decorators import secure_admin_login
admin.autodiscover()
@ -138,6 +138,7 @@ admin.site.register(ChecklistItem)
admin.site.register(AdventureImage, AdventureImageAdmin)
admin.site.register(Category, CategoryAdmin)
admin.site.register(City, CityAdmin)
admin.site.register(VisitedCity)
admin.site.site_header = 'AdventureLog Admin'
admin.site.site_title = 'AdventureLog Admin Site'

View file

@ -0,0 +1,24 @@
# Generated by Django 5.0.8 on 2025-01-09 17:00
import django.db.models.deletion
from django.conf import settings
from django.db import migrations, models
class Migration(migrations.Migration):
dependencies = [
('worldtravel', '0012_city'),
migrations.swappable_dependency(settings.AUTH_USER_MODEL),
]
operations = [
migrations.CreateModel(
name='VisitedCity',
fields=[
('id', models.AutoField(primary_key=True, serialize=False)),
('city', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to='worldtravel.city')),
('user_id', models.ForeignKey(default=1, on_delete=django.db.models.deletion.CASCADE, to=settings.AUTH_USER_MODEL)),
],
),
]

View file

@ -60,4 +60,21 @@ class VisitedRegion(models.Model):
def save(self, *args, **kwargs):
if VisitedRegion.objects.filter(user_id=self.user_id, region=self.region).exists():
raise ValidationError("Region already visited by user.")
super().save(*args, **kwargs)
super().save(*args, **kwargs)
class VisitedCity(models.Model):
id = models.AutoField(primary_key=True)
user_id = models.ForeignKey(
User, on_delete=models.CASCADE, default=default_user_id)
city = models.ForeignKey(City, on_delete=models.CASCADE)
def __str__(self):
return f'{self.city.name} ({self.city.region.name}) visited by: {self.user_id.username}'
def save(self, *args, **kwargs):
if VisitedCity.objects.filter(user_id=self.user_id, city=self.city).exists():
raise ValidationError("City already visited by user.")
super().save(*args, **kwargs)
class Meta:
verbose_name_plural = "Visited Cities"

View file

@ -1,5 +1,5 @@
import os
from .models import Country, Region, VisitedRegion, City
from .models import Country, Region, VisitedRegion, City, VisitedCity
from rest_framework import serializers
from main.utils import CustomModelSerializer
@ -52,4 +52,14 @@ class VisitedRegionSerializer(CustomModelSerializer):
class Meta:
model = VisitedRegion
fields = ['id', 'user_id', 'region', 'longitude', 'latitude', 'name']
read_only_fields = ['user_id', 'id', 'longitude', 'latitude', 'name']
class VisitedCitySerializer(CustomModelSerializer):
longitude = serializers.DecimalField(source='city.longitude', max_digits=9, decimal_places=6, read_only=True)
latitude = serializers.DecimalField(source='city.latitude', max_digits=9, decimal_places=6, read_only=True)
name = serializers.CharField(source='city.name', read_only=True)
class Meta:
model = VisitedCity
fields = ['id', 'user_id', 'city', 'longitude', 'latitude', 'name']
read_only_fields = ['user_id', 'id', 'longitude', 'latitude', 'name']

View file

@ -2,15 +2,17 @@
from django.urls import include, path
from rest_framework.routers import DefaultRouter
from .views import CountryViewSet, RegionViewSet, VisitedRegionViewSet, regions_by_country, visits_by_country, cities_by_region
from .views import CountryViewSet, RegionViewSet, VisitedRegionViewSet, regions_by_country, visits_by_country, cities_by_region, VisitedCityViewSet, visits_by_region
router = DefaultRouter()
router.register(r'countries', CountryViewSet, basename='countries')
router.register(r'regions', RegionViewSet, basename='regions')
router.register(r'visitedregion', VisitedRegionViewSet, basename='visitedregion')
router.register(r'visitedcity', VisitedCityViewSet, basename='visitedcity')
urlpatterns = [
path('', include(router.urls)),
path('<str:country_code>/regions/', regions_by_country, name='regions-by-country'),
path('<str:country_code>/visits/', visits_by_country, name='visits-by-country'),
path('regions/<str:region_id>/cities/', cities_by_region, name='cities-by-region'),
path('regions/<str:region_id>/cities/visits/', visits_by_region, name='visits-by-region'),
]

View file

@ -1,6 +1,6 @@
from django.shortcuts import render
from .models import Country, Region, VisitedRegion, City
from .serializers import CitySerializer, CountrySerializer, RegionSerializer, VisitedRegionSerializer
from .models import Country, Region, VisitedRegion, City, VisitedCity
from .serializers import CitySerializer, CountrySerializer, RegionSerializer, VisitedRegionSerializer, VisitedCitySerializer
from rest_framework import viewsets, status
from rest_framework.permissions import IsAuthenticated
from django.shortcuts import get_object_or_404
@ -41,6 +41,15 @@ def cities_by_region(request, region_id):
serializer = CitySerializer(cities, many=True)
return Response(serializer.data)
@api_view(['GET'])
@permission_classes([IsAuthenticated])
def visits_by_region(request, region_id):
region = get_object_or_404(Region, id=region_id)
visits = VisitedCity.objects.filter(city__region=region, user_id=request.user.id)
serializer = VisitedCitySerializer(visits, many=True)
return Response(serializer.data)
class CountryViewSet(viewsets.ReadOnlyModelViewSet):
queryset = Country.objects.all().order_by('name')
serializer_class = CountrySerializer
@ -101,4 +110,42 @@ class VisitedRegionViewSet(viewsets.ModelViewSet):
serializer.is_valid(raise_exception=True)
self.perform_create(serializer)
headers = self.get_success_headers(serializer.data)
return Response(serializer.data, status=status.HTTP_201_CREATED, headers=headers)
return Response(serializer.data, status=status.HTTP_201_CREATED, headers=headers)
def destroy(self, request, **kwargs):
# delete by region id
region = get_object_or_404(Region, id=kwargs['pk'])
visited_region = VisitedRegion.objects.filter(user_id=request.user.id, region=region)
if visited_region.exists():
visited_region.delete()
return Response(status=status.HTTP_204_NO_CONTENT)
else:
return Response({"error": "Visited region not found."}, status=status.HTTP_404_NOT_FOUND)
class VisitedCityViewSet(viewsets.ModelViewSet):
serializer_class = VisitedCitySerializer
permission_classes = [IsAuthenticated]
def get_queryset(self):
return VisitedCity.objects.filter(user_id=self.request.user.id)
def perform_create(self, serializer):
serializer.save(user_id=self.request.user)
def create(self, request, *args, **kwargs):
request.data['user_id'] = request.user
serializer = self.get_serializer(data=request.data)
serializer.is_valid(raise_exception=True)
self.perform_create(serializer)
headers = self.get_success_headers(serializer.data)
return Response(serializer.data, status=status.HTTP_201_CREATED, headers=headers)
def destroy(self, request, **kwargs):
# delete by city id
city = get_object_or_404(City, id=kwargs['pk'])
visited_city = VisitedCity.objects.filter(user_id=request.user.id, city=city)
if visited_city.exists():
visited_city.delete()
return Response(status=status.HTTP_204_NO_CONTENT)
else:
return Response({"error": "Visited city not found."}, status=status.HTTP_404_NOT_FOUND)