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.

+3


source to share


2 answers


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.

+2


source


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

.

+1


source







All Articles