dealertrack / django-rest-framework-braces

Collection of utilities for working with django rest framework (DRF)
Other
73 stars 20 forks source link

SerializerForm class does not match documentation #20

Open toshka opened 6 years ago

toshka commented 6 years ago

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.serializerform.SerializerFormOptions does not populates fields from serializer's __declaredfields? Is it feature or bug? Can I send PR to change this behavior?

shahmitul commented 4 years ago

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

SebCorbin commented 3 years ago

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