From 4437037710688dd029e24b62c5b45a61cbf52d44 Mon Sep 17 00:00:00 2001 From: Sam Date: Tue, 26 Jan 2021 11:20:48 +0000 Subject: [PATCH] everythin working except for creation product insance with category --- products/factories.py | 2 +- products/serializers.py | 5 +- products/tests.py | 6 +- utils/tag_serializers.py | 178 ++++++++++++++++++++------------------- 4 files changed, 99 insertions(+), 92 deletions(-) diff --git a/products/factories.py b/products/factories.py index efe243f..5fc7e17 100644 --- a/products/factories.py +++ b/products/factories.py @@ -17,7 +17,7 @@ class ProductFactory(DjangoModelFactory): name = FuzzyText(prefix='NAME_', length=10) description = FuzzyText(prefix='DECRIPTION', length=100) image = None - url = FuzzyText(prefix='http://WEB_LINK_', suffix='.test', length=10) + url = FuzzyText(prefix='http://WEB-LINK-', suffix='.test', length=10) price = FuzzyDecimal(low=1.00) shipping_cost = FuzzyDecimal(low=1.00) shipping_terms = FuzzyText(prefix='SHIPPING_TERMS', length=100) diff --git a/products/serializers.py b/products/serializers.py index d0521ea..70f85ab 100644 --- a/products/serializers.py +++ b/products/serializers.py @@ -3,7 +3,7 @@ from rest_framework import serializers from taggit_serializer.serializers import TagListSerializerField, TaggitSerializer from products.models import Product -from utils.tag_serializers import SingleTagSerializerField, CustomTagSerializer +from utils.tag_serializers import TagListSerializerField, TaggitSerializer, SingleTagSerializerField class ProductSerializer(TaggitSerializer, serializers.ModelSerializer): @@ -14,4 +14,5 @@ class ProductSerializer(TaggitSerializer, serializers.ModelSerializer): class Meta: model = Product - exclude = ['created', 'updated', 'creator'] + # exclude = ['created', 'updated', 'creator'] + fields = '__all__' diff --git a/products/tests.py b/products/tests.py index 0c291d0..b7c8759 100644 --- a/products/tests.py +++ b/products/tests.py @@ -118,7 +118,7 @@ class ProductViewSetTest(APITestCase): 'discount': '0.05', 'stock': 22, 'tags': ['tag1, tag2'], - # 'category': 'MayorTagCategory', + # 'category': '', 'attributes': ['color/red', 'size/xxl'], 'identifiers': '34rf34f43c43', } @@ -129,6 +129,7 @@ class ProductViewSetTest(APITestCase): # Query endpoint response = self.client.post(self.endpoint, data=data, format='json') + import ipdb; ipdb.set_trace() # Assert endpoint returns created status self.assertEqual(response.status_code, status.HTTP_201_CREATED) @@ -162,7 +163,7 @@ class ProductViewSetTest(APITestCase): 'discount': '0.05', 'stock': 22, 'tags': ['tag1x, tag2x'], - # 'category': 'MayorTagCategory2', + 'category': 'MayorTagCategory2', 'attributes': ['color/blue', 'size/m'], 'identifiers': '34rf34f43c43', } @@ -174,7 +175,6 @@ class ProductViewSetTest(APITestCase): # Query endpoint url = self.endpoint + f'{instance.pk}/' response = self.client.put(url, data, format='json') - import ipdb; ipdb.set_trace() # Assert endpoint returns OK code self.assertEqual(response.status_code, status.HTTP_200_OK) diff --git a/utils/tag_serializers.py b/utils/tag_serializers.py index 836fcf2..9f74c72 100644 --- a/utils/tag_serializers.py +++ b/utils/tag_serializers.py @@ -1,13 +1,34 @@ -import traceback +# -*- coding: utf-8 -*- +import json +# Third party +import six +from django.utils.translation import ugettext_lazy as _ from rest_framework import serializers -from rest_framework.fields import CharField, ListField -from rest_framework.serializers import raise_errors_on_nested_writes -from rest_framework.utils import model_meta -from tagulous.models.managers import TagRelatedManagerMixin -from taggit_serializer.serializers import TagListSerializerField, TaggitSerializer +class TagList(list): + def __init__(self, *args, **kwargs): + pretty_print = kwargs.pop("pretty_print", True) + list.__init__(self, *args, **kwargs) + self.pretty_print = pretty_print + + def __add__(self, rhs): + return TagList(list.__add__(self, rhs)) + + def __getitem__(self, item): + result = list.__getitem__(self, item) + try: + return TagList(result) + except TypeError: + return result + + def __str__(self): + if self.pretty_print: + return json.dumps( + self, sort_keys=True, indent=4, separators=(',', ': ')) + else: + return json.dumps(self) class SingleTag(str): @@ -16,13 +37,15 @@ class SingleTag(str): pass def __str__(self): - return self + return json.dumps(self) class SingleTagSerializerField(serializers.Field): child = serializers.CharField() default_error_messages = { - 'not_a_str': 'Expected a string but got type "{input_type}".', + 'invalid_json': _('Invalid json str. A tag list submitted in string' + ' form must be valid json.'), + 'not_a_str': _('Expected a string but got type "{input_type}".') } order_by = None @@ -30,7 +53,7 @@ class SingleTagSerializerField(serializers.Field): super(SingleTagSerializerField, self).__init__(**kwargs) def to_internal_value(self, value): - if isinstance(value, str): + if isinstance(value, six.string_types): if not value: value = "" @@ -47,93 +70,76 @@ class SingleTagSerializerField(serializers.Field): return value -class CustomTagSerializer(serializers.Serializer): - """ - Differentiate between tags and single-tags - """ +class TagListSerializerField(serializers.Field): + child = serializers.CharField() + default_error_messages = { + 'not_a_list': _( + 'Expected a list of items but got type "{input_type}".'), + 'invalid_json': _('Invalid json list. A tag list submitted in string' + ' form must be valid json.'), + 'not_a_str': _('All list items must be of string type.') + } + order_by = None + def __init__(self, **kwargs): + pretty_print = kwargs.pop("pretty_print", True) + + style = kwargs.pop("style", {}) + kwargs["style"] = {'base_template': 'textarea.html'} + kwargs["style"].update(style) + + super(TagListSerializerField, self).__init__(**kwargs) + + self.pretty_print = pretty_print + + def to_internal_value(self, value): + if isinstance(value, six.string_types): + if not value: + value = "[]" + try: + value = json.loads(value) + except ValueError: + self.fail('invalid_json') + + if not isinstance(value, list): + self.fail('not_a_list', input_type=type(value).__name__) + + for s in value: + if not isinstance(s, six.string_types): + self.fail('not_a_str') + + self.child.run_validation(s) + + return value + + def to_representation(self, value): + if not isinstance(value, TagList): + if not isinstance(value, list): + if self.order_by: + tags = value.all().order_by(*self.order_by) + else: + tags = value.all() + value = [tag.name for tag in tags] + value = TagList(value, pretty_print=self.pretty_print) + + return value + + +class TaggitSerializer(serializers.Serializer): def create(self, validated_data): to_be_tagged, validated_data = self._pop_tags(validated_data) - # tag_object = super(CustomTagSerializer, self).create(validated_data) - raise_errors_on_nested_writes('create', self, validated_data) + tag_object = super(TaggitSerializer, self).create(validated_data) - ModelClass = self.Meta.model - - # Remove many-to-many relationships from validated_data. - # They are not valid arguments to the default `.create()` method, - # as they require that the instance has already been saved. - info = model_meta.get_field_info(ModelClass) - many_to_many = {} - for field_name, relation_info in info.relations.items(): - if relation_info.to_many and (field_name in validated_data): - many_to_many[field_name] = validated_data.pop(field_name) - - try: - instance = ModelClass._default_manager.create(**validated_data) - except TypeError: - tb = traceback.format_exc() - msg = ( - 'Got a `TypeError` when calling `%s.%s.create()`. ' - 'This may be because you have a writable field on the ' - 'serializer class that is not a valid argument to ' - '`%s.%s.create()`. You may need to make the field ' - 'read-only, or override the %s.create() method to handle ' - 'this correctly.\nOriginal exception was:\n %s' % - ( - ModelClass.__name__, - ModelClass._default_manager.name, - ModelClass.__name__, - ModelClass._default_manager.name, - self.__class__.__name__, - tb - ) - ) - raise TypeError(msg) - - # Save many-to-many relationships after the instance is created. - if many_to_many: - for field_name, value in many_to_many.items(): - field = getattr(instance, field_name) - if field_name in ('tags', 'category', 'attributes'): - for item in value: - field.set(item) - else: - field.set(value) - - return self._save_tags(instance, to_be_tagged) + return self._save_tags(tag_object, to_be_tagged) def update(self, instance, validated_data): to_be_tagged, validated_data = self._pop_tags(validated_data) - raise_errors_on_nested_writes('update', self, validated_data) - info = model_meta.get_field_info(instance) + tag_object = super(TaggitSerializer, self).update( + instance, validated_data) - # Simply set each attribute on the instance, and then save it. - # Note that unlike `.create()` we don't need to treat many-to-many - # relationships as being a special case. During updates we already - # have an instance pk for the relationships to be associated with. - m2m_fields = [] - for attr, value in validated_data.items(): - if attr in info.relations and info.relations[attr].to_many: - m2m_fields.append((attr, value)) - else: - setattr(instance, attr, value) - - instance.save() - - # Note that many-to-many fields are set after updating instance. - # Setting m2m fields triggers signals which could potentially change - # updated instance and we do not want it to collide with .update() - for attr, value in m2m_fields: - field = getattr(instance, attr) - if attr in ('tags', 'category', 'attributes'): - for item in value: - field.set(item) - else: - field.set(value) - - return self._save_tags(instance, to_be_tagged) + return self._save_tags(tag_object, to_be_tagged) def _save_tags(self, tag_object, tags): for key in tags.keys():