mirror of
https://github.com/django/django.git
synced 2024-12-22 17:16:24 +00:00
Fixed #25354 -- Added class/app_label interpolation for related_query_name.
This commit is contained in:
parent
5453aa66cf
commit
f05722a08a
@ -299,6 +299,13 @@ class RelatedField(Field):
|
||||
}
|
||||
self.remote_field.related_name = related_name
|
||||
|
||||
if self.remote_field.related_query_name:
|
||||
related_query_name = force_text(self.remote_field.related_query_name) % {
|
||||
'class': cls.__name__.lower(),
|
||||
'app_label': cls._meta.app_label.lower(),
|
||||
}
|
||||
self.remote_field.related_query_name = related_query_name
|
||||
|
||||
def resolve_related_class(model, related, field):
|
||||
field.remote_field.model = related
|
||||
field.do_related_class(related, model)
|
||||
|
@ -1344,6 +1344,9 @@ The possible values for :attr:`~ForeignKey.on_delete` are found in
|
||||
# That's now the name of the reverse filter
|
||||
Article.objects.filter(tag__name="important")
|
||||
|
||||
Like :attr:`related_name`, ``related_query_name`` supports app label and
|
||||
class interpolation via :ref:`some special syntax <abstract-related-name>`.
|
||||
|
||||
.. attribute:: ForeignKey.to_field
|
||||
|
||||
The field on the related object that the relation is to. By default, Django
|
||||
|
@ -268,6 +268,10 @@ Models
|
||||
* :meth:`QuerySet.in_bulk() <django.db.models.query.QuerySet.in_bulk>`
|
||||
may be called without any arguments to return all objects in the queryset.
|
||||
|
||||
* :attr:`~django.db.models.ForeignKey.related_query_name` now supports
|
||||
app label and class interpolation using the ``'%(app_label)s'`` and
|
||||
``'%(class)s'`` strings.
|
||||
|
||||
Requests and Responses
|
||||
~~~~~~~~~~~~~~~~~~~~~~
|
||||
|
||||
|
@ -967,18 +967,23 @@ the same database table, which is almost certainly not what you want.
|
||||
|
||||
.. _abstract-related-name:
|
||||
|
||||
Be careful with ``related_name``
|
||||
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||
Be careful with ``related_name`` and ``related_query_name``
|
||||
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||
|
||||
If you are using the :attr:`~django.db.models.ForeignKey.related_name` attribute on a ``ForeignKey`` or
|
||||
``ManyToManyField``, you must always specify a *unique* reverse name for the
|
||||
field. This would normally cause a problem in abstract base classes, since the
|
||||
fields on this class are included into each of the child classes, with exactly
|
||||
the same values for the attributes (including :attr:`~django.db.models.ForeignKey.related_name`) each time.
|
||||
If you are using :attr:`~django.db.models.ForeignKey.related_name` or
|
||||
:attr:`~django.db.models.ForeignKey.related_query_name` on a ``ForeignKey`` or
|
||||
``ManyToManyField``, you must always specify a *unique* reverse name and query
|
||||
name for the field. This would normally cause a problem in abstract base
|
||||
classes, since the fields on this class are included into each of the child
|
||||
classes, with exactly the same values for the attributes (including
|
||||
:attr:`~django.db.models.ForeignKey.related_name` and
|
||||
:attr:`~django.db.models.ForeignKey.related_query_name`) each time.
|
||||
|
||||
To work around this problem, when you are using :attr:`~django.db.models.ForeignKey.related_name` in an
|
||||
abstract base class (only), part of the name should contain
|
||||
``'%(app_label)s'`` and ``'%(class)s'``.
|
||||
To work around this problem, when you are using
|
||||
:attr:`~django.db.models.ForeignKey.related_name` or
|
||||
:attr:`~django.db.models.ForeignKey.related_query_name` in an abstract base
|
||||
class (only), part of the value should contain ``'%(app_label)s'`` and
|
||||
``'%(class)s'``.
|
||||
|
||||
- ``'%(class)s'`` is replaced by the lower-cased name of the child class
|
||||
that the field is used in.
|
||||
@ -992,7 +997,11 @@ For example, given an app ``common/models.py``::
|
||||
from django.db import models
|
||||
|
||||
class Base(models.Model):
|
||||
m2m = models.ManyToManyField(OtherModel, related_name="%(app_label)s_%(class)s_related")
|
||||
m2m = models.ManyToManyField(
|
||||
OtherModel,
|
||||
related_name="%(app_label)s_%(class)s_related",
|
||||
related_query_name="%(app_label)s_%(class)ss",
|
||||
)
|
||||
|
||||
class Meta:
|
||||
abstract = True
|
||||
@ -1011,12 +1020,15 @@ Along with another app ``rare/models.py``::
|
||||
pass
|
||||
|
||||
The reverse name of the ``common.ChildA.m2m`` field will be
|
||||
``common_childa_related``, while the reverse name of the
|
||||
``common.ChildB.m2m`` field will be ``common_childb_related``, and finally the
|
||||
reverse name of the ``rare.ChildB.m2m`` field will be ``rare_childb_related``.
|
||||
It is up to you how you use the ``'%(class)s'`` and ``'%(app_label)s`` portion
|
||||
to construct your related name, but if you forget to use it, Django will raise
|
||||
errors when you perform system checks (or run :djadmin:`migrate`).
|
||||
``common_childa_related`` and the reverse query name will be ``common_childas``.
|
||||
The reverse name of the ``common.ChildB.m2m`` field will be
|
||||
``common_childb_related`` and the reverse query name will be
|
||||
``common_childbs``. Finally, the reverse name of the ``rare.ChildB.m2m`` field
|
||||
will be ``rare_childb_related`` and the reverse query name will be
|
||||
``rare_childbs``. It's up to you how you use the `'%(class)s'`` and
|
||||
``'%(app_label)s`` portion to construct your related name or related query name
|
||||
but if you forget to use it, Django will raise errors when you perform system
|
||||
checks (or run :djadmin:`migrate`).
|
||||
|
||||
If you don't specify a :attr:`~django.db.models.ForeignKey.related_name`
|
||||
attribute for a field in an abstract base class, the default reverse name will
|
||||
@ -1027,6 +1039,11 @@ attribute was omitted, the reverse name for the ``m2m`` field would be
|
||||
``childa_set`` in the ``ChildA`` case and ``childb_set`` for the ``ChildB``
|
||||
field.
|
||||
|
||||
.. versionchanged:: 1.10
|
||||
|
||||
Interpolation of ``'%(app_label)s'`` and ``'%(class)s'`` for
|
||||
``related_query_name`` was added.
|
||||
|
||||
.. _multi-table-inheritance:
|
||||
|
||||
Multi-table inheritance
|
||||
|
@ -56,7 +56,12 @@ class Post(models.Model):
|
||||
|
||||
@python_2_unicode_compatible
|
||||
class Attachment(models.Model):
|
||||
post = models.ForeignKey(Post, models.CASCADE, related_name='attached_%(class)s_set')
|
||||
post = models.ForeignKey(
|
||||
Post,
|
||||
models.CASCADE,
|
||||
related_name='attached_%(class)s_set',
|
||||
related_query_name='attached_%(app_label)s_%(class)ss',
|
||||
)
|
||||
content = models.TextField()
|
||||
|
||||
class Meta:
|
||||
|
@ -72,6 +72,15 @@ class ModelInheritanceTests(TestCase):
|
||||
AttributeError, getattr, post, "attached_%(class)s_set"
|
||||
)
|
||||
|
||||
def test_model_with_distinct_related_query_name(self):
|
||||
self.assertQuerysetEqual(Post.objects.filter(attached_model_inheritance_comments__is_spam=True), [])
|
||||
|
||||
# The Post model doesn't have a related query accessor based on
|
||||
# related_name (attached_comment_set).
|
||||
msg = "Cannot resolve keyword 'attached_comment_set' into field."
|
||||
with self.assertRaisesMessage(FieldError, msg):
|
||||
Post.objects.filter(attached_comment_set__is_spam=True)
|
||||
|
||||
def test_meta_fields_and_ordering(self):
|
||||
# Make sure Restaurant and ItalianRestaurant have the right fields in
|
||||
# the right order.
|
||||
|
Loading…
Reference in New Issue
Block a user