なんのこっちゃって感じ。rest_frameworkのソース読んでて、
あー、これはこういう事に使うのかってピンと来たので残しておく。
DjangoのModelもそうなんだけど、シリアライザを作る時に
class CommentSerializer(serializers.Serializer):
email = serializers.EmailField()
content = serializers.CharField(max_length=200)
created = serializers.DateTimeField()
こんな感じで、「クラス変数」にモデルの定義を入れてる。
で、クラス変数ってstaticだから、インスタンス化して使おうが、
結局全インスタンスで共有されてる変数。
でも、DjangoもRestFrameworkもちゃんとインスタンス変数になってる。
なんでかなーって不思議だったんですが、ソース読んでると、
metaclassの定義使って、クラス定義読み込まれる時に、クラス変数を潰して、
インスタンス変数作ってるんですね。
RestFrameworkのソースだと↓の部分。
class SerializerMetaclass(type):
"""
This metaclass sets a dictionary named `_declared_fields` on the class.
Any instances of `Field` included as attributes on either the class
or on any of its superclasses will be include in the
`_declared_fields` dictionary.
"""
@classmethod
def _get_declared_fields(cls, bases, attrs):
fields = [(field_name, attrs.pop(field_name))
for field_name, obj in list(attrs.items())
if isinstance(obj, Field)]
fields.sort(key=lambda x: x[1]._creation_counter)
# If this class is subclassing another Serializer, add that Serializer's
# fields. Note that we loop over the bases in *reverse*. This is necessary
# in order to maintain the correct order of fields.
for base in reversed(bases):
if hasattr(base, '_declared_fields'):
fields = [
(field_name, obj) for field_name, obj
in base._declared_fields.items()
if field_name not in attrs
] + fields
return OrderedDict(fields)
def __new__(cls, name, bases, attrs):
attrs['_declared_fields'] = cls._get_declared_fields(bases, attrs)
return super().__new__(cls, name, bases, attrs)
で、Serializerの定義が↓
class Serializer(BaseSerializer, metaclass=SerializerMetaclass):
SerializerMetaclassの↓のところ
@classmethod
def _get_declared_fields(cls, bases, attrs):
fields = [(field_name, attrs.pop(field_name))
for field_name, obj in list(attrs.items())
if isinstance(obj, Field)]
fields.sort(key=lambda x: x[1]._creation_counter)
attrsにクラス定義でのクラス変数とか入ってて、これが最終的にクラス定義内に
展開されるっぽい。なので、attrs.popでクラス変数として消してる。
消すタイミングで、fieldsって変数に定義を退避してる感じ。
で、typeを継承してて、最後に「return super().__new__(cls, name, bases, attrs)」してる
という感じ。
なるほどー。ってなんか納得した。
pythonでメタプログラミングするならtype継承してクラス定義が作られてるって
知っとけみたいな話見てて「は?」って思ってたんだけど、
こう具体的なtypeの使い方見ると納得できますね。。。
で、↑のが何で便利なのかというと、MVCとMVVMとかで作ってる時って、
View層とのやり取りは専用のViewModel作ると思うんですよ。
バリデーションとか統一しやすいし。
で、ViewModelの定義する時のベースにこれがすごく使える。
一つ一つのフィールドのデータ型を個別に作れるので。
一つ賢くなりましたというお話。。。