Django

Code

Ticket #5986 (closed: duplicate)

Opened 2 years ago

Last modified 1 year ago

Custom field order in newforms

Reported by: emes Assigned to: nobody
Milestone: Component: Forms
Version: SVN Keywords: field order weight form newforms
Cc: marc.garcia@accopensys.com, matti.haavikko@futurice.fi, sime Triage Stage: Design decision needed
Has patch: 1 Needs documentation: 1
Needs tests: 0 Patch needs improvement: 0

Description

Consider this example:

from django import newforms as forms

class UserForm(forms.Form):
    # Used for registering new accounts.
    username = forms.CharField(max_length=20)
    email = forms.EmailField()
    password = forms.CharField(max_length=20)
    password2 = forms.CharField(max_length=20)

    # Lots of validators which check if username is unique,
    # password1 == password2 and so on.

class UserProfileForm(UserForm):
    # Inherits all UserForm fields and validators and adds
    # optional profile entries.
    first_name = forms.CharField(max_length=30)
    last_name = forms.CharField(max_length=30)
    jabber_id = forms.EmailField(required=False)

The UserProfileForm can inherit all the goods of UserForm: fields and validators. But the field order may look a bit messy then:

>>> UserProfileForm.base_fields.keys()
['username',
 'email',
 'password',
 'password2',
 'first_name',
 'last_name',
 'jabber_id']

It would be nice to have email grouped with jabber_id, first_name and last_name with username, etc. It's of course possible to do it using custom template, but violates DRY principle and renders as_*() methods useless.

The attached patch allows to specify a custom Field order within a Form, even with inherited fields.

Every Field may have an additional weight parameter with default value of 0. All fields are sorted in ascending weight order.

The example forms customized with weight parameters:

from django import newforms as forms

class UserForm(forms.Form):
    username = forms.CharField(max_length=20, weight=-2)
    email = forms.EmailField()
    password = forms.CharField(max_length=20, weight=1)
    password2 = forms.CharField(max_length=20, weight=1)

class UserProfileForm(UserForm):
    first_name = forms.CharField(max_length=30, weight=-1)
    last_name = forms.CharField(max_length=30, weight=-1)
    jabber_id = forms.EmailField()

And the effect:

>>> UserProfileForm.base_fields.keys()
['username',
 'first_name',
 'last_name',
 'email',
 'jabber_id',
 'password',
 'password2']

Attachments

django-fields-weight.patch (1.9 kB) - added by emes on 11/19/07 15:18:45.
The patch adding weight parameter to newforms.Field
django-fields-order.patch (1.4 kB) - added by emes on 11/20/07 06:00:07.
Second approach, with Form.Meta.fields_order
django-fields-order.2.patch (4.9 kB) - added by Patryk Zawadzki <patrys@pld-linux.org> on 11/20/07 08:32:12.
Correct patch including regression tests
django-fields-order.3.patch (5.6 kB) - added by Patryk Zawadzki <patrys@pld-linux.org> on 11/20/07 08:58:11.
Added support for form_for_model

Change History

11/19/07 15:18:45 changed by emes

  • attachment django-fields-weight.patch added.

The patch adding weight parameter to newforms.Field

11/19/07 16:58:47 changed by patrys@pld-linux.org

  • needs_better_patch changed.
  • needs_tests changed.
  • needs_docs changed.

Might be more useful to reimplement it as a meta-property containing the desired list of field names (the way you define columns to display for admin interface). This way each form can have its own independent list there and you can actually try to extend two forms at once while maintaining the correct and predictable output.

11/20/07 05:59:28 changed by emes

So, here it goes, with Form.Meta.fields_order list.

Example:

from django import newforms as forms

class UserForm(forms.Form):
    # Used for registering new accounts.
    username = forms.CharField(max_length=20)
    email = forms.EmailField()
    password = forms.CharField(max_length=20)
    password2 = forms.CharField(max_length=20)

    # Lots of validators which check if username is unique,
    # password1 == password2 and so on.


class UserProfileForm(UserForm):
    # Inherits all UserForm fields and validators and adds
    # optional profile entries.
    first_name = forms.CharField(max_length=30)
    last_name = forms.CharField(max_length=30)
    jabber_id = forms.EmailField()

    class Meta:
        fields_order = ['username', 'first_name', 'last_name',
                'email', 'jabber_id', 'password']

11/20/07 06:00:07 changed by emes

  • attachment django-fields-order.patch added.

Second approach, with Form.Meta.fields_order

11/20/07 08:32:12 changed by Patryk Zawadzki <patrys@pld-linux.org>

  • attachment django-fields-order.2.patch added.

Correct patch including regression tests

11/20/07 08:32:32 changed by Patryk Zawadzki <patrys@pld-linux.org>

  • summary changed from Custom field order in newforms to [PATCH] Custom field order in newforms.

11/20/07 08:58:11 changed by Patryk Zawadzki <patrys@pld-linux.org>

  • attachment django-fields-order.3.patch added.

Added support for form_for_model

11/20/07 09:05:16 changed by Patryk Zawadzki <patrys@pld-linux.org>

  • has_patch set to 1.

(follow-ups: ↓ 6 ↓ 7 ) 11/20/07 10:34:29 changed by jkocherhans

I'm sorry to be blunt, but I couldn't be more against this change, or rather the weight=X syntax. I'm working on a new class called ModelForm right now (search django-dev for the relevant thread) that should allow something similar to the fields_order attribute above. It's just called fields and it will actually restrict the fields that occur in the form as well.

(in reply to: ↑ 5 ) 11/20/07 10:54:38 changed by Patryk Zawadzki <patrys@pld-linux.org>

Replying to jkocherhans:

I'm sorry to be blunt, but I couldn't be more against this change, or rather the weight=X syntax. I'm working on a new class called ModelForm right now (search django-dev for the relevant thread) that should allow something similar to the fields_order attribute above. It's just called fields and it will actually restrict the fields that occur in the form as well.

This has little or nothing to do with form_for_* syntax. This adds the ordering ability for all Form subclasses as it only operates inside the metaclass. If your ModelForm? class or any other class extends Form then it gains this feature for free.

The only relevant bit would be removing the small block of code including the comment about form_for_* once these functions die.

I think the patch is still appropriate to commit and it adds a nice regression test so you can be sure things work like they did and will continue to do so.

(in reply to: ↑ 5 ) 11/20/07 11:02:52 changed by Michał Sałaban <michal@salaban.info>

Replying to jkocherhans:

I'm sorry to be blunt, but I couldn't be more against this change, or rather the weight=X syntax. I'm working on a new class called ModelForm right now (search django-dev for the relevant thread) that should allow something similar to the fields_order attribute above. It's just called fields and it will actually restrict the fields that occur in the form as well.

The weight syntax is obsolete. Please, see the latest patch and example.

I found the discussion about ModelForms but don't see if they deal with the order of fields. And how can they be used to create Forms which are not Model-based?

Anyway, I see no problem in coexistence of ModelForms and Meta.fields_order above.

12/12/07 19:53:16 changed by bear330

This might be useless, but my way to order the fields is:

def sortDictByList(dic, seq):
    """
    Sort dictionary by giving a sequence.

    @param dic The dictionary instance you want to sort.
    @param seq The sequence you want to specify how to sort.
    @return Sorted dictionary.
    """
    n = SortedDict()
    for k in seq:
        n[k] = dic[k]

    for k, v in dic.iteritems():
        if n.has_key(k):
            continue
        n[k] = v
    return n

class SortFormFieldsMixin(object):
    """
    Sort the form fields by 'fieldsOrder' attribute in mixined form class.
    The fieldsOrder attribute is a list to figure out the order of form fields.
    """
    def __new__(cls):
        if not hasattr(cls, 'fieldsOrder'):
            raise RuntimeError("Bitch! You use SortFormFieldsMixin but no "
                "fieldsOrder attribute in it, stupid man~~~!!")
                                        
        cls.base_fields = sortDictByList(cls.base_fields, cls.fieldsOrder)
        return super(SortFormFieldsMixin, cls).__new__(cls)
    
BaseAForm = forms.form_for_model(models.A, BaseForm)

class FinalForm(BaseAForm, SortFormFieldsMixin):
    fieldsOrder = ['name', 'desc', 'link']

Anyway, the class Meta with fields_order should be a better way because the coordination with django's convention.

I posted here just for a reference. :)

02/22/08 16:13:39 changed by PJCrosier

  • needs_docs set to 1.
  • summary changed from [PATCH] Custom field order in newforms to Custom field order in newforms.

03/17/08 12:54:04 changed by MichaelBishop

  • stage changed from Unreviewed to Design decision needed.

Decision needs to be made whether adding a custom field order is a "good thing". IMHO this doesn't appear to be a critical feature. Either way a design decision is needed.

03/25/08 06:22:51 changed by ubernostrum

#6369 and #6878 were closed as duplicates.

03/25/08 07:21:02 changed by dcramer

Can we call it ordering to stick w/ a variable name thats already used instead of creating more?

Michael, features don't need to be critical to be wanted :)

04/11/08 04:41:31 changed by simonb

07/02/08 07:17:23 changed by garcia_marc

  • cc set to marc.garcia@accopensys.com.
  • milestone set to 1.0 beta.

+1 on this.

Specially because it's also needed for ModelForm?, where fields Meta attribute couldn't be used for sorting. Without a order parameter, it's not easy to position in the correct place an additional parameter of a ModelForm?.

07/29/08 05:26:16 changed by mir

  • milestone changed from 1.0 beta to post-1.0.

not a bug => not milestone 1.0 beta.

09/22/08 07:51:47 changed by haavikko

  • cc changed from marc.garcia@accopensys.com to marc.garcia@accopensys.com, matti.haavikko@futurice.fi.

10/10/08 13:55:38 changed by GertSteyn

After reeding CookBookNewFormsFieldOrdering this came to mind...

class FooForm?(forms.ModelForm?):

class Meta:

fields = ('first_field', 'second_field', 'third_field',)

def init(self, *args, **kwargs):

super(FooForm?, self).init(*args, **kwargs) self.fields.keyOrder = self.Meta.fields

The patch has now been reduced to a one liner: "self.fields.keyOrder = self.Meta.fields"

10/27/08 00:57:02 changed by sime

  • cc changed from marc.garcia@accopensys.com, matti.haavikko@futurice.fi to marc.garcia@accopensys.com, matti.haavikko@futurice.fi, sime.

11/06/08 06:33:17 changed by lorien

The patch has now been reduced to a one liner: "self.fields.keyOrder = self.Meta.fields"

I think that is not correct. In this case only those fields that described in Meta.fields will be displayed. Custom fields that have been added in the init or in the class will be truncated.

02/12/09 22:18:05 changed by miracle2k

Apparently related ticket: http://code.djangoproject.com/ticket/8164

02/22/09 13:51:16 changed by Alex

  • status changed from new to closed.
  • resolution set to duplicate.

Closing as a dpue of #8164 since it has the better API.

02/25/09 13:51:44 changed by

  • milestone deleted.

Milestone post-1.0 deleted


Add/Change #5986 (Custom field order in newforms)




Change Properties
Action