如何创建动态休息ModelSerializer?

时间:2019-06-26 13:47:10

标签: python-3.x django-rest-framework

我目前正在开发大型代码库,我需要从任何可能的模块发送电子邮件,这些模块以python的循环依赖问题进行操作

所以我尝试使用django.apps中的apps.get_model(),但是在声明序列化程序时,模型尚未准备就绪。

所以我试图创建一个工厂函数,该函数在运行时而不是启动时构建类

from rest_framework.serializers import ModelSerializer


def make_serializer(model: str, fields: tuple, options = None, **nested_fields) -> ModelSerializer:
    """Generate a new serializer "On the fly", so the model does not have to be imported at launch time.
    """
    model_object = apps.get_model(model)
    input_fields = fields

    if options is None:
        options = {}

    class Serializer(ModelSerializer):
        class Meta:
            model = model_object
            fields = input_fields

            def create(self, validated_data):
                # we won't permit to create data from thoses serializers.
                raise NotImplementedError

    # configure nested serializers.
    for nested_field in nested_fields.values():
        for key, nested_serializer_class in nested_field.items():
            serializer_instance = nested_serializer_class(**options.get(key, {}))
            print(model, key, serializer_instance)
            setattr(Serializer, key, serializer_instance)

    return Serializer

我的测试模型看起来像

class Band(Model):
    name = Charfield(max_length=255)


class Influencer(Model):
    entity = Charfield(max_length=255)


class Submission(Model):
    influencer = ForeignKey(Influencer, ...)


class Campaign(Model):
    band = ForeignKey('band.Band', ...)
    submissions = ManyToMany(Submission)

我的测试功能是:

def test():
    serializer = make_serializer(
        model='submission.Campaign',
        fields=['pk', 'submissions', 'band'],
        options={'submissions': {'many': True}},
        nested_fields={
            'submissions': make_serializer(
                model='submission.Submission',
                fields=('influencer',),
                nested_fields={
                    'influencer': make_serializer('influencer.Influencer', ('entity',))
                },
            ),
            'band': make_serializer('band.Band', ('name',))
        }
    )
    return serializer

我没有得到与test()(Campaign.objects.last()).data一致的字段,而是得到了“ pks”,并且序列化程序看起来像:

Serializer():
    pk = IntegerField(label='ID', read_only=True)
    submissions = PrimaryKeyRelatedField(many=True, queryset=Submission.objects.all())
    band = PrimaryKeyRelatedField(allow_null=True, queryset=Band.objects.all(), required=False)

i除外,输出如下:

{
    "pk": 1,
    "band": {
         "name": "BobMarley",
    },
    "submissions": [
        {
            "influencer": {"entity": "The influencer's name"}
        }
    ]
}

但是我有一个ReturnDict包含:

{
    "pk": 1,
    "band": 523,
    "submissions": [6, 7, 8]
}

感谢您的时间

1 个答案:

答案 0 :(得分:-1)

在进行了很多头缓存之后,我发现声明后无法在类上进行attattr,所以我使用了基于字典的技巧

def make_serializer(model: str, fields: tuple, options = None, **nested_fields) -> ModelSerializer:
    """Generate a new serializer "On the fly", so the model does not have to be imported at launch time.
    """
        name = f'Serializer_{model}'
    model_object = apps.get_model(model)
    input_fields = fields

    if options is None:
        options = {}

    def create(self, validated_data):
            # we won't permit to create data from thoses serializers.
            raise NotImplementedError

    class Meta:
        model = model_object
        fields = input_fields

    attrs = {"Meta": Meta}
    # configure nested serializers.
    for key, nested_serializer_class in nested_fields.items():
        attrs[key] = nested_serializer_class(**options.get(key, {}))

    attrs['create'] = create
    return type(ModelDictSerializer)(name, (ModelDictSerializer,), attrs)

语法类似于:

campaign_serializer = make_serializer(
        model='submission.Campaign',
        fields=['pk', 'submissions', 'band'],
        options={'submissions': {'many': True}},
        submissions=make_serializer(
            model='submission.Submission',
            fields=('influencer',),
            influencer=make_serializer('influencer.Influencer', ('entity',))
        ),
        band=make_serializer('band.Band', ('name',))
    )

它就像一种魅力:

Serializer_submission.Campaign(<Campaign: Campaign object (9665)>):
    pk = IntegerField(label='ID', read_only=True)
    submissions = Serializer_submission.Submission(many=True):
        influencer = Serializer_influencer.Influencer():
            entity = CharField(allow_blank=True, max_length=255, required=False)
    band = Serializer_band.Band():
        name = CharField(max_length=255)

我希望这会对其他人有所帮助