Class reference inside a class in python
Context
I have the following django / python snippet:
from rest_framework import serializers
from .models import Profile, Task
class Serializable():
types = {}
def __init__(self, objectid):
self.object = self.types[objectid][0]
self.serializer = self.types[objectid][1]
def serialized(self):
instances = self.object.objects.all()
serialized = self.serializer(instances, many=True)
return serialized
class ProfileSerializer(serializers.ModelSerializer):
class Meta:
oid = 'profile'
model = Profile
fields = ['login', 'status']
Serializable.types[oid] = [model, <class-reference>]
class TaskSerializer(serializers.ModelSerializer):
class Meta:
oid = 'task'
model = Task
fields = ['description', 'date', 'owner']
Serializable.types[oid] = [model, <class-reference>]
I am using Django with the library installed rest_framework
. One of the interesting features that I use is ModelSerializers
( ModelSerializers Documentation ), which save quite a lot of repetition of code. I want the variable to Serializable.types
be populated at runtime (when all serializer classes are declared). This is because I don't have to update my views when a new model type is enabled. For example, I would print the json representation of my model instances like this:
class QueryObject(APIView):
permission_classes = (AllowAny,)
def get(self, request, *args, **kwargs):
oid = request.GET['oid']
serializable= Serializable(oid)
json = serializable.serialized
return JsonResponse(json)
Problem
The main problem is in the last line of each class Serializer
.
Serializable.types[oid] = [model, <class-reference>]
I've tried putting the class name ProfileSerializer
for example to no avail. I tried to do the same outside of the class Meta
, for example:
class ProfileSerializer(serializers.ModelSerializer):
class Meta:
oid = 'profile'
model = Profile
fields = ['login', 'status']
Serializable.types[Meta.oid] = [Meta.model, ProfileSerializer]
also failed. Not sure what else to do, so I hope the community can help me with this.
source to share
This is really the case for defining a metaclass.
I have never found a source of information that provides a complete, clear and satisfactory explanation of what metaclasses are and how they work. I'll try to improve this answer with such information as needed, but for now I'll stick with the solution for your real problem. I am assuming python 3.
Define an additional class like this:
class ModelSerializerMeta(serializers.SerializerMetaclass):
def __init__(cls, class_name, base_classes, attributes):
super(ModelSerialiserMeta, cls).__init__(class_name, base_classes, attributes)
Serializer.types[cls.Meta.oid] = [cls.Meta.model, cls]
Then use this as a metaclass of your serializers, for example
class ProfileSerializer(serializers.ModelSerializer, metaclass=ModelSerializerMeta):
class Meta:
oid = 'profile'
model = Profile
fields = ['login', 'status']
Better yet, create a superclass for all model serializers, assign a metaclass there, make all your serializers inherit from that superclass that will use the metaclass.
source to share
Metaclasses are definitely the right answer if your code cannot require python> = 3.6. Since 3.6 there is a new function called hook. __init_subclass__
__init_subclass__
Thus, you can do something like
class foo:
@classmethod
def __init_subclass__(cls, *args, **kwargs):
Serializers.register_class(cls)
Whenever a child is defined Foo
, the __init_subclass__
method is __init_subclass__
on Foo
, passing a reference to the child class as cls
.
source to share