Как создать динамический отдых ModelSerializer? - PullRequest
0 голосов
/ 26 июня 2019

В настоящее время я работаю над большой кодовой базой, и мне нужно отправлять электронные письма от любого потенциального модуля, который сталкивается с проблемами Круговой зависимости в python

, поэтому я попытался использовать apps.get_model () из django.apps, но когда объявлены сериализаторы, модели не готовы.

Поэтому я пытаюсь создать фабричную функцию, которая создает класс во время выполнения вместо времени запуска

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)

я за исключением и вывод, как:

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

но я получил ReturnDict, содержащий:

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

спасибо за ваше время

1 Ответ

0 голосов
/ 27 июня 2019

через много времени после головной боли я обнаружил, что не могу установить класс после его объявления, так что я использую трюк, основанный на диктате

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)

Я надеюсь, что это поможет кому-то еще

...