L’API _meta
des modèles¶
L’API _meta
des modèles est au cœur de l’ORM de Django. Elle permet à d’autres parties du système telles que les recherches, les requêtes, les formulaires et le site d’administration de comprendre les capacités de chaque modèle. L’API est accessible au travers de l’attribut _meta
de chaque classe de modèle, qui est une instance d’objet django.db.models.options.Options
.
Les méthodes qu’elle fournit peuvent être utilisées pour :
- Récupérer toutes les instances de champ d’un modèle
- Récupérer une instance de champ unique d’un modèle par son nom
API d’accès aux champs¶
Récupération d’une instance de champ unique d’un modèle par son nom¶
-
Options.
get_field
(field_name)[source]¶ Renvoie une instance de champ correspondant au nom indiqué.
field_name
peut être le nom d’un champ du modèle, d’un champ d’un modèle abstrait ou hérité, ou d’un champ défini sur un autre modèle pointant vers ce modèle. Dans ce dernier cas,field_name
est (par ordre de préférence) le nomrelated_query_name
défini par l’utilisateur, le nomrelated_name
défini par l’utilisateur ou le nom généré automatiquement par Django.Les
champs cachés
ne peuvent pas être récupérés par leur nom.Si aucun champ du nom indiqué n’a été trouvé, une exception
FieldDoesNotExist
est générée.>>> from django.contrib.auth.models import User # A field on the model >>> User._meta.get_field('username') <django.db.models.fields.CharField: username> # A field from another model that has a relation with the current model >>> User._meta.get_field('logentry') <ManyToOneRel: admin.logentry> # A non existent field >>> User._meta.get_field('does_not_exist') Traceback (most recent call last): ... FieldDoesNotExist: User has no field named 'does_not_exist'
Récupération de toutes les instances de champ d’un modèle¶
-
Options.
get_fields
(include_parents=True, include_hidden=False)[source]¶ Renvoie un tuple de champs associés à un modèle.
get_fields()
accepte deux paramètres pouvant être utilisés pour contrôler quels champs sont renvoyés :include_parents
True
par défaut. Inclut récursivement les champs définis dans les classes parentes. Lorsque ce paramètre est défini àFalse
,get_fields()
ne renvoie que les champs déclarés directement sur le modèle concerné. Les champs de modèles qui héritent directement de modèles abstraits ou de classes mandataires sont considérés comme locaux, et non pas définis sur le parent.include_hidden
False
par défaut. Lorsque ce paramètre est défini àTrue
,get_fields()
inclut les champs utilisés pour fournir des fonctionnalités d’autres champs. Cela comprend également les champs possédant un nomrelated_name
(tels queManyToManyField
etForeignKey
) qui commence par un « + ».
>>> from django.contrib.auth.models import User >>> User._meta.get_fields() (<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>) # Also include hidden fields. >>> User._meta.get_fields(include_hidden=True) (<ManyToOneRel: auth.user_groups>, <ManyToOneRel: auth.user_user_permissions>, <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>)
Migration à partir de l’ancienne API¶
Dans le cadre de la formalisation de l’API Model._meta
(de la classe django.db.models.options.Options
), un certain nombre de méthodes et de propriétés ont été rendues obsolètes et seront supprimées dans Django 1.10.
Ces anciennes API peuvent être reproduites, soit en :
- invoquant
Options.get_field()
, ou; - en invoquant
Options.get_fields()
pour récupérer une liste de tous les champs, puis en filtrant cette liste à l’aide des attributs de champ qui décrivent (ou récupèrent, dans le cas des variantes_with_model
) les propriétés des champs souhaités.
Même s’il est possible de créer des remplacements strictement équivalents des anciennes méthodes, ce n’est pas forcément la meilleure approche. Prendre le temps de recréer les énumérations de champs afin de faire meilleur usage de la nouvelle API, en incluant potentiellement des champs qui étaient précédemment exclus, aboutira certainement à un code plus propre.
En supposant qu’un modèle est nommé MyModel
, il est possible de procéder aux substitutions suivantes pour convertir du code à la nouvelle API :
MyModel._meta.get_field(name)
devient :f = MyModel._meta.get_field(name)
puis vérifier si :
f.auto_created == False
, parce que la nouvelle APIget_field()
va renvoyer les relations « inverses », et :f.is_relation and f.related_model is None
, parce que la nouvelle APIget_field()
va renvoyer les relationsGenericForeignKey
.
MyModel._meta.get_field_by_name(name)
renvoie un tuple de ces quatre valeurs avec les remplacements suivants :field
peut être trouvé parMyModel._meta.get_field(name)
model
peut être trouvé par l’attributmodel
du champ.direct
peut être trouvé par :not field.auto_created or field.concrete
Le contrôle
auto_created
exclut toute relation « vers l’avant » ou « inverse » créée par Django, mais cela comprend également les champsAutoField
etOneToOneField
sur les modèles mandataires. Nous évitons d’exclure ces champs à l’aide de l’attributconcrete
.m2m
peut être trouvé au moyen de l’attributmany_to_many
du champ.
MyModel._meta.get_fields_with_model()
devient :[ (f, f.model if f.model != MyModel else None) for f in MyModel._meta.get_fields() if not f.is_relation or f.one_to_one or (f.many_to_one and f.related_model) ]
MyModel._meta.get_concrete_fields_with_model()
devient :[ (f, f.model if f.model != MyModel else None) for f in MyModel._meta.get_fields() if f.concrete and ( not f.is_relation or f.one_to_one or (f.many_to_one and f.related_model) ) ]
MyModel._meta.get_m2m_with_model()
devient :[ (f, f.model if f.model != MyModel else None) for f in MyModel._meta.get_fields() if f.many_to_many and not f.auto_created ]
MyModel._meta.get_all_related_objects()
devient :[ f for f in MyModel._meta.get_fields() if (f.one_to_many or f.one_to_one) and f.auto_created and not f.concrete ]
MyModel._meta.get_all_related_objects_with_model()
devient :[ (f, f.model if f.model != MyModel else None) for f in MyModel._meta.get_fields() if (f.one_to_many or f.one_to_one) and f.auto_created and not f.concrete ]
MyModel._meta.get_all_related_many_to_many_objects()
devient :[ f for f in MyModel._meta.get_fields(include_hidden=True) if f.many_to_many and f.auto_created ]
MyModel._meta.get_all_related_m2m_objects_with_model()
devient :[ (f, f.model if f.model != MyModel else None) for f in MyModel._meta.get_fields(include_hidden=True) if f.many_to_many and f.auto_created ]
MyModel._meta.get_all_field_names()
devient :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) )))
Cela fournit un remplacement 100 % rétrocompatible, garantissant que les noms de champ et les noms d’attribut
ForeignKey
sont inclus, mais que les champs associés àGenericForeignKey
ne le sont pas. Une version simplifiée pourrait être :[f.name for f in MyModel._meta.get_fields()]
Même si cette variante n’est pas rétrocompatible à 100 %, elle peut suffire dans bien des situations.