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+unsubscr...@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.