mirror of
https://github.com/seanmorley15/AdventureLog.git
synced 2025-07-25 07:49:37 +02:00
Enhance adventure management: add error handling for category fetch, implement unique email constraint in user model, and update adventure save logic to ensure category assignment
This commit is contained in:
parent
86d213bb8b
commit
736ede2417
15 changed files with 216 additions and 60 deletions
|
@ -0,0 +1,19 @@
|
|||
# Generated by Django 5.0.8 on 2024-11-17 21:43
|
||||
|
||||
from django.conf import settings
|
||||
from django.db import migrations
|
||||
|
||||
|
||||
class Migration(migrations.Migration):
|
||||
|
||||
dependencies = [
|
||||
('adventures', '0013_remove_adventure_type_alter_adventure_category'),
|
||||
migrations.swappable_dependency(settings.AUTH_USER_MODEL),
|
||||
]
|
||||
|
||||
operations = [
|
||||
migrations.AlterUniqueTogether(
|
||||
name='category',
|
||||
unique_together={('name', 'user_id')},
|
||||
),
|
||||
]
|
|
@ -1,4 +1,5 @@
|
|||
from collections.abc import Collection
|
||||
from typing import Iterable
|
||||
import uuid
|
||||
from django.db import models
|
||||
|
||||
|
@ -101,6 +102,17 @@ class Adventure(models.Model):
|
|||
if self.category:
|
||||
if self.user_id != self.category.user_id:
|
||||
raise ValidationError('Adventures must be associated with categories owned by the same user. Category owner: ' + self.category.user_id.username + ' Adventure owner: ' + self.user_id.username)
|
||||
|
||||
def save(self, force_insert: bool = False, force_update: bool = False, using: str | None = None, update_fields: Iterable[str] | None = None) -> None:
|
||||
"""
|
||||
Saves the current instance. If the instance is being inserted for the first time, it will be created in the database.
|
||||
If it already exists, it will be updated.
|
||||
"""
|
||||
if force_insert and force_update:
|
||||
raise ValueError("Cannot force both insert and updating in model saving.")
|
||||
if not self.category:
|
||||
self.category = Category.objects.get_or_create(user_id=self.user_id, name='general', display_name='General', icon='🌎')[0]
|
||||
return super().save(force_insert, force_update, using, update_fields)
|
||||
|
||||
def __str__(self):
|
||||
return self.name
|
||||
|
|
|
@ -37,7 +37,7 @@ class CategorySerializer(CustomModelSerializer):
|
|||
def get_num_adventures(self, obj):
|
||||
return Adventure.objects.filter(category=obj, user_id=obj.user_id).count()
|
||||
|
||||
class VisitSerializer(CustomModelSerializer):
|
||||
class VisitSerializer(serializers.ModelSerializer):
|
||||
|
||||
class Meta:
|
||||
model = Visit
|
||||
|
@ -47,13 +47,34 @@ class VisitSerializer(CustomModelSerializer):
|
|||
class AdventureSerializer(CustomModelSerializer):
|
||||
images = AdventureImageSerializer(many=True, read_only=True)
|
||||
visits = VisitSerializer(many=True, read_only=False)
|
||||
category = CategorySerializer(read_only=True)
|
||||
category = serializers.PrimaryKeyRelatedField(
|
||||
queryset=Category.objects.all(),
|
||||
write_only=True,
|
||||
required=False
|
||||
)
|
||||
category_object = CategorySerializer(source='category', read_only=True)
|
||||
is_visited = serializers.SerializerMethodField()
|
||||
|
||||
class Meta:
|
||||
model = Adventure
|
||||
fields = ['id', 'user_id', 'name', 'description', 'rating', 'activity_types', 'location', 'is_public', 'collection', 'created_at', 'updated_at', 'images', 'link', 'longitude', 'latitude', 'visits', 'is_visited', 'category']
|
||||
fields = [
|
||||
'id', 'user_id', 'name', 'description', 'rating', 'activity_types', 'location',
|
||||
'is_public', 'collection', 'created_at', 'updated_at', 'images', 'link', 'longitude',
|
||||
'latitude', 'visits', 'is_visited', 'category', 'category_object'
|
||||
]
|
||||
read_only_fields = ['id', 'created_at', 'updated_at', 'user_id', 'is_visited']
|
||||
|
||||
def to_representation(self, instance):
|
||||
representation = super().to_representation(instance)
|
||||
representation['category'] = representation.pop('category_object')
|
||||
return representation
|
||||
|
||||
def validate_category(self, category):
|
||||
# Check that the category belongs to the same user
|
||||
if category.user_id != self.context['request'].user:
|
||||
raise serializers.ValidationError('Category does not belong to the user.')
|
||||
return category
|
||||
|
||||
def get_is_visited(self, obj):
|
||||
current_date = timezone.now().date()
|
||||
for visit in obj.visits.all():
|
||||
|
@ -62,32 +83,24 @@ class AdventureSerializer(CustomModelSerializer):
|
|||
elif visit.start_date and not visit.end_date and (visit.start_date <= current_date):
|
||||
return True
|
||||
return False
|
||||
|
||||
|
||||
def to_representation(self, instance):
|
||||
representation = super().to_representation(instance)
|
||||
return representation
|
||||
|
||||
def create(self, validated_data):
|
||||
visits_data = validated_data.pop('visits', [])
|
||||
adventure = Adventure.objects.create(**validated_data)
|
||||
for visit_data in visits_data:
|
||||
Visit.objects.create(adventure=adventure, **visit_data)
|
||||
return adventure
|
||||
|
||||
|
||||
def update(self, instance, validated_data):
|
||||
visits_data = validated_data.pop('visits', [])
|
||||
|
||||
# Update Adventure fields
|
||||
|
||||
for attr, value in validated_data.items():
|
||||
setattr(instance, attr, value)
|
||||
instance.save()
|
||||
|
||||
# Get current visits
|
||||
|
||||
current_visits = instance.visits.all()
|
||||
current_visit_ids = set(current_visits.values_list('id', flat=True))
|
||||
|
||||
# Update or create visits
|
||||
|
||||
updated_visit_ids = set()
|
||||
for visit_data in visits_data:
|
||||
visit_id = visit_data.get('id')
|
||||
|
@ -98,16 +111,14 @@ class AdventureSerializer(CustomModelSerializer):
|
|||
visit.save()
|
||||
updated_visit_ids.add(visit_id)
|
||||
else:
|
||||
# If no ID is provided or ID doesn't exist, create new visit
|
||||
new_visit = Visit.objects.create(adventure=instance, **visit_data)
|
||||
updated_visit_ids.add(new_visit.id)
|
||||
|
||||
# Delete visits that are not in the updated data
|
||||
|
||||
visits_to_delete = current_visit_ids - updated_visit_ids
|
||||
instance.visits.filter(id__in=visits_to_delete).delete()
|
||||
|
||||
|
||||
return instance
|
||||
|
||||
|
||||
class TransportationSerializer(CustomModelSerializer):
|
||||
|
||||
class Meta:
|
||||
|
|
|
@ -116,8 +116,8 @@ class AdventureViewSet(viewsets.ModelViewSet):
|
|||
if not Category.objects.filter(user_id=request.user, name=type).exists():
|
||||
return Response({"error": f"Category {type} does not exist"}, status=400)
|
||||
|
||||
if not types:
|
||||
return Response({"error": "No valid types provided"}, status=400)
|
||||
if not types:
|
||||
return Response({"error": "At least one type must be provided"}, status=400)
|
||||
|
||||
queryset = Adventure.objects.filter(
|
||||
category__in=Category.objects.filter(name__in=types, user_id=request.user),
|
||||
|
|
|
@ -0,0 +1,27 @@
|
|||
# Generated by Django 5.0.8 on 2024-11-18 14:51
|
||||
|
||||
from django.db import migrations, models
|
||||
|
||||
def check_duplicate_email(apps, schema_editor):
|
||||
# sets an email to null if there are duplicates
|
||||
CustomUser = apps.get_model('users', 'CustomUser')
|
||||
duplicates = CustomUser.objects.values('email').annotate(email_count=models.Count('email')).filter(email_count__gt=1)
|
||||
for duplicate in duplicates:
|
||||
CustomUser.objects.filter(email=duplicate['email']).update(email=None)
|
||||
print(f"Duplicate email: {duplicate['email']}")
|
||||
|
||||
|
||||
class Migration(migrations.Migration):
|
||||
|
||||
dependencies = [
|
||||
('users', '0002_customuser_public_profile'),
|
||||
]
|
||||
|
||||
operations = [
|
||||
migrations.RunPython(check_duplicate_email),
|
||||
migrations.AlterField(
|
||||
model_name='customuser',
|
||||
name='email',
|
||||
field=models.EmailField(max_length=254, unique=True),
|
||||
),
|
||||
]
|
|
@ -4,6 +4,7 @@ from django.db import models
|
|||
from django_resized import ResizedImageField
|
||||
|
||||
class CustomUser(AbstractUser):
|
||||
email = models.EmailField(unique=True) # Override the email field with unique constraint
|
||||
profile_pic = ResizedImageField(force_format="WEBP", quality=75, null=True, blank=True, upload_to='profile-pics/')
|
||||
uuid = models.UUIDField(default=uuid.uuid4, editable=False, unique=True)
|
||||
public_profile = models.BooleanField(default=False)
|
||||
|
|
|
@ -63,6 +63,11 @@ class RegisterSerializer(serializers.Serializer):
|
|||
def validate(self, data):
|
||||
if data['password1'] != data['password2']:
|
||||
raise serializers.ValidationError(_("The two password fields didn't match."))
|
||||
|
||||
# check if a user with the same email already exists
|
||||
if User.objects.filter(email=data['email']).exists():
|
||||
raise serializers.ValidationError("This email is already in use.")
|
||||
|
||||
return data
|
||||
|
||||
def custom_signup(self, request, user):
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue