Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Django get Model fields in order

Is there a way to get them in code order, or set an order for the fields of a Django Model? When I call MyModel._meta.get_fields() they come unordered (altough is the same 'unordered' order as long as you don't reset the server it seems).

If possible, without having to change the base lib of overwrite any methods

I've read about ordering in Forms https://docs.djangoproject.com/en/1.9/ref/forms/api/#django.forms.Form.order_fields, but not in the Model itself

The meta property 'ordering' only defines default ordering of objects when making a query

like image 815
Mojimi Avatar asked Oct 30 '22 15:10

Mojimi


2 Answers

Actually get_fields() returns fields in a fixed order.

Take a look into django example:

>>> from django.contrib.auth.models import User
>>> User._meta.get_fields()
(<ManyToOneRel: admin.logentry>,
 <django.db.models.fields.AutoField: id>,
 <django.db.models.fields.CharField: password>,
 <django.db.models.fields.DateTimeField: last_login>,
 <django.db.models.fields.BooleanField: is_superuser>,
 <django.db.models.fields.CharField: username>,
 <django.db.models.fields.CharField: first_name>,
 <django.db.models.fields.CharField: last_name>,
 <django.db.models.fields.EmailField: email>,
 <django.db.models.fields.BooleanField: is_staff>,
 <django.db.models.fields.BooleanField: is_active>,
 <django.db.models.fields.DateTimeField: date_joined>,
 <django.db.models.fields.related.ManyToManyField: groups>,
 <django.db.models.fields.related.ManyToManyField: user_permissions>)

I've repeated this code and got the same results. If you will go deeper and look into the sources, you will find that the order is the following:

  1. Each parent fields fetched in a recursive way.
  2. Remote fields.
  3. Local fields.
  4. Local many-to-many fields.
  5. Private fields.

All that says that if you define some trivial field before some other trivial field in your model, you will always get it in the same order in get_fields() results.

But be careful on relying on a non-documented behaviour, so it could be changed in a future without any warning.

like image 164
vasi1y Avatar answered Jan 02 '23 20:01

vasi1y


If you want to get a list of all fields defined in your model in the order that you defined them in, then you can the following class method to your model definition. (or take the body of the function and move it wherever you feel like)

import itertools
from django.db.models.fields import Field

@classmethod
def getModelFields(cls):
    #Pulled this from djangos ModelForm source code
    sortable_private_fields = [f for f in cls._meta.private_fields if isinstance(f, Field)]
    return sorted(itertools.chain(cls._meta.concrete_fields, sortable_private_fields, cls._meta.many_to_many))

This works because django's Field class defines custom __eq__ and __lt__ methods that reference an internal creation_counter. This allows us to construct a list of all the fields on the model, and simply use sorted to get them in the order.

This is actually how a ModelForm is able to display all a models fields in the order they were defined.

like image 34
DrS Avatar answered Jan 02 '23 21:01

DrS