mirror of
https://github.com/django/django.git
synced 2024-12-29 12:36:08 +00:00
fb48eb0581
Thanks to Russell Keith-Magee for mentoring this Google Summer of Code 2014 project and everyone else who helped with the patch!
288 lines
11 KiB
Plaintext
288 lines
11 KiB
Plaintext
===================
|
|
Model ``_meta`` API
|
|
===================
|
|
|
|
.. module:: django.db.models.options
|
|
:synopsis: Model meta-class layer
|
|
|
|
.. class:: Options
|
|
|
|
The model ``_meta`` API is at the core of the Django ORM. It enables other
|
|
parts of the system such as lookups, queries, forms, and the admin to
|
|
understand the capabilities of each model. The API is accessible through
|
|
the ``_meta`` attribute of each model class, which is an instance of an
|
|
``django.db.models.options.Options`` object.
|
|
|
|
Methods that it provides can be used to:
|
|
|
|
* Retrieve all field instances of a model
|
|
* Retrieve a single field instance of a model by name
|
|
|
|
.. versionchanged:: 1.8
|
|
|
|
The Model ``_meta`` API has always existed as a Django internal, but
|
|
wasn't formally documented and supported. As part of the effort to
|
|
make this API public, some of the already existing API entry points
|
|
have changed slightly. A :ref:`migration guide <migrating-old-meta-api>`
|
|
has been provided to assist in converting your code to use the new,
|
|
official API.
|
|
|
|
.. _model-meta-field-api:
|
|
|
|
Field access API
|
|
================
|
|
|
|
Retrieving a single field instance of a model by name
|
|
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
|
|
|
.. method:: Options.get_field(field_name)
|
|
|
|
Returns the field instance given a name of a field.
|
|
|
|
``field_name`` can be the name of a field on the model, a field
|
|
on an abstract or inherited model, or a field defined on another
|
|
model that points to the model. In the latter case, the ``field_name``
|
|
will be the ``related_name`` defined by the user or the name automatically
|
|
generated by Django itself.
|
|
|
|
:attr:`Hidden fields <django.db.models.Field.hidden>` cannot be retrieved
|
|
by name.
|
|
|
|
If a field with the given name is not found a
|
|
:class:`~django.core.exceptions.FieldDoesNotExist` exception will be
|
|
raised.
|
|
|
|
.. code-block:: python
|
|
|
|
>>> 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'
|
|
|
|
.. deprecated:: 1.8
|
|
|
|
:meth:`Options.get_field()` previously accepted a ``many_to_many``
|
|
parameter which could be set to ``False`` to avoid searching
|
|
``ManyToManyField``\s. The old behavior has been preserved for
|
|
backwards compatibility; however, the parameter and this behavior
|
|
has been deprecated.
|
|
|
|
If you wish to filter out ``ManyToManyField``\s, you can inspect the
|
|
:attr:`Field.many_to_many <django.db.models.Field.many_to_many>`
|
|
attribute after calling ``get_field()``.
|
|
|
|
Retrieving all field instances of a model
|
|
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
|
|
|
.. method:: Options.get_fields(include_parents=True, include_hidden=False)
|
|
|
|
.. versionadded:: 1.8
|
|
|
|
Returns a tuple of fields associated with a model. ``get_fields()`` accepts
|
|
two parameters that can be used to control which fields are returned:
|
|
|
|
``include_parents``
|
|
``True`` by default. Recursively includes fields defined on parent
|
|
classes. If set to ``False``, ``get_fields()`` will only search for
|
|
fields declared directly on the current model. Fields from models that
|
|
directly inherit from abstract models or proxy classes are considered
|
|
to be local, not on the parent.
|
|
|
|
``include_hidden``
|
|
``False`` by default. If set to ``True``, ``get_fields()`` will include
|
|
fields that are used to back other field's functionality. This will
|
|
also include any fields that have a ``related_name`` (such
|
|
as :class:`~django.db.models.ManyToManyField`, or
|
|
:class:`~django.db.models.ForeignKey`) that start with a "+".
|
|
|
|
.. code-block:: python
|
|
|
|
>>> 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>)
|
|
|
|
.. _migrating-old-meta-api:
|
|
|
|
Migrating from the old API
|
|
==========================
|
|
|
|
As part of the formalization of the ``Model._meta`` API (from the
|
|
:class:`django.db.models.options.Options` class), a number of methods and
|
|
properties have been deprecated and will be removed in Django 2.0.
|
|
|
|
These old APIs can be replicated by either:
|
|
|
|
* invoking :meth:`Options.get_field()
|
|
<django.db.models.options.Options.get_field()>`, or;
|
|
|
|
* invoking :meth:`Options.get_fields()
|
|
<django.db.models.options.Options.get_fields()>` to retrieve a list of all
|
|
fields, and then filtering this list using the :ref:`field attributes
|
|
<model-field-attributes>` that describe (or retrieve, in the case of
|
|
``_with_model`` variants) the properties of the desired fields.
|
|
|
|
Although it's possible to make strictly equivalent replacements of the old
|
|
methods, that might not be the best approach. Taking the time to refactor any
|
|
field loops to make better use of the new API - and possibly include fields
|
|
that were previously excluded - will almost certainly result in better code.
|
|
|
|
Assuming you have a model named ``MyModel``, the following substitutions
|
|
can be made to convert your code to the new API:
|
|
|
|
* ``MyModel._meta.get_field(name)``::
|
|
|
|
f = MyModel._meta.get_field(name)
|
|
|
|
then check if:
|
|
|
|
- ``f.auto_created == False``, because the new ``get_field()``
|
|
API will find "reverse" relations), and:
|
|
|
|
- ``f.is_relation and f.related_model is None``, because the new
|
|
``get_field()`` API will find
|
|
:class:`~django.contrib.contenttypes.fields.GenericForeignKey` relations;
|
|
|
|
* ``MyModel._meta.get_field_by_name(name)``:
|
|
|
|
``get_field_by_name()`` returned four values:
|
|
``(field, model, direct, m2m)``:
|
|
|
|
- ``field`` can be found by ``MyModel._meta.get_field(name)``
|
|
|
|
- ``model`` can be found through the
|
|
:attr:`~django.db.models.Field.model` attribute on the field.
|
|
|
|
- ``direct`` can be found by: ``not field.auto_created or field.concrete``
|
|
|
|
The :attr:`~django.db.models.Field.auto_created` check excludes
|
|
all "forward" and "reverse" relations that are created by Django, but
|
|
this also includes ``AutoField`` and ``OneToOneField`` on proxy models.
|
|
We avoid filtering out these attributes using the
|
|
:attr:`concrete <django.db.models.Field.concrete>` attribute.
|
|
|
|
- ``m2m`` can be found through the
|
|
:attr:`~django.db.models.Field.many_to_many` attribute on the field.
|
|
|
|
* ``MyModel._meta.get_fields_with_model()``::
|
|
|
|
[
|
|
(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.one_to_many and f.related_model)
|
|
]
|
|
|
|
* ``MyModel._meta.get_concrete_fields_with_model()``::
|
|
|
|
[
|
|
(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.one_to_many and f.related_model)
|
|
)
|
|
]
|
|
|
|
* ``MyModel._meta.get_m2m_with_model()``::
|
|
|
|
[
|
|
(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()``::
|
|
|
|
[
|
|
f for f in MyModel._meta.get_fields()
|
|
if f.many_to_one and f.auto_created
|
|
]
|
|
|
|
* ``MyModel._meta.get_all_related_objects_with_model()``::
|
|
|
|
[
|
|
(f, f.model if f.model != MyModel else None)
|
|
for f in MyModel._meta.get_fields()
|
|
if f.many_to_one and f.auto_created
|
|
]
|
|
|
|
* ``MyModel._meta.get_all_related_many_to_many_objects()``::
|
|
|
|
[
|
|
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()``::
|
|
|
|
[
|
|
(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()``::
|
|
|
|
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.one_to_many and field.related_model is None)
|
|
)))
|
|
|
|
This provides a 100% backwards compatible replacement, ensuring that both
|
|
field names and attribute names ``ForeignKey``\s are included, but fields
|
|
associated with ``GenericForeignKey``\s are not. A simpler version would be::
|
|
|
|
[f.name for f in MyModel._meta.get_fields()]
|
|
|
|
While this isn't 100% backwards compatible, it may be sufficient in many
|
|
situations.
|