Commit f05722a0 authored by James Pulec's avatar James Pulec Committed by Tim Graham
Browse files

Fixed #25354 -- Added class/app_label interpolation for related_query_name.

parent 5453aa66
Loading
Loading
Loading
Loading
+7 −0
Original line number Diff line number Diff line
@@ -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)
+3 −0
Original line number Diff line number Diff line
@@ -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
+4 −0
Original line number Diff line number Diff line
@@ -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
~~~~~~~~~~~~~~~~~~~~~~

+35 −18
Original line number Diff line number Diff line
@@ -967,18 +967,23 @@ the same database table, which is almost certainly not what you want.

.. _abstract-related-name:

Be careful with ``related_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.
Be careful with ``related_name`` and ``related_query_name``
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~

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'``.
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` 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
+6 −1
Original line number Diff line number Diff line
@@ -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:
Loading