0

I want to use variation attribute inside to_representation function in serializers.py. When I tried to implement it an error occured. I think I still don't understand the fundamental of Python class.
Here's my serializers.py:

class MarketSerializer(serializers.ModelSerializer):
    regions = serializers.PrimaryKeyRelatedField(many=True, queryset=Region.objects.all())
    languages = serializers.PrimaryKeyRelatedField(many=True, queryset=Language.objects.all())
    variation = serializers.SerializerMethodField('get_product_variations')

    class Meta:
        model = Market
        fields = ['id', 'regions', 'languages', 'name', 'status', 'variation']
        depth = 1

    @staticmethod
    def get_product_variations(self):
        return Product.objects.filter(distributor__region__market=self).distinct().count()

    def to_representation(self, instance):
        if self.context['request'].method == 'GET':
            regions = RegionSerializer(instance.regions, many=True).data
            languages = LanguageSerializer(instance.languages, many=True).data

            data = {
                'id': instance.id,
                'regions': regions,
                'languages': languages,
                'name': instance.name,
                'status': instance.status,
                'variation': self.variation,
            }
            return data
        return Serializer.to_representation(self, instance)

'variation': self.variation gives me this error on browsable API:

AttributeError at /api/markets/
'MarketSerializer' object has no attribute 'variation'

While 'variation': MarketSerializer.variation and 'variation': self.__class__.variation give me this error:

AttributeError at /api/markets/
type object 'MarketSerializer' has no attribute 'variation'

Edit
'variation': variation gives me this error:

NameError at /api/markets/
name 'variation' is not defined

1
  • 1
    DRF does some black magic with serializer attributes. But you still call your method directly. Commented Jan 6, 2020 at 8:05

2 Answers 2

1

I am not sure how this suppose to work:

@staticmethod
def get_product_variations(self):
    return Product.objects.filter(distributor__region__market=self).distinct().count()

Because, in serializer method field, the method should be like this:

def get_product_variations(self, obj):
    # not a static method
    return Product.objects.filter(distributor__region__market=obj).distinct().count()

Similarly, you can use that method in to_represent method:

def to_representation(self, instance):
  ...
  data = {
            'id': instance.id,
            'regions': regions,
            'languages': languages,
            'name': instance.name,
            'status': instance.status,
            'variation': self.get_product_variations(instance),
        }
 ...

Or better:

def to_representation(self, instance):
      data = super().to_representation(instance)
      if self.context['request'].method == 'GET':
            data['regions'] = RegionSerializer(instance.regions, many=True).data
            data['languages'] = LanguageSerializer(instance.languages, many=True).data
      return data
Sign up to request clarification or add additional context in comments.

5 Comments

previously get_product_variations wasn't staticmethod. Because when I tried to call it inside data = { '...': ..., } it said get_product_variations expected 2 arguments. And I only fill it with 1 argument which is instance. The auto-solution PyCharm gave me is to implement it with @staticmethod.
well, did you use self.get_product_variations(instance)? get_product_variations is a object method here. BTW, I would recommend to use 2nd solution as its much cleaner
Yes. I tried to use self.get_product_variations(instance) inside data = { }. It also can't be used in the class attribute variation = ... if I didn't apply the staticmethod. It gave me this error Exception Value: get_product_variations() takes 1 positional argument but 2 were given
ruddra, I forgot to reply about the second solution. Thank you so much. I like it and using it now. Btw I think you mistyped data = super().to_represent(instance). Isn't it should be data = super().to_representation(instance) ? Or they're indeed different methods.
No, it should be to_representation. I am fixing it now
0

Since you are trying to access the outer class instance from an inner class, use factory-method to build Inner instance and pass Outer instance to it. We can not guarantee the existence of an outer class when you create an instance of the inner class

class MarketSerializer(object):
    variation = 'h'

    def MarketSerializer(self):
        return MarketSerializer.Meta(self)

    class Meta(object):
        def __init__(self, market_serializer_instance):
            self.market_serializer_instance = market_serializer_instance

        def inner_method(self):
            print(self.market_serializer_instance.variation)

5 Comments

I got new error: Exception Type: NameError Exception Value: name 'variation' is not defined
Marlon, your answer works! Thank you. Although I was a bit confuse why there's no model and fields attributes inside class Meta(object).
I only explained the idea. Had no time to include all your code in it. :p BTW I personally don't recommend nested classes since it does not depict any relationship, does it?
"Since you are trying to access the outer class instance from an inner class" => where have you seen this ?
"BTW I personally don't recommend nested classes" => this is part of the DRF serializers framework (and based on Django's Model and ModelForm idioms). The inner class is a namespace used by the metaclass (Python metaclass).

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.