summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorTim Graham <timograham@gmail.com>2013-07-16 13:21:17 -0400
committerTim Graham <timograham@gmail.com>2013-07-16 19:32:30 -0400
commit2333c9662b5ffbaec5a10fa2973b4e6e7b0555bd (patch)
tree672bc21c469c70182021b0872eb40d76377b07cb
parentfba6c2ede7b25ed8c71a18dede00865dbc5b85af (diff)
Fixed #14007 -- Added model discovery in models module without the need to specify app_label.
Thanks mark@ and Aramgutang for work on the patch.
-rw-r--r--django/db/models/base.py18
-rw-r--r--django/db/models/loading.py6
-rw-r--r--docs/ref/models/options.txt11
-rw-r--r--docs/releases/1.7.txt3
-rw-r--r--tests/model_package/models/article.py3
-rw-r--r--tests/model_package/models/publication.py3
-rw-r--r--tests/model_package/tests.py3
7 files changed, 30 insertions, 17 deletions
diff --git a/django/db/models/base.py b/django/db/models/base.py
index fbfda616a1..01ba559e08 100644
--- a/django/db/models/base.py
+++ b/django/db/models/base.py
@@ -19,7 +19,7 @@ from django.db.models.query_utils import DeferredAttribute, deferred_class_facto
from django.db.models.deletion import Collector
from django.db.models.options import Options
from django.db.models import signals
-from django.db.models.loading import register_models, get_model
+from django.db.models.loading import register_models, get_model, MODELS_MODULE_NAME
from django.utils.translation import ugettext_lazy as _
from django.utils.functional import curry
from django.utils.encoding import force_str, force_text
@@ -86,10 +86,22 @@ class ModelBase(type):
base_meta = getattr(new_class, '_meta', None)
if getattr(meta, 'app_label', None) is None:
- # Figure out the app_label by looking one level up.
+ # Figure out the app_label by looking one level up from the package
+ # or module named 'models'. If no such package or module exists,
+ # fall back to looking one level up from the module this model is
+ # defined in.
+
# For 'django.contrib.sites.models', this would be 'sites'.
+ # For 'geo.models.places' this would be 'geo'.
+
model_module = sys.modules[new_class.__module__]
- kwargs = {"app_label": model_module.__name__.split('.')[-2]}
+ package_components = model_module.__name__.split('.')
+ package_components.reverse() # find the last occurrence of 'models'
+ try:
+ app_label_index = package_components.index(MODELS_MODULE_NAME) + 1
+ except ValueError:
+ app_label_index = 1
+ kwargs = {"app_label": package_components[app_label_index]}
else:
kwargs = {}
diff --git a/django/db/models/loading.py b/django/db/models/loading.py
index fb61098bfa..7280051bd8 100644
--- a/django/db/models/loading.py
+++ b/django/db/models/loading.py
@@ -15,6 +15,8 @@ import os
__all__ = ('get_apps', 'get_app', 'get_models', 'get_model', 'register_models',
'load_app', 'app_cache_ready')
+MODELS_MODULE_NAME = 'models'
+
class UnavailableApp(Exception):
pass
@@ -98,12 +100,12 @@ class AppCache(object):
self.nesting_level += 1
app_module = import_module(app_name)
try:
- models = import_module('.models', app_name)
+ models = import_module('.' + MODELS_MODULE_NAME, app_name)
except ImportError:
self.nesting_level -= 1
# If the app doesn't have a models module, we can just ignore the
# ImportError and return no models for it.
- if not module_has_submodule(app_module, 'models'):
+ if not module_has_submodule(app_module, MODELS_MODULE_NAME):
return None
# But if the app does have a models module, we need to figure out
# whether to suppress or propagate the error. If can_postpone is
diff --git a/docs/ref/models/options.txt b/docs/ref/models/options.txt
index 90099d13a3..d54af37e86 100644
--- a/docs/ref/models/options.txt
+++ b/docs/ref/models/options.txt
@@ -24,12 +24,17 @@ Available ``Meta`` options
.. attribute:: Options.app_label
- If a model exists outside of the standard :file:`models.py` (for instance,
- if the app's models are in submodules of ``myapp.models``), the model must
- define which app it is part of::
+ If a model exists outside of the standard locations (:file:`models.py` or
+ a ``models`` package in an app), the model must define which app it is part
+ of::
app_label = 'myapp'
+ .. versionadded:: 1.7
+
+ ``app_label`` is no longer required for models that are defined
+ in a ``models`` package within an app.
+
``db_table``
------------
diff --git a/docs/releases/1.7.txt b/docs/releases/1.7.txt
index 296150334b..ae2a80d7a3 100644
--- a/docs/releases/1.7.txt
+++ b/docs/releases/1.7.txt
@@ -57,6 +57,9 @@ Minor features
* The :meth:`QuerySet.update_or_create()
<django.db.models.query.QuerySet.update_or_create>` method was added.
+* :attr:`~django.db.models.Options.app_label` is no longer required for models
+ that are defined in a ``models`` package within an app.
+
Backwards incompatible changes in 1.7
=====================================
diff --git a/tests/model_package/models/article.py b/tests/model_package/models/article.py
index f70c971c7e..4dfcdc1056 100644
--- a/tests/model_package/models/article.py
+++ b/tests/model_package/models/article.py
@@ -6,6 +6,3 @@ class Article(models.Model):
sites = models.ManyToManyField(Site)
headline = models.CharField(max_length=100)
publications = models.ManyToManyField("model_package.Publication", null=True, blank=True,)
-
- class Meta:
- app_label = 'model_package'
diff --git a/tests/model_package/models/publication.py b/tests/model_package/models/publication.py
index 29828b9295..b0a49a94a1 100644
--- a/tests/model_package/models/publication.py
+++ b/tests/model_package/models/publication.py
@@ -3,6 +3,3 @@ from django.db import models
class Publication(models.Model):
title = models.CharField(max_length=30)
-
- class Meta:
- app_label = 'model_package'
diff --git a/tests/model_package/tests.py b/tests/model_package/tests.py
index 5cf464858b..5d856a9608 100644
--- a/tests/model_package/tests.py
+++ b/tests/model_package/tests.py
@@ -14,9 +14,6 @@ class Advertisment(models.Model):
"model_package.Publication", null=True, blank=True
)
- class Meta:
- app_label = 'model_package'
-
class ModelPackageTests(TestCase):
def test_model_packages(self):