174

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.

django object get/set field

http://www.djangofoo.com/80/get-list-model-fields

How to introspect django model fields?

Community
  • 1
  • 1
Joe J
  • 9,985
  • 16
  • 68
  • 100
  • possible duplicate of [Django: Get list of model fields?](http://stackoverflow.com/questions/3106295/django-get-list-of-model-fields) – Anto Jan 06 '14 at 23:41

12 Answers12

202

_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:
- https://groups.google.com/forum/#!topic/django-developers/hD4roZq0wyk
- https://code.djangoproject.com/wiki/new_meta_api

user
  • 17,781
  • 20
  • 98
  • 124
Will Hardy
  • 14,588
  • 5
  • 44
  • 43
  • 53
    You should also be aware of he fact, that if you also need the many-to-many fields you need to access `model._meta.many_to_many`! – Bernhard Vallant Sep 05 '10 at 21:57
  • 1
    Thank you Will. Good to know that other people are using _meta as well. I like the idea of having a wrapper function. Lazerscience, thank you also. Good to know there is a nice method to get the many_to_many fields. Joe – Joe J Sep 05 '10 at 22:15
  • 1
    def get_model_fields(self): return self._meta.fields used this to easily return all model fields... Thanks very much... – garmoncheg Oct 18 '11 at 16:56
  • 3
    `django/core/management/commands/loaddata.py` uses _meta to walk the tree of apps, models, and fields. Nice pattern to follow... and you can bet it's the "official way". – hobs Jul 27 '12 at 20:23
  • 2
    If you are using generic foreign keys you should also check `_meta.virtual_fields` – andrei1089 Aug 28 '13 at 09:00
  • Field objects has a couple of methods/attributes, `attname`, `name`, `get_attname`. What's the difference between these? – Jonathan Sep 03 '14 at 13:48
  • 7
    [`_meta` is now a formal, supported API](https://docs.djangoproject.com/en/1.8/ref/models/meta/) (new in Django 1.8) – mgalgs May 01 '15 at 04:34
  • for Django 2.0.7 ,_meta.many_to_many doesn't work,should use _meta.local_many_to_many, and if you use a through model to represent m2m relationship, it won't appear – dogewang Mar 19 '19 at 09:39
152

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

kojiro
  • 74,557
  • 19
  • 143
  • 201
PirosB3
  • 1,961
  • 1
  • 17
  • 21
  • 9
    This is actually right answer for newer version of django. – chhantyal Apr 30 '15 at 15:57
  • 2
    1.10 `get_fields()` https://docs.djangoproject.com/en/1.10/ref/models/meta/#django.db.models.options.Options.get_fields – Risadinha Nov 15 '16 at 14:32
  • 7
    One question: if these are "public and official", why are they still under `_meta`? – krubo Oct 05 '19 at 13:55
  • 1
    @krubo It could be like `NamedTuple`'s `_replace`, where the underscore was added to prevent naming conflicts, not indicate that it's private. If you had a `meta` field, there would be a clash. – Carcigenicate Mar 29 '22 at 22:42
24

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']
JPG
  • 82,442
  • 19
  • 127
  • 206
16

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 “+”

ikoverdyaev
  • 783
  • 1
  • 12
  • 17
10

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)
ecstaticpeon
  • 568
  • 4
  • 7
9

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

Pradeep Mishra
  • 137
  • 2
  • 12
6

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.)

kojiro
  • 74,557
  • 19
  • 143
  • 201
jxqz
  • 119
  • 1
  • 8
3

How about this one.

fields = Model._meta.fields
JDE876
  • 407
  • 1
  • 5
  • 16
  • 1
    I imagine one might want to access the properties of a field. Also, as pointed out in previous answers, there are `many_to_many` and `virtual_fields`. – Jocke Sep 05 '16 at 11:25
  • @Jocke right you are. The need may arise to access another attribute. Answer edited. – JDE876 Sep 06 '16 at 17:05
2

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
        ...
djvg
  • 11,722
  • 5
  • 72
  • 103
2

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')

Devang Padhiyar
  • 3,427
  • 2
  • 22
  • 42
1

instance._meta.get_fields() returns a list of all the fields (i.e. columns) in a Django model.

This method is used to introspect the model's fields, their types, and their relationships with other models. The method returns a list of Field objects, which represent the individual fields in the model.

For example, suppose you have a Django model called MyModel. You can use instance._meta.get_fields() to get a list of all the fields in the model:

from myapp.models import MyModel

my_instance = MyModel.objects.get(id=1)
fields = my_instance._meta.get_fields()

The fields variable will now contain a list of all the fields in the MyModel model, including fields such as id, name, created_at, and any related fields (such as foreign keys). You can use this list to access and manipulate the individual fields in the model.

double-beep
  • 5,031
  • 17
  • 33
  • 41
0

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()
Thomas Turner
  • 2,722
  • 1
  • 27
  • 23