Skip to content

HTTPS clone URL

Subversion checkout URL

You can clone with
or
.
Download ZIP
Browse files

Merge branch 'master' into lookups_3

Conflicts:
	django/db/models/fields/__init__.py
	django/db/models/sql/compiler.py
	django/db/models/sql/query.py
	tests/null_queries/tests.py
  • Loading branch information...
commit 049eebc0703c151127f4f0265beceea7b8b39e72 2 parents ed8fab7 + b80a835
@akaariai akaariai authored
Showing with 3,389 additions and 2,190 deletions.
  1. +2 −5 django/conf/__init__.py
  2. +6 −6 django/contrib/admin/filters.py
  3. +5 −25 django/contrib/admin/helpers.py
  4. +5 −3 django/contrib/admin/options.py
  5. +3 −2 django/contrib/admin/sites.py
  6. +1 −1  django/contrib/admin/templates/admin/app_index.html
  7. +1 −3 django/contrib/admin/templates/admin/index.html
  8. +2 −1  django/contrib/admin/templatetags/admin_list.py
  9. +1 −2  django/contrib/admin/templatetags/admin_modify.py
  10. +5 −4 django/contrib/admin/validation.py
  11. +2 −9 django/contrib/admin/views/main.py
  12. +11 −8 django/contrib/admin/widgets.py
  13. +0 −1  django/contrib/admindocs/models.py
  14. +5 −8 django/contrib/admindocs/templates/admin_doc/template_detail.html
  15. +21 −10 django/contrib/admindocs/templates/admin_doc/view_index.html
  16. +2 −1  django/contrib/admindocs/tests/test_fields.py
  17. +9 −18 django/contrib/admindocs/urls.py
  18. +39 −55 django/contrib/admindocs/views.py
  19. +10 −2 django/contrib/auth/__init__.py
  20. +2 −2 django/contrib/auth/admin.py
  21. +1 −1  django/contrib/auth/decorators.py
  22. +2 −2 django/contrib/auth/forms.py
  23. +1 −1  django/contrib/auth/hashers.py
  24. +5 −4 django/contrib/auth/management/__init__.py
  25. +10 −5 django/contrib/auth/tests/test_basic.py
  26. +1 −1  django/contrib/auth/tests/test_forms.py
  27. +34 −2 django/contrib/auth/tests/test_hashers.py
  28. +14 −9 django/contrib/auth/tests/test_management.py
  29. +5 −3 django/contrib/auth/tests/test_models.py
  30. +36 −10 django/contrib/auth/tests/test_remote_user.py
  31. +13 −0 django/contrib/auth/tests/test_views.py
  32. +1 −1  django/contrib/auth/views.py
  33. +2 −1  django/contrib/comments/views/comments.py
  34. +4 −3 django/contrib/contenttypes/generic.py
  35. +6 −5 django/contrib/contenttypes/management.py
  36. +2 −1  django/contrib/contenttypes/models.py
  37. +8 −7 django/contrib/contenttypes/tests.py
  38. +2 −2 django/contrib/flatpages/forms.py
  39. +2 −2 django/contrib/flatpages/templatetags/flatpages.py
  40. +2 −1  django/contrib/formtools/models.py
  41. +1 −1  django/contrib/formtools/tests/wizard/test_forms.py
  42. +1 −1  django/contrib/formtools/wizard/views.py
  43. +2 −2 django/contrib/gis/db/backends/postgis/operations.py
  44. +0 −2  django/contrib/gis/db/backends/postgis/schema.py
  45. +5 −5 django/contrib/gis/db/backends/spatialite/creation.py
  46. +5 −1 django/contrib/gis/db/backends/spatialite/models.py
  47. +2 −2 django/contrib/gis/db/models/fields.py
  48. +14 −11 django/contrib/gis/db/models/query.py
  49. +1 −1  django/contrib/gis/db/models/sql/where.py
  50. +15 −12 django/contrib/gis/forms/fields.py
  51. +9 −10 django/contrib/gis/forms/widgets.py
  52. +1 −1  django/contrib/gis/gdal/prototypes/generation.py
  53. +1 −1  django/contrib/gis/geoip/tests.py
  54. +5 −3 django/contrib/gis/geos/libgeos.py
  55. +1 −1  django/contrib/gis/geos/polygon.py
  56. +8 −8 django/contrib/gis/geos/tests/test_geos_mutation.py
  57. +1 −1  django/contrib/gis/models.py
  58. +2 −1  django/contrib/gis/sitemaps/kml.py
  59. +8 −2 django/contrib/gis/sitemaps/views.py
  60. +3 −3 django/contrib/gis/tests/geo3d/tests.py
  61. +3 −3 django/contrib/gis/tests/geoapp/test_feeds.py
  62. +5 −4 django/contrib/gis/tests/geoapp/test_regress.py
  63. +7 −4 django/contrib/gis/tests/geoapp/test_sitemaps.py
  64. +45 −0 django/contrib/gis/tests/test_geoforms.py
  65. +3 −3 django/contrib/gis/tests/test_spatialrefsys.py
  66. +1 −1  django/contrib/gis/utils/layermapping.py
  67. 0  django/contrib/humanize/models.py
  68. +1 −1  django/contrib/humanize/tests.py
  69. +0 −1  django/contrib/messages/models.py
  70. +1 −1  django/contrib/messages/storage/cookie.py
  71. +4 −6 django/contrib/messages/tests/base.py
  72. +2 −2 django/contrib/sessions/tests.py
  73. +1 −1  django/contrib/sitemaps/__init__.py
  74. +2 −1  django/contrib/sitemaps/models.py
  75. +2 −2 django/contrib/sitemaps/tests/base.py
  76. +5 −3 django/contrib/sitemaps/tests/test_http.py
  77. +4 −4 django/contrib/sites/tests.py
  78. +1 −8 django/contrib/staticfiles/handlers.py
  79. +0 −6 django/contrib/staticfiles/management/commands/collectstatic.py
  80. 0  django/contrib/staticfiles/models.py
  81. 0  django/contrib/webdesign/models.py
  82. +1 −0  django/core/apps/__init__.py
  83. +47 −0 django/core/apps/base.py
  84. +395 −0 django/core/apps/cache.py
  85. +2 −1  django/core/checks/compatibility/django_1_6_0.py
  86. +64 −38 django/core/exceptions.py
  87. +2 −2 django/core/files/move.py
  88. +9 −4 django/core/files/storage.py
  89. +1 −2  django/core/management/__init__.py
  90. +9 −6 django/core/management/base.py
  91. +4 −2 django/core/management/color.py
  92. +2 −2 django/core/management/commands/compilemessages.py
  93. +19 −17 django/core/management/commands/dumpdata.py
  94. +4 −3 django/core/management/commands/flush.py
  95. +8 −4 django/core/management/commands/loaddata.py
  96. +97 −62 django/core/management/commands/makemessages.py
  97. +116 −24 django/core/management/commands/makemigrations.py
  98. +27 −4 django/core/management/commands/migrate.py
  99. +20 −1 django/core/management/commands/runserver.py
  100. +2 −2 django/core/management/commands/shell.py
  101. +3 −2 django/core/management/commands/sqlsequencereset.py
  102. +2 −1  django/core/management/commands/testserver.py
  103. +21 −16 django/core/management/sql.py
  104. +12 −15 django/core/management/validation.py
  105. +4 −4 django/core/serializers/base.py
  106. +4 −2 django/core/serializers/python.py
  107. +2 −1  django/core/serializers/xml_serializer.py
  108. +1 −2  django/core/urlresolvers.py
  109. +13 −12 django/db/backends/__init__.py
  110. +5 −3 django/db/backends/mysql/base.py
  111. +1 −1  django/db/backends/mysql/compiler.py
  112. +7 −7 django/db/backends/oracle/base.py
  113. +2 −1  django/db/backends/postgresql_psycopg2/base.py
  114. +54 −1 django/db/backends/postgresql_psycopg2/schema.py
  115. +73 −8 django/db/backends/schema.py
  116. +2 −2 django/db/backends/sqlite3/schema.py
  117. +71 −154 django/db/migrations/autodetector.py
  118. +25 −11 django/db/migrations/loader.py
  119. +10 −2 django/db/migrations/operations/fields.py
  120. +64 −50 django/db/migrations/optimizer.py
  121. +119 −0 django/db/migrations/questioner.py
  122. +2 −2 django/db/migrations/recorder.py
  123. +23 −7 django/db/migrations/state.py
  124. +9 −9 django/db/migrations/writer.py
  125. +26 −3 django/db/models/__init__.py
  126. +20 −15 django/db/models/base.py
  127. +2 −2 django/db/models/deletion.py
  128. +15 −6 django/db/models/fields/__init__.py
  129. +5 −0 django/db/models/fields/proxy.py
  130. +91 −31 django/db/models/fields/related.py
  131. +27 −392 django/db/models/loading.py
  132. +19 −8 django/db/models/options.py
  133. +19 −19 django/db/models/query.py
  134. +2 −2 django/db/models/signals.py
  135. +14 −7 django/db/models/sql/compiler.py
  136. +13 −9 django/db/models/sql/query.py
  137. +1 −1  django/db/models/sql/subqueries.py
  138. +2 −2 django/db/models/sql/where.py
  139. +2 −2 django/db/utils.py
  140. +1 −1  django/dispatch/saferef.py
  141. +5 −5 django/forms/fields.py
  142. +48 −4 django/forms/forms.py
  143. +4 −4 django/forms/formsets.py
  144. +28 −30 django/forms/models.py
  145. +62 −22 django/forms/utils.py
  146. +1 −1  django/forms/widgets.py
  147. +3 −4 django/http/multipartparser.py
  148. +1 −1  django/http/request.py
  149. +1 −1  django/middleware/common.py
  150. +0 −10 django/middleware/locale.py
  151. +2 −4 django/template/base.py
  152. +4 −4 django/template/context.py
  153. +7 −8 django/template/defaultfilters.py
  154. +2 −2 django/template/defaulttags.py
  155. +2 −2 django/template/loader_tags.py
  156. +2 −1  django/test/__init__.py
  157. +1 −1  django/test/runner.py
  158. +4 −1 django/test/signals.py
  159. +44 −49 django/test/simple.py
  160. +28 −24 django/test/testcases.py
  161. +2 −2 django/utils/_os.py
  162. +1 −1  django/utils/archive.py
  163. +0 −100 django/utils/autoreload.py
  164. +3 −3 django/utils/feedgenerator.py
  165. +3 −5 django/utils/html.py
  166. +1 −2  django/utils/importlib.py
  167. +7 −5 django/utils/text.py
  168. +18 −13 django/utils/translation/trans_real.py
  169. +4 −4 django/views/debug.py
  170. +3 −3 django/views/generic/base.py
  171. +2 −2 django/views/generic/detail.py
  172. +1 −1  django/views/generic/list.py
  173. +3 −3 docs/_ext/djangodocs.py
  174. +4 −0 docs/_theme/djangodocs/layout.html
  175. +4 −4 docs/faq/general.txt
  176. +2 −2 docs/howto/custom-model-fields.txt
  177. +1 −1  docs/howto/deployment/index.txt
  178. +1 −1  docs/howto/deployment/wsgi/gunicorn.txt
  179. +3 −3 docs/howto/deployment/wsgi/uwsgi.txt
  180. +1 −1  docs/howto/jython.txt
  181. +3 −2 docs/index.txt
  182. +3 −3 docs/internals/committers.txt
  183. +2 −2 docs/internals/contributing/localizing.txt
  184. +1 −1  docs/internals/contributing/writing-code/coding-style.txt
  185. +5 −3 docs/internals/contributing/writing-code/unit-tests.txt
  186. +28 −12 docs/internals/contributing/writing-documentation.txt
  187. +10 −0 docs/internals/deprecation.txt
  188. +5 −5 docs/internals/mailing-lists.txt
  189. +1 −1  docs/internals/security.txt
  190. +2 −2 docs/intro/index.txt
  191. +12 −5 docs/intro/reusable-apps.txt
  192. +2 −1  docs/intro/tutorial04.txt
  193. +1 −1  docs/intro/tutorial05.txt
  194. +2 −1  docs/man/django-admin.1
  195. +2 −2 docs/ref/clickjacking.txt
  196. +0 −2  docs/ref/contrib/admin/admindocs.txt
  197. +1 −1  docs/ref/contrib/gis/admin.txt
  198. +1 −1  docs/ref/contrib/gis/feeds.txt
  199. +1 −1  docs/ref/contrib/gis/forms-api.txt
  200. +3 −3 docs/ref/contrib/gis/install/index.txt
  201. +1 −1  docs/ref/contrib/gis/install/postgis.txt
  202. +54 −0 docs/ref/contrib/gis/install/spatialite.txt
  203. +1 −1  docs/ref/contrib/gis/measure.txt
  204. +1 −1  docs/ref/contrib/gis/sitemaps.txt
  205. +1 −1  docs/ref/contrib/gis/tutorial.txt
  206. +21 −1 docs/ref/contrib/messages.txt
  207. +1 −1  docs/ref/contrib/sitemaps.txt
  208. +1 −1  docs/ref/contrib/sites.txt
  209. +11 −7 docs/ref/contrib/staticfiles.txt
  210. +8 −9 docs/ref/databases.txt
  211. +15 −2 docs/ref/django-admin.txt
  212. +12 −1 docs/ref/files/storage.txt
  213. +40 −0 docs/ref/forms/api.txt
  214. +16 −71 docs/ref/forms/validation.txt
  215. +6 −7 docs/ref/models/fields.txt
  216. +6 −0 docs/ref/models/querysets.txt
  217. +1 −1  docs/ref/request-response.txt
  218. +17 −24 docs/ref/settings.txt
  219. +5 −3 docs/ref/templates/api.txt
  220. +1 −1  docs/releases/1.2.txt
  221. +1 −2  docs/releases/1.3-beta-1.txt
  222. +1 −1  docs/releases/1.4.6.txt
  223. +1 −1  docs/releases/1.4.txt
  224. +1 −1  docs/releases/1.5.2.txt
  225. +28 −8 docs/releases/1.6.1.txt
  226. +12 −0 docs/releases/1.6.2.txt
  227. +55 −11 docs/releases/1.7.txt
  228. +2 −1  docs/releases/index.txt
  229. +4 −4 docs/topics/cache.txt
  230. +1 −1  docs/topics/db/aggregation.txt
  231. +1 −1  docs/topics/db/managers.txt
  232. +13 −11 docs/topics/email.txt
  233. +25 −1 docs/topics/forms/modelforms.txt
  234. +6 −0 docs/topics/http/file-uploads.txt
  235. +3 −3 docs/topics/http/sessions.txt
  236. +5 −0 docs/topics/http/shortcuts.txt
  237. +3 −2 docs/topics/i18n/formatting.txt
  238. +15 −19 docs/topics/i18n/translation.txt
  239. +4 −14 docs/topics/install.txt
  240. +1 −1  docs/topics/logging.txt
  241. +6 −6 docs/topics/migrations.txt
  242. +1 −1  docs/topics/performance.txt
  243. +3 −3 docs/topics/python3.txt
  244. +46 −4 docs/topics/security.txt
  245. +1 −1  docs/topics/templates.txt
  246. +15 −22 docs/topics/testing/advanced.txt
  247. +2 −2 docs/topics/testing/overview.txt
  248. +2 −2 extras/csrf_migration_helper.py
  249. +22 −13 scripts/manage_translations.py
  250. +2 −2 setup.cfg
  251. 0  tests/admin_docs/models.py
  252. +31 −0 tests/admin_docs/tests.py
  253. +6 −1 tests/admin_docs/urls.py
  254. +32 −14 tests/admin_filters/tests.py
  255. +0 −1  tests/admin_scripts/custom_templates/app_template/models.py
  256. +8 −8 tests/admin_scripts/tests.py
  257. +14 −5 tests/admin_views/tests.py
  258. +2 −2 tests/app_cache/models.py
  259. +17 −10 tests/app_cache/tests.py
  260. 0  tests/app_loading/models.py
  261. +27 −31 tests/app_loading/tests.py
  262. 0  tests/bash_completion/models.py
  263. 0  tests/builtin_server/models.py
  264. +8 −7 tests/commands_sql/tests.py
  265. 0  tests/conditional_processing/models.py
  266. +2 −2 tests/contenttypes_tests/tests.py
  267. +0 −1  tests/context_processors/models.py
  268. 0  tests/createsuperuser/models.py
  269. +2 −1  tests/createsuperuser/tests.py
  270. +0 −1  tests/csrf_tests/models.py
  271. +2 −1  tests/custom_columns/tests.py
  272. +9 −1 tests/custom_pk/tests.py
  273. 0  tests/db_typecasts/models.py
  274. +0 −1  tests/decorators/models.py
  275. 0  tests/defaultfilters/models.py
  276. +34 −41 tests/defaultfilters/tests.py
  277. +1 −1  tests/defer/tests.py
  278. +7 −7 tests/defer_regress/tests.py
  279. 0  tests/deprecation/models.py
  280. 0  tests/dispatch/models.py
  281. +0 −6 tests/empty/no_models/tests.py
  282. +0 −21 tests/empty/tests.py
  283. +17 −12 tests/extra_regress/tests.py
  284. 0  tests/field_deconstruction/models.py
  285. 0  tests/files/models.py
  286. +9 −0 tests/fixtures_regress/fixtures_1/inner/absolute.json
  287. +10 −7 tests/fixtures_regress/tests.py
  288. +1 −1  tests/foreign_object/models.py
  289. +7 −1 tests/foreign_object/tests.py
  290. +107 −6 tests/forms_tests/tests/test_forms.py
  291. +12 −0 tests/generic_views/models.py
  292. +18 −0 tests/generic_views/test_base.py
  293. +8 −1 tests/generic_views/test_detail.py
  294. +2 −1  tests/generic_views/urls.py
  295. +5 −0 tests/generic_views/views.py
  296. +14 −7 tests/get_object_or_404/tests.py
  297. +6 −3 tests/get_or_create/tests.py
  298. 0  tests/handlers/models.py
  299. 0  tests/http_utils/models.py
  300. 0  tests/httpwrappers/models.py
Sorry, we could not display the entire diff because too many files (393) changed.
View
7 django/conf/__init__.py
@@ -34,11 +34,8 @@ def _setup(self, name=None):
is used the first time we need any settings at all, if the user has not
previously configured the settings manually.
"""
- try:
- settings_module = os.environ[ENVIRONMENT_VARIABLE]
- if not settings_module: # If it's set but is an empty string.
- raise KeyError
- except KeyError:
+ settings_module = os.environ.get(ENVIRONMENT_VARIABLE)
+ if not settings_module:
desc = ("setting %s" % name) if name else "settings"
raise ImproperlyConfigured(
"Requested %s, but settings are not configured. "
View
12 django/contrib/admin/filters.py
@@ -179,9 +179,9 @@ def __init__(self, field, request, params, model, model_admin, field_path):
self.title = self.lookup_title
def has_output(self):
- if (isinstance(self.field, models.related.RelatedObject)
- and self.field.field.null or hasattr(self.field, 'rel')
- and self.field.null):
+ if (isinstance(self.field, models.related.RelatedObject) and
+ self.field.field.null or hasattr(self.field, 'rel') and
+ self.field.null):
extra = 1
else:
extra = 0
@@ -206,9 +206,9 @@ def choices(self, cl):
}, [self.lookup_kwarg_isnull]),
'display': val,
}
- if (isinstance(self.field, models.related.RelatedObject)
- and self.field.field.null or hasattr(self.field, 'rel')
- and self.field.null):
+ if (isinstance(self.field, models.related.RelatedObject) and
+ self.field.field.null or hasattr(self.field, 'rel') and
+ self.field.null):
yield {
'selected': bool(self.lookup_val_isnull),
'query_string': cl.get_query_string({
View
30 django/contrib/admin/helpers.py
@@ -30,7 +30,7 @@ class ActionForm(forms.Form):
class AdminForm(object):
def __init__(self, form, fieldsets, prepopulated_fields, readonly_fields=None, model_admin=None):
- self.form, self.fieldsets = form, normalize_fieldsets(fieldsets)
+ self.form, self.fieldsets = form, fieldsets
self.prepopulated_fields = [{
'field': form[field_name],
'dependencies': [form[f] for f in dependencies]
@@ -42,7 +42,8 @@ def __init__(self, form, fieldsets, prepopulated_fields, readonly_fields=None, m
def __iter__(self):
for name, options in self.fieldsets:
- yield Fieldset(self.form, name,
+ yield Fieldset(
+ self.form, name,
readonly_fields=self.readonly_fields,
model_admin=self.model_admin,
**options
@@ -328,32 +329,11 @@ class AdminErrorList(forms.utils.ErrorList):
Stores all errors for the form/formsets in an add/change stage view.
"""
def __init__(self, form, inline_formsets):
+ super(AdminErrorList, self).__init__()
+
if form.is_bound:
self.extend(list(six.itervalues(form.errors)))
for inline_formset in inline_formsets:
self.extend(inline_formset.non_form_errors())
for errors_in_inline_form in inline_formset.errors:
self.extend(list(six.itervalues(errors_in_inline_form)))
-
-
-def normalize_fieldsets(fieldsets):
- """
- Make sure the keys in fieldset dictionaries are strings. Returns the
- normalized data.
- """
- result = []
- for name, options in fieldsets:
- result.append((name, normalize_dictionary(options)))
- return result
-
-
-def normalize_dictionary(data_dict):
- """
- Converts all the keys in "data_dict" to strings. The keys must be
- convertible using str().
- """
- for key, value in data_dict.items():
- if not isinstance(key, str):
- del data_dict[key]
- data_dict[str(key)] = value
- return data_dict
View
8 django/contrib/admin/options.py
@@ -613,7 +613,7 @@ def get_changelist_form(self, request, **kwargs):
}
defaults.update(kwargs)
if (defaults.get('fields') is None
- and not modelform_defines_fields(defaults.get('form'))):
+ and not modelform_defines_fields(defaults.get('form'))):
defaults['fields'] = forms.ALL_FIELDS
return modelform_factory(self.model, **defaults)
@@ -1520,7 +1520,8 @@ def changelist_view(self, request, extra_context=None):
selection_note_all = ungettext('%(total_count)s selected',
'All %(total_count)s selected', cl.result_count)
- context = dict(self.admin_site.each_context(),
+ context = dict(
+ self.admin_site.each_context(),
module_name=force_text(opts.verbose_name_plural),
selection_note=_('0 of %(cnt)s selected') % {'cnt': len(cl.result_list)},
selection_note_all=selection_note_all % {'total_count': cl.result_count},
@@ -1587,7 +1588,8 @@ def delete_view(self, request, object_id, extra_context=None):
else:
title = _("Are you sure?")
- context = dict(self.admin_site.each_context(),
+ context = dict(
+ self.admin_site.each_context(),
title=title,
object_name=object_name,
object=obj,
View
5 django/contrib/admin/sites.py
@@ -169,7 +169,7 @@ def check_dependencies(self):
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):
+ 'django.core.context_processors.auth' 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.")
@@ -398,7 +398,8 @@ def index(self, request, extra_context=None):
for app in app_list:
app['models'].sort(key=lambda x: x['name'])
- context = dict(self.each_context(),
+ context = dict(
+ self.each_context(),
title=self.index_title,
app_list=app_list,
)
View
2  django/contrib/admin/templates/admin/app_index.html
@@ -9,7 +9,7 @@
<a href="{% url 'admin:index' %}">{% trans 'Home' %}</a>
&rsaquo;
{% for app in app_list %}
-{% blocktrans with app.name as name %}{{ name }}{% endblocktrans %}
+{{ app.name }}
{% endfor %}
</div>
{% endblock %}
View
4 django/contrib/admin/templates/admin/index.html
@@ -17,9 +17,7 @@
<div class="app-{{ app.app_label }} module">
<table>
<caption>
- <a href="{{ app.app_url }}" class="section" title="{% blocktrans with name=app.name %}Models in the {{ name }} application{% endblocktrans %}">
- {% blocktrans with name=app.name %}{{ name }}{% endblocktrans %}
- </a>
+ <a href="{{ app.app_url }}" class="section" title="{% blocktrans with name=app.name %}Models in the {{ name }} application{% endblocktrans %}">{{ app.name }}</a>
</caption>
{% for model in app.models %}
<tr class="model-{{ model.object_name|lower }}">
View
3  django/contrib/admin/templatetags/admin_list.py
@@ -95,7 +95,8 @@ def result_headers(cl):
"""
ordering_field_columns = cl.get_ordering_field_columns()
for i, field_name in enumerate(cl.list_display):
- text, attr = label_for_field(field_name, cl.model,
+ text, attr = label_for_field(
+ field_name, cl.model,
model_admin=cl.model_admin,
return_attr=True
)
View
3  django/contrib/admin/templatetags/admin_modify.py
@@ -32,8 +32,7 @@ def submit_row(context):
save_as = context['save_as']
ctx = {
'opts': opts,
- 'show_delete_link': (not is_popup and context['has_delete_permission']
- and change and context.get('show_delete', True)),
+ 'show_delete_link': not is_popup and context['has_delete_permission'] and change and context.get('show_delete', True),
'show_save_as_new': not is_popup and change and save_as,
'show_save_and_add_another': context['has_add_permission'] and not is_popup and (not save_as or context['add']),
'show_save_and_continue': not is_popup and context['has_change_permission'],
View
9 django/contrib/admin/validation.py
@@ -1,3 +1,4 @@
+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,9 +16,9 @@
class BaseValidator(object):
def __init__(self):
- # Before we can introspect models, they need to be fully loaded so that
- # inter-relations are set up correctly. We force that here.
- models.get_apps()
+ # 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):
@@ -155,7 +156,7 @@ def validate_prepopulated_fields(self, cls, model):
for field, val in cls.prepopulated_fields.items():
f = get_field(cls, model, 'prepopulated_fields', field)
if isinstance(f, (models.DateTimeField, models.ForeignKey,
- models.ManyToManyField)):
+ models.ManyToManyField)):
raise ImproperlyConfigured("'%s.prepopulated_fields['%s']' "
"is either a DateTimeField, ForeignKey or "
"ManyToManyField. This isn't allowed."
View
11 django/contrib/admin/views/main.py
@@ -9,7 +9,7 @@
from django.db.models.fields import FieldDoesNotExist
from django.utils import six
from django.utils.deprecation import RenameMethodsBase
-from django.utils.encoding import force_str, force_text
+from django.utils.encoding import force_text
from django.utils.translation import ugettext, ugettext_lazy
from django.utils.http import urlencode
@@ -142,14 +142,7 @@ def get_filters(self, request):
lookup_params = self.get_filters_params()
use_distinct = False
- # Normalize the types of keys
for key, value in lookup_params.items():
- if not isinstance(key, str):
- # 'key' will be used as a keyword argument later, so Python
- # requires it to be a string.
- del lookup_params[key]
- lookup_params[force_str(key)] = value
-
if not self.model_admin.lookup_allowed(key, value):
raise DisallowedModelAdminLookup("Filtering by %s not allowed" % key)
@@ -224,7 +217,7 @@ def get_results(self, request):
# Perform a slight optimization:
# full_result_count is equal to paginator.count if no filters
# were applied
- if self.get_filters_params():
+ if self.get_filters_params() or self.params.get(SEARCH_VAR):
full_result_count = self.root_queryset.count()
else:
full_result_count = result_count
View
19 django/contrib/admin/widgets.py
@@ -153,10 +153,13 @@ def render(self, name, value, attrs=None):
extra = []
if rel_to in self.admin_site._registry:
# The related object is registered with the same AdminSite
- related_url = reverse('admin:%s_%s_changelist' %
- (rel_to._meta.app_label,
- rel_to._meta.model_name),
- current_app=self.admin_site.name)
+ related_url = reverse(
+ 'admin:%s_%s_changelist' % (
+ rel_to._meta.app_label,
+ rel_to._meta.model_name,
+ ),
+ current_app=self.admin_site.name,
+ )
params = self.url_parameters()
if params:
@@ -167,10 +170,10 @@ def render(self, name, value, attrs=None):
attrs['class'] = 'vForeignKeyRawIdAdminField' # The JavaScript code looks for this hook.
# TODO: "lookup_id_" is hard-coded here. This should instead use
# the correct API to determine the ID dynamically.
- extra.append('<a href="%s%s" class="related-lookup" id="lookup_id_%s" onclick="return showRelatedObjectLookupPopup(this);"> '
- % (related_url, url, name))
- extra.append('<img src="%s" width="16" height="16" alt="%s" /></a>'
- % (static('admin/img/selector-search.gif'), _('Lookup')))
+ extra.append('<a href="%s%s" class="related-lookup" id="lookup_id_%s" onclick="return showRelatedObjectLookupPopup(this);"> ' %
+ (related_url, url, name))
+ extra.append('<img src="%s" width="16" height="16" alt="%s" /></a>' %
+ (static('admin/img/selector-search.gif'), _('Lookup')))
output = [super(ForeignKeyRawIdWidget, self).render(name, value, attrs)] + extra
if value:
output.append(self.label_for_value(value))
View
1  django/contrib/admindocs/models.py
@@ -1 +0,0 @@
-# Empty models.py to allow for specifying admindocs as a test label.
View
13 django/contrib/admindocs/templates/admin_doc/template_detail.html
@@ -15,15 +15,12 @@
{% block content %}
<h1>{% blocktrans %}Template: "{{ name }}"{% endblocktrans %}</h1>
-{% regroup templates|dictsort:"site_id" by site as templates_by_site %}
-{% for group in templates_by_site %}
- <h2>{% blocktrans with group.grouper as grouper %}Search path for template "{{ name }}" on {{ grouper }}:{% endblocktrans %}</h2>
- <ol>
- {% for template in group.list|dictsort:"order" %}
- <li><code>{{ template.file }}</code>{% if not template.exists %} <em>{% trans '(does not exist)' %}</em>{% endif %}</li>
- {% endfor %}
- </ol>
+<h2>{% blocktrans %}Search path for template "{{ name }}":{% endblocktrans %}</h2>
+<ol>
+{% for template in templates|dictsort:"order" %}
+ <li><code>{{ template.file }}</code>{% if not template.exists %} <em>{% trans '(does not exist)' %}</em>{% endif %}</li>
{% endfor %}
+</ol>
<p class="small"><a href="{% url 'django-admindocs-docroot' %}">&lsaquo; {% trans 'Back to Documentation' %}</a></p>
{% endblock %}
View
31 django/contrib/admindocs/templates/admin_doc/view_index.html
@@ -15,29 +15,40 @@
<h1>{% trans 'View documentation' %}</h1>
-{% regroup views|dictsort:"site_id" by site as views_by_site %}
+{% regroup views|dictsort:'namespace' by namespace as views_by_ns %}
<div id="content-related" class="sidebar">
<div class="module">
-<h2>{% trans 'Jump to site' %}</h2>
+<h2>{% trans 'Jump to namespace' %}</h2>
<ul>
- {% for site_views in views_by_site %}
- <li><a href="#site{{ site_views.grouper.id }}">{{ site_views.grouper.name }}</a></li>
- {% endfor %}
+{% for ns_views in views_by_ns %}
+ <li><a href="#ns|{{ ns_views.grouper }}">
+ {% if ns_views.grouper %}{{ ns_views.grouper }}
+ {% else %}{% trans "Empty namespace" %}{% endif %}
+ </a></li>
+{% endfor %}
</ul>
</div>
</div>
<div id="content-main">
-{% for site_views in views_by_site %}
+{% for ns_views in views_by_ns %}
<div class="module">
-<h2 id="site{{ site_views.grouper.id }}">{% blocktrans with site_views.grouper.name as name %}Views by URL on {{ name }}{% endblocktrans %}</h2>
-
-{% for view in site_views.list|dictsort:"url" %}
+<h2 id="ns|{{ ns_views.grouper }}">
+{% if ns_views.grouper %}
+ {% blocktrans with ns_views.grouper as name %}Views by namespace {{ name }}{% endblocktrans %}
+{% else %}
+ {% blocktrans %}Views by empty namespace{% endblocktrans %}
+{% endif %}
+</h2>
+
+{% for view in ns_views.list|dictsort:"url" %}
{% ifchanged %}
<h3><a href="{% url 'django-admindocs-views-detail' view=view.full_name %}">{{ view.url }}</a></h3>
-<p class="small quiet">{% blocktrans with view.full_name as name %}View function: {{ name }}{% endblocktrans %}</p>
+<p class="small quiet">{% blocktrans with view.full_name as full_name and view.url_name as url_name %}
+ View function: <code>{{ full_name }}</code>. Name: <code>{{ url_name }}</code>.
+{% endblocktrans %}</p>
<p>{{ view.title }}</p>
<hr />
{% endifchanged %}
View
3  django/contrib/admindocs/tests/test_fields.py
@@ -21,7 +21,8 @@ def setUp(self):
pass
def test_field_name(self):
- self.assertRaises(AttributeError,
+ self.assertRaises(
+ AttributeError,
views.get_readable_field_data_type, "NotAField"
)
View
27 django/contrib/admindocs/urls.py
@@ -4,38 +4,29 @@
urlpatterns = patterns('',
url('^$',
views.BaseAdminDocsView.as_view(template_name='admin_doc/index.html'),
- name='django-admindocs-docroot'
- ),
+ name='django-admindocs-docroot'),
url('^bookmarklets/$',
views.BookmarkletsView.as_view(),
- name='django-admindocs-bookmarklets'
- ),
+ name='django-admindocs-bookmarklets'),
url('^tags/$',
views.TemplateTagIndexView.as_view(),
- name='django-admindocs-tags'
- ),
+ name='django-admindocs-tags'),
url('^filters/$',
views.TemplateFilterIndexView.as_view(),
- name='django-admindocs-filters'
- ),
+ name='django-admindocs-filters'),
url('^views/$',
views.ViewIndexView.as_view(),
- name='django-admindocs-views-index'
- ),
+ name='django-admindocs-views-index'),
url('^views/(?P<view>[^/]+)/$',
views.ViewDetailView.as_view(),
- name='django-admindocs-views-detail'
- ),
+ name='django-admindocs-views-detail'),
url('^models/$',
views.ModelIndexView.as_view(),
- name='django-admindocs-models-index'
- ),
+ name='django-admindocs-models-index'),
url('^models/(?P<app_label>[^\.]+)\.(?P<model_name>[^/]+)/$',
views.ModelDetailView.as_view(),
- name='django-admindocs-models-detail'
- ),
+ name='django-admindocs-models-detail'),
url('^templates/(?P<template>.*)/$',
views.TemplateDetailView.as_view(),
- name='django-admindocs-templates'
- ),
+ name='django-admindocs-templates'),
)
View
94 django/contrib/admindocs/views.py
@@ -2,17 +2,18 @@
import inspect
import os
import re
+import warnings
from django import template
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 ImproperlyConfigured, ViewDoesNotExist
+from django.core.exceptions import ViewDoesNotExist
from django.http import Http404
from django.core import urlresolvers
from django.contrib.admindocs import utils
-from django.contrib.sites.models import Site
from django.utils.decorators import method_decorator
from django.utils._os import upath
from django.utils import six
@@ -22,10 +23,10 @@
# Exclude methods starting with these strings from documentation
MODEL_METHODS_EXCLUDE = ('_', 'add_', 'delete', 'save', 'set_')
-
-class GenericSite(object):
- domain = 'example.com'
- name = 'my site'
+if getattr(settings, 'ADMIN_FOR', None):
+ warnings.warn('The ADMIN_FOR setting has been removed, you can remove '
+ 'this setting from your configuration.', DeprecationWarning,
+ stacklevel=2)
class BaseAdminDocsView(TemplateView):
@@ -128,26 +129,17 @@ class ViewIndexView(BaseAdminDocsView):
template_name = 'admin_doc/view_index.html'
def get_context_data(self, **kwargs):
- if settings.ADMIN_FOR:
- settings_modules = [import_module(m) for m in settings.ADMIN_FOR]
- else:
- settings_modules = [settings]
-
views = []
- for settings_mod in settings_modules:
- urlconf = import_module(settings_mod.ROOT_URLCONF)
- view_functions = extract_views_from_urlpatterns(urlconf.urlpatterns)
- if Site._meta.installed:
- site_obj = Site.objects.get(pk=settings_mod.SITE_ID)
- else:
- site_obj = GenericSite()
- for (func, regex) in view_functions:
- views.append({
- 'full_name': '%s.%s' % (func.__module__, getattr(func, '__name__', func.__class__.__name__)),
- 'site_id': settings_mod.SITE_ID,
- 'site': site_obj,
- 'url': simplify_regex(regex),
- })
+ urlconf = import_module(settings.ROOT_URLCONF)
+ view_functions = extract_views_from_urlpatterns(urlconf.urlpatterns)
+ for (func, regex, namespace, name) in view_functions:
+ views.append({
+ 'full_name': '%s.%s' % (func.__module__, getattr(func, '__name__', func.__class__.__name__)),
+ 'url': simplify_regex(regex),
+ 'url_name': ':'.join((namespace or []) + (name and [name] or [])),
+ 'namespace': ':'.join((namespace or [])),
+ 'name': name,
+ })
kwargs.update({'views': views})
return super(ViewIndexView, self).get_context_data(**kwargs)
@@ -182,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 models.get_models()]
+ m_list = [m._meta for m in app_cache.get_models()]
kwargs.update({'models': m_list})
return super(ModelIndexView, self).get_context_data(**kwargs)
@@ -193,17 +185,12 @@ class ModelDetailView(BaseAdminDocsView):
def get_context_data(self, **kwargs):
# Get the model class.
try:
- app_mod = models.get_app(self.kwargs['app_label'])
- except ImproperlyConfigured:
- raise Http404(_("App %r not found") % self.kwargs['app_label'])
- model = None
- for m in models.get_models(app_mod):
- if m._meta.model_name == self.kwargs['model_name']:
- model = m
- break
+ app_cache.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:
- raise Http404(_("Model %(model_name)r not found in app %(app_label)r") % {
- 'model_name': self.kwargs['model_name'], 'app_label': self.kwargs['app_label']})
+ raise Http404(_("Model %(model_name)r not found in app %(app_label)r") % self.kwargs)
opts = model._meta
@@ -296,22 +283,14 @@ class TemplateDetailView(BaseAdminDocsView):
def get_context_data(self, **kwargs):
template = self.kwargs['template']
templates = []
- for site_settings_module in settings.ADMIN_FOR:
- settings_mod = import_module(site_settings_module)
- if Site._meta.installed:
- site_obj = Site.objects.get(pk=settings_mod.SITE_ID)
- else:
- site_obj = GenericSite()
- for dir in settings_mod.TEMPLATE_DIRS:
- template_file = os.path.join(dir, template)
- templates.append({
- 'file': template_file,
- 'exists': os.path.exists(template_file),
- 'contents': lambda: open(template_file).read() if os.path.exists(template_file) else '',
- 'site_id': settings_mod.SITE_ID,
- 'site': site_obj,
- 'order': list(settings_mod.TEMPLATE_DIRS).index(dir),
- })
+ for dir in settings.TEMPLATE_DIRS:
+ template_file = os.path.join(dir, template)
+ templates.append({
+ 'file': template_file,
+ 'exists': os.path.exists(template_file),
+ 'contents': lambda: open(template_file).read() if os.path.exists(template_file) else '',
+ 'order': list(settings.TEMPLATE_DIRS).index(dir),
+ })
kwargs.update({
'name': template,
'templates': templates,
@@ -360,7 +339,7 @@ def get_readable_field_data_type(field):
return field.description % field.__dict__
-def extract_views_from_urlpatterns(urlpatterns, base=''):
+def extract_views_from_urlpatterns(urlpatterns, base='', namespace=None):
"""
Return a list of views from a list of urlpatterns.
@@ -373,10 +352,15 @@ def extract_views_from_urlpatterns(urlpatterns, base=''):
patterns = p.url_patterns
except ImportError:
continue
- views.extend(extract_views_from_urlpatterns(patterns, base + p.regex.pattern))
+ views.extend(extract_views_from_urlpatterns(
+ patterns,
+ base + p.regex.pattern,
+ (namespace or []) + (p.namespace and [p.namespace] or [])
+ ))
elif hasattr(p, 'callback'):
try:
- views.append((p.callback, base + p.regex.pattern))
+ views.append((p.callback, base + p.regex.pattern,
+ namespace, p.name))
except ViewDoesNotExist:
continue
else:
View
12 django/contrib/auth/__init__.py
@@ -105,7 +105,15 @@ def logout(request):
user = None
user_logged_out.send(sender=user.__class__, request=request, user=user)
+ # remember language choice saved to session
+ # for backwards compatibility django_language is also checked (remove in 1.8)
+ language = request.session.get('_language', request.session.get('django_language'))
+
request.session.flush()
+
+ if language is not None:
+ request.session['_language'] = language
+
if hasattr(request, 'user'):
from django.contrib.auth.models import AnonymousUser
request.user = AnonymousUser()
@@ -115,13 +123,13 @@ def get_user_model():
"""
Returns the User model that is active in this project.
"""
- from django.db.models import get_model
+ from django.core.apps import app_cache
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 = get_model(app_label, model_name)
+ user_model = app_cache.get_model(app_label, model_name)
if user_model is None:
raise ImproperlyConfigured("AUTH_USER_MODEL refers to model '%s' that has not been installed" % settings.AUTH_USER_MODEL)
return user_model
View
4 django/contrib/auth/admin.py
@@ -48,8 +48,8 @@ class UserAdmin(admin.ModelAdmin):
add_fieldsets = (
(None, {
'classes': ('wide',),
- 'fields': ('username', 'password1', 'password2')}
- ),
+ 'fields': ('username', 'password1', 'password2'),
+ }),
)
form = UserChangeForm
add_form = UserCreationForm
View
2  django/contrib/auth/decorators.py
@@ -29,7 +29,7 @@ def _wrapped_view(request, *args, **kwargs):
login_scheme, login_netloc = urlparse(resolved_login_url)[:2]
current_scheme, current_netloc = urlparse(path)[:2]
if ((not login_scheme or login_scheme == current_scheme) and
- (not login_netloc or login_netloc == current_netloc)):
+ (not login_netloc or login_netloc == current_netloc)):
path = request.get_full_path()
from django.contrib.auth.views import redirect_to_login
return redirect_to_login(
View
4 django/contrib/auth/forms.py
@@ -75,7 +75,7 @@ class UserCreationForm(forms.ModelForm):
username = forms.RegexField(label=_("Username"), max_length=30,
regex=r'^[\w.@+-]+$',
help_text=_("Required. 30 characters or fewer. Letters, digits and "
- "@/./+/-/_ only."),
+ "@/./+/-/_ only."),
error_messages={
'invalid': _("This value may contain only letters, numbers and "
"@/./+/-/_ characters.")})
@@ -124,7 +124,7 @@ class UserChangeForm(forms.ModelForm):
username = forms.RegexField(
label=_("Username"), max_length=30, regex=r"^[\w.@+-]+$",
help_text=_("Required. 30 characters or fewer. Letters, digits and "
- "@/./+/-/_ only."),
+ "@/./+/-/_ only."),
error_messages={
'invalid': _("This value may contain only letters, numbers and "
"@/./+/-/_ characters.")})
View
2  django/contrib/auth/hashers.py
@@ -57,7 +57,7 @@ def check_password(password, encoded, setter=None, preferred='default'):
must_update = hasher.algorithm != preferred.algorithm
if not must_update:
- must_update = hasher.must_update(encoded)
+ must_update = preferred.must_update(encoded)
is_correct = hasher.verify(password, encoded)
if setter and is_correct and must_update:
setter(password)
View
9 django/contrib/auth/management/__init__.py
@@ -8,10 +8,11 @@
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
-from django.db.models import get_model, get_models, signals, UnavailableApp
+from django.db.models import signals
from django.utils.encoding import DEFAULT_LOCALE_ENCODING
from django.utils import six
from django.utils.six.moves import input
@@ -61,7 +62,7 @@ def _check_permission_clashing(custom, builtin, ctype):
def create_permissions(app, created_models, verbosity, db=DEFAULT_DB_ALIAS, **kwargs):
try:
- get_model('auth', 'Permission')
+ app_cache.get_model('auth', 'Permission')
except UnavailableApp:
return
@@ -70,7 +71,7 @@ def create_permissions(app, created_models, verbosity, db=DEFAULT_DB_ALIAS, **kw
from django.contrib.contenttypes.models import ContentType
- app_models = get_models(app)
+ app_models = app_cache.get_models(app)
# This will hold the permissions we're looking for as
# (content_type, (codename, name))
@@ -119,7 +120,7 @@ def create_permissions(app, created_models, verbosity, db=DEFAULT_DB_ALIAS, **kw
def create_superuser(app, created_models, verbosity, db, **kwargs):
try:
- get_model('auth', 'Permission')
+ app_cache.get_model('auth', 'Permission')
UserModel = get_user_model()
except UnavailableApp:
return
View
15 django/contrib/auth/tests/test_basic.py
@@ -130,7 +130,8 @@ def test_createsuperuser_management_command(self):
"Check the operation of the createsuperuser management command"
# We can use the management command to create a superuser
new_io = StringIO()
- call_command("createsuperuser",
+ call_command(
+ "createsuperuser",
interactive=False,
username="joe",
email="joe@somewhere.org",
@@ -146,7 +147,8 @@ def test_createsuperuser_management_command(self):
# We can supress output on the management command
new_io = StringIO()
- call_command("createsuperuser",
+ call_command(
+ "createsuperuser",
interactive=False,
username="joe2",
email="joe2@somewhere.org",
@@ -159,7 +161,8 @@ def test_createsuperuser_management_command(self):
self.assertEqual(u.email, 'joe2@somewhere.org')
self.assertFalse(u.has_usable_password())
- call_command("createsuperuser",
+ call_command(
+ "createsuperuser",
interactive=False,
username="joe+admin@somewhere.org",
email="joe@somewhere.org",
@@ -182,7 +185,8 @@ def test_createsuperuser_nolocale(self):
locale.getdefaultlocale = lambda: (None, None)
# Call the command in this new environment
- call_command("createsuperuser",
+ call_command(
+ "createsuperuser",
interactive=True,
username="nolocale@somewhere.org",
email="nolocale@somewhere.org",
@@ -212,7 +216,8 @@ def test_createsuperuser_non_ascii_verbose_name(self):
username_field.verbose_name = ulazy('uživatel')
new_io = StringIO()
try:
- call_command("createsuperuser",
+ call_command(
+ "createsuperuser",
interactive=True,
stdout=new_io
)
View
2  django/contrib/auth/tests/test_forms.py
@@ -133,7 +133,7 @@ def test_inactive_user_i18n(self):
[force_text(form.error_messages['inactive'])])
def test_custom_login_allowed_policy(self):
- # The user is inactive, but our custom form policy allows him to log in.
+ # The user is inactive, but our custom form policy allows them to log in.
data = {
'username': 'inactive',
'password': 'password',
View
36 django/contrib/auth/tests/test_hashers.py
@@ -1,13 +1,13 @@
# -*- coding: utf-8 -*-
from __future__ import unicode_literals
-import unittest
from unittest import skipUnless
from django.conf.global_settings import PASSWORD_HASHERS as default_hashers
from django.contrib.auth.hashers import (is_password_usable, BasePasswordHasher,
check_password, make_password, PBKDF2PasswordHasher, load_hashers, PBKDF2SHA1PasswordHasher,
get_hasher, identify_hasher, UNUSABLE_PASSWORD_PREFIX, UNUSABLE_PASSWORD_SUFFIX_LENGTH)
+from django.test import SimpleTestCase
from django.utils import six
@@ -22,7 +22,11 @@
bcrypt = None
-class TestUtilsHashPass(unittest.TestCase):
+class PBKDF2SingleIterationHasher(PBKDF2PasswordHasher):
+ iterations = 1
+
+
+class TestUtilsHashPass(SimpleTestCase):
def setUp(self):
load_hashers(password_hashers=default_hashers)
@@ -279,6 +283,34 @@ def setter(password):
finally:
hasher.iterations = old_iterations
+ def test_pbkdf2_upgrade_new_hasher(self):
+ self.assertEqual('pbkdf2_sha256', get_hasher('default').algorithm)
+ hasher = get_hasher('default')
+ self.assertNotEqual(hasher.iterations, 1)
+
+ state = {'upgraded': False}
+
+ def setter(password):
+ state['upgraded'] = True
+
+ with self.settings(PASSWORD_HASHERS=[
+ 'django.contrib.auth.tests.test_hashers.PBKDF2SingleIterationHasher']):
+ encoded = make_password('letmein')
+ algo, iterations, salt, hash = encoded.split('$', 3)
+ self.assertEqual(iterations, '1')
+
+ # Check that no upgrade is triggerd
+ self.assertTrue(check_password('letmein', encoded, setter))
+ self.assertFalse(state['upgraded'])
+
+ # Revert to the old iteration count and check if the password would get
+ # updated to the new iteration count.
+ with self.settings(PASSWORD_HASHERS=[
+ 'django.contrib.auth.hashers.PBKDF2PasswordHasher',
+ 'django.contrib.auth.tests.test_hashers.PBKDF2SingleIterationHasher']):
+ self.assertTrue(check_password('letmein', encoded, setter))
+ self.assertTrue(state['upgraded'])
+
def test_load_library_no_algorithm(self):
with self.assertRaises(ValueError) as e:
BasePasswordHasher()._load_library()
View
23 django/contrib/auth/tests/test_management.py
@@ -8,11 +8,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.db.models.loading import get_app
from django.test import TestCase
from django.test.utils import override_settings
from django.utils import six
@@ -91,7 +91,8 @@ def test_createsuperuser(self):
"Check the operation of the createsuperuser management command"
# We can use the management command to create a superuser
new_io = StringIO()
- call_command("createsuperuser",
+ call_command(
+ "createsuperuser",
interactive=False,
username="joe",
email="joe@somewhere.org",
@@ -108,7 +109,8 @@ def test_createsuperuser(self):
def test_verbosity_zero(self):
# We can supress output on the management command
new_io = StringIO()
- call_command("createsuperuser",
+ call_command(
+ "createsuperuser",
interactive=False,
username="joe2",
email="joe2@somewhere.org",
@@ -123,7 +125,8 @@ def test_verbosity_zero(self):
def test_email_in_username(self):
new_io = StringIO()
- call_command("createsuperuser",
+ call_command(
+ "createsuperuser",
interactive=False,
username="joe+admin@somewhere.org",
email="joe@somewhere.org",
@@ -140,7 +143,8 @@ def test_swappable_user(self):
# We skip validation because the temporary substitution of the
# swappable User model messes with validation.
new_io = StringIO()
- call_command("createsuperuser",
+ call_command(
+ "createsuperuser",
interactive=False,
email="joe@somewhere.org",
date_of_birth="1976-04-01",
@@ -163,7 +167,8 @@ def test_swappable_user_missing_required_field(self):
# swappable User model messes with validation.
new_io = StringIO()
with self.assertRaises(CommandError):
- call_command("createsuperuser",
+ call_command(
+ "createsuperuser",
interactive=False,
username="joe@somewhere.org",
stdout=new_io,
@@ -179,21 +184,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, get_app('auth'))
+ get_validation_errors(new_io, app_cache.get_app_config('auth').models_module)
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, get_app('auth'))
+ get_validation_errors(new_io, app_cache.get_app_config('auth').models_module)
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, get_app('auth'))
+ get_validation_errors(new_io, app_cache.get_app_config('auth').models_module)
self.assertIn("The USERNAME_FIELD must be unique. Add unique=True to the field parameters.", new_io.getvalue())
View
8 django/contrib/auth/tests/test_models.py
@@ -69,9 +69,11 @@ def test_create_user_email_domain_normalize_with_whitespace(self):
self.assertEqual(returned, 'email\ with_whitespace@d.com')
def test_empty_username(self):
- self.assertRaisesMessage(ValueError,
- 'The given username must be set',
- User.objects.create_user, username='')
+ self.assertRaisesMessage(
+ ValueError,
+ 'The given username must be set',
+ User.objects.create_user, username=''
+ )
class AbstractUserTestCase(TestCase):
View
46 django/contrib/auth/tests/test_remote_user.py
@@ -3,6 +3,7 @@
from django.conf import settings
from django.contrib.auth import authenticate
from django.contrib.auth.backends import RemoteUserBackend
+from django.contrib.auth.middleware import RemoteUserMiddleware
from django.contrib.auth.models import User
from django.contrib.auth.tests.utils import skipIfCustomUser
from django.test import TestCase
@@ -15,6 +16,7 @@ class RemoteUserTest(TestCase):
urls = 'django.contrib.auth.tests.urls'
middleware = 'django.contrib.auth.middleware.RemoteUserMiddleware'
backend = 'django.contrib.auth.backends.RemoteUserBackend'
+ header = 'REMOTE_USER'
# Usernames to be passed in REMOTE_USER for the test_known_user test case.
known_user = 'knownuser'
@@ -37,11 +39,11 @@ def test_no_remote_user(self):
self.assertTrue(response.context['user'].is_anonymous())
self.assertEqual(User.objects.count(), num_users)
- response = self.client.get('/remote_user/', REMOTE_USER=None)
+ response = self.client.get('/remote_user/', **{self.header: None})
self.assertTrue(response.context['user'].is_anonymous())
self.assertEqual(User.objects.count(), num_users)
- response = self.client.get('/remote_user/', REMOTE_USER='')
+ response = self.client.get('/remote_user/', **{self.header: ''})
self.assertTrue(response.context['user'].is_anonymous())
self.assertEqual(User.objects.count(), num_users)
@@ -51,13 +53,13 @@ def test_unknown_user(self):
as a User.
"""
num_users = User.objects.count()
- response = self.client.get('/remote_user/', REMOTE_USER='newuser')
+ response = self.client.get('/remote_user/', **{self.header: 'newuser'})
self.assertEqual(response.context['user'].username, 'newuser')
self.assertEqual(User.objects.count(), num_users + 1)
User.objects.get(username='newuser')
# Another request with same user should not create any new users.
- response = self.client.get('/remote_user/', REMOTE_USER='newuser')
+ response = self.client.get('/remote_user/', **{self.header: 'newuser'})
self.assertEqual(User.objects.count(), num_users + 1)
def test_known_user(self):
@@ -67,12 +69,14 @@ def test_known_user(self):
User.objects.create(username='knownuser')
User.objects.create(username='knownuser2')
num_users = User.objects.count()
- response = self.client.get('/remote_user/', REMOTE_USER=self.known_user)
+ response = self.client.get('/remote_user/',
+ **{self.header: self.known_user})
self.assertEqual(response.context['user'].username, 'knownuser')
self.assertEqual(User.objects.count(), num_users)
# Test that a different user passed in the headers causes the new user
# to be logged in.
- response = self.client.get('/remote_user/', REMOTE_USER=self.known_user2)
+ response = self.client.get('/remote_user/',
+ **{self.header: self.known_user2})
self.assertEqual(response.context['user'].username, 'knownuser2')
self.assertEqual(User.objects.count(), num_users)
@@ -89,13 +93,15 @@ def test_last_login(self):
user.last_login = default_login
user.save()
- response = self.client.get('/remote_user/', REMOTE_USER=self.known_user)
+ response = self.client.get('/remote_user/',
+ **{self.header: self.known_user})
self.assertNotEqual(default_login, response.context['user'].last_login)
user = User.objects.get(username='knownuser')
user.last_login = default_login
user.save()
- response = self.client.get('/remote_user/', REMOTE_USER=self.known_user)
+ response = self.client.get('/remote_user/',
+ **{self.header: self.known_user})
self.assertEqual(default_login, response.context['user'].last_login)
def test_header_disappears(self):
@@ -105,7 +111,8 @@ def test_header_disappears(self):
"""
User.objects.create(username='knownuser')
# Known user authenticates
- response = self.client.get('/remote_user/', REMOTE_USER=self.known_user)
+ response = self.client.get('/remote_user/',
+ **{self.header: self.known_user})
self.assertEqual(response.context['user'].username, 'knownuser')
# During the session, the REMOTE_USER header disappears. Should trigger logout.
response = self.client.get('/remote_user/')
@@ -140,7 +147,7 @@ class that doesn't create unknown users.
def test_unknown_user(self):
num_users = User.objects.count()
- response = self.client.get('/remote_user/', REMOTE_USER='newuser')
+ response = self.client.get('/remote_user/', **{self.header: 'newuser'})
self.assertTrue(response.context['user'].is_anonymous())
self.assertEqual(User.objects.count(), num_users)
@@ -194,3 +201,22 @@ def test_unknown_user(self):
super(RemoteUserCustomTest, self).test_unknown_user()
newuser = User.objects.get(username='newuser')
self.assertEqual(newuser.email, 'user@example.com')
+
+
+class CustomHeaderMiddleware(RemoteUserMiddleware):
+ """
+ Middleware that overrides custom HTTP auth user header.
+ """
+ header = 'HTTP_AUTHUSER'
+
+
+@skipIfCustomUser
+class CustomHeaderRemoteUserTest(RemoteUserTest):
+ """
+ Tests a custom RemoteUserMiddleware subclass with custom HTTP auth user
+ header.
+ """
+ middleware = (
+ 'django.contrib.auth.tests.test_remote_user.CustomHeaderMiddleware'
+ )
+ header = 'HTTP_AUTHUSER'
View
13 django/contrib/auth/tests/test_views.py
@@ -1,3 +1,4 @@
+from importlib import import_module
import itertools
import os
import re
@@ -710,6 +711,18 @@ def test_security_check(self, password='password'):
"%s should be allowed" % good_url)
self.confirm_logged_out()
+ def test_logout_preserve_language(self):
+ """Check that language stored in session is preserved after logout"""
+ # Create a new session with language
+ engine = import_module(settings.SESSION_ENGINE)
+ session = engine.SessionStore()
+ session['_language'] = 'pl'
+ session.save()
+ self.client.cookies[settings.SESSION_COOKIE_NAME] = session.session_key
+
+ self.client.get('/logout/')
+ self.assertEqual(self.client.session['_language'], 'pl')
+
@skipIfCustomUser
@override_settings(
View
2  django/contrib/auth/views.py
@@ -98,7 +98,7 @@ def logout(request, next_page=None,
def logout_then_login(request, login_url=None, current_app=None, extra_context=None):
"""
- Logs out the user if he is logged in. Then redirects to the log-in page.
+ Logs out the user if they are logged in. Then redirects to the log-in page.
"""
if not login_url:
login_url = settings.LOGIN_URL
View
3  django/contrib/comments/views/comments.py
@@ -3,6 +3,7 @@
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
@@ -48,7 +49,7 @@ 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 = models.get_model(*ctype.split(".", 1))
+ model = app_cache.get_model(*ctype.split(".", 1))
target = model._default_manager.using(using).get(pk=object_pk)
except TypeError:
return CommentPostBadRequest(
View
7 django/contrib/contenttypes/generic.py
@@ -453,9 +453,10 @@ def __init__(self, data=None, files=None, instance=None, save_as_new=None,
@classmethod
def get_default_prefix(cls):
opts = cls.model._meta
- return '-'.join((opts.app_label, opts.model_name,
- cls.ct_field.name, cls.ct_fk_field.name,
- ))
+ return '-'.join(
+ (opts.app_label, opts.model_name,
+ cls.ct_field.name, cls.ct_fk_field.name)
+ )
def save_new(self, form, commit=True):
setattr(form.instance, self.ct_field.get_attname(),
View
11 django/contrib/contenttypes/management.py
@@ -1,6 +1,7 @@
from django.contrib.contenttypes.models import ContentType
+from django.core.apps import app_cache, UnavailableApp
from django.db import DEFAULT_DB_ALIAS, router
-from django.db.models import get_apps, get_model, get_models, signals, UnavailableApp
+from django.db.models import signals
from django.utils.encoding import smart_text
from django.utils import six
from django.utils.six.moves import input
@@ -12,7 +13,7 @@ def update_contenttypes(app, created_models, verbosity=2, db=DEFAULT_DB_ALIAS, *
entries that no longer have a matching model class.
"""
try:
- get_model('contenttypes', 'ContentType')
+ app_cache.get_model('contenttypes', 'ContentType')
except UnavailableApp:
return
@@ -20,7 +21,7 @@ def update_contenttypes(app, created_models, verbosity=2, db=DEFAULT_DB_ALIAS, *
return
ContentType.objects.clear_cache()
- app_models = get_models(app)
+ app_models = app_cache.get_models(app)
if not app_models:
return
# They all have the same app_label, get the first one.
@@ -85,8 +86,8 @@ def update_contenttypes(app, created_models, verbosity=2, db=DEFAULT_DB_ALIAS, *
def update_all_contenttypes(verbosity=2, **kwargs):
- for app in get_apps():
- update_contenttypes(app, None, verbosity, **kwargs)
+ for app_config in app_cache.get_app_configs(only_with_models_module=True):
+ update_contenttypes(app_config.models_module, None, verbosity, **kwargs)
signals.post_migrate.connect(update_contenttypes)
View
3  django/contrib/contenttypes/models.py
@@ -1,3 +1,4 @@
+from django.core.apps import app_cache
from django.db import models
from django.utils.translation import ugettext_lazy as _
from django.utils.encoding import smart_text, force_text
@@ -156,7 +157,7 @@ def __str__(self):
def model_class(self):
"Returns the Python model class for this type of content."
- return models.get_model(self.app_label, self.model,
+ return app_cache.get_model(self.app_label, self.model,
only_installed=False)
def get_object_for_this_type(self, **kwargs):
View
15 django/contrib/contenttypes/tests.py
@@ -52,12 +52,13 @@ def get_absolute_url(self):
class ContentTypesTests(TestCase):
+
def setUp(self):
- self.old_Site_meta_installed = Site._meta.installed
+ self._old_installed = Site._meta.app_config.installed
ContentType.objects.clear_cache()
def tearDown(self):
- Site._meta.installed = self.old_Site_meta_installed
+ Site._meta.app_config.installed = self._old_installed
ContentType.objects.clear_cache()
def test_lookup_cache(self):
@@ -222,12 +223,12 @@ def test_shortcut_view(self):
user_ct = ContentType.objects.get_for_model(FooWithUrl)
obj = FooWithUrl.objects.create(name="john")
- if Site._meta.installed:
- response = shortcut(request, user_ct.id, obj.id)
- self.assertEqual("http://%s/users/john/" % get_current_site(request).domain,
- response._headers.get("location")[1])
+ Site._meta.app_config.installed = True
+ response = shortcut(request, user_ct.id, obj.id)
+ self.assertEqual("http://%s/users/john/" % get_current_site(request).domain,
+ response._headers.get("location")[1])
- Site._meta.installed = False
+ Site._meta.app_config.installed = False
response = shortcut(request, user_ct.id, obj.id)
self.assertEqual("http://Example.com/users/john/",
response._headers.get("location")[1])
View
4 django/contrib/flatpages/forms.py
@@ -23,8 +23,8 @@ def clean_url(self):
code='missing_leading_slash',
)
if (settings.APPEND_SLASH and
- 'django.middleware.common.CommonMiddleware' in settings.MIDDLEWARE_CLASSES and
- not url.endswith('/')):
+ 'django.middleware.common.CommonMiddleware' in settings.MIDDLEWARE_CLASSES and
+ not url.endswith('/')):
raise forms.ValidationError(
ugettext("URL is missing a trailing slash."),
code='missing_trailing_slash',
View
4 django/contrib/flatpages/templatetags/flatpages.py
@@ -73,8 +73,8 @@ def get_flatpages(parser, token):
"""
bits = token.split_contents()
syntax_message = ("%(tag_name)s expects a syntax of %(tag_name)s "
- "['url_starts_with'] [for user] as context_name" %
- dict(tag_name=bits[0]))
+ "['url_starts_with'] [for user] as context_name" %
+ dict(tag_name=bits[0]))
# Must have at 3-6 bits in the tag
if len(bits) >= 3 and len(bits) <= 6:
View
3  django/contrib/formtools/models.py
@@ -1 +1,2 @@
-""" models.py (even empty) currently required by the runtests.py to enable unit tests """
+# This file is required to pretend formtools has models.
+# Otherwise test models cannot be registered.
View
2  django/contrib/formtools/tests/wizard/test_forms.py
@@ -48,7 +48,7 @@ class CustomKwargsStep1(Step1):
def __init__(self, test=None, *args, **kwargs):
self.test = test
- return super(CustomKwargsStep1, self).__init__(*args, **kwargs)
+ super(CustomKwargsStep1, self).__init__(*args, **kwargs)
class TestModel(models.Model):
View
2  django/contrib/formtools/wizard/views.py
@@ -123,7 +123,7 @@ def as_view(cls, *args, **kwargs):
@classmethod
def get_initkwargs(cls, form_list=None, initial_dict=None,
- instance_dict=None, condition_dict=None, *args, **kwargs):
+ instance_dict=None, condition_dict=None, *args, **kwargs):
"""
Creates a dict with all needed parameters for the form wizard instances.
View
4 django/contrib/gis/db/backends/postgis/operations.py
@@ -369,7 +369,7 @@ def get_distance(self, f, dist_val, lookup_type):
dist_param = value
if (not geography and geodetic and lookup_type != 'dwithin'
- and option == 'spheroid'):
+ and option == 'spheroid'):
# using distance_spheroid requires the spheroid of the field as
# a parameter.
return [f._spheroid, dist_param]
@@ -467,7 +467,7 @@ def exactly_two(np):
def two_to_three(np):
return np >= 2 and np <= 3
if (lookup_type in self.distance_functions and
- lookup_type != 'dwithin'):
+ lookup_type != 'dwithin'):
return two_to_three(num_param)
else:
return exactly_two(num_param)
View
2  django/contrib/gis/db/backends/postgis/schema.py
@@ -1,6 +1,4 @@
-from django.conf import settings
from django.db.backends.postgresql_psycopg2.schema import DatabaseSchemaEditor
-from django.utils.functional import cached_property
class PostGISSchemaEditor(DatabaseSchemaEditor):
View
10 django/contrib/gis/db/backends/spatialite/creation.py
@@ -13,7 +13,7 @@ def create_test_db(self, verbosity=1, autoclobber=False):
database already exists. Returns the name of the test database created.
This method is overloaded to load up the SpatiaLite initialization
- SQL prior to calling the `syncdb` command.
+ SQL prior to calling the `migrate` command.
"""
# Don't import django.core.management if it isn't needed.
from django.core.management import call_command
@@ -31,13 +31,13 @@ def create_test_db(self, verbosity=1, autoclobber=False):
self.connection.close()
self.connection.settings_dict["NAME"] = test_database_name
- # Need to load the SpatiaLite initialization SQL before running `syncdb`.
+ # Need to load the SpatiaLite initialization SQL before running `migrate`.
self.load_spatialite_sql()
- # Report syncdb messages at one level lower than that requested.
+ # Report migrate messages at one level lower than that requested.
# This ensures we don't get flooded with messages during testing
# (unless you really ask to be flooded)
- call_command('syncdb',
+ call_command('migrate',
verbosity=max(verbosity - 1, 0),
interactive=False,
database=self.connection.alias,
@@ -47,7 +47,7 @@ def create_test_db(self, verbosity=1, autoclobber=False):
# custom SQL has been removed. The only test data should come from
# test fixtures, or autogenerated from post_migrate triggers.
# This has the side effect of loading initial data (which was
- # intentionally skipped in the syncdb).
+ # intentionally skipped in the migrate).
call_command('flush',
verbosity=max(verbosity - 1, 0),
interactive=False,
View
6 django/contrib/gis/db/backends/spatialite/models.py
@@ -1,7 +1,7 @@
"""
The GeometryColumns and SpatialRefSys models for the SpatiaLite backend.
"""
-from django.db import models
+from django.db import connection, models
from django.contrib.gis.db.backends.base import SpatialRefSysMixin
from django.utils.encoding import python_2_unicode_compatible
@@ -53,9 +53,13 @@ class SpatialRefSys(models.Model, SpatialRefSysMixin):
auth_srid = models.IntegerField()
ref_sys_name = models.CharField(max_length=256)
proj4text = models.CharField(max_length=2048)
+ if connection.ops.spatial_version[0] >= 4:
+ srtext = models.CharField(max_length=2048)
@property
def wkt(self):
+ if hasattr(self, 'srtext'):
+ return self.srtext
from django.contrib.gis.gdal import SpatialReference
return SpatialReference(self.proj4text).wkt
View
4 django/contrib/gis/db/models/fields.py
@@ -114,9 +114,9 @@ def deconstruct(self):
kwargs['srid'] = self.srid
if self.dim != 2:
kwargs['dim'] = self.dim
- if self.spatial_index != True:
+ if self.spatial_index is not True:
kwargs['spatial_index'] = self.spatial_index
- if self.geography != False:
+ if self.geography is not False:
kwargs['geography'] = self.geography
return name, path, args, kwargs
View
25 django/contrib/gis/db/models/query.py
@@ -362,12 +362,14 @@ def svg(self, relative=False, precision=8, **kwargs):
relative = int(bool(relative))
if not isinstance(precision, six.integer_types):
raise TypeError('SVG precision keyword argument must be an integer.')
- s = {'desc': 'SVG',
- 'procedure_fmt': '%(geo_col)s,%(rel)s,%(precision)s',
- 'procedure_args': {'rel': relative,
- 'precision': precision,
- }
- }
+ s = {
+ 'desc': 'SVG',
+ 'procedure_fmt': '%(geo_col)s,%(rel)s,%(precision)s',
+ 'procedure_args': {
+ 'rel': relative,
+ 'precision': precision,
+ }
+ }
return self._spatial_attribute('svg', s, **kwargs)