2

I have two serializers which use a same function. I want to define it as a static method and reuse it.

Serializer for Article

class ArticleDetailSerializer(ModelSerializer):
    liked = SerializerMethodField()
    class Meta:
        model = Article
        fields = [
            'id',
            'self_liked',
            'title'
        ]

    def get_liked(self, obj):
        request = self.context.get('request')
        self_like_obj = Reaction.objects.filter(user=request.user.id, content_type=ContentType.objects.get(model='article'), object_id=obj.id)
        if self_like_obj.exists():
            self_like = Reaction.objects.get(user=request.user.id, content_type=ContentType.objects.get(model='article'), object_id=obj.id).react_type
        else:
            self_like = False
        return self_like

Serializer for comment

class CommentSerializer(ModelSerializer):
        liked = SerializerMethodField()
        class Meta:
            model = Comment
            fields = [
                'id',
                'self_liked',
                'content'
            ]

        def get_liked(self, obj):
            request = self.context.get('request')
            self_like_obj = Reaction.objects.filter(user=request.user.id, content_type=ContentType.objects.get(model='comment'), object_id=obj.id)
            if self_like_obj.exists():
                self_like = Reaction.objects.get(user=request.user.id, content_type=ContentType.objects.get(model='comment'), object_id=obj.id).react_type
            else:
                self_like = False
            return self_like

As you see, two serializer use a general function: get_liked How can I define it as a static methods for reuse?

allo
  • 3,955
  • 8
  • 40
  • 71
KitKit
  • 8,549
  • 12
  • 56
  • 82
  • 1
    I dont think staticmethod is what you really need in this case. You can implement method only once in mixin class, and use this class as parent for both serializers. – neverwalkaloner Apr 09 '18 at 04:56
  • Could you please write it for me? I'm a beginner with Django Rest Framework – KitKit Apr 09 '18 at 04:57

3 Answers3

4

As @neverwalkaloner suggested you can implement method only once in mixin class, and use this class as parent for both serializers. Keep in mind that your get_liked method is not the same in ArticleDetailSerializerand CommentSerializer serializers.

ArticleDetailSerializer method has ContentType.objects.get(model='article') line but CommentSerializer has ContentType.objects.get(model='comment') which makes the difference between get_liked method from your serializers.

You can use the same mixin that @neverwalkaloner suggested but instead of ContentType.objects.get(model='article') and ContentType.objects.get(model='comment') lines you can try with ContentType.objects.get(model=self.Meta.model.__name__.lower()).

I think it should look like this:

class LikedMixin(object):

    def get_lowercased_model_name(self, obj):
        return self.Meta.model.__name__.lower()

    def get_liked(self, obj):
        request = self.context.get('request')
        model_name = self.get_lowercased_model_name()
        self_like_obj = Reaction.objects.filter(user=request.user.id, content_type=model_name, object_id=obj.id)
        if self_like_obj.exists():
            self_like = Reaction.objects.get(user=request.user.id, content_type=model_name, object_id=obj.id).react_type
        else:
            self_like = False
        return self_like
T.Tokic
  • 1,264
  • 9
  • 10
2

You can implement get_liked method in mixin class and use this class as parent for both serializers like this:

class LikesMixin:
    def get_liked(self, obj):
        request = self.context.get('request')
        self_like_obj = Reaction.objects.filter(user=request.user.id, content_type=ContentType.objects.get(model='comment'), object_id=obj.id)
        if self_like_obj.exists():
            self_like = Reaction.objects.get(user=request.user.id, content_type=ContentType.objects.get(model='comment'), object_id=obj.id).react_type
        else:
            self_like = False
        return self_like

class CommentSerializer(ModelSerializer, LikesMixin):
    liked = SerializerMethodField()
    class Meta:
        model = Comment
        fields = [
            'id',
            'self_liked',
            'content'
        ]

class ArticleDetailSerializer(ModelSerializer, LikesMixin):
    liked = SerializerMethodField()
    class Meta:
        model = Article
        fields = [
            'id',
            'self_liked',
            'title'
        ]
neverwalkaloner
  • 46,181
  • 7
  • 92
  • 100
  • hi, I'm asking this for a clarification, shouldn't the **model** change in the **class likesMixin** – Pabasara Ranathunga Apr 09 '18 at 05:38
  • @PabasaraRanathunga no, you define model inside each serializer class separately. – neverwalkaloner Apr 09 '18 at 05:40
  • `=ContentType.objects.get(model='comment')` I was referring to this part. could you please explain a little. – Pabasara Ranathunga Apr 09 '18 at 06:15
  • 1
    @PabasaraRanathunga OP using generic relations see details here https://docs.djangoproject.com/en/2.0/ref/contrib/contenttypes/#generic-relations. You dont need this in your mixin, just replace method completely with one you are using. – neverwalkaloner Apr 09 '18 at 06:19
  • Unless the code is for Python 3 only, [the mixin class should inherit from `object`](https://stackoverflow.com/q/38139275/253599). – Tyson Apr 09 '18 at 06:55
  • This answer missed an opportunity to apply the field declaration to the mixin, rather than applying it on each serializer. – Tyson Apr 09 '18 at 06:57
1

This is a good example of when to use a mixin class. The mixin is a new class which contains methods and class attributes you want to use in other classes, like this:

class LikedMixin(object):
    liked = SerializerMethodField()

    def get_liked(self, obj):
        request = self.context.get('request')
        self_like_obj = Reaction.objects.filter(user=request.user.id, content_type=ContentType.objects.get(model='article'), object_id=obj.id)
        if self_like_obj.exists():
            self_like = Reaction.objects.get(user=request.user.id, content_type=ContentType.objects.get(model='article'), object_id=obj.id).react_type
        else:
            self_like = False
        return self_like

You may then declare your serializers to inherit from your mixin:

class ArticleDetailSerializer(LikedMixin, ModelSerializer):
    class Meta:
        model = Article
        fields = [
            'id',
            'self_liked',
            'title'
        ]

class CommentSerializer(LikedMixin, ModelSerializer):
    class Meta:
        model = Comment
        fields = [
            'id',
            'self_liked',
            'content'
        ]

It might be a good idea to familiarise yourself with the basics of method resolution order in Python, in order to recognise any potential issues relating to multiple inheritance down the track.

Tyson
  • 405
  • 4
  • 13