Получить поля модели в Django

121

Учитывая модель Django, я пытаюсь перечислить все ее поля. Я видел несколько примеров этого с использованием атрибута модели _meta, но разве подчеркивание перед мета не означает, что атрибут _meta является частным атрибутом и к нему нельзя обращаться напрямую? ... Потому что, например, макет _meta может измениться в будущем и не будет стабильным API?

_Meta - исключение из этого правила? Он стабилен и готов к использованию или доступ к нему считается плохой практикой? Или есть функция или какой-то другой способ изучить поля модели без использования атрибута _meta? Ниже приведен список некоторых ссылок, показывающих, как это сделать с помощью атрибута _meta.

Любой совет очень ценится.

Поле получения / установки объекта django

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

Как проанализировать поля модели django?

Джо Дж
источник
возможный дубликат Django: получить список полей модели?
Anto

Ответы:

145

_metaявляется частным, но относительно стабильным. Есть попытки формализовать это, задокументировать и убрать подчеркивание, что могло произойти до 1.3 или 1.4. Я предполагаю, что будут предприняты усилия для обеспечения обратной совместимости, потому что многие люди все равно использовали его.

Если вас особенно беспокоит совместимость, напишите функцию, которая принимает модель и возвращает поля. Это означает, что если что-то изменится в будущем, вам нужно будет изменить только одну функцию.

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

Я считаю, что это вернет список Fieldобъектов. Чтобы получить значение каждого поля из экземпляра, используйте getattr(instance, field.name).

Обновление: участники Django работают над API, чтобы заменить объект _Meta как часть Google Summer of Code. См .:
- https://groups.google.com/forum/#!topic/django-developers/hD4roZq0wyk
- https://code.djangoproject.com/wiki/new_meta_api

Уилл Харди
источник
45
Вы также должны знать тот факт, что если вам также нужны поля «многие ко многим», к которым вам нужен доступ model._meta.many_to_many!
Bernhard Vallant 05
1
Спасибо, Уилл. Приятно знать, что другие люди тоже используют _meta. Мне нравится идея иметь функцию-оболочку. Lazerscience, спасибо вам тоже. Полезно знать, что есть хороший способ получить поля many_to_many. Джо
Джо Дж
3
django/core/management/commands/loaddata.pyиспользует _meta для просмотра дерева приложений, моделей и полей. Хороший образец для подражания ... и можете поспорить, что это «официальный путь».
hobs
2
Если вы используете общие внешние ключи, вам также следует проверить_meta.virtual_fields
andrei1089
97

Я знаю, что этот пост довольно старый, но я просто хотел сказать всем, кто ищет то же самое, что для этого есть публичный и официальный API: get_fields()иget_field()

Использование:

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

https://docs.djangoproject.com/en/1.8/ref/models/meta/

PirosB3
источник
6
Это действительно правильный ответ для новой версии django.
chhantyal
3
Один вопрос: если они «публичные и официальные», почему они до сих пор находятся под _metaконтролем?
krubo 05
9

Теперь есть специальный метод - get_fields ()

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

Он принимает два параметра, которые можно использовать для управления возвращаемыми полями:

  • include_parents

    Верно по умолчанию. Рекурсивно включает поля, определенные в родительских классах. Если установлено значение False, get_fields () будет искать только поля, объявленные непосредственно в текущей модели. Поля из моделей, которые напрямую наследуются от абстрактных моделей или прокси-классов, считаются локальными, а не родительскими.

  • include_hidden

    По умолчанию - false. Если установлено значение True, get_fields () будет включать поля, которые используются для поддержки функций других полей. Это также будет включать любые поля, у которых есть related_name (например, ManyToManyField или ForeignKey), которые начинаются со знака «+»

пекос вредитель
источник
9

get_fields()возвращает a, tupleи каждый элемент является Model fieldтипом, который нельзя использовать напрямую как строку. Итак, field.nameвернем имя поля

my_model_fields = [field.name for field in MyModel._meta.get_fields()]
Приведенный выше код вернет список, содержащий все имена полей.

Пример

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
источник
Отличный ответ! Спасибо!
Tms91
8

Это то, что делает сам Django при построении формы из модели. Он использует атрибут _meta, но, как заметил Бернхард, он использует как _meta.fields, так и _meta.many_to_many. Глядя на django.forms.models.fields_for_model, вы можете сделать это следующим образом:

opts = model._meta
for f in sorted(opts.fields + opts.many_to_many):
    print '%s: %s' % (f.name, f)
ecstaticpeon
источник
4

Поля модели, содержащиеся в _meta, перечислены в нескольких местах в виде списков соответствующих объектов поля. Возможно, с ними будет проще работать как со словарем, где ключи - это имена полей.

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

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

(См. Этот пример использования в django.forms.models.fields_for_model.)

jxqz
источник
2
Вы можете сопровождать свой код кратким описанием того, что он делает.
Bas van Dijk
2

Как насчет этого.

fields = Model._meta.fields
JDE876
источник
1
Я предполагаю, что кто-то может захотеть получить доступ к свойствам поля. Кроме того, как указывалось в предыдущих ответах, есть many_to_manyи virtual_fields.
Jocke
@ Джок, ты прав. Может возникнуть необходимость получить доступ к другому атрибуту. Ответ отредактирован.
JDE876 06
2

В соответствии с документацией django 2.2 вы можете использовать:

Чтобы получить все поля: Model._meta.get_fields()

Чтобы получить отдельное поле: Model._meta.get_field('field name')

ех. Session._meta.get_field('expire_date')

Деванг Падхияр
источник
1

Если вам это нужно для вашего административного сайта, есть также ModelAdmin.get_fieldsметод ( docs ), который возвращает listимя поля strings.

Например:

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

Другой способ - добавить функции в модель, и если вы хотите переопределить дату, вы можете вызвать функцию.

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()
Томас Тернер
источник