django-rest-framework-braces icon indicating copy to clipboard operation
django-rest-framework-braces copied to clipboard

SerializerForm class does not match documentation

Open toshka opened this issue 8 years ago • 2 comments
trafficstars

Here described how to create form from serializer class (MyForm). But actually this does not work because there is no fields attribute declared in Meta. Why drf_braces.forms.serializer_form.SerializerFormOptions does not populates fields from serializer's _declared_fields? Is it feature or bug? Can I send PR to change this behavior?

toshka avatar Oct 16 '17 16:10 toshka

@toshka correct, looks like their own example doesn't work. It is not populating any fields.

shahmitul avatar Aug 28 '20 17:08 shahmitul

For the record, it seems SerializerForm only works with basic forms (and basic serializers ?), so if you want to work with model forms, you'll need to create another base class:

from django import forms
from django.forms.models import ModelFormMetaclass
from drf_braces.forms.serializer_form import SerializerFormMeta, SerializerFormOptions


class SerializerModelFormMeta(SerializerFormMeta, ModelFormMetaclass):
    def __new__(cls, name, bases, attrs):
        try:
            parents = [b for b in bases if issubclass(b, SerializerModelForm)]
        except NameError:
            # We are defining SerializerModelForm itself
            parents = None

        meta = attrs.get('Meta', None)

        if not parents or attrs.pop('_is_base', False):
            return ModelFormMetaclass.__new__(cls, name, bases, attrs)

        attrs['_meta'] = options = SerializerFormOptions(meta, name=name)

        new_attrs = cls.get_form_fields_from_serializer(bases, options)
        # attrs should take priority in case a specific field is overwritten
        new_attrs.update(attrs)

        # transfer the fields from serializer to the ModelForm
        if getattr(options.serializer, 'Meta'):
            new_attrs['Meta'].fields = options.serializer.Meta.fields
            new_attrs['Meta'].model = options.serializer.Meta.model

        return ModelFormMetaclass.__new__(cls, name, bases, new_attrs)


class SerializerModelForm(forms.BaseModelForm, metaclass=SerializerModelFormMeta):
    _is_base = True

Which can be used after like this

from django import forms
from rest_framework import serializers
from base.models.person import Person
from base.forms import SerializerModelForm


class PersonSerializer(serializers.ModelSerializer):
    class Meta:
        model = Person
        fields = [
            'first_name',
            'last_name',
            'language',
            'birth_date',
        ]

class PersonForm(SerializerModelForm):
    class Meta:
        serializer = PersonSerializer

SebCorbin avatar Aug 10 '21 14:08 SebCorbin