views:

46

answers:

2

I think I finally figured out they need to use this DeclarativeFieldsMetaclass (to turn the class fields into instance variables and maintain their order with an ordered/sorted dict). However, I'm still not quite sure why they opted to use a BaseForm rather than implementing everything directly within the Form class?

They left a comment,

class Form(BaseForm):
    "A collection of Fields, plus their associated data."
    # This is a separate class from BaseForm in order to abstract the way
    # self.fields is specified. This class (Form) is the one that does the
    # fancy metaclass stuff purely for the semantic sugar -- it allows one
    # to define a form using declarative syntax.
    # BaseForm itself has no way of designating self.fields.

But I don't really understand it. "In order to abstract the way self.fields is specified" -- but Python calls DeclarativeFieldsMetaclass.__new__ before Form.__init__, so they could have taken full advantage of self.fields inside Form.__init__ as is; why do they need an extra layer of abstraction?

+1  A: 

I think reason is simpl,e with BaseForm alone you can't define fields using a decalrative syntax i.e.

class MyForm(Form):
    field_xxx = form.TextField(...)
    field_nnn _ form.IntegerField(...)

For such thing to work for should have a metaclass DeclarativeFieldsMetaclass which is set in Form only, they did that because

This is a separate class from BaseForm in order to abstract the way, self.fields is specifie

so now you can write WierdForm class in which fields can be defined may be in some wierd way e.g. passing params to class object, point is all the API is in BaseForm and Form class just provides an easy to defined fields.

Summary: IMO django preferred to introduce another layer so that if needed different type of field declaration can be implemented, at-least it keeps the non core functionality of forms separate.

Anurag Uniyal
That's my whole point, *yes you can*. You don't need the extra layer to achieve this "declarative syntax". I know so, because I just did it myself, and now I'm pondering why Django decided to do it differently. I'm not even certain it's completely necessary to use a metaclass, although it's probably cleaner and more pythonic to do so. What do you mean by "passing params to class object" -- do you mean giving `WeirdForm(Form)` an `__init__` method? Won't that prevent `BaseForm.__init__` from being called? If not, then that's my answer.
Mark
Just tried it. Adding an `__init__` method to your `WeirdSubForm` skips the call to `BaseForm.__init__` (unless called explicitly), so that can't be the case either.
Mark
Oh... just thought of this... maybe it's efficiency/future-compatibility thing? If you keep `Form` empty then `DeclarativeFieldsMetaclass.__new__` only has to iterate over fields you've defined, not all the extra crap in `BaseForm`? Which in theory would allow you to add actual `Fields` to the `BaseForm` and not have them added to the `fields` collection. Unless the `attrs` in `__new__` *does* contain the `BaseForm` attrs since it's derived... I'll have to test this.
Mark
A: 

Source:

class MetaForm(type):
    def __new__(cls, name, bases, attrs):
        print "%s: %s" % (name, attrs)
        return type.__new__(cls, name, bases, attrs)

class BaseForm(object):
    my_attr = 1
    def __init__(self):
        print "BaseForm.__init__"

class Form(BaseForm):
    __metaclass__ = MetaForm
    def __init__(self):
        print "Form.__init__"

class CustomForm(Form):
    my_field = 2
    def __init__(self):
        print "CustomForm.__init__"

f = CustomForm()

Output:

Form: {'__module__': '__main__', '__metaclass__': <class '__main__.MetaForm'>, '__init__':<function __init__ at 0x0227E0F0>}
CustomForm: {'__module__': '__main__', 'my_field': 2, '__init__': <function __init__ at 0x0227E170>}
CustomForm.__init__

Looks like MetaForm.__new__ is called twice. Once for Form and once for CustomForm, but never for BaseForm. By having a clean (empty) Form class, there won't be any extraneous attributes to loop over. It also means that you can define Fields inside the BaseForm that could be used for internal use, but avoid rendering.

Mark