Skip to content

Commit 12f0d74

Browse files
Merge pull request #21 from RandomProgramm3r/develop
feat: Add PATCH method and PromoDetailSerializer for promo endpoint. - Implement PATCH endpoint at /api/business/promo/{id} to support updating promocode data. - Return appropriate HTTP status codes for success and error responses. (200, 400, 401, 403, 404)
2 parents 44ac9e6 + 4512dbf commit 12f0d74

File tree

2 files changed

+189
-8
lines changed

2 files changed

+189
-8
lines changed

promo_code/business/serializers.py

Lines changed: 140 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -154,20 +154,16 @@ class TargetSerializer(rest_framework.serializers.Serializer):
154154
min_value=0,
155155
max_value=100,
156156
required=False,
157-
allow_null=True,
158157
)
159158
age_until = rest_framework.serializers.IntegerField(
160159
min_value=0,
161160
max_value=100,
162161
required=False,
163-
allow_null=True,
164162
)
165163
country = rest_framework.serializers.CharField(
166164
max_length=2,
167165
min_length=2,
168166
required=False,
169-
allow_null=True,
170-
allow_blank=True,
171167
)
172168
categories = rest_framework.serializers.ListField(
173169
child=rest_framework.serializers.CharField(
@@ -193,9 +189,8 @@ def validate(self, data):
193189

194190
country = data.get('country')
195191
if country:
196-
country = country.strip().upper()
197192
try:
198-
pycountry.countries.lookup(country)
193+
pycountry.countries.lookup(country.strip().upper())
199194
data['country'] = country
200195
except LookupError:
201196
raise rest_framework.serializers.ValidationError(
@@ -218,7 +213,7 @@ class PromoCreateSerializer(rest_framework.serializers.ModelSerializer):
218213
django.core.validators.URLValidator(schemes=['http', 'https']),
219214
],
220215
)
221-
target = TargetSerializer(required=True)
216+
target = TargetSerializer(required=True, allow_null=True)
222217
promo_common = rest_framework.serializers.CharField(
223218
min_length=5,
224219
max_length=30,
@@ -446,3 +441,141 @@ def to_representation(self, instance):
446441
data.pop('promo_common', None)
447442

448443
return data
444+
445+
446+
class PromoDetailSerializer(rest_framework.serializers.ModelSerializer):
447+
promo_id = rest_framework.serializers.UUIDField(
448+
source='id',
449+
read_only=True,
450+
)
451+
target = TargetSerializer(allow_null=True, required=False)
452+
promo_unique = rest_framework.serializers.SerializerMethodField()
453+
company_name = rest_framework.serializers.CharField(
454+
source='company.name',
455+
read_only=True,
456+
)
457+
like_count = rest_framework.serializers.SerializerMethodField()
458+
used_count = rest_framework.serializers.SerializerMethodField()
459+
460+
class Meta:
461+
model = business_models.Promo
462+
fields = (
463+
'promo_id',
464+
'description',
465+
'image_url',
466+
'target',
467+
'max_count',
468+
'active_from',
469+
'active_until',
470+
'mode',
471+
'promo_common',
472+
'promo_unique',
473+
'company_name',
474+
'like_count',
475+
'used_count',
476+
)
477+
478+
def get_promo_unique(self, obj):
479+
if obj.mode == business_models.Promo.MODE_UNIQUE:
480+
return [code.code for code in obj.unique_codes.all()]
481+
482+
return None
483+
484+
def update(self, instance, validated_data):
485+
target_data = validated_data.pop('target', None)
486+
for attr, value in validated_data.items():
487+
setattr(instance, attr, value)
488+
489+
if target_data is not None:
490+
instance.target = target_data
491+
492+
instance.save()
493+
return instance
494+
495+
def validate(self, data):
496+
instance = self.instance
497+
full_data = {
498+
'mode': instance.mode,
499+
'promo_common': instance.promo_common,
500+
'promo_unique': None,
501+
'max_count': instance.max_count,
502+
'active_from': instance.active_from,
503+
'active_until': instance.active_until,
504+
'target': instance.target if instance.target is not None else {},
505+
}
506+
full_data.update(data)
507+
mode = full_data.get('mode')
508+
promo_common = full_data.get('promo_common')
509+
promo_unique = full_data.get('promo_unique')
510+
max_count = full_data.get('max_count')
511+
512+
if mode == business_models.Promo.MODE_COMMON:
513+
if not promo_common:
514+
raise rest_framework.serializers.ValidationError(
515+
{
516+
'promo_common': (
517+
'This field is required for COMMON mode.'
518+
),
519+
},
520+
)
521+
522+
if promo_unique is not None:
523+
raise rest_framework.serializers.ValidationError(
524+
{
525+
'promo_unique': (
526+
'This field is not allowed for COMMON mode.'
527+
),
528+
},
529+
)
530+
531+
if max_count < 0 or max_count > 100000000:
532+
raise rest_framework.serializers.ValidationError(
533+
{'max_count': 'Must be between 0 and 100,000,000.'},
534+
)
535+
536+
elif mode == business_models.Promo.MODE_UNIQUE:
537+
if not promo_unique:
538+
raise rest_framework.serializers.ValidationError(
539+
{
540+
'promo_unique': (
541+
'This field is required for UNIQUE mode.'
542+
),
543+
},
544+
)
545+
546+
if promo_common is not None:
547+
raise rest_framework.serializers.ValidationError(
548+
{
549+
'promo_common': (
550+
'This field is not allowed for UNIQUE mode.'
551+
),
552+
},
553+
)
554+
555+
if max_count != 1:
556+
raise rest_framework.serializers.ValidationError(
557+
{'max_count': 'Must be 1 for UNIQUE mode.'},
558+
)
559+
else:
560+
raise rest_framework.serializers.ValidationError(
561+
{'mode': 'Invalid mode.'},
562+
)
563+
564+
active_from = full_data.get('active_from')
565+
active_until = full_data.get('active_until')
566+
567+
if active_from and active_until and active_from > active_until:
568+
raise rest_framework.serializers.ValidationError(
569+
{'active_until': 'Must be after or equal to active_from.'},
570+
)
571+
572+
return data
573+
574+
def get_like_count(self, obj):
575+
return 0
576+
577+
def get_used_count(self, obj):
578+
if obj.mode == business_models.Promo.MODE_UNIQUE:
579+
return obj.unique_codes.filter(is_used=True).count()
580+
581+
return 0

promo_code/business/views.py

Lines changed: 49 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -308,11 +308,59 @@ def get(self, request, id):
308308
status=rest_framework.status.HTTP_403_FORBIDDEN,
309309
)
310310

311-
serializer = business.serializers.PromoCreateSerializer(
311+
serializer = business.serializers.PromoDetailSerializer(
312312
promo,
313313
)
314314

315315
return rest_framework.response.Response(
316316
serializer.data,
317317
status=rest_framework.status.HTTP_200_OK,
318318
)
319+
320+
def patch(self, request, id, *args, **kwargs):
321+
try:
322+
promo = business.models.Promo.objects.get(
323+
id=id,
324+
)
325+
except business.models.Promo.DoesNotExist:
326+
return rest_framework.response.Response(
327+
{
328+
'status': 'error',
329+
'message': 'Promo code not found.',
330+
},
331+
status=rest_framework.status.HTTP_404_NOT_FOUND,
332+
)
333+
334+
if promo.company != request.user:
335+
return rest_framework.response.Response(
336+
{
337+
'status': 'error',
338+
'message': ('Promo code does not belong to this company.'),
339+
},
340+
status=rest_framework.status.HTTP_403_FORBIDDEN,
341+
)
342+
343+
serializer = business.serializers.PromoDetailSerializer(
344+
promo,
345+
data=request.data,
346+
partial=True,
347+
context={
348+
'request': request,
349+
},
350+
)
351+
352+
if not serializer.is_valid():
353+
return rest_framework.response.Response(
354+
{
355+
'status': 'error',
356+
'message': 'Request data error.',
357+
},
358+
status=rest_framework.status.HTTP_400_BAD_REQUEST,
359+
)
360+
361+
serializer.save()
362+
363+
return rest_framework.response.Response(
364+
serializer.data,
365+
status=rest_framework.status.HTTP_200_OK,
366+
)

0 commit comments

Comments
 (0)