Kaydet (Commit) 87d0a338 authored tarafından Renaud Parent's avatar Renaud Parent Kaydeden (comit) Tim Graham

Fixed #22778 -- Added a model Meta option to define default_related_name.

Thanks jorgecarleitao and mmardini for reviews.
üst de901290
......@@ -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
......
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"
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.")
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment