Skip to content
This repository

HTTPS clone URL

Subversion checkout URL

You can clone with HTTPS or Subversion.

Download ZIP

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

and others added some commits December 01, 2012
Michael 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.

Owner

Thanks, fixed in 0cdfa76

David Reynolds

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

bhuztez bhuztez referenced this pull request April 19, 2013
Closed

Fix management load #866

Natim
Natim commented June 05, 2013

Well it works !

Bruno Rocha

Yes it works! Why isn't accepted yet?

Natim
Natim commented July 11, 2013

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

Florian Apolloner
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)
Tim Graham timgraham closed this September 18, 2013
Tim Graham
Owner

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

Natim

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

Bruno Rocha

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.

Florian Apolloner
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.

Bruno Rocha

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

Florian Apolloner
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

Showing 12 unique commits by 10 authors.

Dec 01, 2012
orblivion Typeo in ValuesQuerySet._as_sql docstring 0ff1d4f
Dec 03, 2012
Preston Holmes Merge pull request #569 from orblivion/master
Fixed typo in ValuesQuerySet._as_sql docstring
6d27547
Claude Paroz Fixed #19416 -- Fixed multi-line commands in initial SQL files
Thanks Aymeric Augustin for detecting this regression.
5fa5621
Sebastián Magrí Fixed #19318 -- Ensured that the admin's SimpleListFilter options can…
… be displayed as selected even if the lookup's first element is not a string.
88e1715
Dec 04, 2012
Ramiro Morales Fixed #18697 -- Made values accepted for two customizable admin templ…
…ates consistent.

Thanks and at cloverfastfood dot com for the report.
b64d304
Andrew Godwin Merge pull request #573 from tominsam/master
Fixed #19070: urlize template filter raises exception in some cases
501c7a2
Claude Paroz Fixed #19378 -- Ensured get_success_url returns a non-lazy URL 795ac7d
Julien Phalip Fixed the admin_filters tests for Postgres. c196e01
Anssi Kääriäinen Fixed #19391 -- Oracle specific failure in tests
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.
632cf32
Ramiro Morales Minor idiomatic and docstring edits in ar localflavor. 45f00db
Daniele Procida Amended explanation of LOCALE_PATHS setting.
Thanks Daniele Procida for the patch.
37abad7
Dec 05, 2012
bhuztez Fixed #14087 -- find management commands in namespace packages 82f5a71
This page is out of date. Refresh to see the latest.

Showing 50 changed files with 329 additions and 59 deletions. Show diff stats Hide diff stats

  1. 0  app1/__init__.py b/tests/regressiontests/admin_scripts/lib1/nsapps/contrib/app1/__init__.py
  2. 0  ...anagement/__init__.py b/tests/regressiontests/admin_scripts/lib1/nsapps/contrib/app1/management/__init__.py
  3. 0  .../__init__.py b/tests/regressiontests/admin_scripts/lib1/nsapps/contrib/app1/management/commands/__init__.py
  4. 0  app1/models.py b/tests/regressiontests/admin_scripts/lib1/nsapps/contrib/app1/models.py
  5. 0  app2/__init__.py b/tests/regressiontests/admin_scripts/lib2/nsapps/contrib/app2/__init__.py
  6. 0  ...anagement/__init__.py b/tests/regressiontests/admin_scripts/lib2/nsapps/contrib/app2/management/__init__.py
  7. 0  .../__init__.py b/tests/regressiontests/admin_scripts/lib2/nsapps/contrib/app2/management/commands/__init__.py
  8. 0  app2/models.py b/tests/regressiontests/admin_scripts/lib2/nsapps/contrib/app2/models.py
  9. 4  django/contrib/admin/filters.py
  10. 6  django/contrib/admin/sites.py
  11. 6  django/contrib/auth/admin.py
  12. 7  django/contrib/localflavor/ar/forms.py
  13. 67  django/core/management/__init__.py
  14. 10  django/core/management/sql.py
  15. 2  django/db/models/query.py
  16. 4  django/views/generic/edit.py
  17. 5  docs/ref/settings.txt
  18. 58  tests/regressiontests/admin_filters/tests.py
  19. 0  tests/regressiontests/admin_scripts/lib1/nons_app/__init__.py
  20. 0  tests/regressiontests/admin_scripts/lib1/nons_app/management/__init__.py
  21. 0  tests/regressiontests/admin_scripts/lib1/nons_app/management/commands/__init__.py
  22. 9  tests/regressiontests/admin_scripts/lib1/nons_app/management/commands/nons_app_command1.py
  23. 0  tests/regressiontests/admin_scripts/lib1/nons_app/models.py
  24. 0  tests/regressiontests/admin_scripts/lib1/npapp/__init__.py
  25. 0  tests/regressiontests/admin_scripts/lib1/npapp/management.py
  26. 0  tests/regressiontests/admin_scripts/lib1/npapp/models.py
  27. 6  tests/regressiontests/admin_scripts/lib1/nsapps/__init__.py
  28. 6  tests/regressiontests/admin_scripts/lib1/nsapps/contrib/__init__.py
  29. 9  tests/regressiontests/admin_scripts/lib1/nsapps/contrib/app1/management/commands/app1_command1.py
  30. 6  tests/regressiontests/admin_scripts/lib2/nsapps/__init__.py
  31. 6  tests/regressiontests/admin_scripts/lib2/nsapps/contrib/__init__.py
  32. 9  tests/regressiontests/admin_scripts/lib2/nsapps/contrib/app2/management/commands/app2_command1.py
  33. 1  tests/regressiontests/admin_scripts/lib3/_addsitedir.py
  34. 1  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  tests/regressiontests/admin_scripts/lib3/exapps/app3/management/commands/app3_command1.py
  39. 0  tests/regressiontests/admin_scripts/lib3/exapps/app3/models.py
  40. 93  tests/regressiontests/admin_scripts/tests.py
  41. 9  tests/regressiontests/admin_views/customadmin.py
  42. 4  tests/regressiontests/admin_views/models.py
  43. 14  tests/regressiontests/admin_views/tests.py
  44. 1  tests/regressiontests/admin_views/urls.py
  45. 9  tests/regressiontests/generic_views/edit.py
  46. 5  tests/regressiontests/generic_views/forms.py
  47. 4  tests/regressiontests/generic_views/tests.py
  48. 4  tests/regressiontests/generic_views/urls.py
  49. 11  tests/regressiontests/generic_views/views.py
  50. 3  tests/regressiontests/initial_sql_regress/sql/simple.sql
0  app1/__init__.py b/tests/regressiontests/admin_scripts/lib1/nsapps/contrib/app1/__init__.py
No changes.
0  ...gement/__init__.py b/tests/regressiontests/admin_scripts/lib1/nsapps/contrib/app1/management/__init__.py
No changes.
0  ...init__.py b/tests/regressiontests/admin_scripts/lib1/nsapps/contrib/app1/management/commands/__init__.py
No changes.
0  app1/models.py b/tests/regressiontests/admin_scripts/lib1/nsapps/contrib/app1/models.py
No changes.
0  app2/__init__.py b/tests/regressiontests/admin_scripts/lib2/nsapps/contrib/app2/__init__.py
No changes.
0  ...gement/__init__.py b/tests/regressiontests/admin_scripts/lib2/nsapps/contrib/app2/management/__init__.py
No changes.
0  ...init__.py b/tests/regressiontests/admin_scripts/lib2/nsapps/contrib/app2/management/commands/__init__.py
No changes.
0  app2/models.py b/tests/regressiontests/admin_scripts/lib2/nsapps/contrib/app2/models.py
No changes.
4  django/contrib/admin/filters.py
@@ -9,7 +9,7 @@
9 9
 
10 10
 from django.db import models
11 11
 from django.core.exceptions import ImproperlyConfigured, ValidationError
12  
-from django.utils.encoding import smart_text
  12
+from django.utils.encoding import smart_text, force_text
13 13
 from django.utils.translation import ugettext_lazy as _
14 14
 from django.utils import timezone
15 15
 from django.contrib.admin.util import (get_model_from_relation,
@@ -102,7 +102,7 @@ def choices(self, cl):
102 102
         }
103 103
         for lookup, title in self.lookup_choices:
104 104
             yield {
105  
-                'selected': self.value() == lookup,
  105
+                'selected': self.value() == force_text(lookup),
106 106
                 'query_string': cl.get_query_string({
107 107
                     self.parameter_name: lookup,
108 108
                 }, []),
6  django/contrib/admin/sites.py
@@ -391,9 +391,9 @@ def index(self, request, extra_context=None):
391 391
             'app_list': app_list,
392 392
         }
393 393
         context.update(extra_context or {})
394  
-        return TemplateResponse(request, [
395  
-            self.index_template or 'admin/index.html',
396  
-        ], context, current_app=self.name)
  394
+        return TemplateResponse(request, self.index_template or
  395
+                                'admin/index.html', context,
  396
+                                current_app=self.name)
397 397
 
398 398
     def app_index(self, request, app_label, extra_context=None):
399 399
         user = request.user
6  django/contrib/auth/admin.py
@@ -148,10 +148,10 @@ def user_change_password(self, request, id, form_url=''):
148 148
             'save_as': False,
149 149
             'show_save': True,
150 150
         }
151  
-        return TemplateResponse(request, [
  151
+        return TemplateResponse(request,
152 152
             self.change_user_password_template or
153  
-            'admin/auth/user/change_password.html'
154  
-        ], context, current_app=self.admin_site.name)
  153
+            'admin/auth/user/change_password.html',
  154
+            context, current_app=self.admin_site.name)
155 155
 
156 156
     def response_add(self, request, obj, **kwargs):
157 157
         """
7  django/contrib/localflavor/ar/forms.py
@@ -24,7 +24,9 @@ class ARPostalCodeField(RegexField):
24 24
     """
25 25
     A field that accepts a 'classic' NNNN Postal Code or a CPA.
26 26
 
27  
-    See http://www.correoargentino.com.ar/consulta_cpa/home.php
  27
+    See:
  28
+        http://www.correoargentino.com.ar/cpa/que_es
  29
+        http://www.correoargentino.com.ar/cpa/como_escribirlo
28 30
     """
29 31
     default_error_messages = {
30 32
         'invalid': _("Enter a postal code in the format NNNN or ANNNNAAA."),
@@ -120,8 +122,7 @@ def _calc_cd(self, cuit):
120 122
         return str(result)
121 123
 
122 124
     def _format(self, cuit, check_digit=None):
123  
-        if check_digit == None:
  125
+        if check_digit is None:
124 126
             check_digit = cuit[-1]
125 127
             cuit = cuit[:-1]
126 128
         return '%s-%s-%s' % (cuit[:2], cuit[2:], check_digit)
127  
-
67  django/core/management/__init__.py
@@ -41,31 +41,56 @@ def find_management_module(app_name):
41 41
     """
42 42
     parts = app_name.split('.')
43 43
     parts.append('management')
44  
-    parts.reverse()
45  
-    part = parts.pop()
46  
-    path = None
47  
-
48  
-    # When using manage.py, the project module is added to the path,
49  
-    # loaded, then removed from the path. This means that
50  
-    # testproject.testapp.models can be loaded in future, even if
51  
-    # testproject isn't in the path. When looking for the management
52  
-    # module, we need look for the case where the project name is part
53  
-    # of the app_name but the project directory itself isn't on the path.
54  
-    try:
55  
-        f, path, descr = imp.find_module(part, path)
56  
-    except ImportError as e:
57  
-        if os.path.basename(os.getcwd()) != part:
58  
-            raise e
  44
+
  45
+    for i in range(len(parts), 0, -1):
  46
+        try:
  47
+            path = sys.modules['.'.join(parts[:i])].__path__
  48
+        except AttributeError:
  49
+            raise ImportError("No package named %s" % parts[i-1])
  50
+        except KeyError:
  51
+            continue
  52
+
  53
+        parts = parts[i:]
  54
+        parts.reverse()
  55
+        break
59 56
     else:
60  
-        if f:
61  
-            f.close()
  57
+        parts.reverse()
  58
+        part = parts.pop()
  59
+        path = sys.path
  60
+
  61
+        # When using manage.py, the project module is added to the path,
  62
+        # loaded, then removed from the path. This means that
  63
+        # testproject.testapp.models can be loaded in future, even if
  64
+        # testproject isn't in the path. When looking for the management
  65
+        # module, we need look for the case where the project name is part
  66
+        # of the app_name but the project directory itself isn't on the path.
  67
+        try:
  68
+            next_path = []
  69
+            for p in path:
  70
+                try:
  71
+                    next_path.append(imp.find_module(part, [p])[1])
  72
+                except ImportError:
  73
+                    pass
  74
+            if not next_path:
  75
+                raise ImportError("No module named %s" % part)
  76
+            path = next_path
  77
+        except ImportError as e:
  78
+            if os.path.basename(os.getcwd()) != part:
  79
+                raise e
62 80
 
63 81
     while parts:
64 82
         part = parts.pop()
65  
-        f, path, descr = imp.find_module(part, path and [path] or None)
66  
-        if f:
67  
-            f.close()
68  
-    return path
  83
+        next_path = []
  84
+        for p in path:
  85
+            try:
  86
+                next_path.append(imp.find_module(part, [p])[1])
  87
+            except ImportError:
  88
+                pass
  89
+        if not next_path:
  90
+            raise ImportError("No module named %s" % part)
  91
+        path = next_path
  92
+
  93
+    return path[0]
69 94
 
70 95
 def load_command_class(app_name, name):
71 96
     """
10  django/core/management/sql.py
@@ -145,15 +145,15 @@ def sql_all(app, style, connection):
145 145
 def _split_statements(content):
146 146
     comment_re = re.compile(r"^((?:'[^']*'|[^'])*?)--.*$")
147 147
     statements = []
148  
-    statement = ""
  148
+    statement = []
149 149
     for line in content.split("\n"):
150 150
         cleaned_line = comment_re.sub(r"\1", line).strip()
151 151
         if not cleaned_line:
152 152
             continue
153  
-        statement += cleaned_line
154  
-        if statement.endswith(";"):
155  
-            statements.append(statement)
156  
-            statement = ""
  153
+        statement.append(cleaned_line)
  154
+        if cleaned_line.endswith(";"):
  155
+            statements.append(" ".join(statement))
  156
+            statement = []
157 157
     return statements
158 158
 
159 159
 
2  django/db/models/query.py
@@ -1077,7 +1077,7 @@ def _setup_aggregate_query(self, aggregates):
1077 1077
 
1078 1078
     def _as_sql(self, connection):
1079 1079
         """
1080  
-        For ValueQuerySet (and subclasses like ValuesListQuerySet), they can
  1080
+        For ValuesQuerySet (and subclasses like ValuesListQuerySet), they can
1081 1081
         only be used as nested queries if they're already set up to select only
1082 1082
         a single field (in which case, that is the field column that is
1083 1083
         returned). This differs from QuerySet.as_sql(), where the column to
4  django/views/generic/edit.py
... ...
@@ -1,6 +1,7 @@
1 1
 from django.forms import models as model_forms
2 2
 from django.core.exceptions import ImproperlyConfigured
3 3
 from django.http import HttpResponseRedirect
  4
+from django.utils.encoding import force_text
4 5
 from django.views.generic.base import TemplateResponseMixin, ContextMixin, View
5 6
 from django.views.generic.detail import (SingleObjectMixin,
6 7
                         SingleObjectTemplateResponseMixin, BaseDetailView)
@@ -50,7 +51,8 @@ def get_success_url(self):
50 51
         Returns the supplied success URL.
51 52
         """
52 53
         if self.success_url:
53  
-            url = self.success_url
  54
+            # Forcing possible reverse_lazy evaluation
  55
+            url = force_text(self.success_url)
54 56
         else:
55 57
             raise ImproperlyConfigured(
56 58
                 "No URL to redirect to. Provide a success_url.")
5  docs/ref/settings.txt
@@ -1242,9 +1242,8 @@ Example::
1242 1242
         '/var/local/translations/locale'
1243 1243
     )
1244 1244
 
1245  
-Note that in the paths you add to the value of this setting, if you have the
1246  
-typical ``/path/to/locale/xx/LC_MESSAGES`` hierarchy, you should use the path to
1247  
-the ``locale`` directory (i.e. ``'/path/to/locale'``).
  1245
+Django will look within each of these paths for the ``<locale_code>/LC_MESSAGES``
  1246
+directories containing the actual translation files.
1248 1247
 
1249 1248
 .. setting:: LOGGING
1250 1249
 
58  tests/regressiontests/admin_filters/tests.py
@@ -78,6 +78,21 @@ class DecadeListFilterParameterEndsWith__Isnull(DecadeListFilter):
78 78
     parameter_name = 'decade__isnull' # Ends with '__isnull"
79 79
 
80 80
 
  81
+class DepartmentListFilterLookupWithNonStringValue(SimpleListFilter):
  82
+    title = 'department'
  83
+    parameter_name = 'department'
  84
+
  85
+    def lookups(self, request, model_admin):
  86
+        return set([
  87
+            (employee.department.id,  # Intentionally not a string (Refs #19318)
  88
+             employee.department.code)
  89
+            for employee in model_admin.queryset(request).all()
  90
+        ])
  91
+
  92
+    def queryset(self, request, queryset):
  93
+        if self.value():
  94
+            return queryset.filter(department__id=self.value())
  95
+
81 96
 class CustomUserAdmin(UserAdmin):
82 97
     list_filter = ('books_authored', 'books_contributed')
83 98
 
@@ -118,6 +133,10 @@ class EmployeeAdmin(ModelAdmin):
118 133
     list_filter = ['department']
119 134
 
120 135
 
  136
+class DepartmentFilterEmployeeAdmin(EmployeeAdmin):
  137
+    list_filter = [DepartmentListFilterLookupWithNonStringValue, ]
  138
+
  139
+
121 140
 class ListFiltersTests(TestCase):
122 141
 
123 142
     def setUp(self):
@@ -140,6 +159,14 @@ def setUp(self):
140 159
         self.gipsy_book.contributors = [self.bob, self.lisa]
141 160
         self.gipsy_book.save()
142 161
 
  162
+        # Departments
  163
+        self.dev = Department.objects.create(code='DEV', description='Development')
  164
+        self.design = Department.objects.create(code='DSN', description='Design')
  165
+
  166
+        # Employees
  167
+        self.john = Employee.objects.create(name='John Blue', department=self.dev)
  168
+        self.jack = Employee.objects.create(name='Jack Red', department=self.design)
  169
+
143 170
     def get_changelist(self, request, model, modeladmin):
144 171
         return ChangeList(request, model, modeladmin.list_display, modeladmin.list_display_links,
145 172
             modeladmin.list_filter, modeladmin.date_hierarchy, modeladmin.search_fields,
@@ -638,6 +665,28 @@ def test_parameter_ends_with__in__or__isnull(self):
638 665
         self.assertEqual(choices[2]['selected'], True)
639 666
         self.assertEqual(choices[2]['query_string'], '?decade__isnull=the+90s')
640 667
 
  668
+    def test_lookup_with_non_string_value(self):
  669
+        """
  670
+        Ensure choices are set the selected class when using non-string values
  671
+        for lookups in SimpleListFilters.
  672
+        Refs #19318
  673
+        """
  674
+
  675
+        modeladmin = DepartmentFilterEmployeeAdmin(Employee, site)
  676
+        request = self.request_factory.get('/', {'department': self.john.pk})
  677
+        changelist = self.get_changelist(request, Employee, modeladmin)
  678
+
  679
+        queryset = changelist.get_query_set(request)
  680
+
  681
+        self.assertEqual(list(queryset), [self.john])
  682
+
  683
+        filterspec = changelist.get_filters(request)[0][-1]
  684
+        self.assertEqual(force_text(filterspec.title), 'department')
  685
+        choices = list(filterspec.choices(changelist))
  686
+        self.assertEqual(choices[2]['display'], 'DEV')
  687
+        self.assertEqual(choices[2]['selected'], True)
  688
+        self.assertEqual(choices[2]['query_string'], '?department=%s' % self.john.pk)
  689
+
641 690
     def test_fk_with_to_field(self):
642 691
         """
643 692
         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):
645 694
         """
646 695
         modeladmin = EmployeeAdmin(Employee, site)
647 696
 
648  
-        dev = Department.objects.create(code='DEV', description='Development')
649  
-        design = Department.objects.create(code='DSN', description='Design')
650  
-        john = Employee.objects.create(name='John Blue', department=dev)
651  
-        jack = Employee.objects.create(name='Jack Red', department=design)
652  
-
653 697
         request = self.request_factory.get('/', {})
654 698
         changelist = self.get_changelist(request, Employee, modeladmin)
655 699
 
656 700
         # Make sure the correct queryset is returned
657 701
         queryset = changelist.get_query_set(request)
658  
-        self.assertEqual(list(queryset), [jack, john])
  702
+        self.assertEqual(list(queryset), [self.jack, self.john])
659 703
 
660 704
         filterspec = changelist.get_filters(request)[0][-1]
661 705
         self.assertEqual(force_text(filterspec.title), 'department')
@@ -680,7 +724,7 @@ def test_fk_with_to_field(self):
680 724
 
681 725
         # Make sure the correct queryset is returned
682 726
         queryset = changelist.get_query_set(request)
683  
-        self.assertEqual(list(queryset), [john])
  727
+        self.assertEqual(list(queryset), [self.john])
684 728
 
685 729
         filterspec = changelist.get_filters(request)[0][-1]
686 730
         self.assertEqual(force_text(filterspec.title), 'department')
0  tests/regressiontests/admin_scripts/lib1/nons_app/__init__.py
No changes.
0  tests/regressiontests/admin_scripts/lib1/nons_app/management/__init__.py
No changes.
0  tests/regressiontests/admin_scripts/lib1/nons_app/management/commands/__init__.py
No changes.
9  tests/regressiontests/admin_scripts/lib1/nons_app/management/commands/nons_app_command1.py
... ...
@@ -0,0 +1,9 @@
  1
+from django.core.management.base import BaseCommand
  2
+
  3
+class Command(BaseCommand):
  4
+    help = 'Test managment commands in non-namespaced app'
  5
+    requires_model_validation = False
  6
+    args = ''
  7
+
  8
+    def handle(self, *labels, **options):
  9
+        print 'EXECUTE:nons_app_command1'
0  tests/regressiontests/admin_scripts/lib1/nons_app/models.py
No changes.
0  tests/regressiontests/admin_scripts/lib1/npapp/__init__.py
No changes.
0  tests/regressiontests/admin_scripts/lib1/npapp/management.py
No changes.
0  tests/regressiontests/admin_scripts/lib1/npapp/models.py
No changes.
6  tests/regressiontests/admin_scripts/lib1/nsapps/__init__.py
... ...
@@ -0,0 +1,6 @@
  1
+# http://packages.python.org/distribute/setuptools.html#namespace-packages
  2
+try:
  3
+    __import__('pkg_resources').declare_namespace(__name__)
  4
+except ImportError:
  5
+    from pkgutil import extend_path
  6
+    __path__ = extend_path(__path__, __name__)
6  tests/regressiontests/admin_scripts/lib1/nsapps/contrib/__init__.py
... ...
@@ -0,0 +1,6 @@
  1
+# http://packages.python.org/distribute/setuptools.html#namespace-packages
  2
+try:
  3
+    __import__('pkg_resources').declare_namespace(__name__)
  4
+except ImportError:
  5
+    from pkgutil import extend_path
  6
+    __path__ = extend_path(__path__, __name__)
9  tests/regressiontests/admin_scripts/lib1/nsapps/contrib/app1/management/commands/app1_command1.py
... ...
@@ -0,0 +1,9 @@
  1
+from django.core.management.base import BaseCommand
  2
+
  3
+class Command(BaseCommand):
  4
+    help = 'Test managment commands in namespaced apps'
  5
+    requires_model_validation = False
  6
+    args = ''
  7
+
  8
+    def handle(self, *labels, **options):
  9
+        print 'EXECUTE:app1_command1'
6  tests/regressiontests/admin_scripts/lib2/nsapps/__init__.py
... ...
@@ -0,0 +1,6 @@
  1
+# http://packages.python.org/distribute/setuptools.html#namespace-packages
  2
+try:
  3
+    __import__('pkg_resources').declare_namespace(__name__)
  4
+except ImportError:
  5
+    from pkgutil import extend_path
  6
+    __path__ = extend_path(__path__, __name__)
6  tests/regressiontests/admin_scripts/lib2/nsapps/contrib/__init__.py
... ...
@@ -0,0 +1,6 @@
  1
+# http://packages.python.org/distribute/setuptools.html#namespace-packages
  2
+try:
  3
+    __import__('pkg_resources').declare_namespace(__name__)
  4
+except ImportError:
  5
+    from pkgutil import extend_path
  6
+    __path__ = extend_path(__path__, __name__)
9  tests/regressiontests/admin_scripts/lib2/nsapps/contrib/app2/management/commands/app2_command1.py
... ...
@@ -0,0 +1,9 @@
  1
+from django.core.management.base import BaseCommand
  2
+
  3
+class Command(BaseCommand):
  4
+    help = 'Test managment commands in namespaced apps'
  5
+    requires_model_validation = False
  6
+    args = ''
  7
+
  8
+    def handle(self, *labels, **options):
  9
+        print 'EXECUTE:app2_command1'
1  tests/regressiontests/admin_scripts/lib3/_addsitedir.py
... ...
@@ -0,0 +1 @@
  1
+import os.path, site; site.addsitedir(os.path.dirname(__file__))
1  tests/regressiontests/admin_scripts/lib3/exapps-nspkg.pth
... ...
@@ -0,0 +1 @@
  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)
0  tests/regressiontests/admin_scripts/lib3/exapps/app3/__init__.py
No changes.
0  tests/regressiontests/admin_scripts/lib3/exapps/app3/management/__init__.py
No changes.
0  tests/regressiontests/admin_scripts/lib3/exapps/app3/management/commands/__init__.py
No changes.
9  tests/regressiontests/admin_scripts/lib3/exapps/app3/management/commands/app3_command1.py
... ...
@@ -0,0 +1,9 @@
  1
+from django.core.management.base import BaseCommand
  2
+
  3
+class Command(BaseCommand):
  4
+    help = 'Test managment commands in namespaced apps'
  5
+    requires_model_validation = False
  6
+    args = ''
  7
+
  8
+    def handle(self, *labels, **options):
  9
+        print 'EXECUTE:app3_command1'
0  tests/regressiontests/admin_scripts/lib3/exapps/app3/models.py
No changes.
93  tests/regressiontests/admin_scripts/tests.py
@@ -96,6 +96,9 @@ def _ext_backend_paths(self):
96 96
     def run_test(self, script, args, settings_file=None, apps=None):
97 97
         project_dir = os.path.dirname(test_dir)
98 98
         base_dir = os.path.dirname(project_dir)
  99
+        lib1_dir = os.path.join(os.path.dirname(__file__), 'lib1')
  100
+        lib2_dir = os.path.join(os.path.dirname(__file__), 'lib2')
  101
+        lib3_dir = os.path.join(os.path.dirname(__file__), 'lib3')
99 102
         ext_backend_base_dirs = self._ext_backend_paths()
100 103
 
101 104
         # Remember the old environment
@@ -113,7 +116,7 @@ def run_test(self, script, args, settings_file=None, apps=None):
113 116
             os.environ['DJANGO_SETTINGS_MODULE'] = settings_file
114 117
         elif 'DJANGO_SETTINGS_MODULE' in os.environ:
115 118
             del os.environ['DJANGO_SETTINGS_MODULE']
116  
-        python_path = [project_dir, base_dir]
  119
+        python_path = [project_dir, base_dir, lib1_dir, lib2_dir, lib3_dir]
117 120
         python_path.extend(ext_backend_base_dirs)
118 121
         os.environ[python_path_var_name] = os.pathsep.join(python_path)
119 122
 
@@ -1613,3 +1616,91 @@ def test_basic(self):
1613 1616
         out, err = self.run_manage(args)
1614 1617
         self.assertNoOutput(err)
1615 1618
         self.assertOutput(out, "FOO = 'bar'  ###")
  1619
+
  1620
+
  1621
+class NamespacePackagedApps(AdminScriptTestCase):
  1622
+    def setUp(self):
  1623
+        self.write_settings('settings.py', apps=['nons_app', 'nsapps.contrib.app1','nsapps.contrib.app2','exapps.app3'])
  1624
+        test_dir = os.path.dirname(os.path.dirname(__file__))
  1625
+        settings_file = open(os.path.join(test_dir, 'settings.py'), 'a')
  1626
+        settings_file.write('import _addsitedir')
  1627
+        settings_file.close()
  1628
+
  1629
+    def tearDown(self):
  1630
+        self.remove_settings('settings.py')
  1631
+
  1632
+    def test_help(self):
  1633
+        out, err = self.run_manage(['help'])
  1634
+        self.assertNoOutput(err)
  1635
+        self.assertOutput(out, "nons_app_command1")
  1636
+        self.assertOutput(out, "app1_command1")
  1637
+        self.assertOutput(out, "app2_command1")
  1638
+        self.assertOutput(out, "app3_command1")
  1639
+
  1640
+    def test_nons_app(self):
  1641
+        args = ['nons_app_command1']
  1642
+        out, err = self.run_manage(args)
  1643
+        self.assertNoOutput(err)
  1644
+        self.assertOutput(out, "EXECUTE:nons_app_command1")
  1645
+
  1646
+    def test_nsapps(self):
  1647
+        args = ['app1_command1']
  1648
+        out, err = self.run_manage(args)
  1649
+        self.assertNoOutput(err)
  1650
+        self.assertOutput(out, "EXECUTE:app1_command1")
  1651
+
  1652
+        args = ['app2_command1']
  1653
+        out, err = self.run_manage(args)
  1654
+        self.assertNoOutput(err)
  1655
+        self.assertOutput(out, "EXECUTE:app2_command1")
  1656
+
  1657
+    def test_exapps(self):
  1658
+        args = ['app3_command1']
  1659
+        out, err = self.run_manage(args)
  1660
+        self.assertNoOutput(err)
  1661
+        self.assertOutput(out, "EXECUTE:app3_command1")
  1662
+
  1663
+class PreloadedNamespacePackagedApps(AdminScriptTestCase):
  1664
+    def setUp(self):
  1665
+        self.write_settings('settings.py', apps=['nsapps.contrib.app1','nsapps.contrib.app2'])
  1666
+        test_dir = os.path.dirname(os.path.dirname(__file__))
  1667
+        settings_file = open(os.path.join(test_dir, 'settings.py'), 'a')
  1668
+        settings_file.write('import nsapps')
  1669
+        settings_file.close()
  1670
+
  1671
+    def tearDown(self):
  1672
+        self.remove_settings('settings.py')
  1673
+
  1674
+    def test_help(self):
  1675
+        out, err = self.run_manage(['help'])
  1676
+        self.assertNoOutput(err)
  1677
+        self.assertOutput(out, "app1_command1")
  1678
+        self.assertOutput(out, "app2_command1")
  1679
+
  1680
+    def test_nsapps(self):
  1681
+        args = ['app1_command1']
  1682
+        out, err = self.run_manage(args)
  1683
+        self.assertNoOutput(err)
  1684
+        self.assertOutput(out, "EXECUTE:app1_command1")
  1685
+
  1686
+        args = ['app2_command1']
  1687
+        out, err = self.run_manage(args)
  1688
+        self.assertNoOutput(err)
  1689
+        self.assertOutput(out, "EXECUTE:app2_command1")
  1690
+
  1691
+
  1692
+class NonPackageManagementApps(AdminScriptTestCase):
  1693
+    def setUp(self):
  1694
+        self.write_settings('settings.py', apps=['npapp'])
  1695
+        settings_file = open(os.path.join(test_dir, 'settings.py'), 'a')
  1696
+        settings_file.write('import npapp.management')
  1697
+        settings_file.close()
  1698
+
  1699
+    def tearDown(self):
  1700
+        self.remove_settings('settings.py')
  1701
+
  1702
+    def test_help(self):
  1703
+        out, err = self.run_manage(['help'])
  1704
+        self.assertNoOutput(err)
  1705
+
  1706
+
9  tests/regressiontests/admin_views/customadmin.py
@@ -16,7 +16,7 @@ class Admin2(admin.AdminSite):
16 16
     login_form = forms.CustomAdminAuthenticationForm
17 17
     login_template = 'custom_admin/login.html'
18 18
     logout_template = 'custom_admin/logout.html'
19  
-    index_template = 'custom_admin/index.html'
  19
+    index_template = ['custom_admin/index.html'] # a list, to test fix for #18697
20 20
     password_change_template = 'custom_admin/password_change_form.html'
21 21
     password_change_done_template = 'custom_admin/password_change_done.html'
22 22
 
@@ -40,6 +40,10 @@ def queryset(self, request):
40 40
         return qs.filter(is_superuser=False)
41 41
 
42 42
 
  43
+class CustomPwdTemplateUserAdmin(UserAdmin):
  44
+    change_user_password_template = ['admin/auth/user/change_password.html'] # a list, to test fix for #18697
  45
+
  46
+
43 47
 site = Admin2(name="admin2")
44 48
 
45 49
 site.register(models.Article, base_admin.ArticleAdmin)
@@ -50,3 +54,6 @@ def queryset(self, request):
50 54
 site.register(User, UserLimitedAdmin)
51 55
 site.register(models.UndeletableObject, base_admin.UndeletableObjectAdmin)
52 56
 site.register(models.Simple, base_admin.AttributeErrorRaisingAdmin)
  57
+
  58
+simple_site = Admin2(name='admin4')
  59
+simple_site.register(User, CustomPwdTemplateUserAdmin)
4  tests/regressiontests/admin_views/models.py
@@ -662,5 +662,5 @@ class Simple(models.Model):
662 662
     """
663 663
 
664 664
 class Choice(models.Model):
665  
-    choice = models.CharField(max_length=1, blank=True, null=True,
666  
-        choices=(('y','Yes'), ('n','No'), (None, 'No opinion')))
  665
+    choice = models.IntegerField(blank=True, null=True,
  666
+        choices=((1, 'Yes'), (0, 'No'), (None, 'No opinion')))
14  tests/regressiontests/admin_views/tests.py
@@ -770,7 +770,10 @@ def testCustomAdminSiteLogoutTemplate(self):
770 770
         self.assertContains(response, 'Hello from a custom logout template')
771 771
 
772 772
     def testCustomAdminSiteIndexViewAndTemplate(self):
773  
-        response = self.client.get('/test_admin/admin2/')
  773
+        try:
  774
+            response = self.client.get('/test_admin/admin2/')
  775
+        except TypeError:
  776
+            self.fail('AdminSite.index_template should accept a list of template paths')
774 777
         self.assertIsInstance(response, TemplateResponse)
775 778
         self.assertTemplateUsed(response, 'custom_admin/index.html')
776 779
         self.assertContains(response, 'Hello from a custom index template *bar*')
@@ -792,6 +795,15 @@ def testCustomAdminSiteView(self):
792 795
         response = self.client.get('/test_admin/%s/my_view/' % self.urlbit)
793 796
         self.assertEqual(response.content, b"Django is a magical pony!")
794 797
 
  798
+    def test_pwd_change_custom_template(self):
  799
+        self.client.login(username='super', password='secret')
  800
+        su = User.objects.get(username='super')
  801
+        try:
  802
+            response = self.client.get('/test_admin/admin4/auth/user/%s/password/' % su.pk)
  803
+        except TypeError:
  804
+            self.fail('ModelAdmin.change_user_password_template should accept a list of template paths')
  805
+        self.assertEqual(response.status_code, 200)
  806
+
795 807
 
796 808
 def get_perm(Model, perm):
797 809
     """Return the permission object, for the Model"""
1  tests/regressiontests/admin_views/urls.py
@@ -11,4 +11,5 @@
11 11
     (r'^test_admin/admin/', include(admin.site.urls)),
12 12
     (r'^test_admin/admin2/', include(customadmin.site.urls)),
13 13
     (r'^test_admin/admin3/', include(admin.site.urls), dict(form_url='pony')),
  14
+    (r'^test_admin/admin4/', include(customadmin.simple_site.urls)),
14 15
 )
9  tests/regressiontests/generic_views/edit.py
@@ -20,6 +20,15 @@ def test_initial_data(self):
20 20
          initial_2 = FormMixin().get_initial()
21 21
          self.assertNotEqual(initial_1, initial_2)
22 22
 
  23
+
  24
+class BasicFormTests(TestCase):
  25
+    urls = 'regressiontests.generic_views.urls'
  26
+
  27
+    def test_post_data(self):
  28
+        res = self.client.post('/contact/', {'name': "Me", 'message': "Hello"})
  29
+        self.assertRedirects(res, 'http://testserver/list/authors/')
  30
+
  31
+
23 32
 class ModelFormMixinTests(TestCase):
24 33
     def test_get_form(self):
25 34
         form_class = views.AuthorGetQuerySetFormView().get_form_class()
5  tests/regressiontests/generic_views/forms.py
@@ -11,3 +11,8 @@ class AuthorForm(forms.ModelForm):
11 11
 
12 12
     class Meta:
13 13
         model = Author
  14
+
  15
+
  16
+class ContactForm(forms.Form):
  17
+    name = forms.CharField()
  18
+    message = forms.CharField(widget=forms.Textarea)
4  tests/regressiontests/generic_views/tests.py
@@ -6,6 +6,6 @@
6 6
     MonthArchiveViewTests, WeekArchiveViewTests, DayArchiveViewTests,
7 7
     DateDetailViewTests)
8 8
 from .detail import DetailViewTest
9  
-from .edit import (FormMixinTests, ModelFormMixinTests, CreateViewTests,
10  
-    UpdateViewTests, DeleteViewTests)
  9
+from .edit import (FormMixinTests, BasicFormTests, ModelFormMixinTests,
  10
+    CreateViewTests, UpdateViewTests, DeleteViewTests)
11 11
 from .list import ListViewTests
4  tests/regressiontests/generic_views/urls.py
@@ -56,6 +56,10 @@
56 56
     (r'^detail/nonmodel/1/$',
57 57
         views.NonModelDetail.as_view()),
58 58
 
  59
+    # FormView
  60
+    (r'^contact/$',
  61
+        views.ContactView.as_view()),
  62
+
59 63
     # Create/UpdateView
60 64
     (r'^edit/artists/create/$',
61 65
         views.ArtistCreate.as_view()),
11  tests/regressiontests/generic_views/views.py
@@ -2,11 +2,11 @@
2 2
 
3 3
 from django.contrib.auth.decorators import login_required
4 4
 from django.core.paginator import Paginator
5  
-from django.core.urlresolvers import reverse
  5
+from django.core.urlresolvers import reverse, reverse_lazy
6 6
 from django.utils.decorators import method_decorator
7 7
 from django.views import generic
8 8
 
9  
-from .forms import AuthorForm
  9
+from .forms import AuthorForm, ContactForm
10 10
 from .models import Artist, Author, Book, Page, BookSigning
11 11
 
12 12
 
@@ -75,6 +75,13 @@ def get_paginator(self, queryset, page_size, orphans=0, allow_empty_first_page=T
75 75
             orphans=2,
76 76
             allow_empty_first_page=allow_empty_first_page)
77 77
 
  78
+
  79
+class ContactView(generic.FormView):
  80
+    form_class = ContactForm
  81
+    success_url = reverse_lazy('authors_list')
  82
+    template_name = 'generic_views/form.html'
  83
+
  84
+
78 85
 class ArtistCreate(generic.CreateView):
79 86
     model = Artist
80 87
 
3  tests/regressiontests/initial_sql_regress/sql/simple.sql
@@ -2,7 +2,8 @@
2 2
 INSERT INTO initial_sql_regress_simple (name) VALUES ('John'); -- another comment
3 3
 INSERT INTO initial_sql_regress_simple (name) VALUES ('-- Comment Man');
4 4
 INSERT INTO initial_sql_regress_simple (name) VALUES ('Paul');
5  
-INSERT INTO initial_sql_regress_simple (name) VALUES ('Ringo');
  5
+INSERT INTO initial_sql_regress_simple
  6
+    VALUES (150, 'Ringo');
6 7
 INSERT INTO initial_sql_regress_simple (name) VALUES ('George');
7 8
 INSERT INTO initial_sql_regress_simple (name) VALUES ('Miles O''Brien');
8 9
 INSERT INTO initial_sql_regress_simple (name) VALUES ('Semicolon;Man');
Commit_comment_tip

Tip: You can add notes to lines in a file. Hover to the left of a line to make a note

Something went wrong with that request. Please try again.