2
2
3
3
import django .contrib .auth .password_validation
4
4
import django .db .transaction
5
- import pycountry
6
5
import rest_framework .exceptions
7
6
import rest_framework .serializers
8
7
import rest_framework_simplejwt .exceptions
@@ -140,36 +139,14 @@ def get_active_company_from_token(self, token):
140
139
return company
141
140
142
141
143
- class CountryField (rest_framework .serializers .CharField ):
144
- """
145
- Custom field for validating country codes according to ISO 3166-1 alpha-2.
146
- """
147
-
148
- def __init__ (self , ** kwargs ):
149
- kwargs ['allow_blank' ] = False
150
- kwargs ['min_length' ] = business .constants .TARGET_COUNTRY_CODE_LENGTH
151
- kwargs ['max_length' ] = business .constants .TARGET_COUNTRY_CODE_LENGTH
152
- super ().__init__ (** kwargs )
153
-
154
- def to_internal_value (self , data ):
155
- code = super ().to_internal_value (data )
156
- try :
157
- pycountry .countries .lookup (code .upper ())
158
- except LookupError :
159
- raise rest_framework .serializers .ValidationError (
160
- 'Invalid ISO 3166-1 alpha-2 country code.' ,
161
- )
162
- return code
163
-
164
-
165
142
class MultiCountryField (rest_framework .serializers .ListField ):
166
143
"""
167
144
Custom field for handling multiple country codes,
168
145
passed either as a comma-separated list or as multiple parameters.
169
146
"""
170
147
171
148
def __init__ (self , ** kwargs ):
172
- kwargs ['child' ] = CountryField ()
149
+ kwargs ['child' ] = core . serializers . CountryField ()
173
150
kwargs ['allow_empty' ] = False
174
151
super ().__init__ (** kwargs )
175
152
@@ -196,234 +173,16 @@ def to_internal_value(self, data):
196
173
return super ().to_internal_value (data )
197
174
198
175
199
- class TargetSerializer (rest_framework .serializers .Serializer ):
200
- age_from = rest_framework .serializers .IntegerField (
201
- min_value = business .constants .TARGET_AGE_MIN ,
202
- max_value = business .constants .TARGET_AGE_MAX ,
203
- required = False ,
204
- )
205
- age_until = rest_framework .serializers .IntegerField (
206
- min_value = business .constants .TARGET_AGE_MIN ,
207
- max_value = business .constants .TARGET_AGE_MAX ,
208
- required = False ,
209
- )
210
- country = CountryField (required = False )
211
-
212
- categories = rest_framework .serializers .ListField (
213
- child = rest_framework .serializers .CharField (
214
- min_length = business .constants .TARGET_CATEGORY_MIN_LENGTH ,
215
- max_length = business .constants .TARGET_CATEGORY_MAX_LENGTH ,
216
- allow_blank = False ,
217
- ),
218
- max_length = business .constants .TARGET_CATEGORY_MAX_ITEMS ,
219
- required = False ,
220
- allow_empty = True ,
221
- )
222
-
223
- def validate (self , data ):
224
- age_from = data .get ('age_from' )
225
- age_until = data .get ('age_until' )
226
-
227
- if (
228
- age_from is not None
229
- and age_until is not None
230
- and age_from > age_until
231
- ):
232
- raise rest_framework .serializers .ValidationError (
233
- {'age_until' : 'Must be greater than or equal to age_from.' },
234
- )
235
- return data
236
-
237
-
238
- class BasePromoSerializer (rest_framework .serializers .ModelSerializer ):
239
- """
240
- Base serializer for promo, containing validation and representation logic.
241
- """
242
-
243
- image_url = rest_framework .serializers .URLField (
244
- required = False ,
245
- allow_blank = False ,
246
- max_length = business .constants .PROMO_IMAGE_URL_MAX_LENGTH ,
247
- )
248
- description = rest_framework .serializers .CharField (
249
- min_length = business .constants .PROMO_DESC_MIN_LENGTH ,
250
- max_length = business .constants .PROMO_DESC_MAX_LENGTH ,
251
- required = True ,
252
- )
253
- target = TargetSerializer (required = True , allow_null = True )
254
- promo_common = rest_framework .serializers .CharField (
255
- min_length = business .constants .PROMO_COMMON_CODE_MIN_LENGTH ,
256
- max_length = business .constants .PROMO_COMMON_CODE_MAX_LENGTH ,
257
- required = False ,
258
- allow_null = True ,
259
- allow_blank = False ,
260
- )
261
- promo_unique = rest_framework .serializers .ListField (
262
- child = rest_framework .serializers .CharField (
263
- min_length = business .constants .PROMO_UNIQUE_CODE_MIN_LENGTH ,
264
- max_length = business .constants .PROMO_UNIQUE_CODE_MAX_LENGTH ,
265
- allow_blank = False ,
266
- ),
267
- min_length = business .constants .PROMO_UNIQUE_LIST_MIN_ITEMS ,
268
- max_length = business .constants .PROMO_UNIQUE_LIST_MAX_ITEMS ,
269
- required = False ,
270
- allow_null = True ,
271
- )
272
-
273
- class Meta :
274
- model = business .models .Promo
275
- fields = (
276
- 'description' ,
277
- 'image_url' ,
278
- 'target' ,
279
- 'max_count' ,
280
- 'active_from' ,
281
- 'active_until' ,
282
- 'mode' ,
283
- 'promo_common' ,
284
- 'promo_unique' ,
285
- )
286
-
287
- def validate (self , data ):
288
- """
289
- Main validation method.
290
- Determines the mode and calls the corresponding validation method.
291
- """
292
-
293
- mode = data .get ('mode' , getattr (self .instance , 'mode' , None ))
294
-
295
- if mode == business .constants .PROMO_MODE_COMMON :
296
- self ._validate_common (data )
297
- elif mode == business .constants .PROMO_MODE_UNIQUE :
298
- self ._validate_unique (data )
299
- elif mode is None :
300
- raise rest_framework .serializers .ValidationError (
301
- {'mode' : 'This field is required.' },
302
- )
303
- else :
304
- raise rest_framework .serializers .ValidationError (
305
- {'mode' : 'Invalid mode.' },
306
- )
307
-
308
- return data
309
-
310
- def _validate_common (self , data ):
311
- """
312
- Validations for COMMON promo mode.
313
- """
314
-
315
- if 'promo_unique' in data and data ['promo_unique' ] is not None :
316
- raise rest_framework .serializers .ValidationError (
317
- {'promo_unique' : 'This field is not allowed for COMMON mode.' },
318
- )
319
-
320
- if self .instance is None and not data .get ('promo_common' ):
321
- raise rest_framework .serializers .ValidationError (
322
- {'promo_common' : 'This field is required for COMMON mode.' },
323
- )
324
-
325
- new_max_count = data .get ('max_count' )
326
- if self .instance and new_max_count is not None :
327
- used_count = self .instance .get_used_codes_count
328
- if used_count > new_max_count :
329
- raise rest_framework .serializers .ValidationError (
330
- {
331
- 'max_count' : (
332
- f'max_count ({ new_max_count } ) cannot be less than '
333
- f'used_count ({ used_count } ).'
334
- ),
335
- },
336
- )
337
-
338
- effective_max_count = (
339
- new_max_count
340
- if new_max_count is not None
341
- else getattr (self .instance , 'max_count' , None )
342
- )
343
-
344
- min_c = business .constants .PROMO_COMMON_MIN_COUNT
345
- max_c = business .constants .PROMO_COMMON_MAX_COUNT
346
- if effective_max_count is not None and not (
347
- min_c <= effective_max_count <= max_c
348
- ):
349
- raise rest_framework .serializers .ValidationError (
350
- {
351
- 'max_count' : (
352
- f'Must be between { min_c } and { max_c } for COMMON mode.'
353
- ),
354
- },
355
- )
356
-
357
- def _validate_unique (self , data ):
358
- """
359
- Validations for UNIQUE promo mode.
360
- """
361
-
362
- if 'promo_common' in data and data ['promo_common' ] is not None :
363
- raise rest_framework .serializers .ValidationError (
364
- {'promo_common' : 'This field is not allowed for UNIQUE mode.' },
365
- )
366
-
367
- if self .instance is None and not data .get ('promo_unique' ):
368
- raise rest_framework .serializers .ValidationError (
369
- {'promo_unique' : 'This field is required for UNIQUE mode.' },
370
- )
371
-
372
- effective_max_count = data .get (
373
- 'max_count' ,
374
- getattr (self .instance , 'max_count' , None ),
375
- )
376
-
377
- if (
378
- effective_max_count is not None
379
- and effective_max_count
380
- != business .constants .PROMO_UNIQUE_MAX_COUNT
381
- ):
382
- raise rest_framework .serializers .ValidationError (
383
- {
384
- 'max_count' : (
385
- 'Must be equal to '
386
- f'{ business .constants .PROMO_UNIQUE_MAX_COUNT } '
387
- 'for UNIQUE mode.'
388
- ),
389
- },
390
- )
391
-
392
- def to_representation (self , instance ):
393
- """
394
- Controls the display of fields in the response.
395
- """
396
-
397
- data = super ().to_representation (instance )
398
-
399
- if not instance .image_url :
400
- data .pop ('image_url' , None )
401
-
402
- if instance .mode == business .constants .PROMO_MODE_UNIQUE :
403
- data .pop ('promo_common' , None )
404
- if 'promo_unique' in self .fields and isinstance (
405
- self .fields ['promo_unique' ],
406
- rest_framework .serializers .SerializerMethodField ,
407
- ):
408
- data ['promo_unique' ] = self .get_promo_unique (instance )
409
- else :
410
- data ['promo_unique' ] = [
411
- code .code for code in instance .unique_codes .all ()
412
- ]
413
- else :
414
- data .pop ('promo_unique' , None )
415
-
416
- return data
417
-
418
-
419
- class PromoCreateSerializer (BasePromoSerializer ):
176
+ class PromoCreateSerializer (core .serializers .BaseCompanyPromoSerializer ):
420
177
url = rest_framework .serializers .HyperlinkedIdentityField (
421
178
view_name = 'api-business:promo-detail' ,
422
179
lookup_field = 'id' ,
423
180
)
424
181
425
- class Meta (BasePromoSerializer .Meta ):
426
- fields = ('url' ,) + BasePromoSerializer .Meta .fields
182
+ class Meta (core .serializers .BaseCompanyPromoSerializer .Meta ):
183
+ fields = (
184
+ 'url' ,
185
+ ) + core .serializers .BaseCompanyPromoSerializer .Meta .fields
427
186
428
187
def create (self , validated_data ):
429
188
target_data = validated_data .pop ('target' )
@@ -468,7 +227,7 @@ def validate(self, attrs):
468
227
return attrs
469
228
470
229
471
- class PromoDetailSerializer (BasePromoSerializer ):
230
+ class PromoDetailSerializer (core . serializers . BaseCompanyPromoSerializer ):
472
231
promo_id = rest_framework .serializers .UUIDField (
473
232
source = 'id' ,
474
233
read_only = True ,
@@ -496,8 +255,8 @@ class PromoDetailSerializer(BasePromoSerializer):
496
255
497
256
promo_unique = rest_framework .serializers .SerializerMethodField ()
498
257
499
- class Meta (BasePromoSerializer .Meta ):
500
- fields = BasePromoSerializer .Meta .fields + (
258
+ class Meta (core . serializers . BaseCompanyPromoSerializer .Meta ):
259
+ fields = core . serializers . BaseCompanyPromoSerializer .Meta .fields + (
501
260
'promo_id' ,
502
261
'company_name' ,
503
262
'like_count' ,
@@ -514,13 +273,12 @@ def get_promo_unique(self, obj):
514
273
def update (self , instance , validated_data ):
515
274
target_data = validated_data .pop ('target' , None )
516
275
517
- for attr , value in validated_data .items ():
518
- setattr (instance , attr , value )
276
+ instance = super ().update (instance , validated_data )
519
277
520
278
if target_data is not None :
521
279
instance .target = target_data
280
+ instance .save (update_fields = ['target' ])
522
281
523
- instance .save ()
524
282
return instance
525
283
526
284
0 commit comments