2

I'm using Python 3.6 with Django 1.11.9 and rest_framework 3.6.2.

I want to inherit from serializers.Serializer to make a SharingSerializer class, that I want to be abstract, because I want to inherit from the latter to implement some ArticleSharingSerializer, ImageSharingSerializer,... and so on.

What I've tried so far:

from abc import ABCMeta, abstractmethod
from rest_framework import serializers
...

class SharingSerializer(serializers.Serializer, metaclass=ABCMeta):
    course = serializers.PrimaryKeyRelatedField(queryset=Course.objects.all())
    students = serializers.PrimaryKeyRelatedField(queryset=User.objects.all(), many=True)

    @abstractmethod
    def validate(self, data):
        # Doing validation stuff with "course" and "students" fields
        ...
        return data


class ArticleSharingSerializer(SharingSerializer):
    articles = serializers.PrimaryKeyRelatedField(queryset=Article.objects.all(), many=True)

    def validate(self, data):
        data = super().validate(data)
        # Doing validation stuff with "articles" and self.context["request"].user
        ...
        return data

But when trying to "runserver", I get the following error:

File ".../school/serializers.py", line 11, in <module>
class SharingSerializer(serializers.Serializer, metaclass=ABCMeta):
TypeError: metaclass conflict: the metaclass of a derived class must be a (non-strict) subclass of the metaclasses of all its bases

Do you know how I can successfully achieve what I'm trying to achieve?

UPDATE : I want to take advantage of the @abstractmethod "enforcement" on instantiation that ABC provides.

UPDATE 2 : TLDR : The best answer given by Ahmed Hosny (see below) is this link

Watchoutman
  • 37
  • 1
  • 10

1 Answers1

2

Short Answer

To keep using the ABCMeta You can also do,

class SharingSerializer(serializers.Serializer):
    __metaclass__ = ABCMeta

    course = serializers.PrimaryKeyRelatedField(queryset=Course.objects.all())
    students = serializers.PrimaryKeyRelatedField(queryset=User.objects.all(), many=True)

    @abstractmethod
    def validate(self, data):
        # Doing validation stuff with "course" and "students" fields
        ...
        return data

Or make intermediate class, check this OP.

Long Answer:

The problem occurs when you try to have a class that inherits from two different classes with different meta_class. Then a conflict occurs!

So in your case you inherited from serializers.Serializer which has a meta class (check this) and also you inherited a different meta_class by metaclass=ABCMeta. This is the reason the conflict occur.

Check also this reference, and this one.

[UPDATE]

Lets Make some points clear:

  • Doing

    class Meta: abstract = True will not make your class abstract the way you know in Java and other compiled languages.

what it actually do is just marking this class with some extra attribute (without going to more details).

So It is not a Django specific, abstract = True alone without anything also will not do anything extra.

Ahmed Hosny
  • 1,162
  • 10
  • 21
  • Thanks for your reply and the references. Nevertheless, tell me if I'm wrong, but it seems that the `class Meta:...` way is specific to some Django classes (and other Python packages) and has nothing to do with Python `metaclass` [see](https://mapleoin.github.io/perma/python-class-meta). Plus, even if the code runs with this, I lost the "power" of ABC (ie, enforcing that abstract methods are defined by raising exception on instantiation) – Watchoutman Aug 14 '18 at 12:53
  • I do believe that `meta classes` are not something specific to Django but Python instead. If you want to keep the "power" of ABC, I'll updated my answer to comply with this. The point is just explicitly say what your meta class should be. You can also check https://stackoverflow.com/a/28727066/4117381 – Ahmed Hosny Aug 14 '18 at 13:02
  • For meta class explanation; you can check this source https://stackoverflow.com/questions/100003/what-are-metaclasses-in-python – Ahmed Hosny Aug 14 '18 at 13:07
  • Thanks again for your answer. Very good news : the "intermediate class" option works like a charm! Thanks! The "\_\_metaclass\_\_" suggestion may work for Python 2 but does not with Python 3. And according to my research, I'm sorry to insist, but the `class Meta: abstract = True` seems only dedicated to declaring abstract Django Models. I've gathered [here](https://repl.it/repls/NotableLimeAstrophysics) tests of your suggestions. (And FYI, I've updated my question so that it is clear that I want to take advantage of @abstractmethod enforcement that ABC provides). – Watchoutman Aug 16 '18 at 08:28
  • Happy it works for you! I've checked your tests but this not contradicts my main point. Making `class Meta: abstract = True` does not make your class abstract the way you know in Java or C#. I was just saying that `metaclass` in general is not Django related. It just marks the class with this attribute for you and you should do your extra checks. This is actually how `ABC works`, check https://github.com/python/cpython/blob/master/Lib/abc.py. – Ahmed Hosny Aug 16 '18 at 08:59
  • I believe you also thought it is a Django specific because also Django do the extra checks for you, check https://github.com/django/django/blob/master/django/db/models/base.py#L62 – Ahmed Hosny Aug 16 '18 at 08:59
  • Oh OK, I see. (I've understood from the beginning that "metaclass" was not Django-specific, but I have not been clear enough, sorry). But do we agree that your very first answer (the one involving "class Meta: abstract = True" may lead one to think that it will make the same "Django-kind" magic happen ? And that the most "efficient" part of your answer is the one related to "the intermediate meta class" ? If we do, could you "clean up" your answer a little bit and then I accept it right away :). Thanks! – Watchoutman Aug 16 '18 at 09:23
  • No need for sorry, I really enjoyed refreshing my mind. yes, since you also updated the question, I've removed this part of the answer to prevent the confusion and kept the explanation of `abstract = True`. Thanks! – Ahmed Hosny Aug 16 '18 at 09:27