279

I've defined a User class which (ultimately) inherits from models.Model. I want to get a list of all the fields defined for this model. For example, phone_number = CharField(max_length=20). Basically, I want to retrieve anything that inherits from the Field class.

I thought I'd be able to retrieve these by taking advantage of inspect.getmembers(model), but the list it returns doesn't contain any of these fields. It looks like Django has already gotten a hold of the class and added all its magic attributes and stripped out what's actually been defined. So... how can I get these fields? They probably have a function for retrieving them for their own internal purposes?

mpen
  • 272,448
  • 266
  • 850
  • 1,236
  • This might help, too https://pypi.python.org/pypi/django-inspect-model/0.5 – Paolo Oct 15 '13 at 09:30
  • possible duplicate of [Get model's fields in Django](http://stackoverflow.com/questions/3647805/get-models-fields-in-django) – markpasc Mar 05 '14 at 14:23

16 Answers16

376

Django versions 1.8 and later:

You should use get_fields():

[f.name for f in MyModel._meta.get_fields()]

The get_all_field_names() method is deprecated starting from Django 1.8 and will be removed in 1.10.

The documentation page linked above provides a fully backwards-compatible implementation of get_all_field_names(), but for most purposes the previous example should work just fine.


Django versions before 1.8:

model._meta.get_all_field_names()

That should do the trick.

That requires an actual model instance. If all you have is a subclass of django.db.models.Model, then you should call myproject.myapp.models.MyModel._meta.get_all_field_names()

daaawx
  • 3,273
  • 2
  • 17
  • 16
rossipedia
  • 56,800
  • 10
  • 90
  • 93
  • 11
    Wanted the objects too, not just their names. This seems to be available in `model._meta.fields` though, and their names are retrievable with `field.name` it seems. I just hope this is the most stable way to retrieve this info :) – mpen Jun 23 '10 at 23:39
  • 2
    not entirely sure. The underscore seems to indicate it's an internal API, would be cool if the Django guys promoted this up to an actually public method call on `django.db.models.Model`. I'll dig into it and see what I can find – rossipedia Jun 23 '10 at 23:41
  • 2
    I guess doing it via the `_meta` attribute is the only way... Additionally look into `_meta.many_to_many` for ManyToMany fields! – Bernhard Vallant Jun 24 '10 at 00:58
  • @lazerscience: I was just going to ask about those! Because I noticed the Django admin has more fields than what I retrieved from `_meta.fields`. @Bryan: Of course :) I review my questions fairly often to accept the best answer... but some of them still don't, and probably never will. Wish I could close em without needing 5 votes. – mpen Jun 24 '10 at 20:25
  • 4
    It's a good solution but this method includes the reverse relation fields such as a reverse ForeignKey and those are not exactly "fields". Anyone know how to distinguish the actual Fields? – viridis Jun 04 '13 at 13:56
  • 1
    @viridis you're absolutely right. `_meta.get_all_field_names()` includes reverse relations. To omit them, get the list of actual field objects by `_meta.fields`. – Dennis Golomazov Jul 30 '13 at 06:24
  • What is returned data type? list? tuple? – andilabs Mar 19 '14 at 11:23
  • It's been a while since I've looked at this, but the "pythonic" answer is: does it matter? You can iterate through the returned object, and if it walks like a duck... (Also see @ben's answer below for a helpful method) – rossipedia May 22 '14 at 16:21
  • 2
    @rossipedia: Just noting that the ._meta API is public (though it used to be private, probably when this answer was first written): https://docs.djangoproject.com/en/1.8/ref/models/meta/#django.db.models.options.Options – Nick S Mar 30 '17 at 01:52
117

As most of answers are outdated I'll try to update you on Django 2.2 Here posts- your app (posts, blog, shop, etc.)

1) From model link: https://docs.djangoproject.com/en/stable/ref/models/meta/

from posts.model import BlogPost

all_fields = BlogPost._meta.fields
#or
all_fields = BlogPost._meta.get_fields()

Note that:

all_fields=BlogPost._meta.get_fields()

Will also get some relationships, which, for ex: you can not display in a view.
As in my case:

Organisation._meta.fields
(<django.db.models.fields.AutoField: id>, <django.db.models.fields.DateField: created>...

and

Organisation._meta.get_fields()
(<ManyToOneRel: crm.activity>, <django.db.models.fields.AutoField: id>, <django.db.models.fields.DateField: created>...

2) From instance

from posts.model import BlogPost

bp = BlogPost()
all_fields = bp._meta.fields

3) From parent model

Let's suppose that we have Post as the parent model and you want to see all the fields in a list, and have the parent fields to be read-only in Edit mode.

from django.contrib import admin
from posts.model import BlogPost 

@admin.register(BlogPost)
class BlogPost(admin.ModelAdmin):
    all_fields = [f.name for f in Organisation._meta.fields]
    parent_fields = BlogPost.get_deferred_fields(BlogPost)

    list_display = all_fields
    read_only = parent_fields
bignose
  • 30,281
  • 14
  • 77
  • 110
Maks
  • 1,527
  • 1
  • 13
  • 16
  • 1
    I'll assume this is correct and accept it as the new answer. Thanks for the update! – mpen Jun 23 '19 at 21:08
  • @mpen Wont claim it the best way or the most pythonic but below will/should get the values u would want to display in a view, so the headers of an HTML table if u will. As get_fields() returns a tuple, u can iterate over it and get the values that look like appname.Model.field_name, below cleans up the values from the second dot, includes the case in which an underscore was used in the field name as well as make them a title, so modify as needed for each unique situation. `clean_field_names = [str(h).split('.')[2].replace("_", " ").title() for h in all_fields]` – Alejandro Suarez Jul 19 '19 at 18:11
  • 1
    The from instance example should be: `all_fields = bp._meta.fields` – George Kettleborough Jan 15 '20 at 13:45
  • @GeorgeKettleborough i see the point. Doesn't it work from class directly also? Don't have the example to test on. Anyway the example is about instance, so it should be from bp or at least bp.__class__ – Maks Jan 16 '20 at 07:28
82

The get_all_related_fields() method mentioned herein has been deprecated in 1.8. From now on it's get_fields().

>> from django.contrib.auth.models import User
>> User._meta.get_fields()
Wil
  • 4,887
  • 3
  • 22
  • 30
59

I find adding this to django models quite helpful:

def __iter__(self):
    for field_name in self._meta.get_all_field_names():
        value = getattr(self, field_name, None)
        yield (field_name, value)

This lets you do:

for field, val in object:
    print field, val
Visgean Skeloru
  • 2,237
  • 1
  • 24
  • 33
bjw
  • 2,046
  • 18
  • 33
  • 3
    What about ForeignKey? I have errors like this `django.db.models.fields.related.RelatedObjectDoesNotExist: CustomModel has no custom_attribute.` – Volodymyr B. Oct 17 '14 at 09:35
  • `ForeignKey` work fine for me. Although, silently catching all exceptions is an anti-pattern. Much better to catch `AttributeError`, or at least log that some exception was silently swallowed. – Sardathrion - against SE abuse Nov 04 '15 at 10:47
  • 2
    `self._meta.get_all_field_names()` has been depreciated and removed. You can use something like `for field in self._meta.get_fields()` and then `yield (field.name, field.value_from_object(self))` – MackM Feb 28 '19 at 19:57
16

This does the trick. I only test it in Django 1.7.

your_fields = YourModel._meta.local_fields
your_field_names = [f.name for f in your_fields]

Model._meta.local_fields does not contain many-to-many fields. You should get them using Model._meta.local_many_to_many.

Rockallite
  • 16,437
  • 7
  • 54
  • 48
14

A detail not mentioned by others:

[f.name for f in MyModel._meta.get_fields()]

get, for example

['id', 'name', 'occupation']

and

[f.get_attname() for f in MyModel._meta.get_fields()]

get

['id', 'name', 'occupation_id']

If

reg = MyModel.objects.first()

then

reg.occupation

get, for example

<Occupation: Dev>

and

reg.occupation_id

get

1
  • This was exactly what I wanted (needed list of filtered fields for `qs.values()`), but unfortunately this code failed: `'OneToOneRel' object has no attribute 'get_attname'` – Miradil Zeynalli Dec 21 '22 at 16:34
13

It is not clear whether you have an instance of the class or the class itself and trying to retrieve the fields, but either way, consider the following code

Using an instance

instance = User.objects.get(username="foo")
instance.__dict__ # returns a dictionary with all fields and their values
instance.__dict__.keys() # returns a dictionary with all fields
list(instance.__dict__.keys()) # returns list with all fields

Using a class

User._meta.__dict__.get("fields") # returns the fields

# to get the field names consider looping over the fields and calling __str__()
for field in User._meta.__dict__.get("fields"):
    field.__str__() # e.g. 'auth.User.id'
Nader Alexan
  • 2,127
  • 22
  • 36
12
def __iter__(self):
    field_names = [f.name for f in self._meta.fields]
    for field_name in field_names:
        value = getattr(self, field_name, None)
        yield (field_name, value)

This worked for me in django==1.11.8

Vivek Anand
  • 621
  • 1
  • 7
  • 15
8

MyModel._meta.get_all_field_names() was deprecated several versions back and removed in Django 1.10.

Here's the backwards-compatible suggestion from the docs:

from itertools import chain

list(set(chain.from_iterable(
    (field.name, field.attname) if hasattr(field, 'attname') else (field.name,)
    for field in MyModel._meta.get_fields()
    # For complete backwards compatibility, you may want to exclude
    # GenericForeignKey from the results.
    if not (field.many_to_one and field.related_model is None)
)))
aboutaaron
  • 4,869
  • 3
  • 36
  • 30
7

Just to add, I am using self object, this worked for me:

[f.name for f in self.model._meta.get_fields()]
arrt_
  • 369
  • 1
  • 6
  • 15
7

At least with Django 1.9.9 -- the version I'm currently using --, note that .get_fields() actually also "considers" any foreign model as a field, which may be problematic. Say you have:

class Parent(models.Model):
    id = UUIDField(primary_key=True)

class Child(models.Model):
    parent = models.ForeignKey(Parent)

It follows that

>>> map(lambda field:field.name, Parent._model._meta.get_fields())
['id', 'child']

while, as shown by @Rockallite

>>> map(lambda field:field.name, Parent._model._meta.local_fields)
['id']
keepAlive
  • 6,369
  • 5
  • 24
  • 39
6

So before I found this post, I successfully found this to work.

Model._meta.fields

It works equally as

Model._meta.get_fields()

I'm not sure what the difference is in the results, if there is one. I ran this loop and got the same output.

for field in Model._meta.fields:
    print(field.name)
Carl Brubaker
  • 1,602
  • 11
  • 26
0

In sometimes we need the db columns as well:

def get_db_field_names(instance):
   your_fields = instance._meta.local_fields
   db_field_names=[f.name+'_id' if f.related_model is not None else f.name  for f in your_fields]
   model_field_names = [f.name for f in your_fields]
   return db_field_names,model_field_names

Call the method to get the fields:

db_field_names,model_field_names=get_db_field_names(Mymodel)
Ramesh Ponnusamy
  • 1,553
  • 11
  • 22
0

Combined multiple answers of the given thread (thanks!) and came up with the following generic solution:

class ReadOnlyBaseModelAdmin(ModelAdmin):
    def has_add_permission(self, request):
        return request.user.is_superuser

    def has_delete_permission(self, request, obj=None):
        return request.user.is_superuser

    def get_readonly_fields(self, request, obj=None):
        return [f.name for f in self.model._meta.get_fields()]
Tobias Ernst
  • 4,214
  • 1
  • 32
  • 30
0

You can try this script to get the model and it's consecutive fields in a dictionary.

Furthermore, this can also reduce the hectic manual work of writing db schemas.

import json
from django.apps import apps
from django.http import HttpResponse
def index(request):
    models = {
        model.__name__: model for model in apps.get_models()
    }
    resp_data = []
    def get_field_names(model_class):
        return [f.name for f in model_class._meta.fields]
    for model in models:
        all_fields = get_field_names(models[model])
        model_dict = {
            model : all_fields
        }
        resp_data.append(model_dict)
    data = {
        'data' : resp_data
    }
    return HttpResponse(json.dumps(data), content_type='application/json')
-2

Why not just use that:

manage.py inspectdb

Example output:

class GuardianUserobjectpermission(models.Model):
    id = models.IntegerField(primary_key=True)  # AutoField?
    object_pk = models.CharField(max_length=255)
    content_type = models.ForeignKey(DjangoContentType, models.DO_NOTHING)
    permission = models.ForeignKey(AuthPermission, models.DO_NOTHING)
    user = models.ForeignKey(CustomUsers, models.DO_NOTHING)

    class Meta:
        managed = False
        db_table = 'guardian_userobjectpermission'
        unique_together = (('user', 'permission', 'object_pk'),)