1
1
import django .db .models
2
2
import django .db .transaction
3
3
import django .shortcuts
4
- import django .utils .timezone
5
- import rest_framework .exceptions
6
4
import rest_framework .generics
7
5
import rest_framework .permissions
8
6
import rest_framework .response
11
9
import rest_framework_simplejwt .tokens
12
10
import rest_framework_simplejwt .views
13
11
14
- import business .constants
15
12
import business .models
16
13
import core .pagination
17
- import user .antifraud_service
18
14
import user .models
19
15
import user .permissions
20
16
import user .serializers
@@ -76,25 +72,19 @@ class UserPromoDetailView(rest_framework.generics.RetrieveAPIView):
76
72
Retrieve (GET) information about the promo without receiving a promo code.
77
73
"""
78
74
79
- queryset = (
80
- business .models .Promo .objects .select_related ('company' )
81
- .prefetch_related (
82
- 'unique_codes' ,
83
- )
84
- .only (
85
- 'id' ,
86
- 'company__id' ,
87
- 'company__name' ,
88
- 'description' ,
89
- 'image_url' ,
90
- 'active' ,
91
- 'active_from' ,
92
- 'active_until' ,
93
- 'mode' ,
94
- 'used_count' ,
95
- 'like_count' ,
96
- 'comment_count' ,
97
- )
75
+ queryset = business .models .Promo .objects .select_related ('company' ).only (
76
+ 'id' ,
77
+ 'company__id' ,
78
+ 'company__name' ,
79
+ 'description' ,
80
+ 'image_url' ,
81
+ 'active' ,
82
+ 'active_from' ,
83
+ 'active_until' ,
84
+ 'mode' ,
85
+ 'used_count' ,
86
+ 'like_count' ,
87
+ 'comment_count' ,
98
88
)
99
89
100
90
serializer_class = user .serializers .UserPromoDetailSerializer
@@ -113,116 +103,26 @@ class UserFeedView(rest_framework.generics.ListAPIView):
113
103
114
104
def get_queryset (self ):
115
105
user = self .request .user
116
- user_age = user .other .get ('age' )
117
- user_country_raw = user .other .get ('country' )
118
- user_country = user_country_raw .lower () if user_country_raw else None
119
-
120
- queryset = business .models .Promo .objects .select_related ('company' )
121
-
122
- today_utc = django .utils .timezone .now ().date ()
123
-
124
- q_active_time = (
125
- django .db .models .Q (active_from__lte = today_utc )
126
- | django .db .models .Q (active_from__isnull = True )
127
- ) & (
128
- django .db .models .Q (active_until__gte = today_utc )
129
- | django .db .models .Q (active_until__isnull = True )
130
- )
131
-
132
- q_common_active = django .db .models .Q (
133
- mode = business .constants .PROMO_MODE_COMMON ,
134
- used_count__lt = django .db .models .F ('max_count' ),
135
- )
136
-
137
- has_available_unique_codes = business .models .PromoCode .objects .filter (
138
- promo = django .db .models .OuterRef ('pk' ),
139
- is_used = False ,
140
- )
141
-
142
- queryset = queryset .annotate (
143
- _has_available_unique_codes = django .db .models .Exists (
144
- has_available_unique_codes ,
145
- ),
146
- )
147
- q_unique_active = django .db .models .Q (
148
- mode = business .constants .PROMO_MODE_UNIQUE ,
149
- _has_available_unique_codes = True ,
150
- )
151
-
152
- q_is_active_by_rules = q_active_time & (
153
- q_common_active | q_unique_active
154
- )
155
-
156
- q_target_empty = django .db .models .Q (target = {})
157
-
158
- q_country_target_matches = django .db .models .Q ()
159
- if user_country :
160
- q_country_target_matches = django .db .models .Q (
161
- target__country__iexact = user_country ,
162
- )
163
-
164
- q_country_target_not_set_or_empty = ~ django .db .models .Q (
165
- target__has_key = 'country' ,
166
- ) | django .db .models .Q (target__country__isnull = True )
167
- q_user_meets_country_target = (
168
- q_country_target_matches | q_country_target_not_set_or_empty
169
- )
170
106
171
- q_age_target_not_set = ~ django .db .models .Q (
172
- target__has_key = 'age_from' ,
173
- ) & ~ django .db .models .Q (target__has_key = 'age_until' )
174
- q_user_meets_age_target = q_age_target_not_set
175
-
176
- if user_age is not None :
177
- q_age_from_ok = (
178
- ~ django .db .models .Q (target__has_key = 'age_from' )
179
- | django .db .models .Q (target__age_from__isnull = True )
180
- | django .db .models .Q (target__age_from__lte = user_age )
181
- )
182
- q_age_until_ok = (
183
- ~ django .db .models .Q (target__has_key = 'age_until' )
184
- | django .db .models .Q (target__age_until__isnull = True )
185
- | django .db .models .Q (target__age_until__gte = user_age )
186
- )
187
- q_user_age_in_defined_range = q_age_from_ok & q_age_until_ok
188
- q_user_meets_age_target = (
189
- q_age_target_not_set | q_user_age_in_defined_range
190
- )
191
-
192
- q_user_is_targeted = q_target_empty | (
193
- q_user_meets_country_target & q_user_meets_age_target
107
+ user_age = user .other .get ('age' )
108
+ user_country = user .other .get ('country' ).lower ()
109
+ active_filter = self .request .query_params .get ('active' )
110
+
111
+ return business .models .Promo .objects .get_feed_for_user (
112
+ user ,
113
+ active_filter = active_filter ,
114
+ user_country = user_country ,
115
+ user_age = user_age ,
194
116
)
195
117
196
- queryset = queryset .filter (q_user_is_targeted )
197
-
198
- active_param_str = self .request .query_params .get ('active' )
199
- if active_param_str is not None :
200
- active_param_bool = active_param_str .lower () == 'true'
201
- if active_param_bool :
202
- queryset = queryset .filter (q_is_active_by_rules )
203
- else :
204
- queryset = queryset .exclude (q_is_active_by_rules )
205
-
206
- return queryset .order_by ('-created_at' )
207
-
208
118
def filter_queryset (self , queryset ):
209
119
queryset = super ().filter_queryset (queryset )
210
-
211
120
category_param = self .request .query_params .get ('category' )
121
+
212
122
if category_param :
213
- category_param = category_param .lower ()
214
- if category_param :
215
- filtered_pks = []
216
- for promo in queryset :
217
- target_categories = promo .target .get ('categories' )
218
- if not isinstance (target_categories , list ):
219
- continue
220
- if any (
221
- cat_name .lower () == category_param
222
- for cat_name in target_categories
223
- ):
224
- filtered_pks .append (promo .pk )
225
- queryset = queryset .filter (pk__in = filtered_pks )
123
+ needle = f'"{ category_param .lower ()} "'
124
+ queryset = queryset .filter (target__categories__icontains = needle )
125
+
226
126
return queryset
227
127
228
128
def list (self , request , * args , ** kwargs ):
@@ -288,7 +188,21 @@ def delete(self, request, id):
288
188
)
289
189
290
190
291
- class PromoCommentListCreateView (rest_framework .generics .ListCreateAPIView ):
191
+ class PromoObjectMixin :
192
+ """Mixin for retrieving the Promo object and saving it to self.promo"""
193
+
194
+ def dispatch (self , request , * args , ** kwargs ):
195
+ self .promo = django .shortcuts .get_object_or_404 (
196
+ business .models .Promo .objects .select_for_update (),
197
+ pk = self .kwargs .get ('promo_id' ),
198
+ )
199
+ return super ().dispatch (request , * args , ** kwargs )
200
+
201
+
202
+ class PromoCommentListCreateView (
203
+ PromoObjectMixin ,
204
+ rest_framework .generics .ListCreateAPIView ,
205
+ ):
292
206
permission_classes = [rest_framework .permissions .IsAuthenticated ]
293
207
294
208
pagination_class = core .pagination .CustomLimitOffsetPagination
@@ -299,28 +213,14 @@ def get_serializer_class(self):
299
213
return user .serializers .CommentSerializer
300
214
301
215
def get_queryset (self ):
302
- promo_id = self .kwargs .get ('promo_id' )
303
- try :
304
- promo = business .models .Promo .objects .get (pk = promo_id )
305
- except business .models .Promo .DoesNotExist :
306
- raise rest_framework .exceptions .NotFound (detail = 'Promo not found.' )
307
-
308
216
return user .models .PromoComment .objects .filter (
309
- promo = promo ,
217
+ promo = self . promo ,
310
218
).select_related ('author' )
311
219
312
220
def perform_create (self , serializer ):
313
- promo_id = self .kwargs .get ('promo_id' )
314
- try :
315
- promo = business .models .Promo .objects .get (pk = promo_id )
316
- except business .models .Promo .DoesNotExist :
317
- raise rest_framework .exceptions .ValidationError (
318
- {'promo_id' : 'Promo not found.' },
319
- )
320
-
321
- serializer .save (author = self .request .user , promo = promo )
322
- promo .comment_count = django .db .models .F ('comment_count' ) + 1
323
- promo .save (update_fields = ['comment_count' ])
221
+ serializer .save (author = self .request .user , promo = self .promo )
222
+ self .promo .comment_count = django .db .models .F ('comment_count' ) + 1
223
+ self .promo .save (update_fields = ['comment_count' ])
324
224
325
225
def create (self , request , * args , ** kwargs ):
326
226
create_serializer = self .get_serializer (data = request .data )
@@ -346,6 +246,7 @@ def list(self, request, *args, **kwargs):
346
246
347
247
348
248
class PromoCommentDetailView (
249
+ PromoObjectMixin ,
349
250
rest_framework .generics .RetrieveUpdateDestroyAPIView ,
350
251
):
351
252
permission_classes = [
@@ -362,13 +263,8 @@ def get_serializer_class(self):
362
263
return user .serializers .CommentSerializer
363
264
364
265
def get_queryset (self ):
365
- promo_id = self .kwargs .get ('promo_id' )
366
- try :
367
- promo = business .models .Promo .objects .get (pk = promo_id )
368
- except business .models .Promo .DoesNotExist :
369
- raise rest_framework .exceptions .NotFound (detail = 'Promo not found.' )
370
266
return user .models .PromoComment .objects .filter (
371
- promo = promo ,
267
+ promo = self . promo ,
372
268
).select_related ('author' )
373
269
374
270
def update (self , request , * args , ** kwargs ):
0 commit comments