Obtenir les champs du modèle dans Django

121

Étant donné un modèle Django, j'essaye de lister tous ses champs. J'ai vu quelques exemples de faire cela en utilisant l'attribut de modèle _meta, mais le trait de soulignement devant meta n'indique-t-il pas que l'attribut _meta est un attribut privé et ne doit pas être accédé directement? ... Parce que, par exemple, la mise en page de _meta pourrait changer dans le futur et ne pas être une API stable?

_Meta est-il une exception à cette règle? Est-il stable et prêt à l'emploi ou est-il considéré comme une mauvaise pratique d'y accéder? Ou existe-t-il une fonction ou une autre manière d'introspecter les champs d'un modèle sans utiliser l'attribut _meta? Vous trouverez ci-dessous une liste de quelques liens montrant comment procéder à l'aide de l'attribut _meta

Tout conseil est fort apprécié.

Champ d'objet django get / set

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

Comment introspecter les champs du modèle django?

Joe J
la source

Réponses:

144

_metaest privé, mais il est relativement stable. Il y a des efforts pour le formaliser, le documenter et supprimer le trait de soulignement, ce qui peut arriver avant la 1.3 ou la 1.4. J'imagine que des efforts seront faits pour s'assurer que les choses sont rétrocompatibles, car beaucoup de gens l'utilisent de toute façon.

Si vous êtes particulièrement préoccupé par la compatibilité, écrivez une fonction qui prend un modèle et renvoie les champs. Cela signifie que si quelque chose change à l'avenir, vous ne devez changer qu'une seule fonction.

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

Je crois que cela renverra une liste d' Fieldobjets. Pour obtenir la valeur de chaque champ à partir de l'instance, utilisez getattr(instance, field.name).

Mise à jour: les contributeurs de Django travaillent sur une API pour remplacer l'objet _Meta dans le cadre d'un Google Summer of Code. Voir:
- https://groups.google.com/forum/#!topic/django-developers/hD4roZq0wyk
- https://code.djangoproject.com/wiki/new_meta_api

Will Hardy
la source
45
Vous devez également être conscient du fait que si vous avez également besoin des champs plusieurs-à-plusieurs, vous devez accéder model._meta.many_to_many!
Bernhard Vallant
1
Merci Will. Il est bon de savoir que d'autres personnes utilisent également _meta. J'aime l'idée d'avoir une fonction wrapper. Lazerscience, merci aussi. Bon à savoir, il existe une méthode intéressante pour obtenir les champs many_to_many. Joe
Joe J
3
django/core/management/commands/loaddata.pyutilise _meta pour parcourir l'arborescence des applications, des modèles et des champs. Joli schéma à suivre ... et vous pouvez parier que c'est la "voie officielle".
plaques de cuisson
2
Si vous utilisez des clés étrangères génériques, vous devez également vérifier_meta.virtual_fields
andrei1089
7
_metaest maintenant une API formelle et prise en charge (nouveau dans Django 1.8)
mgalgs
97

Je sais que cet article est assez ancien, mais je voulais juste dire à quiconque cherche la même chose qu'il existe une API publique et officielle pour le faire: get_fields()etget_field()

Usage:

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

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

PirosB3
la source
6
C'est en fait la bonne réponse pour la nouvelle version de django.
chhantyal
3
Une question: si ceux-ci sont «publics et officiels», pourquoi sont-ils encore sous _meta?
krubo
9

Maintenant, il existe une méthode spéciale - get_fields ()

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

Il accepte deux paramètres qui peuvent être utilisés pour contrôler les champs renvoyés:

  • include_parents

    Vrai par défaut. Inclut de manière récursive les champs définis sur les classes parentes. S'il est défini sur False, get_fields () ne recherchera que les champs déclarés directement sur le modèle courant. Les champs des modèles qui héritent directement de modèles abstraits ou de classes proxy sont considérés comme locaux et non sur le parent.

  • include_hidden

    False par défaut. S'il est défini sur True, get_fields () inclura des champs qui sont utilisés pour soutenir les fonctionnalités d'autres champs. Cela inclura également tous les champs qui ont un nom_relié (tel que ManyToManyField ou ForeignKey) commençant par un «+»

pecos ravageur
la source
9

get_fields()renvoie a tupleet chaque élément est un Model fieldtype, qui ne peut pas être utilisé directement comme chaîne. Donc, field.nameretournera le nom du champ

my_model_fields = [field.name for field in MyModel._meta.get_fields()]
Le code ci-dessus renverra une liste contenant tous les noms de champs

Exemple

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
la source
Très bonne réponse! Merci!
Tms91
8

C'est quelque chose qui est fait par Django lui-même lors de la construction d'un formulaire à partir d'un modèle. Il utilise l'attribut _meta, mais comme Bernhard l'a noté, il utilise à la fois _meta.fields et _meta.many_to_many. En regardant django.forms.models.fields_for_model, voici comment vous pourriez le faire:

opts = model._meta
for f in sorted(opts.fields + opts.many_to_many):
    print '%s: %s' % (f.name, f)
extatique
la source
4

Les champs de modèle contenus par _meta sont répertoriés à plusieurs emplacements sous forme de listes des objets de champ respectifs. Il peut être plus facile de travailler avec eux comme un dictionnaire où les clés sont les noms de champ.

À mon avis, c'est la manière la plus irrédondante et la plus expressive de collecter et d'organiser les objets de champ modèle:

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

(Voir cet exemple d'utilisation dans django.forms.models.fields_for_model.)

jxqz
la source
2
Vous voudrez peut-être accompagner votre code d'une brève description de ce qu'il fait.
Bas van Dijk
2

Celui-ci, ça va.

fields = Model._meta.fields
JDE876
la source
1
J'imagine que l'on peut vouloir accéder aux propriétés d'un champ. De plus, comme indiqué dans les réponses précédentes, il y a many_to_manyet virtual_fields.
Jocke
@Jocke vous avez raison. Le besoin peut survenir d'accéder à un autre attribut. Réponse modifiée.
JDE876
2

Selon la documentation de django 2.2, vous pouvez utiliser:

Pour obtenir tous les champs: Model._meta.get_fields()

Pour obtenir un champ individuel: Model._meta.get_field('field name')

ex. Session._meta.get_field('expire_date')

Devang Padhiyar
la source
1

Si vous en avez besoin pour votre site d' administration , il existe également la ModelAdmin.get_fieldsméthode ( docs ), qui renvoie un listnom de champ strings.

Par exemple:

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
la source
0

Une autre méthode consiste à ajouter des fonctions au modèle et lorsque vous souhaitez remplacer la date, vous pouvez appeler la fonction.

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()
Thomas Turner
la source