Hi,

On 04/03/16 07:00, 'Moritz Sichert' via Django developers (Contributions to Django itself) wrote:
Hello,

I want to propose a refactor of the Form class to make it
easier to customize and subclass.

I've recently had thoughts in this direction, so seeing your post intrigued me...

However, I feel some of your assertions are a bit off the mark...

Motivation:

Currently the Form class (combined with Field, Widget and
BoundField) has many responsibilities and combines much
functionality into a single class. It currently does:

- data validation (using Field validators and clean*
   methods)

Correct - this is the main purpose of Forms.

- parsing HTTP form data and handling special cases like
   file upload (using Widget.value_from_datadict() and
   Field.to_python())

Well, Widgets and Fields are involved in determining how to extract data from the submitted dict... but they don't actually "parse" the http form data -- Django does that much further down the stack in the HttpRequest object.


- displaying values as HTML input fields (using
   Widget.render(), BoundField.value(), Field.prepare_value()
   and even the Media class)


Although those three functionalities are closely related one
can easily think of situations where only one is needed.
Following Django's principle of loose coupling it would also
be desirable to be able to exchange components.

For example, it's conceivable that the form data doesn't
come from a HTTP request with x-www-form-urlencoded or
multipart/form-data content but gets transferred as JSON. In
this case it should be easy to replace the HTTP parsing

As mentioned above, Forms don't handle this parsing. You can pass _any_ dict-like for them to validate. In fact, I (and many others) have used this for a simple and effective way to import csv files, in combination with stdlib's csv.DictReader.

I also frequently use Forms for validating JSON data...

Also HTML forms may not be the only place where data
validation is needed. An application may get an untrusted
dict of Python objects that doesn't have to be parsed but
should still be validated. In that case it would be nice to
be able to just use the data validation component.

Therefore I propose splitting the Form class into multiple
components that can be easily exchanged and used without
another.

Here we somewhat agree.

My view was Forms involve the following actions:

1. Validation - the most important.
2. Rendering - currently handled by widgets
3. Sourcing - pulling in data, initial, defaults [POST, Form initial, ModelForm instance, etc]
4. Sinking - pushing out validated data [cleaned_data, ModelForm.save()]

I feel the current validation pattern is very powerful and flexible, and don't see much need to alter it.

In prior discussions about improving rendering in forms [a long time bugbear of mine] there are a number of subtleties that are not so obvious at first blush, where the current implementation blurs the Field and Widget.

ModelForm's show a reasonable example for how to overlay extensions for sourcing and sinking, but a more formalised approach could certainly benefit the wider range of practices in the modern web.

Rationale:

To give you a better idea about the proposal I already
prepared a general architecture of how I think this could be
implemented. This is of course in no way final and I'll be
happy to receive feedback and suggestions!

Components:

Each of the aforementioned components is implemented in a
class of its own:

- FormValidator:
   Implements the data validation as the name suggests. It
   uses Field instances that can be declared declaratively as
   it is possible with the old Form class. Furthermore it
   provides all methods of the old Form class that dealt with
   data validation, i.e. clean*(), is_valid(), errors(), etc.

Agreed.

- DataProvider:
   Implements the "parsing and providing data" component. It
   has only one method "get_value(name)" that must be
   overwritten by subclasses and returns the data for a given
   field name.

So that would be a "get_field_value"?

   HtmlFormProvider is a subclass of DataProvider that
   provides the parsing of HTTP form content by using Widget
   objects and their "value_from_datadict()" method in
   particular. The widgets of a HtmlFormProvider can be
   specified declaratively just like the fields of a
   FormValidator.

- HtmlForm:
   Implements the "displaying values as HTML input fields"
   component. Same as HtmlFormProvider it uses Widget objects
   and their "render()" method in particular. It also
   features all methods of the old Form class that were used
   mainly in templates, like __iter__(), __getitem__(),
   __str__(), as_p(), etc.

   Furthermore it fetches its data from a given
   HtmlFormProvider instance and provides handling of error
   messages with a FormValidator instance. It is also
   possible to decouple HtmlForm and HtmlFormProvider such
   that HtmlForm works with any DataProvider subclass.

Changes to existing classes:

Obviously the Form class does not exist anymore, however
the Field class was also changed slightly. As it isn't used
for any display logic anymore but solely for validation it
loses a few attributes that are better suited to the Widget
class like "label", "help_text" and "show_hidden_inital" and
it isn't possible to define a set a Field's widget. To
compensate those attributes where moved to the Widget class.

This scale of backwards incompatibility might scare off a lot of people from supporting this project. So be sure to provide (a) an easy migration path, and/or (b) a clear backward compatibility layer.

I'd be happy to further develop these ideas [and code] with you if you like.

--
Curtis

--
You received this message because you are subscribed to the Google Groups "Django 
developers  (Contributions to Django itself)" group.
To unsubscribe from this group and stop receiving emails from it, send an email 
to django-developers+unsubscr...@googlegroups.com.
To post to this group, send email to django-developers@googlegroups.com.
Visit this group at https://groups.google.com/group/django-developers.
To view this discussion on the web visit 
https://groups.google.com/d/msgid/django-developers/56DA3D1A.4060407%40tinbrain.net.
For more options, visit https://groups.google.com/d/optout.
  • P... 'Moritz Sichert' via Django developers (Contributions to Django itself)
    • ... is_null
    • ... Curtis Maloney
      • ... 'Moritz Sichert' via Django developers (Contributions to Django itself)

Reply via email to