Saturday, November 5, 2016

Bug? ModelChoiceField with to_field_name incorrectly returns True on has_changed() function

Should this be considered a bug? When you provide the to_field_name to a ModelChoiceField, the instance object's field (the primary key) will not match the form's field (the to_field_name field name).  Therefore the has_changed() function will return True.  In my example, I created an app called 'app'.

############ app/models.py ###########
from django.conf import settings
from django.db import models


class Account(models.Model):
""" Each user has an account """
name = models.CharField(max_length=64)
user = models.ForeignKey(settings.AUTH_USER_MODEL, null=True)
description = models.TextField()


############ app/forms.py ###########
from django import forms
from django.apps import apps
from django.contrib.auth import get_user_model

class AccountForm(forms.ModelForm):

class Meta:
model = apps.get_model('app.Account')
fields = ['user', 'name', 'description']

user = forms.ModelChoiceField(get_user_model().objects, to_field_name='username')


############ FROM DJANGO SHELL ###########
In [1]: from app.models import Account  In [2]: from app.forms import AccountForm  In [3]: from django.contrib.auth import get_user_model  In [4]: username = 'mynewname'  In [5]: usr = get_user_model().objects.get_or_create(username=username)[0]  In [6]: obj = Account.objects.get_or_create(name='abc', user=usr, description='aaa')[0]  In [7]: new_form = {'name': obj.name, 'user': username, 'description': 'aaa'}  In [8]: frm = AccountForm(new_form, instance=obj)  In [9]: assert frm.is_valid()  In [10]: assert frm.cleaned_data['user'] == usr  In [11]: assert frm.cleaned_data['user'] == obj.user  In [12]: frm.has_changed()  Out[12]: True  In [13]: frm.changed_data  Out[13]: ['user']  


##########################################
# My Override of the ModelChoiceField #
##########################################


############# app/forms.py ############
from django import forms
from django.apps import apps
from django.contrib.auth import get_user_model


class UsernameToObjectField(forms.ModelChoiceField):
""" Take a the value of a name and return the object. """

def has_changed(self, initial, data):
if self.to_field_name is not None:
data_value = self.to_python(data) if data is not None else ''
if data_value is not None:
data_value = data_value.pk
else:
data_value = data
return super(UsernameToObjectField, self).has_changed(initial, data_value)


class AccountForm(forms.ModelForm):

class Meta:
model = apps.get_model('app.Account')
fields = ['user', 'name', 'description']

user = UsernameToObjectField(get_user_model().objects, to_field_name='username')

############ FROM DJANGO SHELL ###########
In [1]: from app.models import Account  In [2]: from app.forms import AccountForm  In [3]: from django.contrib.auth import get_user_model  In [4]: username = 'mynewname'  In [5]: usr = get_user_model().objects.get_or_create(username=username)[0]  In [6]: obj = Account.objects.get_or_create(name='abc', user=usr, description='aaa')[0]  In [7]: new_form = {'name': obj.name, 'user': username, 'description': 'aaa'}  In [8]: frm = AccountForm(new_form, instance=obj)  In [9]: assert frm.is_valid()  In [10]: assert frm.cleaned_data['user'] == usr  In [11]: assert frm.cleaned_data['user'] == obj.user  In [12]: frm.has_changed()
Out[12]: False  In [13]: frm.changed_data  Out[13]: []

--
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+unsubscribe@googlegroups.com.
To post to this group, send email to django-users@googlegroups.com.
Visit this group at https://groups.google.com/group/django-users.
To view this discussion on the web visit https://groups.google.com/d/msgid/django-users/41095b7e-6bb5-4518-8558-dcbb86dc4047%40googlegroups.com.
For more options, visit https://groups.google.com/d/optout.

No comments:

Post a Comment