Get model's fields in Django

DjangoDjango ModelsIntrospectionMetamodel

Django Problem Overview


Given a Django model, I'm trying to list all of its fields. I've seen some examples of doing this using the _meta model attribute, but doesn't the underscore in front of meta indicate that the _meta attribute is a private attribute and shouldn't be accessed directly? ... Because, for example, the layout of _meta could change in the future and not be a stable API?

Is _meta an exception to this rule? Is it stable and ready to use or is it considered bad practice to access it? Or is there a function or some other way to introspect the fields of a model without using the _meta attribute? Below is a list of some links showing how to do this using the _meta attribute

Any advice is much appreciated.

https://stackoverflow.com/questions/763558/django-object-get-set-field">https://stackoverflow.com/questions/763558/django-object-get-set-field</a>

http://www.djangofoo.com/80/get-list-model-fields">http://www.djangofoo.com/80/get-list-model-fields</a>

https://stackoverflow.com/questions/2384436/how-to-introspect-django-model-fields

Django Solutions


Solution 1 - Django

_meta is private, but it's relatively stable. There are efforts to formalise it, document it and remove the underscore, which might happen before 1.3 or 1.4. I imagine effort will be made to ensure things are backwards compatible, because lots of people have been using it anyway.

If you're particularly concerned about compatibility, write a function that takes a model and returns the fields. This means if something does change in the future, you only have to change one function.

def get_model_fields(model):
    return model._meta.fields

I believe this will return a list of Field objects. To get the value of each field from the instance, use getattr(instance, field.name).

Update: Django contributors are working on an API to replace the _Meta object as part of a Google Summer of Code. See:

Solution 2 - Django

I know this post is pretty old, but I just cared to tell anyone who is searching for the same thing that there is a public and official API to do this: get_fields() and get_field()

Usage:

fields = model._meta.get_fields()
my_field = model._meta.get_field('my_field')

https://docs.djangoproject.com/en/3.2/ref/models/meta/#retrieving-all-field-instances-of-a-model

Solution 3 - Django

get_fields() returns a tuple and each element is a Model field type, which can't be used directly as a string. So, field.name will return the field name

my_model_fields = [field.name for field in MyModel._meta.get_fields()]
The above code will return a list conatining all fields name

Example

In [11]: from django.contrib.auth.models import User

In [12]: User._meta.get_fields()
Out[12]: 
(<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>)

In [13]: [field.name for field in User._meta.get_fields()]
Out[13]: 
['logentry', 'id', 'password', 'last_login', 'is_superuser', 'username', 'first_name', 'last_name', 'email', 'is_staff', 'is_active', 'date_joined', 'groups', 'user_permissions']

Solution 4 - Django

Now there is special method - get_fields()

    >>> from django.contrib.auth.models import User
    >>> User._meta.get_fields()

It accepts two parameters that can be used to control which fields are returned:

  • include_parents

    True by default. Recursively includes fields defined on parent classes. If set to False, get_fields() will only search for fields declared directly on the current model. Fields from models that directly inherit from abstract models or proxy classes are considered to be local, not on the parent.

  • include_hidden

    False by default. If set to True, get_fields() will include fields that are used to back other field’s functionality. This will also include any fields that have a related_name (such as ManyToManyField, or ForeignKey) that start with a “+”

Solution 5 - Django

This is something that is done by Django itself when building a form from a model. It is using the _meta attribute, but as Bernhard noted, it uses both _meta.fields and _meta.many_to_many. Looking at django.forms.models.fields_for_model, this is how you could do it:

opts = model._meta
for f in sorted(opts.fields + opts.many_to_many):
    print '%s: %s' % (f.name, f)

Solution 6 - Django

The model fields contained by _meta are listed in multiple locations as lists of the respective field objects. It may be easier to work with them as a dictionary where the keys are the field names.

In my opinion, this is most irredundant and expressive way to collect and organize the model field objects:

def get_model_fields(model):
  fields = {}
  options = model._meta
  for field in sorted(options.concrete_fields + options.many_to_many + options.virtual_fields):
    fields[field.name] = field
  return fields

(See This example usage in django.forms.models.fields_for_model.)

Solution 7 - Django

How about this one.

fields = Model._meta.fields

Solution 8 - Django

If you need this for your admin site, there is also the ModelAdmin.get_fields method (docs), which returns a list of field name strings.

For example:

class MyModelAdmin(admin.ModelAdmin):
    # extending change_view, just as an example
    def change_view(self, request, object_id=None, form_url='', extra_context=None):
        # get the model field names
        field_names = self.get_fields(request)
        # use the field names
        ...
        

Solution 9 - Django

As per the django documentation 2.2 you can use:

To get all fields: Model._meta.get_fields()

To get an individual field: Model._meta.get_field('field name')

ex. Session._meta.get_field('expire_date')

Solution 10 - Django

fields = [f"{f.name}_id" if f.is_relation else f.name for f in model._meta.fields]

Solution 11 - Django

Another way is add functions to the model and when you want to override the date you can call the function.

class MyModel(models.Model):
    name = models.CharField(max_length=256)
    created = models.DateTimeField(auto_now_add=True)
    modified = models.DateTimeField(auto_now=True)

    def set_created_date(self, created_date):
        field = self._meta.get_field('created')
        field.auto_now_add = False
        self.created = created_date

    def set_modified_date(self, modified_date):
        field = self._meta.get_field('modified')
        field.auto_now = False
        self.modified = modified_date

my_model = MyModel(name='test')
my_model.set_modified_date(new_date)
my_model.set_created_date(new_date)
my_model.save()

Attributions

All content for this solution is sourced from the original question on Stackoverflow.

The content on this page is licensed under the Attribution-ShareAlike 4.0 International (CC BY-SA 4.0) license.

Content TypeOriginal AuthorOriginal Content on Stackoverflow
QuestionJoe JView Question on Stackoverflow
Solution 1 - DjangoWill HardyView Answer on Stackoverflow
Solution 2 - DjangoPirosB3View Answer on Stackoverflow
Solution 3 - DjangoJPGView Answer on Stackoverflow
Solution 4 - DjangoikoverdyaevView Answer on Stackoverflow
Solution 5 - DjangoecstaticpeonView Answer on Stackoverflow
Solution 6 - DjangojxqzView Answer on Stackoverflow
Solution 7 - DjangoJDE876View Answer on Stackoverflow
Solution 8 - DjangodjvgView Answer on Stackoverflow
Solution 9 - Djangouser10960338View Answer on Stackoverflow
Solution 10 - DjangoPradeep MishraView Answer on Stackoverflow
Solution 11 - DjangoThomas TurnerView Answer on Stackoverflow