Skip to content

HTTPS clone URL

Subversion checkout URL

You can clone with HTTPS or Subversion.

Download ZIP

Loading…

Fixed #14087 -- find management commands in namespace packages #178

Closed
wants to merge 12 commits into from
@bhuztez

improve joh's and nfg's patches, find management commands in namespace packages only. packages imported by pep-302 importers should be in another patch.

https://code.djangoproject.com/ticket/14087

orblivion and others added some commits
@manfre

Why was this test converted to an identity insert? This bypasses SQLCompilers and any backend that needs to do special handling for identity inserts.

Collaborator

Thanks, fixed in 0cdfa76

@dreynolds

This certainly works for me. Anything we can do to get it committed?

@bhuztez bhuztez referenced this pull request
Closed

Fix management load #866

@Natim

Well it works !

@rochacbruno

Yes it works! Why isn't accepted yet?

@Natim

Please merge. For now to each deployement I have to patch manually in order to see namespace commands.

@apollo13
Owner

As I noted on the previous PR (#866) I'd like to see:

  • Support for PEP 420
  • Check other locations in Django for the same issues (app template loaders etc come to mind)
@timgraham
Owner

Closing this for now in light of @apollo13's comments.

@timgraham timgraham closed this
@Natim

So it is not fixed !
The code of all the PR is not enough to explain the solution and the problem ?

@rochacbruno

Why is it not fixed? I testes this PR, it works well and solves the problem, more than one year, what would be the solution?

@Natim

What else do you mean by support for PEP#420 ?
There is no other location bug so far. We are using this fix for a year without problem.

@apollo13
Owner

@Natim PEP420 gives python 3 implicit namespaces, so this PR should at least have a test to test that those new namespaces work too. Even though the current patch solves your issue, as a core dev our goal is to improve Django as a whole, which for me in that case means to include support for all (common) namespace solutions out there.

@rochacbruno A statement or rather tested support for PEP 420.

As a sidenote: this PR currently includes 12 commits, most of them unrelated to this PR, so that's not really nice to review either… Also this PR has no two pretty much identical codepaths in django/core/management/init.py, which should at least be factored out into a helper function.

@rochacbruno

So all we need is to isolate the code related to the namespace issue, create the test for PEP 420 and send another PR?

@apollo13
Owner

Kind of, at least then the PR would be in a reviewable shape.

@bhuztez

@apollo13 just one commit, 82f5a71. I guess it is because master has changed after this PR was made.

Django try very hard NOT to import any module when finding management commands, but Django do import app package before finding templates, static files, fixture. So other parts do not have to be patched.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Commits on Dec 2, 2012
  1. @orblivion
Commits on Dec 3, 2012
  1. @ptone

    Merge pull request #569 from orblivion/master

    ptone authored
    Fixed typo in ValuesQuerySet._as_sql docstring
  2. @claudep

    Fixed #19416 -- Fixed multi-line commands in initial SQL files

    claudep authored
    Thanks Aymeric Augustin for detecting this regression.
  3. @sebasmagri @jphalip

    Fixed #19318 -- Ensured that the admin's SimpleListFilter options can…

    sebasmagri authored jphalip committed
    … be displayed as selected even if the lookup's first element is not a string.
Commits on Dec 4, 2012
  1. @ramiro

    Fixed #18697 -- Made values accepted for two customizable admin templ…

    ramiro authored
    …ates consistent.
    
    Thanks and at cloverfastfood dot com for the report.
  2. @andrewgodwin

    Merge pull request #573 from tominsam/master

    andrewgodwin authored
    Fixed #19070: urlize template filter raises exception in some cases
  3. @claudep
  4. @jphalip
  5. @akaariai

    Fixed #19391 -- Oracle specific failure in tests

    akaariai authored
    The failure was caused by using None as a choice for a CharField. To
    avoid Oracle's "" <-> NULL handling the field type was changed to
    IntegerField.
  6. @ramiro
  7. @evildmp @ramiro

    Amended explanation of LOCALE_PATHS setting.

    evildmp authored ramiro committed
    Thanks Daniele Procida for the patch.
Commits on Dec 5, 2012
  1. @bhuztez
This page is out of date. Refresh to see the latest.
Showing with 329 additions and 59 deletions.
  1. +2 −2 django/contrib/admin/filters.py
  2. +3 −3 django/contrib/admin/sites.py
  3. +3 −3 django/contrib/auth/admin.py
  4. +4 −3 django/contrib/localflavor/ar/forms.py
  5. +46 −21 django/core/management/__init__.py
  6. +5 −5 django/core/management/sql.py
  7. +1 −1  django/db/models/query.py
  8. +3 −1 django/views/generic/edit.py
  9. +2 −3 docs/ref/settings.txt
  10. +51 −7 tests/regressiontests/admin_filters/tests.py
  11. 0  tests/regressiontests/admin_scripts/lib1/nons_app/__init__.py
  12. 0  tests/regressiontests/admin_scripts/lib1/nons_app/management/__init__.py
  13. 0  tests/regressiontests/admin_scripts/lib1/nons_app/management/commands/__init__.py
  14. +9 −0 tests/regressiontests/admin_scripts/lib1/nons_app/management/commands/nons_app_command1.py
  15. 0  tests/regressiontests/admin_scripts/lib1/nons_app/models.py
  16. 0  tests/regressiontests/admin_scripts/lib1/npapp/__init__.py
  17. 0  tests/regressiontests/admin_scripts/lib1/npapp/management.py
  18. 0  tests/regressiontests/admin_scripts/lib1/npapp/models.py
  19. +6 −0 tests/regressiontests/admin_scripts/lib1/nsapps/__init__.py
  20. +6 −0 tests/regressiontests/admin_scripts/lib1/nsapps/contrib/__init__.py
  21. 0  tests/regressiontests/admin_scripts/lib1/nsapps/contrib/app1/__init__.py
  22. 0  tests/regressiontests/admin_scripts/lib1/nsapps/contrib/app1/management/__init__.py
  23. 0  tests/regressiontests/admin_scripts/lib1/nsapps/contrib/app1/management/commands/__init__.py
  24. +9 −0 tests/regressiontests/admin_scripts/lib1/nsapps/contrib/app1/management/commands/app1_command1.py
  25. 0  tests/regressiontests/admin_scripts/lib1/nsapps/contrib/app1/models.py
  26. +6 −0 tests/regressiontests/admin_scripts/lib2/nsapps/__init__.py
  27. +6 −0 tests/regressiontests/admin_scripts/lib2/nsapps/contrib/__init__.py
  28. 0  tests/regressiontests/admin_scripts/lib2/nsapps/contrib/app2/__init__.py
  29. 0  tests/regressiontests/admin_scripts/lib2/nsapps/contrib/app2/management/__init__.py
  30. 0  tests/regressiontests/admin_scripts/lib2/nsapps/contrib/app2/management/commands/__init__.py
  31. +9 −0 tests/regressiontests/admin_scripts/lib2/nsapps/contrib/app2/management/commands/app2_command1.py
  32. 0  tests/regressiontests/admin_scripts/lib2/nsapps/contrib/app2/models.py
  33. +1 −0  tests/regressiontests/admin_scripts/lib3/_addsitedir.py
  34. +1 −0  tests/regressiontests/admin_scripts/lib3/exapps-nspkg.pth
  35. 0  tests/regressiontests/admin_scripts/lib3/exapps/app3/__init__.py
  36. 0  tests/regressiontests/admin_scripts/lib3/exapps/app3/management/__init__.py
  37. 0  tests/regressiontests/admin_scripts/lib3/exapps/app3/management/commands/__init__.py
  38. +9 −0 tests/regressiontests/admin_scripts/lib3/exapps/app3/management/commands/app3_command1.py
  39. 0  tests/regressiontests/admin_scripts/lib3/exapps/app3/models.py
  40. +92 −1 tests/regressiontests/admin_scripts/tests.py
  41. +8 −1 tests/regressiontests/admin_views/customadmin.py
  42. +2 −2 tests/regressiontests/admin_views/models.py
  43. +13 −1 tests/regressiontests/admin_views/tests.py
  44. +1 −0  tests/regressiontests/admin_views/urls.py
  45. +9 −0 tests/regressiontests/generic_views/edit.py
  46. +5 −0 tests/regressiontests/generic_views/forms.py
  47. +2 −2 tests/regressiontests/generic_views/tests.py
  48. +4 −0 tests/regressiontests/generic_views/urls.py
  49. +9 −2 tests/regressiontests/generic_views/views.py
  50. +2 −1  tests/regressiontests/initial_sql_regress/sql/simple.sql
View
4 django/contrib/admin/filters.py
@@ -9,7 +9,7 @@
from django.db import models
from django.core.exceptions import ImproperlyConfigured, ValidationError
-from django.utils.encoding import smart_text
+from django.utils.encoding import smart_text, force_text
from django.utils.translation import ugettext_lazy as _
from django.utils import timezone
from django.contrib.admin.util import (get_model_from_relation,
@@ -102,7 +102,7 @@ def choices(self, cl):
}
for lookup, title in self.lookup_choices:
yield {
- 'selected': self.value() == lookup,
+ 'selected': self.value() == force_text(lookup),
'query_string': cl.get_query_string({
self.parameter_name: lookup,
}, []),
View
6 django/contrib/admin/sites.py
@@ -391,9 +391,9 @@ def index(self, request, extra_context=None):
'app_list': app_list,
}
context.update(extra_context or {})
- return TemplateResponse(request, [
- self.index_template or 'admin/index.html',
- ], context, current_app=self.name)
+ return TemplateResponse(request, self.index_template or
+ 'admin/index.html', context,
+ current_app=self.name)
def app_index(self, request, app_label, extra_context=None):
user = request.user
View
6 django/contrib/auth/admin.py
@@ -148,10 +148,10 @@ def user_change_password(self, request, id, form_url=''):
'save_as': False,
'show_save': True,
}
- return TemplateResponse(request, [
+ return TemplateResponse(request,
self.change_user_password_template or
- 'admin/auth/user/change_password.html'
- ], context, current_app=self.admin_site.name)
+ 'admin/auth/user/change_password.html',
+ context, current_app=self.admin_site.name)
def response_add(self, request, obj, **kwargs):
"""
View
7 django/contrib/localflavor/ar/forms.py
@@ -24,7 +24,9 @@ class ARPostalCodeField(RegexField):
"""
A field that accepts a 'classic' NNNN Postal Code or a CPA.
- See http://www.correoargentino.com.ar/consulta_cpa/home.php
+ See:
+ http://www.correoargentino.com.ar/cpa/que_es
+ http://www.correoargentino.com.ar/cpa/como_escribirlo
"""
default_error_messages = {
'invalid': _("Enter a postal code in the format NNNN or ANNNNAAA."),
@@ -120,8 +122,7 @@ def _calc_cd(self, cuit):
return str(result)
def _format(self, cuit, check_digit=None):
- if check_digit == None:
+ if check_digit is None:
check_digit = cuit[-1]
cuit = cuit[:-1]
return '%s-%s-%s' % (cuit[:2], cuit[2:], check_digit)
-
View
67 django/core/management/__init__.py
@@ -41,31 +41,56 @@ def find_management_module(app_name):
"""
parts = app_name.split('.')
parts.append('management')
- parts.reverse()
- part = parts.pop()
- path = None
-
- # When using manage.py, the project module is added to the path,
- # loaded, then removed from the path. This means that
- # testproject.testapp.models can be loaded in future, even if
- # testproject isn't in the path. When looking for the management
- # module, we need look for the case where the project name is part
- # of the app_name but the project directory itself isn't on the path.
- try:
- f, path, descr = imp.find_module(part, path)
- except ImportError as e:
- if os.path.basename(os.getcwd()) != part:
- raise e
+
+ for i in range(len(parts), 0, -1):
+ try:
+ path = sys.modules['.'.join(parts[:i])].__path__
+ except AttributeError:
+ raise ImportError("No package named %s" % parts[i-1])
+ except KeyError:
+ continue
+
+ parts = parts[i:]
+ parts.reverse()
+ break
else:
- if f:
- f.close()
+ parts.reverse()
+ part = parts.pop()
+ path = sys.path
+
+ # When using manage.py, the project module is added to the path,
+ # loaded, then removed from the path. This means that
+ # testproject.testapp.models can be loaded in future, even if
+ # testproject isn't in the path. When looking for the management
+ # module, we need look for the case where the project name is part
+ # of the app_name but the project directory itself isn't on the path.
+ try:
+ next_path = []
+ for p in path:
+ try:
+ next_path.append(imp.find_module(part, [p])[1])
+ except ImportError:
+ pass
+ if not next_path:
+ raise ImportError("No module named %s" % part)
+ path = next_path
+ except ImportError as e:
+ if os.path.basename(os.getcwd()) != part:
+ raise e
while parts:
part = parts.pop()
- f, path, descr = imp.find_module(part, path and [path] or None)
- if f:
- f.close()
- return path
+ next_path = []
+ for p in path:
+ try:
+ next_path.append(imp.find_module(part, [p])[1])
+ except ImportError:
+ pass
+ if not next_path:
+ raise ImportError("No module named %s" % part)
+ path = next_path
+
+ return path[0]
def load_command_class(app_name, name):
"""
View
10 django/core/management/sql.py
@@ -145,15 +145,15 @@ def sql_all(app, style, connection):
def _split_statements(content):
comment_re = re.compile(r"^((?:'[^']*'|[^'])*?)--.*$")
statements = []
- statement = ""
+ statement = []
for line in content.split("\n"):
cleaned_line = comment_re.sub(r"\1", line).strip()
if not cleaned_line:
continue
- statement += cleaned_line
- if statement.endswith(";"):
- statements.append(statement)
- statement = ""
+ statement.append(cleaned_line)
+ if cleaned_line.endswith(";"):
+ statements.append(" ".join(statement))
+ statement = []
return statements
View
2  django/db/models/query.py
@@ -1077,7 +1077,7 @@ def _setup_aggregate_query(self, aggregates):
def _as_sql(self, connection):
"""
- For ValueQuerySet (and subclasses like ValuesListQuerySet), they can
+ For ValuesQuerySet (and subclasses like ValuesListQuerySet), they can
only be used as nested queries if they're already set up to select only
a single field (in which case, that is the field column that is
returned). This differs from QuerySet.as_sql(), where the column to
View
4 django/views/generic/edit.py
@@ -1,6 +1,7 @@
from django.forms import models as model_forms
from django.core.exceptions import ImproperlyConfigured
from django.http import HttpResponseRedirect
+from django.utils.encoding import force_text
from django.views.generic.base import TemplateResponseMixin, ContextMixin, View
from django.views.generic.detail import (SingleObjectMixin,
SingleObjectTemplateResponseMixin, BaseDetailView)
@@ -50,7 +51,8 @@ def get_success_url(self):
Returns the supplied success URL.
"""
if self.success_url:
- url = self.success_url
+ # Forcing possible reverse_lazy evaluation
+ url = force_text(self.success_url)
else:
raise ImproperlyConfigured(
"No URL to redirect to. Provide a success_url.")
View
5 docs/ref/settings.txt
@@ -1242,9 +1242,8 @@ Example::
'/var/local/translations/locale'
)
-Note that in the paths you add to the value of this setting, if you have the
-typical ``/path/to/locale/xx/LC_MESSAGES`` hierarchy, you should use the path to
-the ``locale`` directory (i.e. ``'/path/to/locale'``).
+Django will look within each of these paths for the ``<locale_code>/LC_MESSAGES``
+directories containing the actual translation files.
.. setting:: LOGGING
View
58 tests/regressiontests/admin_filters/tests.py
@@ -78,6 +78,21 @@ class DecadeListFilterParameterEndsWith__Isnull(DecadeListFilter):
parameter_name = 'decade__isnull' # Ends with '__isnull"
+class DepartmentListFilterLookupWithNonStringValue(SimpleListFilter):
+ title = 'department'
+ parameter_name = 'department'
+
+ def lookups(self, request, model_admin):
+ return set([
+ (employee.department.id, # Intentionally not a string (Refs #19318)
+ employee.department.code)
+ for employee in model_admin.queryset(request).all()
+ ])
+
+ def queryset(self, request, queryset):
+ if self.value():
+ return queryset.filter(department__id=self.value())
+
class CustomUserAdmin(UserAdmin):
list_filter = ('books_authored', 'books_contributed')
@@ -118,6 +133,10 @@ class EmployeeAdmin(ModelAdmin):
list_filter = ['department']
+class DepartmentFilterEmployeeAdmin(EmployeeAdmin):
+ list_filter = [DepartmentListFilterLookupWithNonStringValue, ]
+
+
class ListFiltersTests(TestCase):
def setUp(self):
@@ -140,6 +159,14 @@ def setUp(self):
self.gipsy_book.contributors = [self.bob, self.lisa]
self.gipsy_book.save()
+ # Departments
+ self.dev = Department.objects.create(code='DEV', description='Development')
+ self.design = Department.objects.create(code='DSN', description='Design')
+
+ # Employees
+ self.john = Employee.objects.create(name='John Blue', department=self.dev)
+ self.jack = Employee.objects.create(name='Jack Red', department=self.design)
+
def get_changelist(self, request, model, modeladmin):
return ChangeList(request, model, modeladmin.list_display, modeladmin.list_display_links,
modeladmin.list_filter, modeladmin.date_hierarchy, modeladmin.search_fields,
@@ -638,6 +665,28 @@ def test_parameter_ends_with__in__or__isnull(self):
self.assertEqual(choices[2]['selected'], True)
self.assertEqual(choices[2]['query_string'], '?decade__isnull=the+90s')
+ def test_lookup_with_non_string_value(self):
+ """
+ Ensure choices are set the selected class when using non-string values
+ for lookups in SimpleListFilters.
+ Refs #19318
+ """
+
+ modeladmin = DepartmentFilterEmployeeAdmin(Employee, site)
+ request = self.request_factory.get('/', {'department': self.john.pk})
+ changelist = self.get_changelist(request, Employee, modeladmin)
+
+ queryset = changelist.get_query_set(request)
+
+ self.assertEqual(list(queryset), [self.john])
+
+ filterspec = changelist.get_filters(request)[0][-1]
+ self.assertEqual(force_text(filterspec.title), 'department')
+ choices = list(filterspec.choices(changelist))
+ self.assertEqual(choices[2]['display'], 'DEV')
+ self.assertEqual(choices[2]['selected'], True)
+ self.assertEqual(choices[2]['query_string'], '?department=%s' % self.john.pk)
+
def test_fk_with_to_field(self):
"""
Ensure that a filter on a FK respects the FK's to_field attribute.
@@ -645,17 +694,12 @@ def test_fk_with_to_field(self):
"""
modeladmin = EmployeeAdmin(Employee, site)
- dev = Department.objects.create(code='DEV', description='Development')
- design = Department.objects.create(code='DSN', description='Design')
- john = Employee.objects.create(name='John Blue', department=dev)
- jack = Employee.objects.create(name='Jack Red', department=design)
-
request = self.request_factory.get('/', {})
changelist = self.get_changelist(request, Employee, modeladmin)
# Make sure the correct queryset is returned
queryset = changelist.get_query_set(request)
- self.assertEqual(list(queryset), [jack, john])
+ self.assertEqual(list(queryset), [self.jack, self.john])
filterspec = changelist.get_filters(request)[0][-1]
self.assertEqual(force_text(filterspec.title), 'department')
@@ -680,7 +724,7 @@ def test_fk_with_to_field(self):
# Make sure the correct queryset is returned
queryset = changelist.get_query_set(request)
- self.assertEqual(list(queryset), [john])
+ self.assertEqual(list(queryset), [self.john])
filterspec = changelist.get_filters(request)[0][-1]
self.assertEqual(force_text(filterspec.title), 'department')
View
0  tests/regressiontests/admin_scripts/lib1/nons_app/__init__.py
No changes.
View
0  tests/regressiontests/admin_scripts/lib1/nons_app/management/__init__.py
No changes.
View
0  tests/regressiontests/admin_scripts/lib1/nons_app/management/commands/__init__.py
No changes.
View
9 tests/regressiontests/admin_scripts/lib1/nons_app/management/commands/nons_app_command1.py
@@ -0,0 +1,9 @@
+from django.core.management.base import BaseCommand
+
+class Command(BaseCommand):
+ help = 'Test managment commands in non-namespaced app'
+ requires_model_validation = False
+ args = ''
+
+ def handle(self, *labels, **options):
+ print 'EXECUTE:nons_app_command1'
View
0  tests/regressiontests/admin_scripts/lib1/nons_app/models.py
No changes.
View
0  tests/regressiontests/admin_scripts/lib1/npapp/__init__.py
No changes.
View
0  tests/regressiontests/admin_scripts/lib1/npapp/management.py
No changes.
View
0  tests/regressiontests/admin_scripts/lib1/npapp/models.py
No changes.
View
6 tests/regressiontests/admin_scripts/lib1/nsapps/__init__.py
@@ -0,0 +1,6 @@
+# http://packages.python.org/distribute/setuptools.html#namespace-packages
+try:
+ __import__('pkg_resources').declare_namespace(__name__)
+except ImportError:
+ from pkgutil import extend_path
+ __path__ = extend_path(__path__, __name__)
View
6 tests/regressiontests/admin_scripts/lib1/nsapps/contrib/__init__.py
@@ -0,0 +1,6 @@
+# http://packages.python.org/distribute/setuptools.html#namespace-packages
+try:
+ __import__('pkg_resources').declare_namespace(__name__)
+except ImportError:
+ from pkgutil import extend_path
+ __path__ = extend_path(__path__, __name__)
View
0  tests/regressiontests/admin_scripts/lib1/nsapps/contrib/app1/__init__.py
No changes.
View
0  tests/regressiontests/admin_scripts/lib1/nsapps/contrib/app1/management/__init__.py
No changes.
View
0  tests/regressiontests/admin_scripts/lib1/nsapps/contrib/app1/management/commands/__init__.py
No changes.
View
9 tests/regressiontests/admin_scripts/lib1/nsapps/contrib/app1/management/commands/app1_command1.py
@@ -0,0 +1,9 @@
+from django.core.management.base import BaseCommand
+
+class Command(BaseCommand):
+ help = 'Test managment commands in namespaced apps'
+ requires_model_validation = False
+ args = ''
+
+ def handle(self, *labels, **options):
+ print 'EXECUTE:app1_command1'
View
0  tests/regressiontests/admin_scripts/lib1/nsapps/contrib/app1/models.py
No changes.
View
6 tests/regressiontests/admin_scripts/lib2/nsapps/__init__.py
@@ -0,0 +1,6 @@
+# http://packages.python.org/distribute/setuptools.html#namespace-packages
+try:
+ __import__('pkg_resources').declare_namespace(__name__)
+except ImportError:
+ from pkgutil import extend_path
+ __path__ = extend_path(__path__, __name__)
View
6 tests/regressiontests/admin_scripts/lib2/nsapps/contrib/__init__.py
@@ -0,0 +1,6 @@
+# http://packages.python.org/distribute/setuptools.html#namespace-packages
+try:
+ __import__('pkg_resources').declare_namespace(__name__)
+except ImportError:
+ from pkgutil import extend_path
+ __path__ = extend_path(__path__, __name__)
View
0  tests/regressiontests/admin_scripts/lib2/nsapps/contrib/app2/__init__.py
No changes.
View
0  tests/regressiontests/admin_scripts/lib2/nsapps/contrib/app2/management/__init__.py
No changes.
View
0  tests/regressiontests/admin_scripts/lib2/nsapps/contrib/app2/management/commands/__init__.py
No changes.
View
9 tests/regressiontests/admin_scripts/lib2/nsapps/contrib/app2/management/commands/app2_command1.py
@@ -0,0 +1,9 @@
+from django.core.management.base import BaseCommand
+
+class Command(BaseCommand):
+ help = 'Test managment commands in namespaced apps'
+ requires_model_validation = False
+ args = ''
+
+ def handle(self, *labels, **options):
+ print 'EXECUTE:app2_command1'
View
0  tests/regressiontests/admin_scripts/lib2/nsapps/contrib/app2/models.py
No changes.
View
1  tests/regressiontests/admin_scripts/lib3/_addsitedir.py
@@ -0,0 +1 @@
+import os.path, site; site.addsitedir(os.path.dirname(__file__))
View
1  tests/regressiontests/admin_scripts/lib3/exapps-nspkg.pth
@@ -0,0 +1 @@
+import sys,new,os; p = os.path.join(sys._getframe(1).f_locals['sitedir'], *('exapps',)); ie = os.path.exists(os.path.join(p,'__init__.py')); m = not ie and sys.modules.setdefault('exapps',new.module('exapps')); mp = (m or []) and m.__dict__.setdefault('__path__',[]); (p not in mp) and mp.append(p)
View
0  tests/regressiontests/admin_scripts/lib3/exapps/app3/__init__.py
No changes.
View
0  tests/regressiontests/admin_scripts/lib3/exapps/app3/management/__init__.py
No changes.
View
0  tests/regressiontests/admin_scripts/lib3/exapps/app3/management/commands/__init__.py
No changes.
View
9 tests/regressiontests/admin_scripts/lib3/exapps/app3/management/commands/app3_command1.py
@@ -0,0 +1,9 @@
+from django.core.management.base import BaseCommand
+
+class Command(BaseCommand):
+ help = 'Test managment commands in namespaced apps'
+ requires_model_validation = False
+ args = ''
+
+ def handle(self, *labels, **options):
+ print 'EXECUTE:app3_command1'
View
0  tests/regressiontests/admin_scripts/lib3/exapps/app3/models.py
No changes.
View
93 tests/regressiontests/admin_scripts/tests.py
@@ -96,6 +96,9 @@ def _ext_backend_paths(self):
def run_test(self, script, args, settings_file=None, apps=None):
project_dir = os.path.dirname(test_dir)
base_dir = os.path.dirname(project_dir)
+ lib1_dir = os.path.join(os.path.dirname(__file__), 'lib1')
+ lib2_dir = os.path.join(os.path.dirname(__file__), 'lib2')
+ lib3_dir = os.path.join(os.path.dirname(__file__), 'lib3')
ext_backend_base_dirs = self._ext_backend_paths()
# Remember the old environment
@@ -113,7 +116,7 @@ def run_test(self, script, args, settings_file=None, apps=None):
os.environ['DJANGO_SETTINGS_MODULE'] = settings_file
elif 'DJANGO_SETTINGS_MODULE' in os.environ:
del os.environ['DJANGO_SETTINGS_MODULE']
- python_path = [project_dir, base_dir]
+ python_path = [project_dir, base_dir, lib1_dir, lib2_dir, lib3_dir]
python_path.extend(ext_backend_base_dirs)
os.environ[python_path_var_name] = os.pathsep.join(python_path)
@@ -1613,3 +1616,91 @@ def test_basic(self):
out, err = self.run_manage(args)
self.assertNoOutput(err)
self.assertOutput(out, "FOO = 'bar' ###")
+
+
+class NamespacePackagedApps(AdminScriptTestCase):
+ def setUp(self):
+ self.write_settings('settings.py', apps=['nons_app', 'nsapps.contrib.app1','nsapps.contrib.app2','exapps.app3'])
+ test_dir = os.path.dirname(os.path.dirname(__file__))
+ settings_file = open(os.path.join(test_dir, 'settings.py'), 'a')
+ settings_file.write('import _addsitedir')
+ settings_file.close()
+
+ def tearDown(self):
+ self.remove_settings('settings.py')
+
+ def test_help(self):
+ out, err = self.run_manage(['help'])
+ self.assertNoOutput(err)
+ self.assertOutput(out, "nons_app_command1")
+ self.assertOutput(out, "app1_command1")
+ self.assertOutput(out, "app2_command1")
+ self.assertOutput(out, "app3_command1")
+
+ def test_nons_app(self):
+ args = ['nons_app_command1']
+ out, err = self.run_manage(args)
+ self.assertNoOutput(err)
+ self.assertOutput(out, "EXECUTE:nons_app_command1")
+
+ def test_nsapps(self):
+ args = ['app1_command1']
+ out, err = self.run_manage(args)
+ self.assertNoOutput(err)
+ self.assertOutput(out, "EXECUTE:app1_command1")
+
+ args = ['app2_command1']
+ out, err = self.run_manage(args)
+ self.assertNoOutput(err)
+ self.assertOutput(out, "EXECUTE:app2_command1")
+
+ def test_exapps(self):
+ args = ['app3_command1']
+ out, err = self.run_manage(args)
+ self.assertNoOutput(err)
+ self.assertOutput(out, "EXECUTE:app3_command1")
+
+class PreloadedNamespacePackagedApps(AdminScriptTestCase):
+ def setUp(self):
+ self.write_settings('settings.py', apps=['nsapps.contrib.app1','nsapps.contrib.app2'])
+ test_dir = os.path.dirname(os.path.dirname(__file__))
+ settings_file = open(os.path.join(test_dir, 'settings.py'), 'a')
+ settings_file.write('import nsapps')
+ settings_file.close()
+
+ def tearDown(self):
+ self.remove_settings('settings.py')
+
+ def test_help(self):
+ out, err = self.run_manage(['help'])
+ self.assertNoOutput(err)
+ self.assertOutput(out, "app1_command1")
+ self.assertOutput(out, "app2_command1")
+
+ def test_nsapps(self):
+ args = ['app1_command1']
+ out, err = self.run_manage(args)
+ self.assertNoOutput(err)
+ self.assertOutput(out, "EXECUTE:app1_command1")
+
+ args = ['app2_command1']
+ out, err = self.run_manage(args)
+ self.assertNoOutput(err)
+ self.assertOutput(out, "EXECUTE:app2_command1")
+
+
+class NonPackageManagementApps(AdminScriptTestCase):
+ def setUp(self):
+ self.write_settings('settings.py', apps=['npapp'])
+ settings_file = open(os.path.join(test_dir, 'settings.py'), 'a')
+ settings_file.write('import npapp.management')
+ settings_file.close()
+
+ def tearDown(self):
+ self.remove_settings('settings.py')
+
+ def test_help(self):
+ out, err = self.run_manage(['help'])
+ self.assertNoOutput(err)
+
+
View
9 tests/regressiontests/admin_views/customadmin.py
@@ -16,7 +16,7 @@ class Admin2(admin.AdminSite):
login_form = forms.CustomAdminAuthenticationForm
login_template = 'custom_admin/login.html'
logout_template = 'custom_admin/logout.html'
- index_template = 'custom_admin/index.html'
+ index_template = ['custom_admin/index.html'] # a list, to test fix for #18697
password_change_template = 'custom_admin/password_change_form.html'
password_change_done_template = 'custom_admin/password_change_done.html'
@@ -40,6 +40,10 @@ def queryset(self, request):
return qs.filter(is_superuser=False)
+class CustomPwdTemplateUserAdmin(UserAdmin):
+ change_user_password_template = ['admin/auth/user/change_password.html'] # a list, to test fix for #18697
+
+
site = Admin2(name="admin2")
site.register(models.Article, base_admin.ArticleAdmin)
@@ -50,3 +54,6 @@ def queryset(self, request):
site.register(User, UserLimitedAdmin)
site.register(models.UndeletableObject, base_admin.UndeletableObjectAdmin)
site.register(models.Simple, base_admin.AttributeErrorRaisingAdmin)
+
+simple_site = Admin2(name='admin4')
+simple_site.register(User, CustomPwdTemplateUserAdmin)
View
4 tests/regressiontests/admin_views/models.py
@@ -662,5 +662,5 @@ class Simple(models.Model):
"""
class Choice(models.Model):
- choice = models.CharField(max_length=1, blank=True, null=True,
- choices=(('y','Yes'), ('n','No'), (None, 'No opinion')))
+ choice = models.IntegerField(blank=True, null=True,
+ choices=((1, 'Yes'), (0, 'No'), (None, 'No opinion')))
View
14 tests/regressiontests/admin_views/tests.py
@@ -770,7 +770,10 @@ def testCustomAdminSiteLogoutTemplate(self):
self.assertContains(response, 'Hello from a custom logout template')
def testCustomAdminSiteIndexViewAndTemplate(self):
- response = self.client.get('/test_admin/admin2/')
+ try:
+ response = self.client.get('/test_admin/admin2/')
+ except TypeError:
+ self.fail('AdminSite.index_template should accept a list of template paths')
self.assertIsInstance(response, TemplateResponse)
self.assertTemplateUsed(response, 'custom_admin/index.html')
self.assertContains(response, 'Hello from a custom index template *bar*')
@@ -792,6 +795,15 @@ def testCustomAdminSiteView(self):
response = self.client.get('/test_admin/%s/my_view/' % self.urlbit)
self.assertEqual(response.content, b"Django is a magical pony!")
+ def test_pwd_change_custom_template(self):
+ self.client.login(username='super', password='secret')
+ su = User.objects.get(username='super')
+ try:
+ response = self.client.get('/test_admin/admin4/auth/user/%s/password/' % su.pk)
+ except TypeError:
+ self.fail('ModelAdmin.change_user_password_template should accept a list of template paths')
+ self.assertEqual(response.status_code, 200)
+
def get_perm(Model, perm):
"""Return the permission object, for the Model"""
View
1  tests/regressiontests/admin_views/urls.py
@@ -11,4 +11,5 @@
(r'^test_admin/admin/', include(admin.site.urls)),
(r'^test_admin/admin2/', include(customadmin.site.urls)),
(r'^test_admin/admin3/', include(admin.site.urls), dict(form_url='pony')),
+ (r'^test_admin/admin4/', include(customadmin.simple_site.urls)),
)
View
9 tests/regressiontests/generic_views/edit.py
@@ -20,6 +20,15 @@ def test_initial_data(self):
initial_2 = FormMixin().get_initial()
self.assertNotEqual(initial_1, initial_2)
+
+class BasicFormTests(TestCase):
+ urls = 'regressiontests.generic_views.urls'
+
+ def test_post_data(self):
+ res = self.client.post('/contact/', {'name': "Me", 'message': "Hello"})
+ self.assertRedirects(res, 'http://testserver/list/authors/')
+
+
class ModelFormMixinTests(TestCase):
def test_get_form(self):
form_class = views.AuthorGetQuerySetFormView().get_form_class()
View
5 tests/regressiontests/generic_views/forms.py
@@ -11,3 +11,8 @@ class AuthorForm(forms.ModelForm):
class Meta:
model = Author
+
+
+class ContactForm(forms.Form):
+ name = forms.CharField()
+ message = forms.CharField(widget=forms.Textarea)
View
4 tests/regressiontests/generic_views/tests.py
@@ -6,6 +6,6 @@
MonthArchiveViewTests, WeekArchiveViewTests, DayArchiveViewTests,
DateDetailViewTests)
from .detail import DetailViewTest
-from .edit import (FormMixinTests, ModelFormMixinTests, CreateViewTests,
- UpdateViewTests, DeleteViewTests)
+from .edit import (FormMixinTests, BasicFormTests, ModelFormMixinTests,
+ CreateViewTests, UpdateViewTests, DeleteViewTests)
from .list import ListViewTests
View
4 tests/regressiontests/generic_views/urls.py
@@ -56,6 +56,10 @@
(r'^detail/nonmodel/1/$',
views.NonModelDetail.as_view()),
+ # FormView
+ (r'^contact/$',
+ views.ContactView.as_view()),
+
# Create/UpdateView
(r'^edit/artists/create/$',
views.ArtistCreate.as_view()),
View
11 tests/regressiontests/generic_views/views.py
@@ -2,11 +2,11 @@
from django.contrib.auth.decorators import login_required
from django.core.paginator import Paginator
-from django.core.urlresolvers import reverse
+from django.core.urlresolvers import reverse, reverse_lazy
from django.utils.decorators import method_decorator
from django.views import generic
-from .forms import AuthorForm
+from .forms import AuthorForm, ContactForm
from .models import Artist, Author, Book, Page, BookSigning
@@ -75,6 +75,13 @@ def get_paginator(self, queryset, page_size, orphans=0, allow_empty_first_page=T
orphans=2,
allow_empty_first_page=allow_empty_first_page)
+
+class ContactView(generic.FormView):
+ form_class = ContactForm
+ success_url = reverse_lazy('authors_list')
+ template_name = 'generic_views/form.html'
+
+
class ArtistCreate(generic.CreateView):
model = Artist
View
3  tests/regressiontests/initial_sql_regress/sql/simple.sql
@@ -2,7 +2,8 @@
INSERT INTO initial_sql_regress_simple (name) VALUES ('John'); -- another comment
INSERT INTO initial_sql_regress_simple (name) VALUES ('-- Comment Man');
INSERT INTO initial_sql_regress_simple (name) VALUES ('Paul');
-INSERT INTO initial_sql_regress_simple (name) VALUES ('Ringo');
+INSERT INTO initial_sql_regress_simple
+ VALUES (150, 'Ringo');
INSERT INTO initial_sql_regress_simple (name) VALUES ('George');
INSERT INTO initial_sql_regress_simple (name) VALUES ('Miles O''Brien');
INSERT INTO initial_sql_regress_simple (name) VALUES ('Semicolon;Man');
Something went wrong with that request. Please try again.