pythondjangodjango-formsmodelformdjango-3.0

How do I specify order of fields in Django form?


We are using Django 2.2 and I want to upgrade to Django 3.0. We have a mixin (written in 2017) that add fields to forms:

class LocalizedFirstLastNameMixin(object):
    def __init__(self, *args, **kwargs):
        self.language_code = kwargs.pop('language_code', 'en')
        super().__init__(*args, **kwargs)
        for loc_field in reversed(self.get_localized_fields()):
            self.fields[loc_field] = User._meta.get_field(loc_field).formfield()
            self.fields[loc_field].required = True
            self.fields.move_to_end(loc_field, last=False)
            self.initial[loc_field] = getattr(self.instance, loc_field, '')

self.get_localized_fields() returns ('first_name_en', 'last_name_en') (in this order) in English, or the same localized to the current language we are using.

This mixin is used as one of the bases classes for forms which inherit from ModelForm:

class RegistrationForm(AddAttributesToFieldsMixin, CleanEmailMixin, CleanNewPasswordMixin, CleanDateOfBirthMixin, LocalizedFirstLastNameMixin, forms.ModelForm):
    ....

class ProfileForm(AddAttributesToFieldsMixin, CleanDateOfBirthMixin, LocalizedFirstLastNameMixin, forms.ModelForm):
    ....

It works with Django versions up to 2.2. But when I upgrade to 3.0, I get this error message:

AttributeError: 'dict' object has no attribute 'move_to_end'

This function's info:

Move an existing element to the end (or beginning if last==False).

And it belongs to OrderedDict.

So I guess we want these fields to be in the beginning of the form fields.

Is there a change in the implementation of the fields in forms in Django 3.0 and how do I specify the order of fields? And if I change it, will it work in previous versions such as Django 2.2?

I checked the Django 3.0 release notes and also releases from 3.0.1 to 3.0.5 and I didn't find any documentation of this issue.

Update: I found out that I can call self.order_fields(...), but how do I define the fields which come from the models? I only want to add two additional fields in the beginning of the list of fields.


Solution

  • I asked in the Django developers mailing list and I have been told not to manipulate the order of fields myself, but instead to use the supported API methods documented here. So I changed the code and used self.order_fields instead:

    class LocalizedFirstLastNameMixin(object):
        def __init__(self, *args, **kwargs):
            self.language_code = kwargs.pop('language_code', 'en')
            super().__init__(*args, **kwargs)
            localized_fields = self.get_localized_fields()
            for loc_field in localized_fields:
                self.fields[loc_field] = User._meta.get_field(loc_field).formfield()
                self.fields[loc_field].required = True
                self.initial[loc_field] = getattr(self.instance, loc_field, '')
            self.order_fields(field_order=localized_fields)
    

    Notice that I only order the first two fields. All the other fields are kept in the default order. I also don't have to add the fields now in a reversed order.