Skip to content

HTTPS clone URL

Subversion checkout URL

You can clone with HTTPS or Subversion.

Download ZIP
Browse files

Merge branch 'master' into lookups_3

  • Loading branch information...
commit 90e7004ec14e15503f828cc9bde2a7dab593814d 2 parents 66649ff + f7c2c0a
@akaariai akaariai authored
Showing with 5,527 additions and 3,910 deletions.
  1. +2 −1  CONTRIBUTING.rst
  2. +7 −9 README.rst
  3. +13 −0 django/__init__.py
  4. +2 −0  django/apps/__init__.py
  5. +165 −0 django/apps/base.py
  6. +413 −0 django/apps/registry.py
  7. +0 −32 django/conf/__init__.py
  8. +1 −1  django/conf/global_settings.py
  9. +0 −1  django/contrib/admin/actions.py
  10. +8 −0 django/contrib/admin/apps.py
  11. +5 −5 django/contrib/admin/options.py
  12. +11 −13 django/contrib/admin/sites.py
  13. +2 −2 django/contrib/admin/templates/admin/auth/user/change_password.html
  14. +2 −2 django/contrib/admin/templates/admin/change_form.html
  15. +2 −2 django/contrib/admin/templates/admin/change_list.html
  16. +2 −2 django/contrib/admin/templates/admin/delete_confirmation.html
  17. +2 −2 django/contrib/admin/templates/admin/delete_selected_confirmation.html
  18. +1 −1  django/contrib/admin/templates/admin/object_history.html
  19. +1 −1  django/contrib/admin/templatetags/admin_list.py
  20. +2 −2 django/contrib/admin/templatetags/admin_static.py
  21. +33 −2 django/contrib/admin/tests.py
  22. +0 −5 django/contrib/admin/validation.py
  23. +8 −0 django/contrib/admindocs/apps.py
  24. +1 −1  django/contrib/admindocs/templates/admin_doc/model_index.html
  25. +6 −5 django/contrib/admindocs/views.py
  26. +4 −3 django/contrib/auth/__init__.py
  27. +8 −0 django/contrib/auth/apps.py
  28. +5 −1 django/contrib/auth/forms.py
  29. +23 −19 django/contrib/auth/management/__init__.py
  30. +2 −0  django/contrib/auth/management/commands/changepassword.py
  31. +1 −2  django/contrib/auth/tests/test_auth_backends.py
  32. +3 −2 django/contrib/auth/tests/test_basic.py
  33. +1 −2  django/contrib/auth/tests/test_context_processors.py
  34. +1 −2  django/contrib/auth/tests/test_forms.py
  35. +1 −1  django/contrib/auth/tests/test_handlers.py
  36. +30 −12 django/contrib/auth/tests/test_management.py
  37. +1 −2  django/contrib/auth/tests/test_models.py
  38. +1 −1  django/contrib/auth/tests/test_signals.py
  39. +1 −1  django/contrib/auth/tests/test_templates.py
  40. +2 −2 django/contrib/auth/tests/test_views.py
  41. +6 −13 django/contrib/comments/__init__.py
  42. +8 −0 django/contrib/comments/apps.py
  43. +3 −3 django/contrib/comments/views/comments.py
  44. +8 −0 django/contrib/contenttypes/apps.py
  45. +19 −15 django/contrib/contenttypes/management.py
  46. +5 −3 django/contrib/contenttypes/models.py
  47. +11 −14 django/contrib/contenttypes/tests.py
  48. +8 −0 django/contrib/flatpages/apps.py
  49. +1 −1  django/contrib/flatpages/tests/test_csrf.py
  50. +1 −2  django/contrib/flatpages/tests/test_forms.py
  51. +1 −2  django/contrib/flatpages/tests/test_middleware.py
  52. +1 −2  django/contrib/flatpages/tests/test_templatetags.py
  53. +1 −2  django/contrib/flatpages/tests/test_views.py
  54. +8 −0 django/contrib/formtools/apps.py
  55. +1 −2  django/contrib/formtools/tests/tests.py
  56. +8 −0 django/contrib/gis/apps.py
  57. +1 −1  django/contrib/gis/db/backends/postgis/creation.py
  58. +2 −2 django/contrib/gis/geos/__init__.py
  59. +2 −6 django/contrib/gis/geos/collections.py
  60. +13 −26 django/contrib/gis/geos/geometry.py
  61. +7 −19 django/contrib/gis/geos/libgeos.py
  62. +36 −3 django/contrib/gis/geos/prepared.py
  63. +6 −10 django/contrib/gis/geos/prototypes/misc.py
  64. +8 −1 django/contrib/gis/geos/prototypes/prepared.py
  65. +10 −12 django/contrib/gis/geos/prototypes/topology.py
  66. +22 −23 django/contrib/gis/geos/tests/test_geos.py
  67. +8 −12 django/contrib/gis/management/commands/ogrinspect.py
  68. +2 −2 django/contrib/gis/sitemaps/kml.py
  69. +4 −3 django/contrib/gis/sitemaps/views.py
  70. +17 −45 django/contrib/gis/tests/distapp/models.py
  71. +26 −48 django/contrib/gis/tests/geo3d/models.py
  72. +4 −0 django/contrib/gis/tests/geoadmin/models.py
  73. +1 −2  django/contrib/gis/tests/geoadmin/tests.py
  74. +30 −30 django/contrib/gis/tests/geoapp/models.py
  75. +2 −6 django/contrib/gis/tests/geoapp/test_feeds.py
  76. +2 −7 django/contrib/gis/tests/geoapp/test_sitemaps.py
  77. +12 −12 django/contrib/gis/tests/geogapp/models.py
  78. +4 −0 django/contrib/gis/tests/inspectapp/models.py
  79. +7 −0 django/contrib/gis/tests/inspectapp/tests.py
  80. +31 −17 django/contrib/gis/tests/layermap/models.py
  81. +20 −14 django/contrib/gis/tests/relatedapp/models.py
  82. +8 −0 django/contrib/humanize/apps.py
  83. +2 −2 django/contrib/humanize/tests.py
  84. +8 −0 django/contrib/messages/apps.py
  85. +9 −5 django/contrib/messages/storage/__init__.py
  86. +15 −23 django/contrib/messages/tests/base.py
  87. +1 −2  django/contrib/messages/tests/test_cookie.py
  88. +8 −0 django/contrib/redirects/apps.py
  89. +2 −1  django/contrib/redirects/middleware.py
  90. +8 −16 django/contrib/redirects/tests.py
  91. +8 −0 django/contrib/sessions/apps.py
  92. +2 −2 django/contrib/sessions/tests.py
  93. +8 −0 django/contrib/sitemaps/apps.py
  94. +22 −0 django/contrib/sitemaps/locale/en/LC_MESSAGES/django.po
  95. +0 −4 django/contrib/sitemaps/tests/base.py
  96. +2 −1  django/contrib/sitemaps/tests/test_flatpages.py
  97. +1 −1  django/contrib/sitemaps/tests/test_generic.py
  98. +5 −6 django/contrib/sitemaps/tests/test_http.py
  99. +1 −1  django/contrib/sitemaps/tests/test_https.py
  100. +8 −0 django/contrib/sites/apps.py
  101. +16 −10 django/contrib/sites/management.py
  102. +6 −11 django/contrib/sites/tests.py
  103. +8 −0 django/contrib/staticfiles/apps.py
  104. +8 −6 django/contrib/staticfiles/finders.py
  105. +22 −0 django/contrib/staticfiles/locale/en/LC_MESSAGES/django.po
  106. +23 −17 django/contrib/staticfiles/management/commands/collectstatic.py
  107. +4 −7 django/contrib/staticfiles/storage.py
  108. +8 −0 django/contrib/syndication/apps.py
  109. +22 −0 django/contrib/syndication/locale/en/LC_MESSAGES/django.po
  110. +8 −0 django/contrib/webdesign/apps.py
  111. +22 −0 django/contrib/webdesign/locale/en/LC_MESSAGES/django.po
  112. +0 −1  django/core/apps/__init__.py
  113. +0 −47 django/core/apps/base.py
  114. +0 −395 django/core/apps/cache.py
  115. +2 −2 django/core/checks/compatibility/django_1_6_0.py
  116. +10 −19 django/core/handlers/wsgi.py
  117. +12 −3 django/core/mail/backends/console.py
  118. +6 −1 django/core/mail/backends/filebased.py
  119. +1 −4 django/core/mail/backends/smtp.py
  120. +38 −39 django/core/mail/message.py
  121. +37 −26 django/core/management/__init__.py
  122. +39 −26 django/core/management/base.py
  123. +37 −36 django/core/management/commands/dumpdata.py
  124. +5 −6 django/core/management/commands/flush.py
  125. +1 −1  django/core/management/commands/inspectdb.py
  126. +2 −2 django/core/management/commands/loaddata.py
  127. +3 −3 django/core/management/commands/makemigrations.py
  128. +21 −22 django/core/management/commands/migrate.py
  129. +2 −6 django/core/management/commands/shell.py
  130. +6 −2 django/core/management/commands/sql.py
  131. +6 −2 django/core/management/commands/sqlall.py
  132. +6 −2 django/core/management/commands/sqlclear.py
  133. +6 −2 django/core/management/commands/sqlcustom.py
  134. +6 −2 django/core/management/commands/sqldropindexes.py
  135. +6 −2 django/core/management/commands/sqlindexes.py
  136. +1 −1  django/core/management/commands/sqlmigrate.py
  137. +6 −3 django/core/management/commands/sqlsequencereset.py
  138. +35 −17 django/core/management/sql.py
  139. +9 −7 django/core/management/validation.py
  140. +0 −4 django/core/serializers/base.py
  141. +3 −7 django/core/serializers/python.py
  142. +3 −5 django/core/serializers/xml_serializer.py
  143. +14 −2 django/core/validators.py
  144. +2 −0  django/core/wsgi.py
  145. +9 −9 django/db/backends/__init__.py
  146. +0 −1  django/db/backends/oracle/base.py
  147. +25 −14 django/db/backends/schema.py
  148. +5 −4 django/db/backends/sqlite3/schema.py
  149. +8 −7 django/db/migrations/autodetector.py
  150. +3 −3 django/db/migrations/executor.py
  151. +5 −2 django/db/migrations/graph.py
  152. +45 −4 django/db/migrations/loader.py
  153. +28 −28 django/db/migrations/operations/models.py
  154. +1 −1  django/db/migrations/operations/special.py
  155. +5 −3 django/db/migrations/questioner.py
  156. +2 −2 django/db/migrations/recorder.py
  157. +35 −20 django/db/migrations/state.py
  158. +2 −2 django/db/migrations/writer.py
  159. +40 −32 django/db/models/base.py
  160. +17 −16 django/db/models/fields/__init__.py
  161. +10 −10 django/db/models/fields/related.py
  162. +14 −15 django/db/models/loading.py
  163. +22 −20 django/db/models/options.py
  164. +19 −12 django/db/models/query_utils.py
  165. +14 −15 django/db/models/signals.py
  166. +1 −0  django/db/models/sql/expressions.py
  167. +3 −4 django/db/utils.py
  168. +1 −1  django/forms/forms.py
  169. +6 −2 django/template/base.py
  170. +18 −18 django/template/loaders/app_directories.py
  171. +4 −3 django/template/loaders/eggs.py
  172. +2 −2 django/test/__init__.py
  173. +9 −9 django/test/client.py
  174. +20 −3 django/test/runner.py
  175. +17 −0 django/test/signals.py
  176. +5 −5 django/test/simple.py
  177. +41 −14 django/test/testcases.py
  178. +71 −7 django/test/utils.py
  179. +3 −6 django/utils/autoreload.py
  180. +10 −10 django/utils/cache.py
  181. +21 −0 django/utils/log.py
  182. +17 −5 django/utils/module_loading.py
  183. +10 −9 django/utils/text.py
  184. +3 −5 django/utils/translation/trans_real.py
  185. +1 −1  django/views/debug.py
  186. +2 −2 django/views/generic/dates.py
  187. +5 −1 django/views/i18n.py
  188. +1 −1  docs/conf.py
  189. +1 −1  docs/faq/general.txt
  190. +27 −10 docs/howto/custom-management-commands.txt
  191. +7 −4 docs/howto/custom-model-fields.txt
  192. +28 −0 docs/howto/custom-template-tags.txt
  193. +1 −1  docs/howto/deployment/wsgi/apache-auth.txt
  194. +2 −2 docs/howto/initial-data.txt
  195. +1 −1  docs/howto/upgrade-version.txt
  196. +3 −0  docs/index.txt
  197. +2 −2 docs/internals/committers.txt
  198. +29 −20 docs/internals/contributing/writing-code/unit-tests.txt
  199. +10 −7 docs/internals/deprecation.txt
  200. +8 −8 docs/intro/contributing.txt
  201. +3 −7 docs/intro/index.txt
  202. +4 −4 docs/intro/install.txt
  203. +11 −12 docs/intro/overview.txt
  204. +12 −11 docs/intro/reusable-apps.txt
  205. +63 −56 docs/intro/tutorial01.txt
  206. +1 −1  docs/intro/tutorial02.txt
  207. +0 −34 docs/man/daily_cleanup.1
  208. +12 −12 docs/man/django-admin.1
  209. +226 −0 docs/ref/applications.txt
  210. +2 −2 docs/ref/contrib/admin/admindocs.txt
  211. +4 −0 docs/ref/contrib/auth.txt
  212. +20 −0 docs/ref/contrib/gis/forms-api.txt
  213. +44 −24 docs/ref/contrib/gis/geos.txt
  214. +1 −2  docs/ref/contrib/gis/install/geolibs.txt
  215. +1 −2  docs/ref/contrib/gis/model-api.txt
  216. +4 −0 docs/ref/contrib/redirects.txt
  217. +11 −0 docs/ref/databases.txt
  218. +44 −38 docs/ref/django-admin.txt
  219. +34 −0 docs/ref/forms/api.txt
  220. +1 −1  docs/ref/forms/fields.txt
  221. +1 −0  docs/ref/index.txt
  222. +2 −2 docs/ref/models/options.txt
  223. +36 −11 docs/ref/settings.txt
  224. +104 −19 docs/ref/signals.txt
  225. +16 −2 docs/ref/validators.txt
  226. +10 −1 docs/releases/1.6.2.txt
  227. +184 −7 docs/releases/1.7.txt
  228. +1 −2  docs/topics/auth/customizing.txt
  229. +7 −1 docs/topics/cache.txt
  230. +5 −5 docs/topics/db/sql.txt
  231. +2 −3 docs/topics/email.txt
  232. +2 −2 docs/topics/http/sessions.txt
  233. +11 −3 docs/topics/http/urls.txt
  234. +6 −0 docs/topics/i18n/timezones.txt
  235. +28 −23 docs/topics/i18n/translation.txt
  236. +2 −2 docs/topics/install.txt
  237. +44 −12 docs/topics/logging.txt
  238. +1 −1  docs/topics/migrations.txt
  239. +27 −15 docs/topics/signals.txt
  240. +7 −9 docs/topics/testing/index.txt
  241. +6 −1,537 docs/topics/testing/overview.txt
  242. +1,603 −0 docs/topics/testing/tools.txt
  243. +3 −3 scripts/manage_translations.py
  244. +1 −1  setup.cfg
  245. +1 −2  tests/admin_custom_urls/tests.py
  246. +3 −12 tests/admin_docs/tests.py
  247. +2 −2 tests/admin_filters/tests.py
  248. +1 −2  tests/admin_inlines/tests.py
  249. 0  tests/{syndication → admin_scripts/complex_app/management}/__init__.py
  250. 0  tests/{invalid_models/invalid_models → admin_scripts/complex_app/management/commands}/__init__.py
  251. +7 −0 tests/admin_scripts/complex_app/management/commands/duplicate.py
  252. +3 −3 tests/admin_scripts/management/commands/app_command.py
  253. 0  tests/{invalid_models → admin_scripts/simple_app/management}/__init__.py
  254. 0  tests/{app_cache → admin_scripts/simple_app/management/commands}/__init__.py
  255. +7 −0 tests/admin_scripts/simple_app/management/commands/duplicate.py
  256. +37 −22 tests/admin_scripts/tests.py
  257. +16 −32 tests/admin_views/admin.py
  258. +1 −1  tests/admin_views/tests.py
  259. +8 −18 tests/admin_widgets/tests.py
  260. +0 −51 tests/app_cache/tests.py
  261. +11 −1 tests/app_loading/not_installed/models.py
  262. +28 −71 tests/app_loading/tests.py
  263. 0  tests/apps/__init__.py
  264. +34 −0 tests/apps/apps.py
  265. +7 −4 tests/{app_cache → apps}/models.py
  266. +161 −0 tests/apps/tests.py
  267. +3 −3 tests/backends/tests.py
  268. +1 −1  tests/base/models.py
  269. +5 −3 tests/bash_completion/tests.py
  270. +1 −1  tests/bulk_create/tests.py
  271. +64 −10 tests/cache/tests.py
  272. +13 −13 tests/commands_sql/tests.py
  273. +1 −2  tests/comment_tests/tests/__init__.py
  274. +5 −16 tests/comment_tests/tests/test_app_api.py
  275. +1 −1  tests/comment_tests/tests/test_comment_form.py
  276. +1 −1  tests/comment_tests/tests/test_comment_utils_moderators.py
  277. +2 −2 tests/contenttypes_tests/tests.py
  278. +1 −2  tests/csrf_tests/tests.py
  279. +0 −4 tests/defer_regress/models.py
  280. +12 −29 tests/defer_regress/tests.py
  281. +7 −0 tests/expressions_regress/tests.py
  282. +1 −1  tests/file_storage/tests.py
  283. +1 −1  tests/file_uploads/tests.py
  284. +5 −4 tests/files/tests.py
  285. +1 −1  tests/fixtures_regress/tests.py
  286. +1 −2  tests/forms_tests/tests/test_extra.py
  287. +1 −1  tests/forms_tests/tests/test_input_formats.py
  288. +1 −2  tests/forms_tests/tests/test_media.py
  289. +1 −2  tests/forms_tests/tests/test_widgets.py
  290. +4 −4 tests/forms_tests/tests/tests.py
  291. +1 −2  tests/generic_inline_admin/tests.py
  292. +2 −2 tests/generic_views/test_dates.py
  293. +1 −2  tests/generic_views/test_list.py
  294. +1 −1  tests/handlers/tests.py
  295. +2 −2 tests/i18n/contenttypes/tests.py
  296. +1 −2  tests/i18n/patterns/tests.py
  297. +1 −1  tests/i18n/test_compilation.py
  298. +1 −1  tests/i18n/test_extraction.py
  299. +20 −6 tests/i18n/tests.py
  300. +1 −2  tests/initial_sql_regress/tests.py
Sorry, we could not display the entire diff because too many files (389) changed.
View
3  CONTRIBUTING.rst
@@ -15,7 +15,8 @@ Extensive contribution guidelines are available in the repository at
https://docs.djangoproject.com/en/dev/internals/contributing/
-**Warning: pull requests are ignored!** `File a ticket`__ to suggest changes.
+**Warning: non-trivial pull requests (anything more than fixing a typo) without
+Trac tickets will be closed!** `Please file a ticket`__ to suggest changes.
__ https://code.djangoproject.com/newticket
View
16 README.rst
@@ -2,8 +2,8 @@ Django is a high-level Python Web framework that encourages rapid development
and clean, pragmatic design. Thanks for checking it out.
All documentation is in the "docs" directory and online at
-http://docs.djangoproject.com/en/dev/. If you're just getting started, here's
-how we recommend you read the docs:
+https://docs.djangoproject.com/en/stable/. If you're just getting started,
+here's how we recommend you read the docs:
* First, read docs/intro/install.txt for instructions on installing Django.
@@ -19,11 +19,9 @@ how we recommend you read the docs:
* See docs/README for instructions on building an HTML version of the docs.
-Docs are updated rigorously. If you find any problems in the docs, or think they
-should be clarified in any way, please take 30 seconds to fill out a ticket
-here:
-
-http://code.djangoproject.com/newticket
+Docs are updated rigorously. If you find any problems in the docs, or think
+they should be clarified in any way, please take 30 seconds to fill out a
+ticket here: https://code.djangoproject.com/newticket
To get more help:
@@ -31,11 +29,11 @@ To get more help:
there. Read the archives at http://django-irc-logs.com/.
* Join the django-users mailing list, or read the archives, at
- http://groups.google.com/group/django-users.
+ https://groups.google.com/group/django-users.
To contribute to Django:
-* Check out http://www.djangoproject.com/community/ for information about
+* Check out https://www.djangoproject.com/community/ for information about
getting involved.
To run Django's test suite:
View
13 django/__init__.py
@@ -6,3 +6,16 @@ def get_version(*args, **kwargs):
# Only import if it's actually called.
from django.utils.version import get_version
return get_version(*args, **kwargs)
+
+
+def setup():
+ """
+ Configure the settings (this happens as a side effect of accessing the
+ first setting), configure logging and populate the app registry.
+ """
+ from django.apps import apps
+ from django.conf import settings
+ from django.utils.log import configure_logging
+
+ configure_logging(settings.LOGGING_CONFIG, settings.LOGGING)
+ apps.populate(settings.INSTALLED_APPS)
View
2  django/apps/__init__.py
@@ -0,0 +1,2 @@
+from .base import AppConfig # NOQA
+from .registry import apps # NOQA
View
165 django/apps/base.py
@@ -0,0 +1,165 @@
+from importlib import import_module
+
+from django.core.exceptions import ImproperlyConfigured
+from django.utils.module_loading import module_has_submodule
+from django.utils._os import upath
+
+
+MODELS_MODULE_NAME = 'models'
+
+
+class AppConfig(object):
+ """
+ Class representing a Django application and its configuration.
+ """
+
+ def __init__(self, app_name, app_module):
+ # Full Python path to the application eg. 'django.contrib.admin'.
+ self.name = app_name
+
+ # Root module for the application eg. <module 'django.contrib.admin'
+ # from 'django/contrib/admin/__init__.pyc'>.
+ self.module = app_module
+
+ # The following attributes could be defined at the class level in a
+ # subclass, hence the test-and-set pattern.
+
+ # Last component of the Python path to the application eg. 'admin'.
+ # This value must be unique across a Django project.
+ if not hasattr(self, 'label'):
+ self.label = app_name.rpartition(".")[2]
+
+ # Human-readable name for the application eg. "Admin".
+ if not hasattr(self, 'verbose_name'):
+ self.verbose_name = self.label.title()
+
+ # Filesystem path to the application directory eg.
+ # u'/usr/lib/python2.7/dist-packages/django/contrib/admin'. May be
+ # None if the application isn't a bona fide package eg. if it's an
+ # egg. Otherwise it's a unicode on Python 2 and a str on Python 3.
+ if not hasattr(self, 'path'):
+ try:
+ self.path = upath(app_module.__path__[0])
+ except AttributeError:
+ self.path = None
+
+ # Module containing models eg. <module 'django.contrib.admin.models'
+ # from 'django/contrib/admin/models.pyc'>. Set by import_models().
+ # None if the application doesn't have a models module.
+ self.models_module = None
+
+ # Mapping of lower case model names to model classes. Initally set to
+ # None to prevent accidental access before import_models() runs.
+ self.models = None
+
+ def __repr__(self):
+ return '<%s: %s>' % (self.__class__.__name__, self.label)
+
+ @classmethod
+ def create(cls, entry):
+ """
+ Factory that creates an app config from an entry in INSTALLED_APPS.
+ """
+ try:
+ # If import_module succeeds, entry is a path to an app module.
+ # Otherwise, entry is a path to an app config class or an error.
+ module = import_module(entry)
+
+ except ImportError:
+ # Avoid django.utils.module_loading.import_by_path because it
+ # masks errors -- it reraises ImportError as ImproperlyConfigured.
+ mod_path, _, cls_name = entry.rpartition('.')
+
+ # Raise the original exception when entry cannot be a path to an
+ # app config class.
+ if not mod_path:
+ raise
+
+ mod = import_module(mod_path)
+ try:
+ cls = getattr(mod, cls_name)
+ except AttributeError:
+ # Emulate the error that "from <mod_path> import <cls_name>"
+ # would raise when <mod_path> exists but not <cls_name>, with
+ # more context (Python just says "cannot import name ...").
+ raise ImportError(
+ "cannot import name '%s' from '%s'" % (cls_name, mod_path))
+
+ # Check for obvious errors. (This check prevents duck typing, but
+ # it could be removed if it became a problem in practice.)
+ if not issubclass(cls, AppConfig):
+ raise ImproperlyConfigured(
+ "'%s' isn't a subclass of AppConfig." % entry)
+
+ # Obtain app name here rather than in AppClass.__init__ to keep
+ # all error checking for entries in INSTALLED_APPS in one place.
+ try:
+ app_name = cls.name
+ except AttributeError:
+ raise ImproperlyConfigured(
+ "'%s' must supply a name attribute." % entry)
+
+ # Ensure app_names points to a valid module.
+ app_module = import_module(app_name)
+
+ # Entry is a path to an app config class.
+ return cls(app_name, app_module)
+
+ else:
+ # Entry is a path to an app module.
+ return cls(entry, module)
+
+ def get_model(self, model_name):
+ """
+ Returns the model with the given case-insensitive model_name.
+
+ Raises LookupError if no model exists with this name.
+ """
+ if self.models is None:
+ raise LookupError(
+ "App '%s' doesn't have any models." % self.label)
+ try:
+ return self.models[model_name.lower()]
+ except KeyError:
+ raise LookupError(
+ "App '%s' doesn't have a '%s' model." % (self.label, model_name))
+
+ def get_models(self, include_auto_created=False,
+ include_deferred=False, include_swapped=False):
+ """
+ Returns an iterable of models.
+
+ By default, the following models aren't included:
+
+ - auto-created models for many-to-many relations without
+ an explicit intermediate table,
+ - models created to satisfy deferred attribute queries,
+ - models that have been swapped out.
+
+ Set the corresponding keyword argument to True to include such models.
+ Keyword arguments aren't documented; they're a private API.
+ """
+ for model in self.models.values():
+ if model._deferred and not include_deferred:
+ continue
+ if model._meta.auto_created and not include_auto_created:
+ continue
+ if model._meta.swapped and not include_swapped:
+ continue
+ yield model
+
+ def import_models(self, all_models):
+ # Dictionary of models for this app, primarily maintained in the
+ # 'all_models' attribute of the Apps this AppConfig is attached to.
+ # Injected as a parameter because it gets populated when models are
+ # imported, which might happen before populate() imports models.
+ self.models = all_models
+
+ if module_has_submodule(self.module, MODELS_MODULE_NAME):
+ models_module_name = '%s.%s' % (self.name, MODELS_MODULE_NAME)
+ self.models_module = import_module(models_module_name)
+
+ def ready(self):
+ """
+ Override this method in subclasses to run code when Django starts.
+ """
View
413 django/apps/registry.py
@@ -0,0 +1,413 @@
+from collections import Counter, defaultdict, OrderedDict
+import os
+import sys
+import warnings
+
+from django.core.exceptions import ImproperlyConfigured
+from django.utils import lru_cache
+from django.utils.module_loading import import_lock
+from django.utils._os import upath
+
+from .base import AppConfig
+
+
+class Apps(object):
+ """
+ A registry that stores the configuration of installed applications.
+
+ It also keeps track of models eg. to provide reverse-relations.
+ """
+
+ def __init__(self, installed_apps=()):
+ # installed_apps is set to None when creating the master registry
+ # because it cannot be populated at that point. Other registries must
+ # provide a list of installed apps and are populated immediately.
+ if installed_apps is None and hasattr(sys.modules[__name__], 'apps'):
+ raise RuntimeError("You must supply an installed_apps argument.")
+
+ # Mapping of app labels => model names => model classes. Every time a
+ # model is imported, ModelBase.__new__ calls apps.register_model which
+ # creates an entry in all_models. All imported models are registered,
+ # regardless of whether they're defined in an installed application
+ # and whether the registry has been populated. Since it isn't possible
+ # to reimport a module safely (it could reexecute initialization code)
+ # all_models is never overridden or reset.
+ self.all_models = defaultdict(OrderedDict)
+
+ # Mapping of labels to AppConfig instances for installed apps.
+ self.app_configs = OrderedDict()
+
+ # Stack of app_configs. Used to store the current state in
+ # set_available_apps and set_installed_apps.
+ self.stored_app_configs = []
+
+ # Whether the registry is populated.
+ self.ready = False
+
+ # Pending lookups for lazy relations.
+ self._pending_lookups = {}
+
+ # Populate apps and models, unless it's the master registry.
+ if installed_apps is not None:
+ self.populate(installed_apps)
+
+ def populate(self, installed_apps=None):
+ """
+ Loads application configurations and models.
+
+ This method imports each application module and then each model module.
+
+ It is thread safe and idempotent, but not reentrant.
+ """
+ if self.ready:
+ return
+ # Since populate() may be a side effect of imports, and since it will
+ # itself import modules, an ABBA deadlock between threads would be
+ # possible if we didn't take the import lock. See #18251.
+ with import_lock():
+ if self.ready:
+ return
+
+ # app_config should be pristine, otherwise the code below won't
+ # guarantee that the order matches the order in INSTALLED_APPS.
+ if self.app_configs:
+ raise RuntimeError("populate() isn't reentrant")
+
+ # Load app configs and app modules.
+ for entry in installed_apps:
+ if isinstance(entry, AppConfig):
+ app_config = entry
+ else:
+ app_config = AppConfig.create(entry)
+ if app_config.label in self.app_configs:
+ raise ImproperlyConfigured(
+ "Application labels aren't unique, "
+ "duplicates: %s" % app_config.label)
+
+ self.app_configs[app_config.label] = app_config
+
+ # Check for duplicate app names.
+ counts = Counter(
+ app_config.name for app_config in self.app_configs.values())
+ duplicates = [
+ name for name, count in counts.most_common() if count > 1]
+ if duplicates:
+ raise ImproperlyConfigured(
+ "Application names aren't unique, "
+ "duplicates: %s" % ", ".join(duplicates))
+
+ # Load models.
+ for app_config in self.app_configs.values():
+ all_models = self.all_models[app_config.label]
+ app_config.import_models(all_models)
+
+ self.clear_cache()
+ self.ready = True
+
+ for app_config in self.get_app_configs():
+ app_config.ready()
+
+ def check_ready(self):
+ """
+ Raises an exception if the registry isn't ready.
+ """
+ if not self.ready:
+ raise RuntimeError("App registry isn't ready yet.")
+
+ def get_app_configs(self):
+ """
+ Imports applications and returns an iterable of app configs.
+ """
+ self.check_ready()
+ return self.app_configs.values()
+
+ def get_app_config(self, app_label):
+ """
+ Imports applications and returns an app config for the given label.
+
+ Raises LookupError if no application exists with this label.
+ """
+ self.check_ready()
+ try:
+ return self.app_configs[app_label]
+ except KeyError:
+ raise LookupError("No installed app with label '%s'." % app_label)
+
+ # This method is performance-critical at least for Django's test suite.
+ @lru_cache.lru_cache(maxsize=None)
+ def get_models(self, app_mod=None, include_auto_created=False,
+ include_deferred=False, include_swapped=False):
+ """
+ Returns a list of all installed models.
+
+ By default, the following models aren't included:
+
+ - auto-created models for many-to-many relations without
+ an explicit intermediate table,
+ - models created to satisfy deferred attribute queries,
+ - models that have been swapped out.
+
+ Set the corresponding keyword argument to True to include such models.
+ """
+ self.check_ready()
+ if app_mod:
+ warnings.warn(
+ "The app_mod argument of get_models is deprecated.",
+ PendingDeprecationWarning, stacklevel=2)
+ app_label = app_mod.__name__.split('.')[-2]
+ try:
+ return list(self.get_app_config(app_label).get_models(
+ include_auto_created, include_deferred, include_swapped))
+ except LookupError:
+ return []
+
+ result = []
+ for app_config in self.app_configs.values():
+ result.extend(list(app_config.get_models(
+ include_auto_created, include_deferred, include_swapped)))
+ return result
+
+ def get_model(self, app_label, model_name):
+ """
+ Returns the model matching the given app_label and model_name.
+
+ model_name is case-insensitive.
+
+ Raises LookupError if no application exists with this label, or no
+ model exists with this name in the application.
+ """
+ self.check_ready()
+ return self.get_app_config(app_label).get_model(model_name.lower())
+
+ def register_model(self, app_label, model):
+ # Since this method is called when models are imported, it cannot
+ # perform imports because of the risk of import loops. It mustn't
+ # call get_app_config().
+ model_name = model._meta.model_name
+ app_models = self.all_models[app_label]
+ if model_name in app_models:
+ raise RuntimeError(
+ "Conflicting '%s' models in application '%s': %s and %s." %
+ (model_name, app_label, app_models[model_name], model))
+ app_models[model_name] = model
+ self.clear_cache()
+
+ def is_installed(self, app_name):
+ """
+ Checks whether an application with this name exists in the registry.
+
+ app_name is the full name of the app eg. 'django.contrib.admin'.
+
+ It's safe to call this method at import time, even while the registry
+ is being populated. It returns False for apps that aren't loaded yet.
+ """
+ app_config = self.app_configs.get(app_name.rpartition(".")[2])
+ return app_config is not None and app_config.name == app_name
+
+ def get_containing_app_config(self, object_name):
+ """
+ Look for an app config containing a given object.
+
+ object_name is the dotted Python path to the object.
+
+ Returns the app config for the inner application in case of nesting.
+ Returns None if the object isn't in any registered app config.
+
+ It's safe to call this method at import time, even while the registry
+ is being populated.
+ """
+ candidates = []
+ for app_config in self.app_configs.values():
+ if object_name.startswith(app_config.name):
+ subpath = object_name[len(app_config.name):]
+ if subpath == '' or subpath[0] == '.':
+ candidates.append(app_config)
+ if candidates:
+ return sorted(candidates, key=lambda ac: -len(ac.name))[0]
+
+ def get_registered_model(self, app_label, model_name):
+ """
+ Similar to get_model(), but doesn't require that an app exists with
+ the given app_label.
+
+ It's safe to call this method at import time, even while the registry
+ is being populated.
+ """
+ model = self.all_models[app_label].get(model_name.lower())
+ if model is None:
+ raise LookupError(
+ "Model '%s.%s' not registered." % (app_label, model_name))
+ return model
+
+ def set_available_apps(self, available):
+ """
+ Restricts the set of installed apps used by get_app_config[s].
+
+ available must be an iterable of application names.
+
+ set_available_apps() must be balanced with unset_available_apps().
+
+ Primarily used for performance optimization in TransactionTestCase.
+
+ This method is safe is the sense that it doesn't trigger any imports.
+ """
+ available = set(available)
+ installed = set(app_config.name for app_config in self.get_app_configs())
+ if not available.issubset(installed):
+ raise ValueError("Available apps isn't a subset of installed "
+ "apps, extra apps: %s" % ", ".join(available - installed))
+
+ self.stored_app_configs.append(self.app_configs)
+ self.app_configs = OrderedDict(
+ (label, app_config)
+ for label, app_config in self.app_configs.items()
+ if app_config.name in available)
+ self.clear_cache()
+
+ def unset_available_apps(self):
+ """
+ Cancels a previous call to set_available_apps().
+ """
+ self.app_configs = self.stored_app_configs.pop()
+ self.clear_cache()
+
+ def set_installed_apps(self, installed):
+ """
+ Enables a different set of installed apps for get_app_config[s].
+
+ installed must be an iterable in the same format as INSTALLED_APPS.
+
+ set_installed_apps() must be balanced with unset_installed_apps(),
+ even if it exits with an exception.
+
+ Primarily used as a receiver of the setting_changed signal in tests.
+
+ This method may trigger new imports, which may add new models to the
+ registry of all imported models. They will stay in the registry even
+ after unset_installed_apps(). Since it isn't possible to replay
+ imports safely (eg. that could lead to registering listeners twice),
+ models are registered when they're imported and never removed.
+ """
+ self.check_ready()
+ self.stored_app_configs.append(self.app_configs)
+ self.app_configs = OrderedDict()
+ self.ready = False
+ self.clear_cache()
+ self.populate(installed)
+
+ def unset_installed_apps(self):
+ """
+ Cancels a previous call to set_installed_apps().
+ """
+ self.app_configs = self.stored_app_configs.pop()
+ self.ready = True
+ self.clear_cache()
+
+ def clear_cache(self):
+ """
+ Clears all internal caches, for methods that alter the app registry.
+
+ This is mostly used in tests.
+ """
+ self.get_models.cache_clear()
+
+ ### DEPRECATED METHODS GO BELOW THIS LINE ###
+
+ def load_app(self, app_name):
+ """
+ Loads the app with the provided fully qualified name, and returns the
+ model module.
+ """
+ warnings.warn(
+ "load_app(app_name) is deprecated.",
+ PendingDeprecationWarning, stacklevel=2)
+ app_config = AppConfig.create(app_name)
+ app_config.import_models(self.all_models[app_config.label])
+ self.app_configs[app_config.label] = app_config
+ self.clear_cache()
+ return app_config.models_module
+
+ def app_cache_ready(self):
+ warnings.warn(
+ "app_cache_ready() is deprecated in favor of the ready property.",
+ PendingDeprecationWarning, stacklevel=2)
+ return self.ready
+
+ def get_app(self, app_label):
+ """
+ Returns the module containing the models for the given app_label.
+ """
+ warnings.warn(
+ "get_app_config(app_label).models_module supersedes get_app(app_label).",
+ PendingDeprecationWarning, stacklevel=2)
+ try:
+ models_module = self.get_app_config(app_label).models_module
+ except LookupError as exc:
+ # Change the exception type for backwards compatibility.
+ raise ImproperlyConfigured(*exc.args)
+ if models_module is None:
+ raise ImproperlyConfigured(
+ "App '%s' doesn't have a models module." % app_label)
+ return models_module
+
+ def get_apps(self):
+ """
+ Returns a list of all installed modules that contain models.
+ """
+ warnings.warn(
+ "[a.models_module for a in get_app_configs()] supersedes get_apps().",
+ PendingDeprecationWarning, stacklevel=2)
+ app_configs = self.get_app_configs()
+ return [app_config.models_module for app_config in app_configs
+ if app_config.models_module is not None]
+
+ def _get_app_package(self, app):
+ return '.'.join(app.__name__.split('.')[:-1])
+
+ def get_app_package(self, app_label):
+ warnings.warn(
+ "get_app_config(label).name supersedes get_app_package(label).",
+ PendingDeprecationWarning, stacklevel=2)
+ return self._get_app_package(self.get_app(app_label))
+
+ def _get_app_path(self, app):
+ if hasattr(app, '__path__'): # models/__init__.py package
+ app_path = app.__path__[0]
+ else: # models.py module
+ app_path = app.__file__
+ return os.path.dirname(upath(app_path))
+
+ def get_app_path(self, app_label):
+ warnings.warn(
+ "get_app_config(label).path supersedes get_app_path(label).",
+ PendingDeprecationWarning, stacklevel=2)
+ return self._get_app_path(self.get_app(app_label))
+
+ def get_app_paths(self):
+ """
+ Returns a list of paths to all installed apps.
+
+ Useful for discovering files at conventional locations inside apps
+ (static files, templates, etc.)
+ """
+ warnings.warn(
+ "[a.path for a in get_app_configs()] supersedes get_app_paths().",
+ PendingDeprecationWarning, stacklevel=2)
+ self.check_ready()
+ app_paths = []
+ for app in self.get_apps():
+ app_paths.append(self._get_app_path(app))
+ return app_paths
+
+ def register_models(self, app_label, *models):
+ """
+ Register a set of models as belonging to an app.
+ """
+ warnings.warn(
+ "register_models(app_label, *models) is deprecated.",
+ PendingDeprecationWarning, stacklevel=2)
+ for model in models:
+ self.register_model(app_label, model)
+
+
+apps = Apps(installed_apps=None)
View
32 django/conf/__init__.py
@@ -7,16 +7,12 @@
"""
import importlib
-import logging
import os
-import sys
import time # Needed for Windows
-import warnings
from django.conf import global_settings
from django.core.exceptions import ImproperlyConfigured
from django.utils.functional import LazyObject, empty
-from django.utils.module_loading import import_by_path
from django.utils import six
ENVIRONMENT_VARIABLE = "DJANGO_SETTINGS_MODULE"
@@ -44,34 +40,12 @@ def _setup(self, name=None):
% (desc, ENVIRONMENT_VARIABLE))
self._wrapped = Settings(settings_module)
- self._configure_logging()
def __getattr__(self, name):
if self._wrapped is empty:
self._setup(name)
return getattr(self._wrapped, name)
- def _configure_logging(self):
- """
- Setup logging from LOGGING_CONFIG and LOGGING settings.
- """
- if not sys.warnoptions:
- # Route warnings through python logging
- logging.captureWarnings(True)
- # Allow DeprecationWarnings through the warnings filters
- warnings.simplefilter("default", DeprecationWarning)
-
- if self.LOGGING_CONFIG:
- from django.utils.log import DEFAULT_LOGGING
- # First find the logging configuration function ...
- logging_config_func = import_by_path(self.LOGGING_CONFIG)
-
- logging_config_func(DEFAULT_LOGGING)
-
- # ... then invoke it with the logging settings
- if self.LOGGING:
- logging_config_func(self.LOGGING)
-
def configure(self, default_settings=global_settings, **options):
"""
Called to manually configure the settings. The 'default_settings'
@@ -84,7 +58,6 @@ def configure(self, default_settings=global_settings, **options):
for name, value in options.items():
setattr(holder, name, value)
self._wrapped = holder
- self._configure_logging()
@property
def configured(self):
@@ -104,11 +77,6 @@ def __setattr__(self, name, value):
elif name == "ALLOWED_INCLUDE_ROOTS" and isinstance(value, six.string_types):
raise ValueError("The ALLOWED_INCLUDE_ROOTS setting must be set "
"to a tuple, not a string.")
- elif name == "INSTALLED_APPS":
- value = list(value) # force evaluation of generators on Python 3
- if len(value) != len(set(value)):
- raise ImproperlyConfigured("The INSTALLED_APPS setting must contain unique values.")
-
object.__setattr__(self, name, value)
View
2  django/conf/global_settings.py
@@ -290,7 +290,7 @@
# Absolute path to the directory static files should be collected to.
# Example: "/var/www/example.com/static/"
-STATIC_ROOT = ''
+STATIC_ROOT = None
# URL that handles the static files served from STATIC_ROOT.
# Example: "http://example.com/static/", "http://static.example.com/"
View
1  django/contrib/admin/actions.py
@@ -71,7 +71,6 @@ def delete_selected(modeladmin, request, queryset):
"perms_lacking": perms_needed,
"protected": protected,
"opts": opts,
- "app_label": app_label,
'action_checkbox_name': helpers.ACTION_CHECKBOX_NAME,
}
View
8 django/contrib/admin/apps.py
@@ -0,0 +1,8 @@
+from django.apps import AppConfig
+
+from django.utils.translation import ugettext_lazy as _
+
+
+class AdminConfig(AppConfig):
+ name = 'django.contrib.admin'
+ verbose_name = _("administration")
View
10 django/contrib/admin/options.py
@@ -46,9 +46,12 @@
IS_POPUP_VAR = '_popup'
TO_FIELD_VAR = '_to_field'
+
HORIZONTAL, VERTICAL = 1, 2
-# returns the <ul> class for a given radio_admin field
-get_ul_class = lambda x: 'radiolist%s' % (' inline' if x == HORIZONTAL else '')
+
+
+def get_ul_class(radio_style):
+ return 'radiolist' if radio_style == VERTICAL else 'radiolist inline'
class IncorrectLookupParameters(Exception):
@@ -1308,7 +1311,6 @@ def add_view(self, request, form_url='', extra_context=None):
media=media,
inline_admin_formsets=inline_admin_formsets,
errors=helpers.AdminErrorList(form, formsets),
- app_label=opts.app_label,
preserved_filters=self.get_preserved_filters(request),
)
context.update(extra_context or {})
@@ -1532,7 +1534,6 @@ def changelist_view(self, request, extra_context=None):
media=media,
has_add_permission=self.has_add_permission(request),
opts=cl.opts,
- app_label=app_label,
action_form=action_form,
actions_on_top=self.actions_on_top,
actions_on_bottom=self.actions_on_bottom,
@@ -1627,7 +1628,6 @@ def history_view(self, request, object_id, extra_context=None):
action_list=action_list,
module_name=capfirst(force_text(opts.verbose_name_plural)),
object=obj,
- app_label=app_label,
opts=opts,
preserved_filters=self.get_preserved_filters(request),
)
View
24 django/contrib/admin/sites.py
@@ -6,6 +6,7 @@
from django.contrib.contenttypes import views as contenttype_views
from django.views.decorators.csrf import csrf_protect
from django.db.models.base import ModelBase
+from django.apps import apps
from django.core.exceptions import ImproperlyConfigured, PermissionDenied
from django.core.urlresolvers import reverse, NoReverseMatch
from django.template.response import TemplateResponse
@@ -156,20 +157,16 @@ def check_dependencies(self):
"""
Check that all things needed to run the admin have been correctly installed.
- The default implementation checks that LogEntry, ContentType and the
- auth context processor are installed.
+ The default implementation checks that admin and contenttypes apps are
+ installed, as well as the auth context processor.
"""
- from django.contrib.admin.models import LogEntry
- from django.contrib.contenttypes.models import ContentType
-
- if not LogEntry._meta.installed:
+ if not apps.is_installed('django.contrib.admin'):
raise ImproperlyConfigured("Put 'django.contrib.admin' in your "
"INSTALLED_APPS setting in order to use the admin application.")
- if not ContentType._meta.installed:
+ if not apps.is_installed('django.contrib.contenttypes'):
raise ImproperlyConfigured("Put 'django.contrib.contenttypes' in "
"your INSTALLED_APPS setting in order to use the admin application.")
- if not ('django.contrib.auth.context_processors.auth' in settings.TEMPLATE_CONTEXT_PROCESSORS or
- 'django.core.context_processors.auth' in settings.TEMPLATE_CONTEXT_PROCESSORS):
+ if 'django.contrib.auth.context_processors.auth' not in settings.TEMPLATE_CONTEXT_PROCESSORS:
raise ImproperlyConfigured("Put 'django.contrib.auth.context_processors.auth' "
"in your TEMPLATE_CONTEXT_PROCESSORS setting in order to use the admin application.")
@@ -383,7 +380,7 @@ def index(self, request, extra_context=None):
app_dict[app_label]['models'].append(model_dict)
else:
app_dict[app_label] = {
- 'name': app_label.title(),
+ 'name': apps.get_app_config(app_label).verbose_name,
'app_label': app_label,
'app_url': reverse('admin:app_list', kwargs={'app_label': app_label}, current_app=self.name),
'has_module_perms': has_module_perms,
@@ -392,7 +389,7 @@ def index(self, request, extra_context=None):
# Sort the apps alphabetically.
app_list = list(six.itervalues(app_dict))
- app_list.sort(key=lambda x: x['name'])
+ app_list.sort(key=lambda x: x['name'].lower())
# Sort the models alphabetically within each app.
for app in app_list:
@@ -410,6 +407,7 @@ def index(self, request, extra_context=None):
def app_index(self, request, app_label, extra_context=None):
user = request.user
+ app_name = apps.get_app_config(app_label).verbose_name
has_module_perms = user.has_module_perms(app_label)
if not has_module_perms:
raise PermissionDenied
@@ -444,7 +442,7 @@ def app_index(self, request, app_label, extra_context=None):
# something to display, add in the necessary meta
# information.
app_dict = {
- 'name': app_label.title(),
+ 'name': app_name,
'app_label': app_label,
'app_url': '',
'has_module_perms': has_module_perms,
@@ -455,7 +453,7 @@ def app_index(self, request, app_label, extra_context=None):
# Sort the models alphabetically within each app.
app_dict['models'].sort(key=lambda x: x['name'])
context = dict(self.each_context(),
- title=_('%s administration') % capfirst(app_label),
+ title=_('%(app)s administration') % {'app': app_name},
app_list=[app_dict],
app_label=app_label,
)
View
4 django/contrib/admin/templates/admin/auth/user/change_password.html
@@ -6,12 +6,12 @@
<script type="text/javascript" src="{% url 'admin:jsi18n' %}"></script>
{% endblock %}
{% block extrastyle %}{{ block.super }}<link rel="stylesheet" type="text/css" href="{% static "admin/css/forms.css" %}" />{% endblock %}
-{% block bodyclass %}{{ opts.app_label }}-{{ opts.object_name.lower }} change-form{% endblock %}
+{% block bodyclass %}{{ opts.app_label }}-{{ opts.model_name }} change-form{% endblock %}
{% if not is_popup %}
{% block breadcrumbs %}
<div class="breadcrumbs">
<a href="{% url 'admin:index' %}">{% trans 'Home' %}</a>
-&rsaquo; <a href="{% url 'admin:app_list' app_label=opts.app_label %}">{{ opts.app_label|capfirst|escape }}</a>
+&rsaquo; <a href="{% url 'admin:app_list' app_label=opts.app_label %}">{{ opts.app_config.verbose_name }}</a>
&rsaquo; <a href="{% url opts|admin_urlname:'changelist' %}">{{ opts.verbose_name_plural|capfirst }}</a>
&rsaquo; <a href="{% url opts|admin_urlname:'change' original.pk|admin_urlquote %}">{{ original|truncatewords:"18" }}</a>
&rsaquo; {% trans 'Change password' %}
View
4 django/contrib/admin/templates/admin/change_form.html
@@ -10,13 +10,13 @@
{% block coltype %}colM{% endblock %}
-{% block bodyclass %}app-{{ opts.app_label }} model-{{ opts.object_name.lower }} change-form{% endblock %}
+{% block bodyclass %}app-{{ opts.app_label }} model-{{ opts.model_name }} change-form{% endblock %}
{% if not is_popup %}
{% block breadcrumbs %}
<div class="breadcrumbs">
<a href="{% url 'admin:index' %}">{% trans 'Home' %}</a>
-&rsaquo; <a href="{% url 'admin:app_list' app_label=opts.app_label %}">{{ app_label|capfirst|escape }}</a>
+&rsaquo; <a href="{% url 'admin:app_list' app_label=opts.app_label %}">{{ opts.app_config.verbose_name }}</a>
&rsaquo; {% if has_change_permission %}<a href="{% url opts|admin_urlname:'changelist' %}">{{ opts.verbose_name_plural|capfirst }}</a>{% else %}{{ opts.verbose_name_plural|capfirst }}{% endif %}
&rsaquo; {% if add %}{% trans 'Add' %} {{ opts.verbose_name }}{% else %}{{ original|truncatewords:"18" }}{% endif %}
</div>
View
4 django/contrib/admin/templates/admin/change_list.html
@@ -32,13 +32,13 @@
{% endif %}{% endif %}
{% endblock %}
-{% block bodyclass %}app-{{ opts.app_label }} model-{{ opts.object_name.lower }} change-list{% endblock %}
+{% block bodyclass %}app-{{ opts.app_label }} model-{{ opts.model_name }} change-list{% endblock %}
{% if not is_popup %}
{% block breadcrumbs %}
<div class="breadcrumbs">
<a href="{% url 'admin:index' %}">{% trans 'Home' %}</a>
-&rsaquo; <a href="{% url 'admin:app_list' app_label=cl.opts.app_label %}">{{ app_label|capfirst|escape }}</a>
+&rsaquo; <a href="{% url 'admin:app_list' app_label=cl.opts.app_label %}">{{ cl.opts.app_config.verbose_name }}</a>
&rsaquo; {{ cl.opts.verbose_name_plural|capfirst }}
</div>
{% endblock %}
View
4 django/contrib/admin/templates/admin/delete_confirmation.html
@@ -1,12 +1,12 @@
{% extends "admin/base_site.html" %}
{% load i18n admin_urls %}
-{% block bodyclass %}app-{{ opts.app_label }} model-{{ opts.object_name.lower }} delete-confirmation{% endblock %}
+{% block bodyclass %}app-{{ opts.app_label }} model-{{ opts.model_name }} delete-confirmation{% endblock %}
{% block breadcrumbs %}
<div class="breadcrumbs">
<a href="{% url 'admin:index' %}">{% trans 'Home' %}</a>
-&rsaquo; <a href="{% url 'admin:app_list' app_label=opts.app_label %}">{{ app_label|capfirst }}</a>
+&rsaquo; <a href="{% url 'admin:app_list' app_label=opts.app_label %}">{{ opts.app_config.verbose_name }}</a>
&rsaquo; <a href="{% url opts|admin_urlname:'changelist' %}">{{ opts.verbose_name_plural|capfirst|escape }}</a>
&rsaquo; <a href="{% url opts|admin_urlname:'change' object.pk|admin_urlquote %}">{{ object|truncatewords:"18" }}</a>
&rsaquo; {% trans 'Delete' %}
View
4 django/contrib/admin/templates/admin/delete_selected_confirmation.html
@@ -1,12 +1,12 @@
{% extends "admin/base_site.html" %}
{% load i18n l10n admin_urls %}
-{% block bodyclass %}app-{{ opts.app_label }} model-{{ opts.object_name.lower }} delete-confirmation delete-selected-confirmation{% endblock %}
+{% block bodyclass %}app-{{ opts.app_label }} model-{{ opts.model_name }} delete-confirmation delete-selected-confirmation{% endblock %}
{% block breadcrumbs %}
<div class="breadcrumbs">
<a href="{% url 'admin:index' %}">{% trans 'Home' %}</a>
-&rsaquo; <a href="{% url 'admin:app_list' app_label=app_label %}">{{ app_label|capfirst|escape }}</a>
+&rsaquo; <a href="{% url 'admin:app_list' app_label=opts.app_label %}">{{ opts.app_config.verbose_name }}</a>
&rsaquo; <a href="{% url opts|admin_urlname:'changelist' %}">{{ opts.verbose_name_plural|capfirst }}</a>
&rsaquo; {% trans 'Delete multiple objects' %}
</div>
View
2  django/contrib/admin/templates/admin/object_history.html
@@ -4,7 +4,7 @@
{% block breadcrumbs %}
<div class="breadcrumbs">
<a href="{% url 'admin:index' %}">{% trans 'Home' %}</a>
-&rsaquo; <a href="{% url 'admin:app_list' app_label=app_label %}">{{ app_label|capfirst|escape }}</a>
+&rsaquo; <a href="{% url 'admin:app_list' app_label=opts.app_label %}">{{ opts.app_config.verbose_name }}</a>
&rsaquo; <a href="{% url opts|admin_urlname:'changelist' %}">{{ module_name }}</a>
&rsaquo; <a href="{% url opts|admin_urlname:'change' object.pk|admin_urlquote %}">{{ object|truncatewords:"18" }}</a>
&rsaquo; {% trans 'History' %}
View
2  django/contrib/admin/templatetags/admin_list.py
@@ -333,7 +333,7 @@ def date_hierarchy(cl):
month_lookup = cl.params.get(month_field)
day_lookup = cl.params.get(day_field)
- link = lambda d: cl.get_query_string(d, [field_generic])
+ link = lambda filters: cl.get_query_string(filters, [field_generic])
if not (year_lookup or month_lookup or day_lookup):
# select appropriate start level
View
4 django/contrib/admin/templatetags/admin_static.py
@@ -1,9 +1,9 @@
-from django.conf import settings
+from django.apps import apps
from django.template import Library
register = Library()
-if 'django.contrib.staticfiles' in settings.INSTALLED_APPS:
+if apps.is_installed('django.contrib.staticfiles'):
from django.contrib.staticfiles.templatetags.staticfiles import static
else:
from django.templatetags.static import static
View
35 django/contrib/admin/tests.py
@@ -32,7 +32,6 @@ def setUpClass(cls):
@classmethod
def _tearDownClassInternal(cls):
if hasattr(cls, 'selenium'):
- cls.selenium.refresh() # see ticket #21227
cls.selenium.quit()
super(AdminSeleniumWebDriverTestCase, cls)._tearDownClassInternal()
@@ -51,8 +50,40 @@ def wait_loaded_tag(self, tag_name, timeout=10):
Helper function that blocks until the element with the given tag name
is found on the page.
"""
+ self.wait_for(tag_name, timeout)
+
+ def wait_for(self, css_selector, timeout=10):
+ """
+ Helper function that blocks until an css selector is found on the page.
+ """
+ from selenium.webdriver.common.by import By
+ from selenium.webdriver.support import expected_conditions as ec
+ self.wait_until(
+ ec.presence_of_element_located((By.CSS_SELECTOR, css_selector)),
+ timeout
+ )
+
+ def wait_for_text(self, css_selector, text, timeout=10):
+ """
+ Helper function that blocks until the text is found in the css selector.
+ """
+ from selenium.webdriver.common.by import By
+ from selenium.webdriver.support import expected_conditions as ec
+ self.wait_until(
+ ec.text_to_be_present_in_element(
+ (By.CSS_SELECTOR, css_selector), text),
+ timeout
+ )
+
+ def wait_for_value(self, css_selector, text, timeout=10):
+ """
+ Helper function that blocks until the value is found in the css selector.
+ """
+ from selenium.webdriver.common.by import By
+ from selenium.webdriver.support import expected_conditions as ec
self.wait_until(
- lambda driver: driver.find_element_by_tag_name(tag_name),
+ ec.text_to_be_present_in_element_value(
+ (By.CSS_SELECTOR, css_selector), text),
timeout
)
View
5 django/contrib/admin/validation.py
@@ -1,4 +1,3 @@
-from django.core.apps import app_cache
from django.core.exceptions import ImproperlyConfigured
from django.db import models
from django.db.models.fields import FieldDoesNotExist
@@ -15,10 +14,6 @@
class BaseValidator(object):
- def __init__(self):
- # Before we can introspect models, they need the app cache to be fully
- # loaded so that inter-relations are set up correctly.
- app_cache.populate()
def validate(self, cls, model):
for m in dir(self):
View
8 django/contrib/admindocs/apps.py
@@ -0,0 +1,8 @@
+from django.apps import AppConfig
+
+from django.utils.translation import ugettext_lazy as _
+
+
+class AdminDocsConfig(AppConfig):
+ name = 'django.contrib.admindocs'
+ verbose_name = _("administrative documentation")
View
2  django/contrib/admindocs/templates/admin_doc/model_index.html
@@ -27,7 +27,7 @@ <h2 id="app-{{ group.grouper }}">{{ group.grouper|capfirst }}</h2>
<table class="xfull">
{% for model in group.list %}
<tr>
-<th><a href="{% url 'django-admindocs-models-detail' app_label=model.app_label model_name=model.object_name.lower %}">{{ model.object_name }}</a></th>
+<th><a href="{% url 'django-admindocs-models-detail' app_label=model.app_label model_name=model.model_name %}">{{ model.object_name }}</a></th>
</tr>
{% endfor %}
</table>
View
11 django/contrib/admindocs/views.py
@@ -5,10 +5,10 @@
import warnings
from django import template
+from django.apps import apps
from django.conf import settings
from django.contrib import admin
from django.contrib.admin.views.decorators import staff_member_required
-from django.core.apps import app_cache
from django.db import models
from django.core.exceptions import ViewDoesNotExist
from django.http import Http404
@@ -174,7 +174,7 @@ class ModelIndexView(BaseAdminDocsView):
template_name = 'admin_doc/model_index.html'
def get_context_data(self, **kwargs):
- m_list = [m._meta for m in app_cache.get_models()]
+ m_list = [m._meta for m in apps.get_models()]
kwargs.update({'models': m_list})
return super(ModelIndexView, self).get_context_data(**kwargs)
@@ -185,11 +185,12 @@ class ModelDetailView(BaseAdminDocsView):
def get_context_data(self, **kwargs):
# Get the model class.
try:
- app_cache.get_app_config(self.kwargs['app_label'])
+ apps.get_app_config(self.kwargs['app_label'])
except LookupError:
raise Http404(_("App %(app_label)r not found") % self.kwargs)
- model = app_cache.get_model(self.kwargs['app_label'], self.kwargs['model_name'])
- if model is None:
+ try:
+ model = apps.get_model(self.kwargs['app_label'], self.kwargs['model_name'])
+ except LookupError:
raise Http404(_("Model %(model_name)r not found in app %(app_label)r") % self.kwargs)
opts = model._meta
View
7 django/contrib/auth/__init__.py
@@ -123,14 +123,15 @@ def get_user_model():
"""
Returns the User model that is active in this project.
"""
- from django.core.apps import app_cache
+ from django.apps import apps
try:
app_label, model_name = settings.AUTH_USER_MODEL.split('.')
except ValueError:
raise ImproperlyConfigured("AUTH_USER_MODEL must be of the form 'app_label.model_name'")
- user_model = app_cache.get_model(app_label, model_name)
- if user_model is None:
+ try:
+ user_model = apps.get_model(app_label, model_name)
+ except LookupError:
raise ImproperlyConfigured("AUTH_USER_MODEL refers to model '%s' that has not been installed" % settings.AUTH_USER_MODEL)
return user_model
View
8 django/contrib/auth/apps.py
@@ -0,0 +1,8 @@
+from django.apps import AppConfig
+
+from django.utils.translation import ugettext_lazy as _
+
+
+class AuthConfig(AppConfig):
+ name = 'django.contrib.auth'
+ verbose_name = _("authentication and authorization")
View
6 django/contrib/auth/forms.py
@@ -21,7 +21,11 @@
UNMASKED_DIGITS_TO_SHOW = 6
-mask_password = lambda p: "%s%s" % (p[:UNMASKED_DIGITS_TO_SHOW], "*" * max(len(p) - UNMASKED_DIGITS_TO_SHOW, 0))
+
+def mask_password(password):
+ shown = password[:UNMASKED_DIGITS_TO_SHOW]
+ masked = "*" * max(len(password) - UNMASKED_DIGITS_TO_SHOW, 0)
+ return shown + masked
class ReadOnlyPasswordHashWidget(forms.Widget):
View
42 django/contrib/auth/management/__init__.py
@@ -6,9 +6,9 @@
import getpass
import unicodedata
+from django.apps import apps
from django.contrib.auth import (models as auth_app, get_permission_codename,
get_user_model)
-from django.core.apps import app_cache, UnavailableApp
from django.core import exceptions
from django.core.management.base import CommandError
from django.db import DEFAULT_DB_ALIAS, router
@@ -60,25 +60,26 @@ def _check_permission_clashing(custom, builtin, ctype):
pool.add(codename)
-def create_permissions(app, created_models, verbosity, db=DEFAULT_DB_ALIAS, **kwargs):
+def create_permissions(app_config, verbosity=2, interactive=True, db=DEFAULT_DB_ALIAS, **kwargs):
+ if not app_config.models_module:
+ return
+
try:
- app_cache.get_model('auth', 'Permission')
- except UnavailableApp:
+ Permission = apps.get_model('auth', 'Permission')
+ except LookupError:
return
- if not router.allow_migrate(db, auth_app.Permission):
+ if not router.allow_migrate(db, Permission):
return
from django.contrib.contenttypes.models import ContentType
- app_models = app_cache.get_models(app)
-
# This will hold the permissions we're looking for as
# (content_type, (codename, name))
searched_perms = list()
# The codenames and ctypes that should exist.
ctypes = set()
- for klass in app_models:
+ for klass in app_config.get_models():
# Force looking up the content types in the current database
# before creating foreign keys to them.
ctype = ContentType.objects.db_manager(db).get_for_model(klass)
@@ -89,20 +90,20 @@ def create_permissions(app, created_models, verbosity, db=DEFAULT_DB_ALIAS, **kw
# Find all the Permissions that have a content_type for a model we're
# looking for. We don't need to check for codenames since we already have
# a list of the ones we're going to create.
- all_perms = set(auth_app.Permission.objects.using(db).filter(
+ all_perms = set(Permission.objects.using(db).filter(
content_type__in=ctypes,
).values_list(
"content_type", "codename"
))
perms = [
- auth_app.Permission(codename=codename, name=name, content_type=ctype)
+ Permission(codename=codename, name=name, content_type=ctype)
for ctype, (codename, name) in searched_perms
if (ctype.pk, codename) not in all_perms
]
# Validate the permissions before bulk_creation to avoid cryptic
# database error when the verbose_name is longer than 50 characters
- permission_name_max_length = auth_app.Permission._meta.get_field('name').max_length
+ permission_name_max_length = Permission._meta.get_field('name').max_length
verbose_name_max_length = permission_name_max_length - 11 # len('Can change ') prefix
for perm in perms:
if len(perm.name) > permission_name_max_length:
@@ -112,23 +113,24 @@ def create_permissions(app, created_models, verbosity, db=DEFAULT_DB_ALIAS, **kw
verbose_name_max_length,
)
)
- auth_app.Permission.objects.using(db).bulk_create(perms)
+ Permission.objects.using(db).bulk_create(perms)
if verbosity >= 2:
for perm in perms:
print("Adding permission '%s'" % perm)
-def create_superuser(app, created_models, verbosity, db, **kwargs):
+def create_superuser(app_config, verbosity=2, interactive=True, db=DEFAULT_DB_ALIAS, **kwargs):
try:
- app_cache.get_model('auth', 'Permission')
- UserModel = get_user_model()
- except UnavailableApp:
+ apps.get_model('auth', 'Permission')
+ except LookupError:
return
+ UserModel = get_user_model()
+
from django.core.management import call_command
- if UserModel in created_models and kwargs.get('interactive', True):
- msg = ("\nYou just installed Django's auth system, which means you "
+ if not UserModel.objects.exists() and interactive:
+ msg = ("\nYou have installed Django's auth system, and "
"don't have any superusers defined.\nWould you like to create one "
"now? (yes/no): ")
confirm = input(msg)
@@ -202,7 +204,9 @@ def get_default_username(check_db=True):
return ''
return default_username
+
signals.post_migrate.connect(create_permissions,
dispatch_uid="django.contrib.auth.management.create_permissions")
signals.post_migrate.connect(create_superuser,
- sender=auth_app, dispatch_uid="django.contrib.auth.management.create_superuser")
+ sender=apps.get_app_config('auth'),
+ dispatch_uid="django.contrib.auth.management.create_superuser")
View
2  django/contrib/auth/management/commands/changepassword.py
@@ -1,3 +1,5 @@
+from __future__ import unicode_literals
+
import getpass
from optparse import make_option
View
3  django/contrib/auth/tests/test_auth_backends.py
@@ -10,8 +10,7 @@
from django.core.exceptions import ImproperlyConfigured, PermissionDenied
from django.contrib.auth import authenticate, get_user
from django.http import HttpRequest
-from django.test import TestCase
-from django.test.utils import override_settings
+from django.test import TestCase, override_settings
from django.contrib.auth.hashers import MD5PasswordHasher
View
5 django/contrib/auth/tests/test_basic.py
@@ -3,6 +3,7 @@
import locale
+from django.apps import apps
from django.contrib.auth import get_user_model
from django.contrib.auth.management.commands import createsuperuser
from django.contrib.auth.models import User, AnonymousUser
@@ -11,9 +12,8 @@
from django.core.exceptions import ImproperlyConfigured
from django.core.management import call_command
from django.dispatch import receiver
-from django.test import TestCase
+from django.test import TestCase, override_settings
from django.test.signals import setting_changed
-from django.test.utils import override_settings
from django.utils import translation
from django.utils.encoding import force_str
from django.utils.six import binary_type, PY2, StringIO
@@ -26,6 +26,7 @@ def user_model_swapped(**kwargs):
# Reset User manager
setattr(User, 'objects', User._default_manager)
ensure_default_manager(User)
+ apps.clear_cache()
def mock_inputs(inputs):
View
3  django/contrib/auth/tests/test_context_processors.py
@@ -7,8 +7,7 @@
from django.contrib.contenttypes.models import ContentType
from django.contrib.auth.context_processors import PermWrapper, PermLookupDict
from django.db.models import Q
-from django.test import TestCase
-from django.test.utils import override_settings
+from django.test import TestCase, override_settings
from django.utils._os import upath
View
3  django/contrib/auth/tests/test_forms.py
@@ -12,8 +12,7 @@
from django.contrib.auth.tests.utils import skipIfCustomUser
from django.core import mail
from django.forms.fields import Field, CharField
-from django.test import TestCase
-from django.test.utils import override_settings
+from django.test import TestCase, override_settings
from django.utils.encoding import force_text
from django.utils._os import upath
from django.utils import translation
View
2  django/contrib/auth/tests/test_handlers.py
@@ -5,7 +5,7 @@
from django.contrib.auth.tests.custom_user import CustomUser
from django.contrib.auth.tests.utils import skipIfCustomUser
from django.test import TransactionTestCase
-from django.test.utils import override_settings
+from django.test import override_settings
# This must be a TransactionTestCase because the WSGI auth handler performs
View
42 django/contrib/auth/tests/test_management.py
@@ -1,6 +1,7 @@
from __future__ import unicode_literals
from datetime import date
+from django.apps import apps
from django.contrib.auth import models, management
from django.contrib.auth.management import create_permissions
from django.contrib.auth.management.commands import changepassword
@@ -8,13 +9,11 @@
from django.contrib.auth.tests.custom_user import CustomUser
from django.contrib.auth.tests.utils import skipIfCustomUser
from django.contrib.contenttypes.models import ContentType
-from django.core.apps import app_cache
from django.core import exceptions
from django.core.management import call_command
from django.core.management.base import CommandError
from django.core.management.validation import get_validation_errors
-from django.test import TestCase
-from django.test.utils import override_settings
+from django.test import TestCase, override_settings
from django.utils import six
from django.utils.six import StringIO
@@ -83,6 +82,19 @@ def test_that_max_tries_exits_1(self):
with self.assertRaises(CommandError):
command.execute("joe", stdout=self.stdout, stderr=self.stderr)
+ def test_that_changepassword_command_works_with_nonascii_output(self):
+ """
+ #21627 -- Executing the changepassword management command should allow
+ non-ASCII characters from the User object representation.
+ """
+ # 'Julia' with accented 'u':
+ models.User.objects.create_user(username='J\xfalia', password='qwerty')
+
+ command = changepassword.Command()
+ command._get_pass = lambda *args: 'not qwerty'
+
+ command.execute("J\xfalia", stdout=self.stdout)
+
@skipIfCustomUser
class CreatesuperuserManagementCommandTestCase(TestCase):
@@ -184,21 +196,21 @@ class CustomUserModelValidationTestCase(TestCase):
def test_required_fields_is_list(self):
"REQUIRED_FIELDS should be a list."
new_io = StringIO()
- get_validation_errors(new_io, app_cache.get_app_config('auth').models_module)
+ get_validation_errors(new_io, apps.get_app_config('auth'))
self.assertIn("The REQUIRED_FIELDS must be a list or tuple.", new_io.getvalue())
@override_settings(AUTH_USER_MODEL='auth.CustomUserBadRequiredFields')
def test_username_not_in_required_fields(self):
"USERNAME_FIELD should not appear in REQUIRED_FIELDS."
new_io = StringIO()
- get_validation_errors(new_io, app_cache.get_app_config('auth').models_module)
+ get_validation_errors(new_io, apps.get_app_config('auth'))
self.assertIn("The field named as the USERNAME_FIELD should not be included in REQUIRED_FIELDS on a swappable User model.", new_io.getvalue())
@override_settings(AUTH_USER_MODEL='auth.CustomUserNonUniqueUsername')
def test_username_non_unique(self):
"A non-unique USERNAME_FIELD should raise a model validation error."
new_io = StringIO()
- get_validation_errors(new_io, app_cache.get_app_config('auth').models_module)
+ get_validation_errors(new_io, apps.get_app_config('auth'))
self.assertIn("The USERNAME_FIELD must be unique. Add unique=True to the field parameters.", new_io.getvalue())
@@ -220,13 +232,15 @@ def test_duplicated_permissions(self):
Test that we show proper error message if we are trying to create
duplicate permissions.
"""
+ auth_app_config = apps.get_app_config('auth')
+
# check duplicated default permission
models.Permission._meta.permissions = [
('change_permission', 'Can edit permission (duplicate)')]
six.assertRaisesRegex(self, CommandError,
"The permission codename 'change_permission' clashes with a "
"builtin permission for model 'auth.Permission'.",
- create_permissions, models, [], verbosity=0)
+ create_permissions, auth_app_config, verbosity=0)
# check duplicated custom permissions
models.Permission._meta.permissions = [
@@ -237,21 +251,23 @@ def test_duplicated_permissions(self):
six.assertRaisesRegex(self, CommandError,
"The permission codename 'my_custom_permission' is duplicated for model "
"'auth.Permission'.",
- create_permissions, models, [], verbosity=0)
+ create_permissions, auth_app_config, verbosity=0)
# should not raise anything
models.Permission._meta.permissions = [
('my_custom_permission', 'Some permission'),
('other_one', 'Some other permission'),
]
- create_permissions(models, [], verbosity=0)
+ create_permissions(auth_app_config, verbosity=0)
def test_default_permissions(self):
+ auth_app_config = apps.get_app_config('auth')
+
permission_content_type = ContentType.objects.get_by_natural_key('auth', 'permission')
models.Permission._meta.permissions = [
('my_custom_permission', 'Some permission'),
]
- create_permissions(models, [], verbosity=0)
+ create_permissions(auth_app_config, verbosity=0)
# add/change/delete permission by default + custom permission
self.assertEqual(models.Permission.objects.filter(
@@ -260,7 +276,7 @@ def test_default_permissions(self):
models.Permission.objects.filter(content_type=permission_content_type).delete()
models.Permission._meta.default_permissions = []
- create_permissions(models, [], verbosity=0)
+ create_permissions(auth_app_config, verbosity=0)
# custom permission only since default permissions is empty
self.assertEqual(models.Permission.objects.filter(
@@ -268,10 +284,12 @@ def test_default_permissions(self):
).count(), 1)
def test_verbose_name_length(self):
+ auth_app_config = apps.get_app_config('auth')
+
permission_content_type = ContentType.objects.get_by_natural_key('auth', 'permission')
models.Permission.objects.filter(content_type=permission_content_type).delete()
models.Permission._meta.verbose_name = "some ridiculously long verbose name that is out of control"
six.assertRaisesRegex(self, exceptions.ValidationError,
"The verbose_name of permission is longer than 39 characters",
- create_permissions, models, [], verbosity=0)
+ create_permissions, auth_app_config, verbosity=0)
View
3  django/contrib/auth/tests/test_models.py
@@ -3,8 +3,7 @@
from django.contrib.auth.tests.utils import skipIfCustomUser
from django.core import mail
from django.db.models.signals import post_save
-from django.test import TestCase
-from django.test.utils import override_settings
+from django.test import TestCase, override_settings
@skipIfCustomUser
View
2  django/contrib/auth/tests/test_signals.py
@@ -3,7 +3,7 @@
from django.contrib.auth.tests.utils import skipIfCustomUser
from django.test import TestCase
from django.test.client import RequestFactory
-from django.test.utils import override_settings
+from django.test import override_settings
@skipIfCustomUser
View
2  django/contrib/auth/tests/test_templates.py
@@ -7,7 +7,7 @@
password_reset_complete, password_change, password_change_done,
)
from django.test import RequestFactory, TestCase
-from django.test.utils import override_settings
+from django.test import override_settings
from django.utils.encoding import force_bytes, force_text
from django.utils.http import urlsafe_base64_encode
View
4 django/contrib/auth/tests/test_views.py
@@ -14,8 +14,8 @@
from django.utils.http import urlquote
from django.utils.six.moves.urllib.parse import urlparse, ParseResult
from django.utils._os import upath
-from django.test import TestCase
-from django.test.utils import override_settings, patch_logger
+from django.test import TestCase, override_settings
+from django.test.utils import patch_logger
from django.middleware.csrf import CsrfViewMiddleware
from django.contrib.sessions.middleware import SessionMiddleware
View
19 django/contrib/comments/__init__.py
@@ -1,5 +1,6 @@
from importlib import import_module
import warnings
+from django.apps import apps
from django.conf import settings
from django.core import urlresolvers
from django.core.exceptions import ImproperlyConfigured
@@ -14,20 +15,12 @@ def get_comment_app():
"""
Get the comment app (i.e. "django.contrib.comments") as defined in the settings
"""
- # Make sure the app's in INSTALLED_APPS
- comments_app = get_comment_app_name()
- if comments_app not in settings.INSTALLED_APPS:
- raise ImproperlyConfigured("The COMMENTS_APP (%r) "\
- "must be in INSTALLED_APPS" % settings.COMMENTS_APP)
-
- # Try to import the package
try:
- package = import_module(comments_app)
- except ImportError as e:
- raise ImproperlyConfigured("The COMMENTS_APP setting refers to "\
- "a non-existing package. (%s)" % e)
-
- return package
+ app_config = apps.get_app_config(get_comment_app_name().rpartition(".")[2])
+ except LookupError:
+ raise ImproperlyConfigured("The COMMENTS_APP (%r) "
+ "must be in INSTALLED_APPS" % settings.COMMENTS_APP)
+ return app_config.module
def get_comment_app_name():
"""
View
8 django/contrib/comments/apps.py
@@ -0,0 +1,8 @@
+from django.apps import AppConfig
+
+from django.utils.translation import ugettext_lazy as _
+
+
+class CommentsConfig(AppConfig):
+ name = 'django.contrib.comments'
+ verbose_name = _("comments")
View
6 django/contrib/comments/views/comments.py
@@ -1,9 +1,9 @@
from django import http
+from django.apps import apps
from django.conf import settings
from django.contrib import comments
from django.contrib.comments import signals
from django.contrib.comments.views.utils import next_redirect, confirmation_view
-from django.core.apps import app_cache
from django.core.exceptions import ObjectDoesNotExist, ValidationError
from django.db import models
from django.shortcuts import render_to_response
@@ -49,12 +49,12 @@ def post_comment(request, next=None, using=None):
if ctype is None or object_pk is None:
return CommentPostBadRequest("Missing content_type or object_pk field.")
try:
- model = app_cache.get_model(*ctype.split(".", 1))
+ model = apps.get_model(*ctype.split(".", 1))
target = model._default_manager.using(using).get(pk=object_pk)
except TypeError:
return CommentPostBadRequest(
"Invalid content_type value: %r" % escape(ctype))
- except AttributeError:
+ except LookupError:
return CommentPostBadRequest(
"The given content-type %r does not resolve to a valid model." % \
escape(ctype))
View
8 django/contrib/contenttypes/apps.py
@@ -0,0 +1,8 @@
+from django.apps import AppConfig
+
+from django.utils.translation import ugettext_lazy as _
+
+
+class ContentTypesConfig(AppConfig):
+ name = 'django.contrib.contenttypes'
+ verbose_name = _("content types")
View
34 django/contrib/contenttypes/management.py
@@ -1,5 +1,4 @@
-from django.contrib.contenttypes.models import ContentType
-from django.core.apps import app_cache, UnavailableApp
+from django.apps import apps
from django.db import DEFAULT_DB_ALIAS, router
from django.db.models import signals
from django.utils.encoding import smart_text
@@ -7,29 +6,32 @@
from django.utils.six.moves import input
-def update_contenttypes(app, created_models, verbosity=