Commits

Michael Elsdörfer committed dbb3c0d

Port set_field_order() to new Django.

Comments (0)

Files changed (1)

djutils/models/__init__.py

         class MyModel(AbstractBase):
             __metaclass__ = set_field_order(('name', 'aliases',))
 
-    ``requests`` is expected to be tuple of field names, or an iterable of
-    such tuples.
+    ``requests`` is expected to be tuple of field names, or an iterable
+    of such tuples.
 
     Ultimately, the model field order should be irrelevant and the form
     system might be a better place to do this. Generic admin appliations
     something similar. Once newforms-admin lands, applications uses this
     should probably consider switching to using the same config data as
     newforms-admin.
+
+    This used to just change the Options (_meta) field_cache order, but
+    since https://github.com/django/django/commit/7d11c30994c5f90603264c6e2fcf1d9c79ab8924
+    ModelForms started to resort the fields on it's own.
     """
 
     if not requests:
     if not isinstance(requests[0], (list,tuple)):
         requests = (requests,)
 
-    class OrderedOptions(Options):
-        def _fill_fields_cache(self):
-            super(OrderedOptions, self)._fill_fields_cache()
-            cache = list(self._field_cache)
-            def idx(name):
-                for i in range(0, len(cache)):
-                    if cache[i][0].name == name:
-                        return i
-                return None
+    class OrderMetaclass(ModelBase):
+        def __new__(cls, name, bases, attrs):
+            model = super(OrderMetaclass, cls).__new__(cls, name, bases, attrs)
 
-            # handle each sort request in order
+            # Start with all fields in default order. Create a copy, because
+            # we don't care to actually change it's order. Make sure it is
+            # sorted (will be according to Field.creation_counter).
+            fields = sorted(list(model._meta.fields))
+
+            # Change the order of the fields in this list according to
+            # user's request.
             for request in requests:
                 anchor_idx = None
-                for field_name in request:
-                    field_idx = idx(field_name)
-                    if field_idx is None:
-                        raise ValueError('Invalid field name "%s"'%field_name)
+                for to_be_ordered in request:
+                    # Find the current index of this field.
+                    for i in range(0, len(fields)):
+                        if fields[i].name == to_be_ordered:
+                            field_idx = i
+                            break
+                    else:
+                        raise ValueError('Invalid field name "%s"' % to_be_ordered)
 
-                    # first field in the request is the anchor, all the
-                    # other fields will be inserted after it.
+                    # First field in the request is the anchor, all the
+                    # other listed fields will be inserted after it.
                     if anchor_idx is None:
                         anchor_idx = field_idx
                     else:
-                        field = cache[field_idx]
-                        cache.insert(anchor_idx+1, field)
-                        del cache[field_idx]
+                        field = fields[field_idx]
+                        fields.insert(anchor_idx+1, field)
+                        del fields[field_idx]
                         if field_idx > anchor_idx:
                             anchor_idx += 1
 
-            # already initialized by parent; override with our new
-            # data now, with the correct sort order.
-            self._field_cache = cache
-            self._field_name_cache = [x for x, _ in cache]
+            # Rewrite the whole model's creation_counters to match the new
+            # field order.
+            for idx, field in enumerate(fields):
+                field.creation_counter = idx + 1  # start with 1
 
-    class OrderMetaclass(ModelBase):
-        def __new__(cls, name, bases, attrs):
-            model = super(OrderMetaclass, cls).__new__(cls, name, bases, attrs)
-            model._meta.__class__ = OrderedOptions
-            model._meta._fill_fields_cache()  # already filled, regen
             return model
 
     return OrderMetaclass