Get model's fields in Django
Asked Answered
R

13

183

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?

Radiochemical answered 5/9, 2010 at 21:6 Comment(1)
possible duplicate of Django: Get list of model fields?Py
O
209

_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

Oech answered 5/9, 2010 at 21:44 Comment(8)
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!Marzipan
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. JoeRadiochemical
def get_model_fields(self): return self._meta.fields used this to easily return all model fields... Thanks very much...Gastroenteritis
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".Carbamate
If you are using generic foreign keys you should also check _meta.virtual_fieldsBurnish
Field objects has a couple of methods/attributes, attname, name, get_attname. What's the difference between these?Matsu
_meta is now a formal, supported API (new in Django 1.8)Adley
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 appearQualification
S
162

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

Sturgill answered 2/4, 2015 at 23:7 Comment(4)
This is actually right answer for newer version of django.Hundredth
1.10 get_fields() docs.djangoproject.com/en/1.10/ref/models/meta/…Brenza
One question: if these are "public and official", why are they still under _meta?Marino
@Marino 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.Kimura
B
27

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']
Bummer answered 31/7, 2018 at 6:29 Comment(0)
E
17

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

Excommunication answered 1/12, 2016 at 6:13 Comment(0)
B
11

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

Boxing answered 23/12, 2021 at 5:22 Comment(0)
S
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)
Songsongbird answered 16/1, 2013 at 18:58 Comment(0)
H
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.)

Haplo answered 8/12, 2013 at 21:39 Comment(1)
You might want to accompany your code with a brief description of what it is doing.Thickskinned
S
3

How about this one.

fields = Model._meta.fields
Scincoid answered 3/8, 2016 at 15:31 Comment(2)
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.Nepos
@Jocke right you are. The need may arise to access another attribute. Answer edited.Scincoid
R
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
        ...
Revanche answered 16/1, 2019 at 9:6 Comment(0)
A
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')

Artistic answered 7/4, 2019 at 16:0 Comment(0)
P
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.

Pangermanism answered 16/2, 2023 at 6:31 Comment(0)
B
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()
Bichloride answered 26/9, 2019 at 11:48 Comment(0)
M
0

To get all fields in a concise and readable manner you can use "model_to_dict" to collect them into a dictionary:

from django.forms.models import model_to_dict

my_object = ModelName.objects.get(id=1) 
data_dict = model_to_dict(my_instance)
print(data_dict)
Marchetti answered 22/1 at 16:54 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.