Skip to content

Commit ddaeb73

Browse files
refactor: Simplify serializers and manager logic
This commit introduces several refactoring improvements across the codebase to enhance clarity, remove redundancy, and simplify complex logic. Key changes include: - **PromoManager:** Simplified the filtering logic for promo codes by restructuring the query conditions for age and country, making it more readable. - **Promo Model:** Streamlined property methods (`is_available`, `get_available_unique_codes`) for better readability and consistency. - **Serializers (`business`, `core`, `user`):** - Removed unnecessary validation checks that are already handled by Django Rest Framework's default behavior (e.g., required fields). - Simplified complex conditional logic and improved handling of query parameters (`MultiCountryField`). - Made user-context checks in `BaseUserPromoSerializer` more direct and less defensive, assuming an authenticated request context. - **Settings:** Updated paths for password validators to reflect their new location in the `user` application.
1 parent 91644b8 commit ddaeb73

File tree

6 files changed

+41
-92
lines changed

6 files changed

+41
-92
lines changed

promo_code/business/managers.py

Lines changed: 18 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -122,32 +122,29 @@ def _q_is_targeted(self, country, age):
122122
"""
123123
empty = django.db.models.Q(target={})
124124

125-
if country:
126-
match_country = django.db.models.Q(target__country__iexact=country)
127-
else:
128-
match_country = django.db.models.Q()
125+
match_country = (
126+
django.db.models.Q(target__country__iexact=country)
127+
if country
128+
else django.db.models.Q()
129+
)
130+
129131
no_country = ~django.db.models.Q(
130132
target__has_key='country',
131133
) | django.db.models.Q(target__country__isnull=True)
134+
132135
country_ok = match_country | no_country
133136

134-
no_age_limits = ~django.db.models.Q(
135-
target__has_key='age_from',
136-
) & ~django.db.models.Q(target__has_key='age_until')
137-
if age is None:
138-
age_ok = no_age_limits
139-
else:
140-
from_ok = (
141-
~django.db.models.Q(target__has_key='age_from')
142-
| django.db.models.Q(target__age_from__isnull=True)
143-
| django.db.models.Q(target__age_from__lte=age)
144-
)
145-
until_ok = (
146-
~django.db.models.Q(target__has_key='age_until')
147-
| django.db.models.Q(target__age_until__isnull=True)
148-
| django.db.models.Q(target__age_until__gte=age)
149-
)
150-
age_ok = no_age_limits | (from_ok & until_ok)
137+
from_ok = (
138+
~django.db.models.Q(target__has_key='age_from')
139+
| django.db.models.Q(target__age_from__isnull=True)
140+
| django.db.models.Q(target__age_from__lte=age)
141+
)
142+
until_ok = (
143+
~django.db.models.Q(target__has_key='age_until')
144+
| django.db.models.Q(target__age_until__isnull=True)
145+
| django.db.models.Q(target__age_until__gte=age)
146+
)
147+
age_ok = from_ok & until_ok
151148

152149
return empty | (country_ok & age_ok)
153150

promo_code/business/models.py

Lines changed: 3 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -102,10 +102,7 @@ def is_active(self) -> bool:
102102

103103
if self.mode == business.constants.PROMO_MODE_UNIQUE:
104104
return self.unique_codes.filter(is_used=False).exists()
105-
if self.mode == business.constants.PROMO_MODE_COMMON:
106-
return self.used_count < self.max_count
107-
108-
return True
105+
return self.used_count < self.max_count
109106

110107
@property
111108
def get_like_count(self) -> int:
@@ -122,10 +119,8 @@ def get_used_codes_count(self) -> int:
122119
return self.used_count
123120

124121
@property
125-
def get_available_unique_codes(self) -> list[str] | None:
126-
if self.mode == business.constants.PROMO_MODE_UNIQUE:
127-
return [c.code for c in self.unique_codes.filter(is_used=False)]
128-
return None
122+
def get_available_unique_codes(self) -> list[str]:
123+
return [c.code for c in self.unique_codes.filter(is_used=False)]
129124

130125

131126
class PromoCode(django.db.models.Model):

promo_code/business/serializers.py

Lines changed: 6 additions & 23 deletions
Original file line numberDiff line numberDiff line change
@@ -70,11 +70,6 @@ def validate(self, attrs):
7070
email = attrs.get('email')
7171
password = attrs.get('password')
7272

73-
if not email or not password:
74-
raise rest_framework.serializers.ValidationError(
75-
'Both email and password are required.',
76-
)
77-
7873
try:
7974
company = business.models.Company.objects.get(email=email)
8075
except business.models.Company.DoesNotExist:
@@ -151,24 +146,12 @@ def __init__(self, **kwargs):
151146
super().__init__(**kwargs)
152147

153148
def to_internal_value(self, data):
154-
if not data or not isinstance(data, list):
155-
raise rest_framework.serializers.ValidationError(
156-
'At least one country must be specified.',
157-
)
158-
159-
# (&country=us,fr)
160-
if len(data) == 1 and ',' in data[0]:
161-
countries_str = data[0]
162-
if '' in [s.strip() for s in countries_str.split(',')]:
163-
raise rest_framework.serializers.ValidationError(
164-
'Invalid country format.',
165-
)
166-
data = [country.strip() for country in countries_str.split(',')]
167-
168-
if any(not item for item in data):
169-
raise rest_framework.serializers.ValidationError(
170-
'Empty value for country is not allowed.',
171-
)
149+
if (
150+
isinstance(data, list)
151+
and len(data) == 1
152+
and isinstance(data[0], str)
153+
):
154+
data = [item.strip() for item in data[0].split(',')]
172155

173156
return super().to_internal_value(data)
174157

promo_code/core/serializers.py

Lines changed: 6 additions & 27 deletions
Original file line numberDiff line numberDiff line change
@@ -161,16 +161,8 @@ def validate(self, data):
161161

162162
if mode == business.constants.PROMO_MODE_COMMON:
163163
self._validate_common(data)
164-
elif mode == business.constants.PROMO_MODE_UNIQUE:
165-
self._validate_unique(data)
166-
elif mode is None:
167-
raise rest_framework.serializers.ValidationError(
168-
{'mode': 'This field is required.'},
169-
)
170164
else:
171-
raise rest_framework.serializers.ValidationError(
172-
{'mode': 'Invalid mode.'},
173-
)
165+
self._validate_unique(data)
174166

175167
return data
176168

@@ -374,31 +366,18 @@ def get_is_liked_by_user(self, obj: business.models.Promo) -> bool:
374366
"""
375367
Checks whether the current user has liked this promo.
376368
"""
377-
request = self.context.get('request')
378-
if (
379-
request
380-
and hasattr(request, 'user')
381-
and request.user.is_authenticated
382-
):
383-
return user.models.PromoLike.objects.filter(
384-
promo=obj,
385-
user=request.user,
386-
).exists()
387-
return False
369+
request = self.context['request']
370+
return user.models.PromoLike.objects.filter(
371+
promo=obj,
372+
user=request.user,
373+
).exists()
388374

389375
def get_is_activated_by_user(self, obj: business.models.Promo) -> bool:
390376
"""
391377
Checks whether the current user has activated this promo code.
392378
"""
393379
request = self.context.get('request')
394380

395-
if not (
396-
request
397-
and hasattr(request, 'user')
398-
and request.user.is_authenticated
399-
):
400-
return False
401-
402381
return user.models.PromoActivationHistory.objects.filter(
403382
promo=obj,
404383
user=request.user,

promo_code/promo_code/settings.py

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -180,20 +180,20 @@ def load_bool(name, default):
180180
'.NumericPasswordValidator',
181181
},
182182
{
183-
'NAME': 'promo_code.validators.AsciiValidator',
183+
'NAME': 'user.validators.AsciiValidator',
184184
},
185185
{
186-
'NAME': 'promo_code.validators.SpecialCharacterValidator',
186+
'NAME': 'user.validators.SpecialCharacterValidator',
187187
'OPTIONS': {'special_chars': '[@$!%*?&]'},
188188
},
189189
{
190-
'NAME': 'promo_code.validators.NumericValidator',
190+
'NAME': 'user.validators.NumericValidator',
191191
},
192192
{
193-
'NAME': 'promo_code.validators.LowercaseValidator',
193+
'NAME': 'user.validators.LowercaseValidator',
194194
},
195195
{
196-
'NAME': 'promo_code.validators.UppercaseValidator',
196+
'NAME': 'user.validators.UppercaseValidator',
197197
},
198198
]
199199

promo_code/user/serializers.py

Lines changed: 3 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -60,12 +60,6 @@ def authenticate_user(self, attrs):
6060
email = attrs.get('email')
6161
password = attrs.get('password')
6262

63-
if not email or not password:
64-
raise rest_framework.exceptions.ValidationError(
65-
{'detail': 'Both email and password are required'},
66-
code='required',
67-
)
68-
6963
user = django.contrib.auth.authenticate(
7064
request=self.context.get('request'),
7165
email=email,
@@ -153,8 +147,9 @@ def _invalidate_cache(self, instance):
153147

154148
user_type = instance.__class__.__name__.lower()
155149
token_version = getattr(instance, 'token_version', None)
156-
cache_key = f'auth_instance_{user_type}_{instance.id}_v{token_version}'
157-
django.core.cache.cache.delete(cache_key)
150+
django.core.cache.cache.delete(
151+
f'auth_instance_{user_type}_{instance.id}_v{token_version}',
152+
)
158153

159154

160155
class UserFeedQuerySerializer(

0 commit comments

Comments
 (0)