|
3 | 3 | import django.contrib.auth.password_validation
|
4 | 4 | import django.core.exceptions
|
5 | 5 | import django.core.validators
|
| 6 | +import pycountry |
6 | 7 | import rest_framework.exceptions
|
7 | 8 | import rest_framework.serializers
|
8 | 9 | import rest_framework.status
|
@@ -130,3 +131,209 @@ def validate(self, attrs):
|
130 | 131 | )
|
131 | 132 |
|
132 | 133 | return super().validate(attrs)
|
| 134 | + |
| 135 | + |
| 136 | +class TargetSerializer(rest_framework.serializers.Serializer): |
| 137 | + age_from = rest_framework.serializers.IntegerField( |
| 138 | + min_value=0, |
| 139 | + max_value=100, |
| 140 | + required=False, |
| 141 | + allow_null=True, |
| 142 | + ) |
| 143 | + age_until = rest_framework.serializers.IntegerField( |
| 144 | + min_value=0, |
| 145 | + max_value=100, |
| 146 | + required=False, |
| 147 | + allow_null=True, |
| 148 | + ) |
| 149 | + country = rest_framework.serializers.CharField( |
| 150 | + required=False, |
| 151 | + allow_null=True, |
| 152 | + allow_blank=True, |
| 153 | + ) |
| 154 | + categories = rest_framework.serializers.ListField( |
| 155 | + child=rest_framework.serializers.CharField( |
| 156 | + min_length=2, |
| 157 | + max_length=20, |
| 158 | + ), |
| 159 | + max_length=20, |
| 160 | + required=False, |
| 161 | + allow_empty=True, |
| 162 | + ) |
| 163 | + |
| 164 | + def validate(self, data): |
| 165 | + age_from = data.get('age_from') |
| 166 | + age_until = data.get('age_until') |
| 167 | + if ( |
| 168 | + age_from is not None |
| 169 | + and age_until is not None |
| 170 | + and age_from > age_until |
| 171 | + ): |
| 172 | + raise rest_framework.serializers.ValidationError( |
| 173 | + {'age_until': 'Must be greater than or equal to age_from.'}, |
| 174 | + ) |
| 175 | + |
| 176 | + # change validation |
| 177 | + country = data.get('country') |
| 178 | + if country: |
| 179 | + country = country.strip().upper() |
| 180 | + try: |
| 181 | + pycountry.countries.lookup(country) |
| 182 | + data['country'] = country |
| 183 | + except LookupError: |
| 184 | + raise rest_framework.serializers.ValidationError( |
| 185 | + {'country': 'Invalid ISO 3166-1 alpha-2 country code.'}, |
| 186 | + ) |
| 187 | + |
| 188 | + return data |
| 189 | + |
| 190 | + |
| 191 | +class PromoCreateSerializer(rest_framework.serializers.ModelSerializer): |
| 192 | + target = TargetSerializer(required=True) |
| 193 | + promo_common = rest_framework.serializers.CharField( |
| 194 | + min_length=5, |
| 195 | + max_length=30, |
| 196 | + required=False, |
| 197 | + allow_null=True, |
| 198 | + ) |
| 199 | + promo_unique = rest_framework.serializers.ListField( |
| 200 | + child=rest_framework.serializers.CharField( |
| 201 | + min_length=3, |
| 202 | + max_length=30, |
| 203 | + ), |
| 204 | + min_length=1, |
| 205 | + max_length=5000, |
| 206 | + required=False, |
| 207 | + allow_null=True, |
| 208 | + ) |
| 209 | + |
| 210 | + class Meta: |
| 211 | + model = business_models.Promo |
| 212 | + fields = ( |
| 213 | + 'description', |
| 214 | + 'image_url', |
| 215 | + 'target', |
| 216 | + 'max_count', |
| 217 | + 'active_from', |
| 218 | + 'active_until', |
| 219 | + 'mode', |
| 220 | + 'promo_common', |
| 221 | + 'promo_unique', |
| 222 | + ) |
| 223 | + extra_kwargs = { |
| 224 | + 'description': {'min_length': 10, 'max_length': 300}, |
| 225 | + 'image_url': {'max_length': 350}, |
| 226 | + } |
| 227 | + |
| 228 | + def validate(self, data): |
| 229 | + mode = data.get('mode') |
| 230 | + promo_common = data.get('promo_common') |
| 231 | + promo_unique = data.get('promo_unique') |
| 232 | + max_count = data.get('max_count') |
| 233 | + |
| 234 | + if mode == business_models.Promo.MODE_COMMON: |
| 235 | + if not promo_common: |
| 236 | + raise rest_framework.serializers.ValidationError( |
| 237 | + { |
| 238 | + 'promo_common': ( |
| 239 | + 'This field is required for COMMON mode.' |
| 240 | + ), |
| 241 | + }, |
| 242 | + ) |
| 243 | + |
| 244 | + if promo_unique is not None: |
| 245 | + raise rest_framework.serializers.ValidationError( |
| 246 | + { |
| 247 | + 'promo_unique': ( |
| 248 | + 'This field is not allowed for COMMON mode.' |
| 249 | + ), |
| 250 | + }, |
| 251 | + ) |
| 252 | + |
| 253 | + if max_count < 0 or max_count > 100000000: |
| 254 | + raise rest_framework.serializers.ValidationError( |
| 255 | + { |
| 256 | + 'max_count': ( |
| 257 | + 'Must be between 0 and 100,000,000 ' |
| 258 | + 'for COMMON mode.' |
| 259 | + ), |
| 260 | + }, |
| 261 | + ) |
| 262 | + |
| 263 | + elif mode == business_models.Promo.MODE_UNIQUE: |
| 264 | + if not promo_unique: |
| 265 | + raise rest_framework.serializers.ValidationError( |
| 266 | + { |
| 267 | + 'promo_unique': ( |
| 268 | + 'This field is required for UNIQUE mode.' |
| 269 | + ), |
| 270 | + }, |
| 271 | + ) |
| 272 | + |
| 273 | + if promo_common is not None: |
| 274 | + raise rest_framework.serializers.ValidationError( |
| 275 | + { |
| 276 | + 'promo_common': ( |
| 277 | + 'This field is not allowed for UNIQUE mode.' |
| 278 | + ), |
| 279 | + }, |
| 280 | + ) |
| 281 | + |
| 282 | + if max_count != 1: |
| 283 | + raise rest_framework.serializers.ValidationError( |
| 284 | + {'max_count': 'Must be 1 for UNIQUE mode.'}, |
| 285 | + ) |
| 286 | + |
| 287 | + else: |
| 288 | + raise rest_framework.serializers.ValidationError( |
| 289 | + {'mode': 'Invalid mode.'}, |
| 290 | + ) |
| 291 | + |
| 292 | + active_from = data.get('active_from') |
| 293 | + active_until = data.get('active_until') |
| 294 | + if active_from and active_until and active_from > active_until: |
| 295 | + raise rest_framework.serializers.ValidationError( |
| 296 | + {'active_until': 'Must be after or equal to active_from.'}, |
| 297 | + ) |
| 298 | + |
| 299 | + return data |
| 300 | + |
| 301 | + def create(self, validated_data): |
| 302 | + target_data = validated_data.pop('target') |
| 303 | + promo_common = validated_data.pop('promo_common', None) |
| 304 | + promo_unique = validated_data.pop('promo_unique', None) |
| 305 | + mode = validated_data['mode'] |
| 306 | + |
| 307 | + user = self.context['request'].user |
| 308 | + validated_data['company'] = user |
| 309 | + |
| 310 | + promo = business_models.Promo.objects.create( |
| 311 | + **validated_data, |
| 312 | + target=target_data, |
| 313 | + ) |
| 314 | + |
| 315 | + if mode == business_models.Promo.MODE_COMMON: |
| 316 | + promo.promo_common = promo_common |
| 317 | + promo.save() |
| 318 | + elif mode == business_models.Promo.MODE_UNIQUE and promo_unique: |
| 319 | + promo_codes = [ |
| 320 | + business_models.PromoCode(promo=promo, code=code) |
| 321 | + for code in promo_unique |
| 322 | + ] |
| 323 | + business_models.PromoCode.objects.bulk_create(promo_codes) |
| 324 | + |
| 325 | + return promo |
| 326 | + |
| 327 | + def to_representation(self, instance): |
| 328 | + data = super().to_representation(instance) |
| 329 | + data['target'] = instance.target |
| 330 | + |
| 331 | + if instance.mode == business_models.Promo.MODE_UNIQUE: |
| 332 | + data['promo_unique'] = [ |
| 333 | + code.code for code in instance.unique_codes.all() |
| 334 | + ] |
| 335 | + data.pop('promo_common', None) |
| 336 | + else: |
| 337 | + data.pop('promo_unique', None) |
| 338 | + |
| 339 | + return data |
0 commit comments