Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 4 additions & 0 deletions auth_app/api/serializers.py
Original file line number Diff line number Diff line change
Expand Up @@ -17,20 +17,23 @@ class RegistrationSerializer(serializers.Serializer):
)

def validate_username(self, value):
"""Reject a username that is already taken."""
if get_user_model().objects.filter(username=value).exists():
raise serializers.ValidationError(
'A user with that username already exists.'
)
return value

def validate(self, attrs):
"""Confirm the password and its repetition match."""
if attrs['password'] != attrs['repeated_password']:
raise serializers.ValidationError(
{'repeated_password': ['Passwords do not match.']}
)
return attrs

def create(self, validated_data):
"""Create the user and its matching profile from validated input."""
validated_data.pop('repeated_password', None)
user = get_user_model().objects.create_user(
username=validated_data['username'],
Expand All @@ -51,6 +54,7 @@ class LoginSerializer(serializers.Serializer):
password = serializers.CharField(write_only=True)

def validate(self, attrs):
"""Authenticate the credentials and attach the resolved user."""
user = authenticate(
username=attrs['username'],
password=attrs['password'],
Expand Down
7 changes: 7 additions & 0 deletions offers_app/api/serializers.py
Original file line number Diff line number Diff line change
Expand Up @@ -84,6 +84,7 @@ class Meta:
]

def validate(self, attrs):
"""Require the full detail set when creating a new offer."""
if self.instance is None and 'details' not in attrs:
raise serializers.ValidationError(
{
Expand All @@ -93,6 +94,7 @@ def validate(self, attrs):
return super().validate(attrs)

def validate_details(self, value):
"""Enforce exactly one basic/standard/premium detail on create."""
if self.instance is not None:
return self._validate_update_details(value)

Expand All @@ -105,6 +107,7 @@ def validate_details(self, value):
return value

def _validate_update_details(self, value):
"""Ensure updated details reference offer types that already exist."""
existing_types = set(self.instance.details.values_list('offer_type', flat=True))
for detail in value:
offer_type = detail.get('offer_type')
Expand All @@ -119,6 +122,7 @@ def _validate_update_details(self, value):
return value

def create(self, validated_data):
"""Create the offer for the request user with its nested details."""
details_data = validated_data.pop('details', None)
if 'image' in validated_data and validated_data['image'] is None:
validated_data['image'] = ''
Expand All @@ -130,6 +134,7 @@ def create(self, validated_data):
return offer

def update(self, instance, validated_data):
"""Update the offer and apply changes to its existing details."""
details_data = validated_data.pop('details', [])
if 'image' in validated_data and validated_data['image'] is None:
validated_data['image'] = ''
Expand All @@ -138,10 +143,12 @@ def update(self, instance, validated_data):
return offer

def _create_details(self, offer, details_data):
"""Create the nested detail rows for a newly created offer."""
for detail_data in details_data:
OfferDetail.objects.create(offer=offer, **detail_data)

def _update_details(self, offer, details_data):
"""Apply field updates to each existing detail by offer type."""
for detail_data in details_data:
offer_type = detail_data.pop('offer_type')
detail = offer.details.get(offer_type=offer_type)
Expand Down
3 changes: 3 additions & 0 deletions orders_app/api/serializers.py
Original file line number Diff line number Diff line change
Expand Up @@ -46,6 +46,7 @@ def get_price(self, obj):
return float(obj.price)

def validate(self, attrs):
"""Restrict updates to the status field only."""
if self.instance is not None:
invalid_fields = set(self.initial_data) - {'status'}
if invalid_fields:
Expand All @@ -55,6 +56,7 @@ def validate(self, attrs):
return super().validate(attrs)

def create(self, validated_data):
"""Create an order by copying the referenced offer detail."""
offer_detail = self._get_offer_detail(validated_data)
return Order.objects.create(
customer_user=self.context['request'].user,
Expand All @@ -68,5 +70,6 @@ def create(self, validated_data):
)

def _get_offer_detail(self, validated_data):
"""Fetch the referenced offer detail or raise a 404."""
queryset = OfferDetail.objects.select_related('offer', 'offer__user')
return get_object_or_404(queryset, id=validated_data.pop('offer_detail_id'))
1 change: 1 addition & 0 deletions profiles_app/api/serializers.py
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,7 @@ class Meta:
]

def update(self, instance, validated_data):
"""Update the profile and sync the email onto the related user."""
user_data = validated_data.pop('user', {})
if 'email' in user_data:
instance.user.email = user_data['email']
Expand Down
4 changes: 4 additions & 0 deletions reviews_app/api/serializers.py
Original file line number Diff line number Diff line change
Expand Up @@ -26,19 +26,22 @@ class Meta:
]

def validate(self, attrs):
"""Check the target is a business user and not yet reviewed."""
business_user = attrs.get('business_user')
reviewer = self.context['request'].user
self._validate_business_user(business_user)
self._validate_unique_review(business_user, reviewer)
return super().validate(attrs)

def create(self, validated_data):
"""Create the review with the request user as its reviewer."""
return Review.objects.create(
reviewer=self.context['request'].user,
**validated_data,
)

def _validate_business_user(self, business_user):
"""Reject a review target that is not a business user."""
is_business_user = UserProfile.objects.filter(
user=business_user,
type=UserProfile.ProfileType.BUSINESS,
Expand All @@ -49,6 +52,7 @@ def _validate_business_user(self, business_user):
)

def _validate_unique_review(self, business_user, reviewer):
"""Reject a second review by the same reviewer for the business."""
if Review.objects.filter(
business_user=business_user,
reviewer=reviewer,
Expand Down