4👍
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.
2👍
Is there a change in the implementation of the fields in forms
What changed between Django 2.2 and 3 is how declared fields are initialized:
- Django 2.2.12 https://github.com/django/django/blob/2.2.12/django/forms/forms.py#L39
- Django 3 https://github.com/django/django/blob/master/django/forms/forms.py#L36.
I guess this is because Django 3 supports Python from version 3.6 or later (https://docs.djangoproject.com/en/3.0/faq/install/) and since Python 3.6 dicts are insertion ordered (Are dictionaries ordered in Python 3.6+?).
I would convert self.fields
to an OrderedDict
(basically going back to what it used to be in version 2.2) to enable self.fields.move_to_end
again.
from collections import OrderedDict
class LocalizedFirstLastNameMixin(object):
def __init__(self, *args, **kwargs):
self.language_code = kwargs.pop('language_code', 'en')
super().__init__(*args, **kwargs)
self.fields = OrderedDict(self.fields)
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, '')
0👍
OrderedDict
is from the standard library.
from collections import OrderedDict
and instances have .move_to_end()
, It just says you dict
object has no .move_to_end()
which implies you’re using a normal dict. Cast it to an OrderedDict
like this
x = { "key" : "value"}
y = OrderedDict(x)
# OrderedDict([('key', 'value')])
now .move_to_end()
will work
0👍
I’m late to this party, but this may be of use to somebody else. With recent Python (3.6+), an ordinary dict
keeps keys in the order they are added. With earlier versions, you need to use collections.OrderedDict
. Django used to use it’s own dict-like class, but now uses a vanilla dict
. print( type(form_instance.fields))
if in doubt.
Anyway, you can re-order the fields any way you like by re-defining self.fields
in a form’s __init__
method. As in:
class OrderUncWaferBatchForm( ModelForm):
#added non-model fields ...
ponum = forms.CharField( max_length=10,
label=mark_safe("Purchase Order<br>Number")
)
supplier = forms.CharField( max_length = 16)
def __init__(self, *args, **kwargs):
super().__init__( *args, **kwargs)
self.fields = reorder_dict( self.fields,
# these fields in this order at the top of the form
['ponum','supplier', 's_cost', 'wafer_stock_orig'])
class Meta:
model=WaferBatch
fields = ...
And since I’ve written a pretty general reorder_dict function, here it is
def reorder_dict(old_dict, reordering, dict_class=None):
""" Returns a new dict with keys added in the order specified by
reordering. This is a sequence of tuples (fromkey, tokey) which
operates on the list of keys of old_dict. Each tuple
(from_key, to_key) removes the named key from its current
posotion and inserts it before to_key. Each tuple operates
on the list permuted by all previous ones.
Any key that is not in old_dict will cause an IndexError
to_key can also be an integer for list.insert position. Using
a bignum will insert at the end. Using 0, at the top.
As a special case reordering can also be a list of key strings (not tuples).
This means to put the keys in the list at the top (positions 0,1,...) and
leave any keys not mentioned in theit original order after the
ones in the list.
Efficiency is not a consideration. It's intended for use
on form.fields, which rarely contains more than a handful of fields
"""
if dict_class is None:
dict_class = dict # on old Python wouold have to be an OrderedDict
if isinstance( reordering[0], str):
reordering = [ (k,n) for n,k in enumerate( reordering) ]
keys = list( old_dict.keys() )
for from_key, to_key in reordering:
fom = keys.index( from_key) # from is reserved word
if isinstance( to_key, int):
to = to_key
else:
to = keys.index( to_key)
keys.insert( to, from_key)
if to <= fom:
fom += 1 # it's been pushed down
del keys[fom]
result = dict_class()
for k in keys:
result[k] = old_dict[k]
return result
- [Django]-Google app engine + python (django) deployment error: Error loading MySQLdb module
- [Django]-Error message: "'NoneType' object is not callable". – received from zappa deployed django application on AWS Lambda
- [Django]-Many to many relationship in django database model
- [Django]-Django app is working while not included in settings.py
- [Django]-Send an email from an API endpoint using Django Rest Framework and Angular