Skip to content

Commit 77fb3f0

Browse files
Merge pull request #29 from RandomProgramm3r/develop
refactor(business): Use constants for promo codes - Move promo-related magic values (mode identifiers, length limits, choice lists) into `business.constants` - Update `Promo` and `PromoCode` models to reference `PROMO_MODE_CHOICES`, `PROMO_UNIQUE_CODE_MAX_LENGTH`, etc. - Update serializers to use these constants for validation and field definitions
2 parents cfac401 + 3118d3c commit 77fb3f0

File tree

5 files changed

+97
-48
lines changed

5 files changed

+97
-48
lines changed

promo_code/business/constants.py

Lines changed: 36 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,36 @@
1+
# === Promo ===
2+
PROMO_MODE_COMMON = 'COMMON'
3+
PROMO_MODE_UNIQUE = 'UNIQUE'
4+
PROMO_MODE_CHOICES = [
5+
(PROMO_MODE_COMMON, 'Common'),
6+
(PROMO_MODE_UNIQUE, 'Unique'),
7+
]
8+
9+
10+
PROMO_DESC_MIN_LENGTH = 10
11+
PROMO_DESC_MAX_LENGTH = 300
12+
PROMO_IMAGE_URL_MAX_LENGTH = 350
13+
14+
15+
# === Promo Common ===
16+
PROMO_COMMON_CODE_MIN_LENGTH = 5
17+
PROMO_COMMON_CODE_MAX_LENGTH = 30
18+
PROMO_COMMON_MIN_COUNT = 1
19+
PROMO_COMMON_MAX_COUNT = 100_000_000
20+
21+
22+
# === Promo Unique ===
23+
PROMO_UNIQUE_CODE_MIN_LENGTH = 3
24+
PROMO_UNIQUE_CODE_MAX_LENGTH = 30
25+
PROMO_UNIQUE_LIST_MIN_ITEMS = 1
26+
PROMO_UNIQUE_LIST_MAX_ITEMS = 5000
27+
PROMO_UNIQUE_MAX_COUNT = 1
28+
29+
30+
# === Target ===
31+
TARGET_AGE_MIN = 0
32+
TARGET_AGE_MAX = 100
33+
TARGET_COUNTRY_CODE_LENGTH = 2
34+
TARGET_CATEGORY_MIN_LENGTH = 2
35+
TARGET_CATEGORY_MAX_LENGTH = 20
36+
TARGET_CATEGORY_MAX_ITEMS = 20

promo_code/business/managers.py

Lines changed: 5 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
import django.contrib.auth.models
22
import django.db.models
33

4+
import business.constants
45
import business.models
56

67

@@ -36,10 +37,12 @@ def create_promo(
3637
**kwargs,
3738
)
3839

39-
if promo.mode == business.models.Promo.MODE_COMMON:
40+
if promo.mode == business.constants.PROMO_MODE_COMMON:
4041
promo.promo_common = promo_common
4142
promo.save(update_fields=['promo_common'])
42-
elif promo.mode == business.models.Promo.MODE_UNIQUE and promo_unique:
43+
elif (
44+
promo.mode == business.constants.PROMO_MODE_UNIQUE and promo_unique
45+
):
4346
self._create_unique_codes(promo, promo_unique)
4447

4548
return promo

promo_code/business/models.py

Lines changed: 13 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@
33
import django.contrib.auth.models
44
import django.db.models
55

6+
import business.constants
67
import business.managers
78

89

@@ -33,13 +34,6 @@ def __str__(self):
3334

3435

3536
class Promo(django.db.models.Model):
36-
MODE_COMMON = 'COMMON'
37-
MODE_UNIQUE = 'UNIQUE'
38-
MODE_CHOICES = [
39-
(MODE_COMMON, 'Common'),
40-
(MODE_UNIQUE, 'Unique'),
41-
]
42-
4337
id = django.db.models.UUIDField(
4438
'UUID',
4539
primary_key=True,
@@ -53,19 +47,24 @@ class Promo(django.db.models.Model):
5347
null=True,
5448
blank=True,
5549
)
56-
description = django.db.models.CharField(max_length=300)
50+
description = django.db.models.CharField(
51+
max_length=business.constants.PROMO_DESC_MAX_LENGTH,
52+
)
5753
image_url = django.db.models.URLField(
58-
max_length=350,
54+
max_length=business.constants.PROMO_IMAGE_URL_MAX_LENGTH,
5955
blank=True,
6056
null=True,
6157
)
6258
target = django.db.models.JSONField(default=dict)
6359
max_count = django.db.models.IntegerField()
6460
active_from = django.db.models.DateField(null=True, blank=True)
6561
active_until = django.db.models.DateField(null=True, blank=True)
66-
mode = django.db.models.CharField(max_length=10, choices=MODE_CHOICES)
62+
mode = django.db.models.CharField(
63+
max_length=10,
64+
choices=business.constants.PROMO_MODE_CHOICES,
65+
)
6766
promo_common = django.db.models.CharField(
68-
max_length=30,
67+
max_length=business.constants.PROMO_COMMON_CODE_MAX_LENGTH,
6968
blank=True,
7069
null=True,
7170
)
@@ -86,7 +85,9 @@ class PromoCode(django.db.models.Model):
8685
related_name='unique_codes',
8786
to_field='id',
8887
)
89-
code = django.db.models.CharField(max_length=30)
88+
code = django.db.models.CharField(
89+
max_length=business.constants.PROMO_UNIQUE_CODE_MAX_LENGTH,
90+
)
9091
is_used = django.db.models.BooleanField(default=False)
9192
used_at = django.db.models.DateTimeField(null=True, blank=True)
9293

promo_code/business/serializers.py

Lines changed: 32 additions & 27 deletions
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,7 @@
1313
import rest_framework_simplejwt.tokens
1414
import rest_framework_simplejwt.views
1515

16+
import business.constants
1617
import business.models as business_models
1718
import business.validators
1819

@@ -151,26 +152,26 @@ def validate(self, attrs):
151152

152153
class TargetSerializer(rest_framework.serializers.Serializer):
153154
age_from = rest_framework.serializers.IntegerField(
154-
min_value=0,
155-
max_value=100,
155+
min_value=business.constants.TARGET_AGE_MIN,
156+
max_value=business.constants.TARGET_AGE_MAX,
156157
required=False,
157158
)
158159
age_until = rest_framework.serializers.IntegerField(
159-
min_value=0,
160-
max_value=100,
160+
min_value=business.constants.TARGET_AGE_MIN,
161+
max_value=business.constants.TARGET_AGE_MAX,
161162
required=False,
162163
)
163164
country = rest_framework.serializers.CharField(
164-
max_length=2,
165-
min_length=2,
165+
max_length=business.constants.TARGET_COUNTRY_CODE_LENGTH,
166+
min_length=business.constants.TARGET_COUNTRY_CODE_LENGTH,
166167
required=False,
167168
)
168169
categories = rest_framework.serializers.ListField(
169170
child=rest_framework.serializers.CharField(
170-
min_length=2,
171-
max_length=20,
171+
min_length=business.constants.TARGET_CATEGORY_MIN_LENGTH,
172+
max_length=business.constants.TARGET_CATEGORY_MAX_LENGTH,
172173
),
173-
max_length=20,
174+
max_length=business.constants.TARGET_CATEGORY_MAX_ITEMS,
174175
required=False,
175176
allow_empty=True,
176177
)
@@ -202,31 +203,31 @@ def validate(self, data):
202203

203204
class PromoCreateSerializer(rest_framework.serializers.ModelSerializer):
204205
description = rest_framework.serializers.CharField(
205-
min_length=10,
206-
max_length=300,
206+
min_length=business.constants.PROMO_DESC_MIN_LENGTH,
207+
max_length=business.constants.PROMO_DESC_MAX_LENGTH,
207208
required=True,
208209
)
209210
image_url = rest_framework.serializers.CharField(
210211
required=False,
211-
max_length=350,
212+
max_length=business.constants.PROMO_IMAGE_URL_MAX_LENGTH,
212213
validators=[
213214
django.core.validators.URLValidator(schemes=['http', 'https']),
214215
],
215216
)
216217
target = TargetSerializer(required=True, allow_null=True)
217218
promo_common = rest_framework.serializers.CharField(
218-
min_length=5,
219-
max_length=30,
219+
min_length=business.constants.PROMO_COMMON_CODE_MIN_LENGTH,
220+
max_length=business.constants.PROMO_COMMON_CODE_MAX_LENGTH,
220221
required=False,
221222
allow_null=True,
222223
)
223224
promo_unique = rest_framework.serializers.ListField(
224225
child=rest_framework.serializers.CharField(
225-
min_length=3,
226-
max_length=30,
226+
min_length=business.constants.PROMO_UNIQUE_CODE_MIN_LENGTH,
227+
max_length=business.constants.PROMO_UNIQUE_CODE_MAX_LENGTH,
227228
),
228-
min_length=1,
229-
max_length=5000,
229+
min_length=business.constants.PROMO_UNIQUE_LIST_MIN_ITEMS,
230+
max_length=business.constants.PROMO_UNIQUE_LIST_MAX_ITEMS,
230231
required=False,
231232
allow_null=True,
232233
)
@@ -267,7 +268,7 @@ def to_representation(self, instance):
267268
data = super().to_representation(instance)
268269
data['target'] = instance.target
269270

270-
if instance.mode == business_models.Promo.MODE_UNIQUE:
271+
if instance.mode == business.constants.PROMO_MODE_UNIQUE:
271272
data['promo_unique'] = [
272273
code.code for code in instance.unique_codes.all()
273274
]
@@ -318,18 +319,20 @@ class Meta:
318319
)
319320

320321
def get_promo_unique(self, obj):
321-
if obj.mode == business_models.Promo.MODE_UNIQUE:
322+
if obj.mode == business.constants.PROMO_MODE_UNIQUE:
322323
return [code.code for code in obj.unique_codes.all()]
323324

324325
return None
325326

326327
def get_like_count(self, obj):
328+
# TODO
327329
return 0
328330

329331
def get_used_count(self, obj):
330-
if obj.mode == business_models.Promo.MODE_UNIQUE:
332+
if obj.mode == business.constants.PROMO_MODE_UNIQUE:
331333
return obj.unique_codes.filter(is_used=True).count()
332334

335+
# TODO
333336
return 0
334337

335338
def get_active(self, obj):
@@ -354,7 +357,7 @@ def get_active(self, obj):
354357

355358
def to_representation(self, instance):
356359
data = super().to_representation(instance)
357-
if instance.mode == business_models.Promo.MODE_COMMON:
360+
if instance.mode == business.constants.PROMO_MODE_COMMON:
358361
data.pop('promo_unique', None)
359362
else:
360363
data.pop('promo_common', None)
@@ -368,13 +371,13 @@ class PromoDetailSerializer(rest_framework.serializers.ModelSerializer):
368371
read_only=True,
369372
)
370373
description = rest_framework.serializers.CharField(
371-
min_length=10,
372-
max_length=300,
374+
min_length=business.constants.PROMO_DESC_MIN_LENGTH,
375+
max_length=business.constants.PROMO_DESC_MAX_LENGTH,
373376
required=True,
374377
)
375378
image_url = rest_framework.serializers.CharField(
376379
required=False,
377-
max_length=350,
380+
max_length=business.constants.PROMO_IMAGE_URL_MAX_LENGTH,
378381
validators=[
379382
django.core.validators.URLValidator(schemes=['http', 'https']),
380383
],
@@ -407,7 +410,7 @@ class Meta:
407410
)
408411

409412
def get_promo_unique(self, obj):
410-
if obj.mode == business_models.Promo.MODE_UNIQUE:
413+
if obj.mode == business.constants.PROMO_MODE_UNIQUE:
411414
return [code.code for code in obj.unique_codes.all()]
412415

413416
return None
@@ -432,10 +435,12 @@ def validate(self, data):
432435
return validator.validate()
433436

434437
def get_like_count(self, obj):
438+
# TODO
435439
return 0
436440

437441
def get_used_count(self, obj):
438-
if obj.mode == business_models.Promo.MODE_UNIQUE:
442+
if obj.mode == business.constants.PROMO_MODE_UNIQUE:
439443
return obj.unique_codes.filter(is_used=True).count()
440444

445+
# TODO
441446
return 0

promo_code/business/validators.py

Lines changed: 11 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
import rest_framework.exceptions
2-
import rest_framework.permissions
32

3+
import business.constants
44
import business.models
55

66

@@ -60,14 +60,14 @@ def validate(self):
6060
active_until = self.full_data.get('active_until')
6161

6262
if mode not in [
63-
business.models.Promo.MODE_COMMON,
64-
business.models.Promo.MODE_UNIQUE,
63+
business.constants.PROMO_MODE_COMMON,
64+
business.constants.PROMO_MODE_UNIQUE,
6565
]:
6666
raise rest_framework.exceptions.ValidationError(
6767
{'mode': 'Invalid mode.'},
6868
)
6969

70-
if mode == business.models.Promo.MODE_COMMON:
70+
if mode == business.constants.PROMO_MODE_COMMON:
7171
if not promo_common:
7272
raise rest_framework.exceptions.ValidationError(
7373
{
@@ -84,7 +84,11 @@ def validate(self):
8484
),
8585
},
8686
)
87-
if max_count is None or not (0 <= max_count <= 100_000_000):
87+
if max_count is None or not (
88+
business.constants.PROMO_COMMON_MIN_COUNT
89+
< max_count
90+
<= business.constants.PROMO_COMMON_MAX_COUNT
91+
):
8892
raise rest_framework.exceptions.ValidationError(
8993
{
9094
'max_count': (
@@ -94,7 +98,7 @@ def validate(self):
9498
},
9599
)
96100

97-
elif mode == business.models.Promo.MODE_UNIQUE:
101+
elif mode == business.constants.PROMO_MODE_UNIQUE:
98102
if promo_common is not None:
99103
raise rest_framework.exceptions.ValidationError(
100104
{
@@ -103,7 +107,7 @@ def validate(self):
103107
),
104108
},
105109
)
106-
if max_count != 1:
110+
if max_count != business.constants.PROMO_UNIQUE_MAX_COUNT:
107111
raise rest_framework.exceptions.ValidationError(
108112
{'max_count': 'Must be 1 for UNIQUE mode.'},
109113
)

0 commit comments

Comments
 (0)