Джанго: Получить список полей модели?

198

Я определил Userкласс, который (в конечном счете) наследуется от models.Model. Я хочу получить список всех полей, определенных для этой модели. Например, phone_number = CharField(max_length=20). По сути, я хочу получить все, что унаследовано от Fieldкласса.

Я думал, что смогу получить их, воспользовавшись этим inspect.getmembers(model), но список, который он возвращает, не содержит ни одного из этих полей. Похоже, Django уже овладел классом, добавил все его магические атрибуты и вычеркнул то, что на самом деле было определено. Итак ... как я могу получить эти поля? Возможно, у них есть функция для извлечения их для своих внутренних целей?

mpen
источник
Это тоже может помочь pypi.python.org/pypi/django-inspect-model/0.5
Паоло
возможный дубликат полей модели Get в Django
markpasc

Ответы:

48

Поскольку большинство ответов устарели, я постараюсь обновить вас в Django 2.2. Здесь posts- ваше приложение (записи, блог, магазин и т. Д.)

1) Из ссылки на модель : https://docs.djangoproject.com/en/2.2/ref/models/meta/.

from posts.model import BlogPost

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

Обратите внимание, что:

all_fields=BlogPost._meta.get_fields()

Также получатся некоторые отношения, которые, например, вы не сможете отобразить в представлении.
Как в моем случае:

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

и

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

2) Из экземпляра

from posts.model import BlogPost

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

3) От родительской модели

Давайте предположим, что у нас есть Post в качестве родительской модели, и вы хотите видеть все поля в списке, а родительские поля должны быть доступны только для чтения в режиме редактирования.

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
Maks
источник
1
Я буду считать, что это правильно, и приму это как новый ответ. Спасибо за обновление!
mpen
@mpen Не буду утверждать, что это лучший или самый питонический способ, но ниже будут / должны быть получены значения, которые вы бы хотели отобразить в представлении, поэтому заголовки таблицы HTML, если вы захотите. Поскольку get_fields () возвращает кортеж, вы можете выполнить итерацию по нему и получить значения, которые выглядят как appname.Model.field_name, ниже очищает значения от второй точки, включая случай, в котором подчеркивание использовалось в имени поля как а также сделать их заголовком, так что измените по мере необходимости для каждой уникальной ситуации. clean_field_names = [str(h).split('.')[2].replace("_", " ").title() for h in all_fields]
Алехандро Суарес
1
Пример из примера должен быть:all_fields = bp._meta.fields
Джордж Кеттлборо
@ Джорджетлборо, я вижу смысл. Разве это не работает прямо из класса? Нет примера для тестирования. В любом случае пример касается экземпляра, поэтому он должен быть от bp или хотя бы от bp .__ class__
Макс
294

Джанго версии 1.8 и позже:

Вы должны использовать get_fields():

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

Начиная с Django 1.8, этот get_all_field_names()метод устарел и будет удален в 1.10 .

Страница документации, ссылка на которую приведена выше, обеспечивает полностью обратно совместимую реализацию get_all_field_names(), но для большинства целей предыдущий пример должен работать просто отлично.


Джанго версии до 1.8:

model._meta.get_all_field_names()

Это должно делать свое дело.

Это требует фактического экземпляра модели. Если все, что у вас есть, это подкласс django.db.models.Model, то вам следует позвонитьmyproject.myapp.models.MyModel._meta.get_all_field_names()

rossipedia
источник
11
Требуются также объекты, а не только их имена. Это, кажется, доступно в, model._meta.fieldsхотя, и их имена могут быть найдены с field.nameэтим, кажется. Я просто надеюсь, что это самый стабильный способ получить эту информацию :)
mpen
2
не совсем уверен. Подчеркивание, кажется, указывает на то, что это внутренний API, было бы здорово, если бы парни из Django повысили его до фактически открытого вызова метода django.db.models.Model. Я покопаюсь в этом и посмотрю, что я смогу найти
rossipedia
2
Я думаю, что делать это через _metaатрибут - это единственный способ ... Дополнительно _meta.many_to_manyизучите поля ManyToMany!
Бернхард Валлант
3
Это хорошее решение, но этот метод включает поля обратной связи, такие как обратный ForeignKey, и они не совсем "поля". Кто-нибудь знает, как отличить настоящие поля?
viridis
2
@rossipedia: Просто отметив, что API ._meta является публичным (хотя раньше он был закрытым, вероятно, когда этот ответ был впервые написан): docs.djangoproject.com/en/1.8/ref/models/meta/…
Ник С
76

get_all_related_fields()Упомянутый здесь метод был устаревшим в версии 1.8 . Отныне это get_fields().

>> from django.contrib.auth.models import User
>> User._meta.get_fields()
Wil
источник
1
Теперь это должен быть принятый ответ. Кратко и по существу.
Арно П
Да, это ответ.
Эрик
56

Я считаю, что добавление этого к моделям Django весьма полезно:

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

Это позволяет вам делать:

for field, val in object:
    print field, val
bjw
источник
2
Как насчет ForeignKey? У меня есть такие ошибкиdjango.db.models.fields.related.RelatedObjectDoesNotExist: CustomModel has no custom_attribute.
SAKrisT
ForeignKeyотлично работает для меня. Хотя, молча ловить все исключения - это анти-паттерн. Гораздо лучше поймать AttributeErrorили хотя бы записать, что какое-то исключение было тихо проглочено.
Сардатрион - против злоупотребления SE
1
self._meta.get_all_field_names()был амортизирован и удален. Вы можете использовать что-то вроде, for field in self._meta.get_fields()а затемyield (field.name, field.value_from_object(self))
MackM
13

Это делает трюк. Я проверяю это только в Django 1.7.

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

Model._meta.local_fieldsне содержит много-много полей. Вы должны заставить их использовать Model._meta.local_many_to_many.

Rockallite
источник
10

Не ясно, есть ли у вас экземпляр класса или самого класса и вы пытаетесь получить поля, но в любом случае рассмотрите следующий код

Использование экземпляра

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

Используя класс

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'
Надер Алексан
источник
10
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)

Это сработало для меня в django==1.11.8

Вивек Ананд
источник
8

MyModel._meta.get_all_field_names()было отклонено несколько версий назад и удалено в Django 1.10.

Вот обратная совместимость с документами :

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
источник
6

Просто добавлю, я использую объект self, это сработало для меня:

[f.name for f in self.model._meta.get_fields()]
arrt_
источник
5

По крайней мере с Django 1.9.9 - версией, которую я сейчас использую - обратите внимание, что на .get_fields()самом деле также «рассматривает» любую стороннюю модель как поле, что может быть проблематично. Скажем, у вас есть:

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

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

Это следует из того

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

в то время как, как показывает @Rockallite

>>> map(lambda field:field.name, Parent._model._meta.local_fields)
['id']
Keepalive
источник
4

Поэтому, прежде чем я нашел этот пост, я успешно нашел, что это работает.

Model._meta.fields

Это работает так же, как

Model._meta.get_fields()

Я не уверен, какая разница в результатах, если таковые имеются. Я запустил этот цикл и получил тот же вывод.

for field in Model._meta.fields:
    print(field.name)
Карл Брубейкер
источник
-2

Почему бы просто не использовать это:

manage.py inspectdb

Пример вывода:

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'),)
Адам Павлючук
источник
1
Это не ответ на вопрос.
Шейн