Model form with optional fields issue

335 views
Skip to first unread message

marcin....@gmail.com

unread,
Dec 19, 2014, 1:44:00 PM12/19/14
to django...@googlegroups.com
Hello!

I have model form defined with all optional fields (required=False).
I want use this form to update only provided fields leaving others unchanged.
But Django replaces optional fields to empty values and removes data from models.


A short example:

class MyForm(forms.ModelForm):
    class Meta:
        model = User
        fields = ('username', 'email')

    def __init__(self, *args, **kw):
        super(MyForm, self).__init__(*args, **kw)
        for field in self.fields.values():
            field.required = False


In [21]: admin = User.objects.get(username='admin')

In [22]: admin.email

In [23]: form = MyForm(data={'username': 'admin'}, instance=admin)

In [24]: form.is_valid()
Out[24]: True

In [25]: form.save()
Out[25]: <User: admin>

In [26]: admin = User.objects.get(username='admin')

In [27]: admin.email
Out[27]: u''

In my opinion this is big, fat, ugly bug or a design misconception.  
If something is not provided does not mean that someting does not exist!
Django should skip updating these unprovided fields by removig them from cleaned_data dict. 

You can even try to save this form without data (ok with data, but with unknown keys):

form = MyForm(data={'unknownfield': 'some-value'}, instance=admin)
form.is_valid()

# form.cleaned_data has empty strings

form.save()


Username and email will be replaced with empty strings. 
Where is admin?, you'll ask... And you'll hear a strange voice - "admin is cooking borscht with ravioli, dude!"

What do you think - is this expected? 


/Marcin

Carl Meyer

unread,
Dec 19, 2014, 2:40:59 PM12/19/14
to django...@googlegroups.com
Hi Marcin,

On 12/19/2014 11:44 AM, marcin....@gmail.com wrote:
> Hello!
>
> I have model form defined with all optional fields (required=False).
> I want use this form to update only provided fields leaving others
> unchanged.
> But Django replaces optional fields to empty values and removes data from
> models.
>
>
> A short example:
>
> class MyForm(forms.ModelForm):
> class Meta:
> model = User
> fields = ('username', 'email')
>
> def __init__(self, *args, **kw):
> super(MyForm, self).__init__(*args, **kw)
> for field in self.fields.values():
> field.required = False
>
>
> In [21]: admin = User.objects.get(username='admin')
>
> *In [22]: admin.email*
> *Out[22]: u'ad...@example.com'*
>
> In [23]: form = MyForm(data={'username': 'admin'}, instance=admin)
>
> In [24]: form.is_valid()
> Out[24]: True
>
> In [25]: form.save()
> Out[25]: <User: admin>
>
> In [26]: admin = User.objects.get(username='admin')
>
> *In [27]: admin.email*
> *Out[27]: u''*
>
> In my opinion this is big, fat, ugly bug or a design misconception.
> If something is not provided does not mean that someting does not exist!
> Django should skip updating these unprovided fields by removig them from
> cleaned_data dict.
>
> You can even try to save this form without data (ok with data, but with
> unknown keys):
>
> form = MyForm(data={'unknownfield': 'some-value'}, instance=admin)
> form.is_valid()
>
> # form.cleaned_data has empty strings
>
> form.save()
>
>
> Username and email will be replaced with empty strings.
> Where is admin?, you'll ask... And you'll hear a strange voice - "admin is
> cooking borscht with ravioli, dude!"
>
> What do you think - is this expected?

Yes, this is expected. Django's form system is designed for use with
HTML forms, which do not provide partial data: unchanged field values
are sent too, so this problem does not occur.

That in itself wouldn't prevent switching to the behavior you want,
except that it is not compatible with how checkboxes are handled in HTML
forms. If a checkbox is unchecked, the browser will not send that
checkbox's name in the submitted POST data at all. So with the behavior
you're asking for, it would be impossible to ever change a boolean field
represented by a checkbox from True to False, using an HTML form.

Carl

signature.asc

marcin....@gmail.com

unread,
Dec 22, 2014, 12:14:07 PM12/22/14
to django...@googlegroups.com

> What do you think - is this expected?

Yes, this is expected. Django's form system is designed for use with
HTML forms, which do not provide partial data: unchanged field values
are sent too, so this problem does not occur.

I thought that Web framework can validate RESTful(-like) requests (like PATCH or even GET), but I see that without redesigning/overriding built-in forms it is not possible.. :(

I would like to say that in my opinion validation and cleaning (forms essentials) should be decoupled from HTML input and rendering. 
Without that, forms are only partially usable. It's a design issue, IMO.

Nowdays I'm creating more apps with separated clients written in pure js, and I need solid backend. 
Input cleaning, validation and processing logic is the most important thing.
But with validation designed mostly for plain-old-html input I can't rely on Django as a web framework.
That's sad, because I like it for rapid development. 

Kind Regards,
/Marcin

James Schneider

unread,
Dec 22, 2014, 9:49:07 PM12/22/14
to django...@googlegroups.com

That seems like a bit of a broad brush stroke against Django. Have you looked at the Django REST Framework module? It can often utilize your existing views, and has support for partial updates.

http://www.django-rest-framework.org/api-guide/serializers/#partial-updates

Django is primarily used for displaying/validating data received from HTML forms, hence the bias towards that behavior where partial updates are not common. However, the internal behavior is decoupled enough that the behavior you desire can be easily attained, hence the Django REST Framework.

-James

--
You received this message because you are subscribed to the Google Groups "Django users" group.
To unsubscribe from this group and stop receiving emails from it, send an email to django-users...@googlegroups.com.
To post to this group, send email to django...@googlegroups.com.
Visit this group at http://groups.google.com/group/django-users.
To view this discussion on the web visit https://groups.google.com/d/msgid/django-users/d15de090-649f-4e95-9262-f2f22462598f%40googlegroups.com.
For more options, visit https://groups.google.com/d/optout.

marcin....@gmail.com

unread,
Dec 23, 2014, 5:44:54 AM12/23/14
to django...@googlegroups.com


On Tuesday, December 23, 2014 3:49:07 AM UTC+1, James Schneider wrote:

Have you looked at the Django REST Framework module?

Yes, I have. They wrote own serializers and fields for validation, and they don't use builtin forms.
Personally I don't like DRF API, because it is too heavy and not following PEP20 (simple is better than complex), same as CBV in Django.

I wrote custom REST(-like) framework, independent from any framework but with bridge to Django. It's a little inspired by Cornice framework and I don't need (and don't want) to change it to Django REST Framework.

In my handlers I was using Django forms to clean & validate data, but they failed. I though that I've found bug, but now I see that I need to replace clean & validation to something better (maybe Colander).
  

Django is primarily used for displaying/validating data received from HTML forms

Now I know... I thought that Django is a generic web framework, but it isn't. It's a HTML-oriented web framework.
 

However, the internal behavior is decoupled enough

I disagree. It isn't decoupled enough for generic web framework:

1. Widgets are to close to fields (field.widget), and base Form class is using widgets directly in validation (field.widget.value_from_datadict calls in form`s methods).
2. Form._clean_fields() treats None value same as not existing key in input data - this is logical bug or improper simplification.

But in the context of validation only HTML fields this is OK.

Regards,
Marcin

Collin Anderson

unread,
Dec 26, 2014, 3:51:11 PM12/26/14
to django...@googlegroups.com
Hi Marcin,

It's very true that Django "forms" are very HTML specific. They are built for rendering html form inputs, interpreting raw browser POST data, and displaying errors as HTML.

An API isn't really "form". Validation and cleaning are already decoupled from "forms":

Collin
Reply all
Reply to author
Forward
0 new messages