Re: Strange behavior using ModelForms

27 views
Skip to first unread message

Chris Cogdon

unread,
Dec 18, 2012, 4:31:02 PM12/18/12
to django...@googlegroups.com
Rather than comparing to instance, why not compare to request.user ?

On Tuesday, December 18, 2012 1:11:21 PM UTC-8, fvianna wrote:
Hello everyone,

I want to apologize if I came to the wrong place to talk about this, but I've been using Django for a while now, and crossed to a very strange behavior that hits me as bug, but I'm not quite sure. First time trying to get a little deeper and maybe report something to help the community.

Django's "How to contribute" page lead me to FAQ, and then here.



So, I have a ModelForm to edit some of my auth.User data, here it is:

class UserForm(ModelForm):
    """ ModelForm for user relevant fields """

    class Meta:
        model = User
        fields = ('first_name', 'last_name', 'email')

    def clean_email(self):
        cleaned_data = super(UserForm, self).clean()
        unavailable_error = ValidationError("Unavailable")
        invalid_error = ValidationError("Invalid")

        email = cleaned_data.get("email")
        if email:
            try:
                user_from_form_email = User.objects.get(email=email)
                if user_from_form_email != self.instance:
                    raise unavailable_error
            except User.DoesNotExist:
                pass
            except User.MultipleObjectsReturned:
                raise unavailable_error
        else:
            raise invalid_error

        # Always return the full collection of cleaned data.
        return cleaned_data.get("email")


Pretty straightforward, with a "unique" verification for the email.
In my view, I receive the form with some edited data from a user. The view looks as it follows (just ignore the ajax stuff and error returning):


def edit_basic_info(request, id):
    response = {'success': False, 'errors': []}

    if request.method == 'POST' and request.is_ajax():
        u_form = UserForm(request.POST, instance=request.user)

        if u_form.is_valid(): 
        if u_form.instance.email != u_form.cleaned_data['email']:
            tmp_profile = u_form.instance.get_profile()
            tmp_profile.email_confirmed = False
            tmp_profile.save()         
           
  u_form.save()
            response['success'] = True
        else:
            response['errors'].append(u_form.errors)


When I get to test if the form e-mail is diferent from the instance e-mail in order to set a flag in my model, both emails

u_form.instance.email

and


u_form.cleaned_data['email']

are the same. After some debugging, I realized they become the same after calling "is_valid" to the bound form. Now, I'm not sure if I am missing something conceptually about ModelForms binding.
Its very ackward to me that I can have a 'instance' field in a ModelForm, but can't distiguish the data after performing the validation. In my case specifically, I need to check first if the email provided by the user is valid, and only then check if its diferent from the instance's e-mail in order to set my flag. But I can't do it, or I will lose the new e-mail provided by the user in the form.

Can anyone enlighten this matter? Is this behavior expected?
Thank you for your time.

Francisco Vianna

unread,
Dec 18, 2012, 9:19:45 PM12/18/12
to django...@googlegroups.com
Because unless I'm getting some real debbuging issues, request.user is also changed during the process.

It happens as if all references were pointing to the same object, when i believe thet, as far as I understand these Django machanisms and functionalities, this should not happen.

The basic issue I'm trying to understand here, I believe, is "What really happens and what is the purpose of binding a modelform to an instance?".


--
You received this message because you are subscribed to the Google Groups "Django users" group.
To view this discussion on the web visit https://groups.google.com/d/msg/django-users/-/e1VZcZs_3loJ.

To post to this group, send email to django...@googlegroups.com.
To unsubscribe from this group, send email to django-users...@googlegroups.com.
For more options, visit this group at http://groups.google.com/group/django-users?hl=en.

Chris Cogdon

unread,
Dec 18, 2012, 10:04:18 PM12/18/12
to django...@googlegroups.com
The binding allows the modelform to read the current values of an instance, and also to write them back to the same instance when the values are changed.

If you're only ever creating new instances, then you don't need to bind it.

Karen Tracey

unread,
Dec 19, 2012, 12:02:50 AM12/19/12
to django...@googlegroups.com
On Tue, Dec 18, 2012 at 4:11 PM, Francisco Vianna <francisco...@gmail.com> wrote:
After some debugging, I realized they become the same after calling "is_valid" to the bound form. Now, I'm not sure if I am missing something conceptually about ModelForms binding.
Its very ackward to me that I can have a 'instance' field in a ModelForm, but can't distiguish the data after performing the validation. In my case specifically, I need to check first if the email provided by the user is valid, and only then check if its diferent from the instance's e-mail in order to set my flag. But I can't do it, or I will lose the new e-mail provided by the user in the form.

Can anyone enlighten this matter? Is this behavior expected?

Yes, is_valid() called on a model form updates the model form's instance in-place with the valid cleaned data. This is documented:

https://docs.djangoproject.com/en/dev/topics/forms/modelforms/#the-is-valid-method-and-errors

Your form field's clean method would have access to the "old" value, since that runs before the new value has been fully validated and saved in the instance.

Karen
--
http://tracey.org/kmt/

Reply all
Reply to author
Forward
0 new messages