简体   繁体   中英

Class reference inside class scope in python

Some 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 rest_framework library installed. One of the interesting features I am using is ModelSerializers ( ModelSerializers Documentation ), which save quite a lot of code repetition. I want Serializable.types variable to be populated on runtime (when all the serializer classes are declared). The whole point of this is that I will not have to update my views whens a new type of model is included. 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)

The Problem

The major problem is in the last line of each Serializer class.

Serializable.types[oid] = [model, <class-reference>]

I've tried putting the name of the class, ProfileSerializer for example, to no avail. I've tried doing the same outside of the Meta class, such as:

class ProfileSerializer(serializers.ModelSerializer):
    class Meta:
        oid = 'profile'
        model = Profile
        fields = ['login', 'status']

    Serializable.types[Meta.oid] = [Meta.model, ProfileSerializer]

also not successful. Not sure what else to do, which is why I'm hoping the community can help me on this one.

This is actually a case for defining a metaclass.

I've never actually found a source of information which gives a complete, clear and satisfactory explanation as to what metaclasses are or how they work. I will try to enhance this answer with such information if required but for the time being I am going to stick to a solution for your present problem. I am assuming python 3.

Define an additional class, thus:

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 the metaclass of your Serializers, eg

class ProfileSerializer(serializers.ModelSerializer, metaclass=ModelSerializerMeta):

    class Meta:
        oid = 'profile'
        model = Profile
        fields = ['login', 'status']

Better yet, create some superclass for all your model serializers, assign the metaclass there, make all of your serializers inherit from that superclass which will then use the metaclass throughout.

Metaclasses are definitely the right answer unless your code can require python >= 3.6. Starting with 3.6 there is a new feature called the __init_subclass__ hook.

So you can do something like

class foo:

    @classmethod
    def __init_subclass__(cls, *args, **kwargs):
        Serializers.register_class(cls)

Whenever a child of Foo is defined, the __init_subclass__ method on Foo will be called, passing in the child class reference as cls .

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM