Django: Отримайте список модельних полів?


198

Я визначив Userклас, який (зрештою) успадковує models.Model. Я хочу отримати список усіх полів, визначених для цієї моделі. Наприклад, phone_number = CharField(max_length=20). В основному, я хочу отримати все, що успадковується від Fieldкласу.

Я думав, що зможу їх отримати, скориставшись inspect.getmembers(model), але список, який він повертає, не містить жодного з цих полів. Схоже, Джанго вже захопив клас і додав усі його магічні атрибути і викреслив те, що насправді було визначено. Отже ... як я можу отримати ці поля? Вони, мабуть, мають функцію їх отримання для власних внутрішніх цілей?


Це може допомогти також pypi.python.org/pypi/django-inspect-model/0.5
Паоло

можливий дублікат полів Get model у Django
markpasc

Відповіді:


48

Оскільки більшість відповідей застаріли, я спробую оновити вас на Django 2.2 Тут публікації - ваш додаток (повідомлення, блог, магазин тощо)

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) Від батьківської моделі

Припустимо, у нас є Пост як батьківська модель, і ви хочете побачити всі поля в списку, а батьківські поля мають бути доступними лише для читання в режимі редагування.

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

1
Я припускаю, що це правильно, і прийму це як нову відповідь. Дякуємо за оновлення!
mpen

@mpen Wont стверджують, що це найкращий спосіб або найбільш пітонічне, але нижче буде / повинно отримати значення u, які хотіли б відобразити у поданні, тож заголовки таблиці 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
Джордж Кеттлборо

@GeorgeKettleborough я бачу сенс. Це також не працює безпосередньо з класу? Не майте приклад для тестування. У будь-якому випадку приклад стосується екземпляра, тому він повинен бути з bp або принаймні bp .__ class__
Макс

294

Версії Django 1.8 та новіших версій:

Ви повинні використовувати get_fields():

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

get_all_field_names()Метод застарілий , починаючи з Django 1.8 і треба увійти на сайт версії 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()


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/…
Nick S


55

Я вважаю, що додавання цього до моделей 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

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.


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

6

Просто додам, я використовую self object, це працювало для мене:

[f.name for f in self.model._meta.get_fields()]

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

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
Це не відповідь на питання.
Шейне
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.