Hans de Jong
Hans de Jong

Reputation: 2078

django forms set field order

I am trying to set the fieldorder of my form. but somehow it just stays in alphabetical order. Anyone has some suggestions? i tried class Meta: fields = ["field", "field"] and adding a keyOrder in the init

form:

class HangarFilterForm(forms.Form):

    FIELDS = [
        ("", ""),
        ("warp", "Warp"),
        ("cargo_space", "Cargo Space"),
        ("smuggle_bay", "Smuggle Bay"),
        ("dock", "Dock/Undock"),
        ("enter_warp", "Enter Warp"),
        ("fuel_bay", "Fuel Bay"),
        ("fuel_cost", "Fuel Cost"),
    ]

    PER_PAGE = [
        (10, ""),
        (5, "5 ships"),
        (10, "10 ships"),
        (25, "25 ships"),
        (50, "50 ships"),
    ]

    field_1 = forms.ChoiceField(choices=FIELDS, label="1st attribute", required=False)
    field_2 = forms.ChoiceField(choices=FIELDS, label="2nd attribute", required=False)
    per_page = forms.ChoiceField(choices=PER_PAGE, required=False)

    def __init__(self, *args, **kwargs):
        super(HangarFilterForm, self).__init__(*args, **kwargs)
        self.fields['planet'] = forms.ChoiceField(
                        choices=[("", "")] + [ (o.id, o.name) for o in    lanet.objects.all().order_by("name")], 
                        required=False)
        self.fields['type'] = forms.ChoiceField(
                        choices=[("", "")] + [ (o[0], o[1]) for o in ShipTemplate.SHIP_TYPES], required=False)
        self.fields.keyOrder = ["planet", "type", "field_1", "field_2", "per_page"]

Upvotes: 8

Views: 7728

Answers (5)

aysum
aysum

Reputation: 71

I used the bspink's way with some improvements. You don't need to define the fields that you don't want to change their ordering. Define only what you want to put to the top as ordered in themself.

(Be sure you are using the Python 3.7+)

class SomeForm(forms.Form):
    # define only you want to put top, no need to define all of them
    # unless you need more specific ordering        
    ordered_field_names = ['planet', 'type']

    def __init__(self, *args, **kwargs):
        super(SomeForm, self).__init__(*args, **kwargs)
        # call initialization code
        self.rearrange_field_order()

    def rearrange_field_order(self):
        # add defined fields first 
        new_fields = {field_name: self.fields.get(field_name) for field_name in self.ordered_field_names}

        # then add others whose not defined in order list
        for key, value in self.fields.items():
            if key not in new_fields:
                new_fields[key] = value

        self.fields = new_fields

Upvotes: 0

Ghasem
Ghasem

Reputation: 15643

I tried setting fields in Meta part of form in django 2.1 and it also did the trick:

class MyForm(forms.ModelForm):
    ...

    class Meta:
        model = Contact
        fields = ('field_1', 'field_2', 'field_3', 'field_4',)

Upvotes: 3

Jack L.
Jack L.

Reputation: 1335

In Django 1.9, new way of forcing the order of form's fields has been added : field_order.

Take a look (link to version 1.9): https://docs.djangoproject.com/en/1.9/ref/forms/api/#django.forms.Form.field_order

(and a link to dev version): https://docs.djangoproject.com/en/dev/ref/forms/api/#django.forms.Form.field_order

Find below a short example (using Django 1.9)

models.py:

from django.db import models

class Project(models.Model):
     end_date = models.DateField(verbose_name='End date',
                                blank=True)

    start_date = models.DateField(verbose_name='Start date',
                                  blank=True)

    title = models.CharField(max_length=255,
                             blank=False,
                             verbose_name='Title')

    def __str__(self):
        return self.title

forms.py

from django.forms import ModelForm, DateTimeField, SelectDateWidget

from XXX.models import Project

class ProjectForm(ModelForm):
    class Meta:
        model = Project
        fields = '__all__'

    start_date = DateTimeField(widget=SelectDateWidget)
    end_date = DateTimeField(widget=SelectDateWidget)

    field_order = ['start_date', 'end_date']

In this example the fields will be rearranged to:

  1. start_date <== using the list in the form class
  2. end_date <== using the list in the form class
  3. title <== not mentioned in the list, thus using the default ordering

Upvotes: 12

Thomas Maurin
Thomas Maurin

Reputation: 367

Might be a little bit off topic. Using django crispy forms and their Layout objects can help a great deal with formatting forms the way you exactly want. Which includes rearranging the field order.

A sample to illustrate:

class UpdateUserForm(forms.ModelForm):

    def __init__(self, *args, **kwargs):
        super().__init__(*args, **kwargs)
        self.helper = FormHelper()
        self.helper.layout = Layout(
            Field('email'),
            Field('quote'),
            Field('website', placeholder="http://"),
            Field('logo', template="user/logoInput.html"),
            HTML('<label class="control-label">Other settings</label>'),
            Field('emailVisible'),
            Field('subscribeToEmails'),
            Field('mpEmailNotif'),
            Field('showSmileys'),
            Field('fullscreen'),
        )

    class Meta:
        model = ForumUser
        fields = ('email', 'emailVisible', 'subscribeToEmails', 'mpEmailNotif',
                  'logo', 'quote', 'website', 'showSmileys', 'fullscreen')

Upvotes: 1

bspink
bspink

Reputation: 333

This is some code that I've done in the past to rearrange the field order in forms that has worked; you could probably put this into a mixin for use elsewhere. Let me know how it goes.

from django.utils.datastructures import SortedDict


class HangarFilterForm(forms.Form):

    ordered_field_names = ['planet', 'type', 'field_1', 'field_2', 'per_page']

    def __init__(self, *args, **kwargs):
        super(HangarFilterForm, self).__init__(*args, **kwargs)
        # Your field initialisation code
        self.rearrange_field_order()

    def rearrange_field_order(self):

        original_fields = self.fields
        new_fields = SortedDict()

        for field_name in self.ordered_field_names:
            field = original_fields.get(field_name)
            if field:
                new_fields[field_name] = field

        self.fields = new_fields

If you want to keep track of the original file order for some reason, you can just change original_fields to self.original_fields in rearrange_field_order.

Upvotes: 2

Related Questions