Fixed #22778 -- Added a model Meta option to define default_related_name.
Thanks jorgecarleitao and mmardini for reviews.
This commit is contained in:
parent
de90129070
commit
87d0a3384c
1
AUTHORS
1
AUTHORS
|
@ -487,6 +487,7 @@ answer newbie questions, and generally made Django that much better:
|
|||
Tomek Paczkowski <tomek@hauru.eu>
|
||||
Jens Page
|
||||
Guillaume Pannatier <guillaume.pannatier@gmail.com>
|
||||
Renaud Parent <renaud.parent@gmail.com>
|
||||
Jay Parlar <parlar@gmail.com>
|
||||
Carlos Eduardo de Paula <carlosedp@gmail.com>
|
||||
John Paulett <john@paulett.org>
|
||||
|
|
|
@ -20,7 +20,7 @@ DEFAULT_NAMES = ('verbose_name', 'verbose_name_plural', 'db_table', 'ordering',
|
|||
'order_with_respect_to', 'app_label', 'db_tablespace',
|
||||
'abstract', 'managed', 'proxy', 'swappable', 'auto_created',
|
||||
'index_together', 'apps', 'default_permissions',
|
||||
'select_on_save')
|
||||
'select_on_save', 'default_related_name')
|
||||
|
||||
|
||||
def normalize_together(option_together):
|
||||
|
@ -99,6 +99,8 @@ class Options(object):
|
|||
# A custom app registry to use, if you're making a separate model set.
|
||||
self.apps = apps
|
||||
|
||||
self.default_related_name = None
|
||||
|
||||
@property
|
||||
def app_config(self):
|
||||
# Don't go through get_app_config to avoid triggering imports.
|
||||
|
|
|
@ -57,7 +57,14 @@ class RelatedObject(object):
|
|||
# If this is a symmetrical m2m relation on self, there is no reverse accessor.
|
||||
if getattr(self.field.rel, 'symmetrical', False) and self.model == self.parent_model:
|
||||
return None
|
||||
return self.field.rel.related_name or (self.opts.model_name + '_set')
|
||||
if self.field.rel.related_name:
|
||||
return self.field.rel.related_name
|
||||
if self.opts.default_related_name:
|
||||
return self.opts.default_related_name % {
|
||||
'model_name': self.opts.model_name.lower(),
|
||||
'app_label': self.opts.app_label.lower(),
|
||||
}
|
||||
return self.opts.model_name + '_set'
|
||||
else:
|
||||
return self.field.rel.related_name or (self.opts.model_name)
|
||||
|
||||
|
|
|
@ -95,6 +95,23 @@ Django quotes column and table names behind the scenes.
|
|||
setting, if set. If the backend doesn't support tablespaces, this option is
|
||||
ignored.
|
||||
|
||||
``default_related_name``
|
||||
------------------------
|
||||
|
||||
.. attribute:: Options.default_related_name
|
||||
|
||||
.. versionadded:: 1.8
|
||||
|
||||
The name that will be used by default for the relation from a related object
|
||||
back to this one. The default is ``<model_name>_set``.
|
||||
|
||||
As the reverse name for a field should be unique, be careful if you intend
|
||||
to subclass your model. To work around name collisions, part of the name
|
||||
should contain ``'%(app_label)s'`` and ``'%(model_name)s'``, which are
|
||||
replaced respectively by the name of the application the model is in,
|
||||
and the name of the model, both lowercased. See the paragraph on
|
||||
:ref:`related names for abstract models <abstract-related-name>`.
|
||||
|
||||
``get_latest_by``
|
||||
-----------------
|
||||
|
||||
|
|
|
@ -178,6 +178,10 @@ Models
|
|||
* Django now logs at most 9000 queries in ``connections.queries``, in order
|
||||
to prevent excessive memory usage in long-running processes in debug mode.
|
||||
|
||||
* There is now a model ``Meta`` option to define a
|
||||
:attr:`default related name <django.db.models.Options.default_related_name>`
|
||||
for all relational fields of a model.
|
||||
|
||||
* Pickling models and querysets across different versions of Django isn't
|
||||
officially supported (it may work, but there's no guarantee). An extra
|
||||
variable that specifies the current Django version is now added to the
|
||||
|
|
|
@ -0,0 +1,41 @@
|
|||
from django.db import models
|
||||
|
||||
|
||||
class Author(models.Model):
|
||||
first_name = models.CharField(max_length=128)
|
||||
last_name = models.CharField(max_length=128)
|
||||
|
||||
|
||||
class Editor(models.Model):
|
||||
name = models.CharField(max_length=128)
|
||||
bestselling_author = models.ForeignKey(Author)
|
||||
|
||||
|
||||
class Book(models.Model):
|
||||
title = models.CharField(max_length=128)
|
||||
authors = models.ManyToManyField(Author)
|
||||
editor = models.ForeignKey(Editor, related_name="edited_books")
|
||||
|
||||
class Meta:
|
||||
default_related_name = "books"
|
||||
|
||||
|
||||
class Store(models.Model):
|
||||
name = models.CharField(max_length=128)
|
||||
address = models.CharField(max_length=128)
|
||||
|
||||
class Meta:
|
||||
abstract = True
|
||||
default_related_name = "%(app_label)s_%(model_name)ss"
|
||||
|
||||
|
||||
class BookStore(Store):
|
||||
available_books = models.ManyToManyField(Book)
|
||||
|
||||
|
||||
class EditorStore(Store):
|
||||
editor = models.ForeignKey(Editor)
|
||||
available_books = models.ManyToManyField(Book)
|
||||
|
||||
class Meta:
|
||||
default_related_name = "editor_stores"
|
|
@ -0,0 +1,46 @@
|
|||
from django.test import TestCase
|
||||
|
||||
from .models.default_related_name import Author, Editor, Book
|
||||
|
||||
|
||||
class DefaultRelatedNameTests(TestCase):
|
||||
|
||||
def setUp(self):
|
||||
self.author = Author.objects.create(first_name="Dave", last_name="Loper")
|
||||
self.editor = Editor.objects.create(name="Test Editions",
|
||||
bestselling_author=self.author)
|
||||
self.book = Book.objects.create(title="Test Book", editor=self.editor)
|
||||
self.book.authors.add(self.author)
|
||||
self.book.save()
|
||||
|
||||
def test_no_default_related_name(self):
|
||||
try:
|
||||
self.author.editor_set
|
||||
except AttributeError:
|
||||
self.fail("Author should have an editor_set relation.")
|
||||
|
||||
def test_default_related_name(self):
|
||||
try:
|
||||
self.author.books
|
||||
except AttributeError:
|
||||
self.fail("Author should have a books relation.")
|
||||
|
||||
def test_related_name_overrides_default_related_name(self):
|
||||
try:
|
||||
self.editor.edited_books
|
||||
except AttributeError:
|
||||
self.fail("Editor should have a edited_books relation.")
|
||||
|
||||
def test_inheritance(self):
|
||||
try:
|
||||
# Here model_options corresponds to the name of the application used
|
||||
# in this test
|
||||
self.book.model_options_bookstores
|
||||
except AttributeError:
|
||||
self.fail("Book should have a model_options_bookstores relation.")
|
||||
|
||||
def test_inheritance_with_overrided_default_related_name(self):
|
||||
try:
|
||||
self.book.editor_stores
|
||||
except AttributeError:
|
||||
self.fail("Book should have a editor_stores relation.")
|
Loading…
Reference in New Issue