Browse Source

Merge branch 'dev'

Denis K 8 years ago
parent
commit
05b543368c
34 changed files with 236 additions and 87 deletions
  1. 32 15
      .travis.yml
  2. 14 0
      CHANGELOG.rst
  3. 79 28
      docs/config_file.rst
  4. 1 1
      jet/__init__.py
  5. 9 8
      jet/dashboard/dashboard.py
  6. 5 1
      jet/dashboard/dashboard_modules/google_analytics.py
  7. 5 1
      jet/dashboard/dashboard_modules/google_analytics_views.py
  8. 5 1
      jet/dashboard/dashboard_modules/yandex_metrika.py
  9. 5 1
      jet/dashboard/dashboard_modules/yandex_metrika_views.py
  10. 2 2
      jet/dashboard/modules.py
  11. 5 1
      jet/dashboard/views.py
  12. 4 1
      jet/filters.py
  13. 1 1
      jet/management/commands/jet_side_menu_items_example.py
  14. 1 1
      jet/static/jet/css/_sidebar.scss
  15. 0 0
      jet/static/jet/css/themes/default/base.css
  16. 0 0
      jet/static/jet/css/themes/default/base.css.map
  17. 0 0
      jet/static/jet/css/themes/green/base.css
  18. 0 0
      jet/static/jet/css/themes/green/base.css.map
  19. 0 0
      jet/static/jet/css/themes/light-blue/base.css
  20. 0 0
      jet/static/jet/css/themes/light-blue/base.css.map
  21. 0 0
      jet/static/jet/css/themes/light-gray/base.css
  22. 0 0
      jet/static/jet/css/themes/light-gray/base.css.map
  23. 0 0
      jet/static/jet/css/themes/light-green/base.css
  24. 0 0
      jet/static/jet/css/themes/light-green/base.css.map
  25. 0 0
      jet/static/jet/css/themes/light-violet/base.css
  26. 0 0
      jet/static/jet/css/themes/light-violet/base.css.map
  27. 0 0
      jet/static/jet/css/vendor.css
  28. 3 3
      jet/templates/admin/base.html
  29. 6 3
      jet/templatetags/jet_tags.py
  30. 5 1
      jet/tests/test_dashboard.py
  31. 10 6
      jet/tests/test_tags.py
  32. 5 1
      jet/tests/test_views.py
  33. 32 11
      jet/utils.py
  34. 7 0
      setup.py

+ 32 - 15
.travis.yml

@@ -5,18 +5,21 @@ python:
   - 3.2
   - 3.3
   - 3.4
+  - 3.5
+  - 3.6
   - pypy
 env:
-  - DJANGO=1.6.11
-  - DJANGO=1.7.7
-  - DJANGO=1.8.3
-  - DJANGO=1.9.8
-  - DJANGO=1.10.0
+  - DJANGO="<1.7"
+  - DJANGO="<1.8"
+  - DJANGO="<1.9"
+  - DJANGO="<1.10"
+  - DJANGO="<1.11"
+  - DJANGO="<1.12"
 before_install:
   - export DJANGO_SETTINGS_MODULE=jet.tests.settings
 install:
-  - pip install -q Django==$DJANGO --use-mirrors
-  - pip install . --use-mirrors
+  - pip install -q "Django${DJANGO}"
+  - pip install .
   - pip install coverage==3.7.1
   - pip install coveralls==0.5
 script:
@@ -27,18 +30,32 @@ after_success:
 matrix:
   exclude:
     - python: 2.6
-      env: DJANGO=1.7.7
+      env: DJANGO="<1.8"
     - python: 2.6
-      env: DJANGO=1.8.3
+      env: DJANGO="<1.9"
     - python: 2.6
-      env: DJANGO=1.9.8
+      env: DJANGO="<1.10"
     - python: 3.2
-      env: DJANGO=1.9.8
+      env: DJANGO="<1.10"
     - python: 3.3
-      env: DJANGO=1.9.8
+      env: DJANGO="<1.10"
     - python: 2.6
-      env: DJANGO=1.10.0
+      env: DJANGO="<1.11"
     - python: 3.2
-      env: DJANGO=1.10.0
+      env: DJANGO="<1.11"
     - python: 3.3
-      env: DJANGO=1.10.0
+      env: DJANGO="<1.11"
+    - python: 2.6
+      env: DJANGO="<1.12"
+    - python: 3.2
+      env: DJANGO="<1.12"
+    - python: 3.3
+      env: DJANGO="<1.12"
+    - python: 3.5
+      env: DJANGO="<1.7"
+    - python: 3.5
+      env: DJANGO="<1.8"
+    - python: 3.6
+      env: DJANGO="<1.7"
+    - python: 3.6
+      env: DJANGO="<1.8"

+ 14 - 0
CHANGELOG.rst

@@ -1,6 +1,20 @@
 Changelog
 =========
 
+1.0.6
+-----
+* PR-191: Added sidebar pinning functionality (thanks to grigory51 for PR)
+* Issue-199: Fixed Django 1.11 context issue (thanks to gileadslostson for report)
+* Issue-202: Fixed inline-group-row:added event (thanks to a1Gupta for report)
+* Issue-188: Make testing use latest major Django versions and Python 3.5, 3.6 (thanks to liminspace for report)
+* Added new flexible menu customizing setting JET_SIDE_MENU_ITEMS
+* Added labels to sibling buttons
+* Fixed django.jQuery select change events
+* Fixed sidebar "Search..." label localization
+* Added select disabled style
+* Fixed initial value for select2 ajax fields when POST request
+
+
 1.0.5
 -----
 * PR-167: Added fallback to window.opener to support old Django popups (thanks to michaelkuty for PR)

+ 79 - 28
docs/config_file.rst

@@ -95,61 +95,112 @@ CUSTOM MENU
 -----------
 
 By default JET displays all applications and it models in the side menu in the alphabetical order.
-To display applications and models you want or to change their order you can use ``JET_SIDE_MENU_CUSTOM_APPS`` setting.
+To display applications and models you want or to change their order you can use ``JET_SIDE_MENU_ITEMS`` setting.
 
 .. code:: python
 
-    JET_SIDE_MENU_CUSTOM_APPS = [
-        ('core', [ # Each list element is a tuple with application name (app_label) and list of models
-            'User',
-            'MenuItem',
-            'Block',
-        ]),
-        ('shops', [
-            'Shop',
-            'City',
-            'MetroStation',
-        ]),
-        ('feedback', [
-            'Feedback',
-        ]),
+    JET_SIDE_MENU_ITEMS = [  # A list of application or custom item dicts
+        {'label': _('General'), 'app_label': 'core', 'items': [
+            {'name': 'help.question'},
+            {'name': 'pages.page', 'label': _('Static page')},
+            {'name': 'city'},
+            {'name': 'validationcode'},
+            {'label': _('Analytics'), 'url': 'http://example.com', 'url_blank': True},
+        ]},
+        {'label': _('Users'), 'items': [
+            {'name': 'core.user'},
+            {'name': 'auth.group'},
+            {'name': 'core.userprofile', 'permissions': ['core.user']},
+        ]},
+        {'app_label': 'banners', 'items': [
+            {'name': 'banner'},
+            {'name': 'bannertype'},
+        ]},
     ]
 
-If want to show all application's models use ``__all__`` keyword.
+JET_SIDE_MENU_ITEMS is a list of application or custom item dicts. Each item can have the following keys:
 
-.. code:: python
+* `app_label` - application name
+* `label` - application text label
+* `items` - list of children items
+* `url` - custom url (format is described below)
+* `url_blank` - open url in new table (boolean)
+* `permissions` - list of required permissions to display item
 
-    JET_SIDE_MENU_CUSTOM_APPS = [
-        ('core', ['__all__']),
-        ...
-    ]
+Setting `items` and either `app_label` or `label` is required. Other keys are optional to override default behavior.
+Order of items is respected. Each menu item is also a dict with the following keys:
+
+* `name` - model name (can be either `MODEL_NAME` or `APP_LABEL.MODEL_NAME`)
+* `label` - item text label
+* `url` - custom url (format is described below)
+* `url_blank` - open url in new table (boolean)
+* `permissions` - list of required permissions to display item
+
+Setting either `name` or `label` is required. Other keys are optional to override default behavior.
+Order of items is respected.
+
+URLs can be either `string` or `dict`. Examples of possible values:
+
+* http://example.com/
+* {'type': 'app', 'app_label': 'pages'}
+* {'type': 'model', 'app_label': 'pages', 'model': 'page'}
+* {'type': 'reverse', 'name': 'pages:list', 'args': [1], 'kwargs': {'category': 2}}
+
+.. deprecated:: 1.0.6
+
+    Old way of customizing menu items via `JET_SIDE_MENU_CUSTOM_APPS` setting is now deprecated in favor
+    of new `JET_SIDE_MENU_ITEMS` setting.
+
+    .. code:: python
+
+        JET_SIDE_MENU_CUSTOM_APPS = [
+            ('core', [ # Each list element is a tuple with application name (app_label) and list of models
+                'User',
+                'MenuItem',
+                'Block',
+            ]),
+            ('shops', [
+                'Shop',
+                'City',
+                'MetroStation',
+            ]),
+            ('feedback', [
+                'Feedback',
+            ]),
+        ]
 
 If have multiple admin sites and you want to specify different menu applications for each admin site, wrap menu lists
 in dictionary with admin site names as keys:
 
 .. code:: python
 
-    JET_SIDE_MENU_CUSTOM_APPS = {
+    JET_SIDE_MENU_ITEMS = {
         'admin': [
-            ('core', ['__all__']),
+            {'label': _('General'), 'app_label': 'core', 'items': [
+                {'name': 'help.question'},
+                {'name': 'pages.page'},
+                {'name': 'city'},
+                {'name': 'validationcode'},
+            ]},
             ...
         ],
         'custom_admin': [
-            ('custom_admin_app', [
-                'CustomAdminAppModel',
-            ]),
+            {'app_label': 'talks', 'items': [
+                {'name': 'talk'},
+                {'name': 'talkmessage'},
+            ]},
             ...
         ]
     }
 
 .. note::
 
-    You can use ``jet_custom_apps_example`` management command to generate example ``JET_SIDE_MENU_CUSTOM_APPS``
+    You can use ``jet_side_menu_items_example`` management command to generate example ``JET_SIDE_MENU_ITEMS``
     setting which includes all your applications and models. You can use it this way:
 
     .. code:: python
 
-        python manage.py jet_custom_apps_example
+        python manage.py jet_side_menu_items_example
 
 JET_CHANGE_FORM_SIBLING_LINKS
 -----------------------------

+ 1 - 1
jet/__init__.py

@@ -1 +1 @@
-VERSION = '1.0.5'
+VERSION = '1.0.6'

+ 9 - 8
jet/dashboard/dashboard.py

@@ -1,12 +1,16 @@
-from django.template import Context
 from importlib import import_module
-from django.core.urlresolvers import reverse
+try:
+    from django.core.urlresolvers import reverse
+except ImportError: # Django 1.11
+    from django.urls import reverse
+
 from django.template.loader import render_to_string
 from jet.dashboard import modules
 from jet.dashboard.models import UserDashboardModule
 from django.utils.translation import ugettext_lazy as _
 from jet.ordered_set import OrderedSet
-from jet.utils import get_admin_site_name
+from jet.utils import get_admin_site_name, context_to_dict
+
 try:
     from django.template.context_processors import csrf
 except ImportError:
@@ -48,9 +52,6 @@ class Dashboard(object):
         self.set_context(context)
 
     def set_context(self, context):
-        if isinstance(context, Context):
-            context = context.flatten()
-
         self.context = context
         self.init_with_context(context)
         self.load_modules()
@@ -150,7 +151,7 @@ class Dashboard(object):
         self.modules = loaded_modules
 
     def render(self):
-        context = self.context
+        context = context_to_dict(self.context)
         context.update({
             'columns': range(self.columns),
             'modules': self.modules,
@@ -161,7 +162,7 @@ class Dashboard(object):
         return render_to_string('jet.dashboard/dashboard.html', context)
 
     def render_tools(self):
-        context = self.context
+        context = context_to_dict(self.context)
         context.update({
             'children': self.children,
             'app_label': self.app_label,

+ 5 - 1
jet/dashboard/dashboard_modules/google_analytics.py

@@ -2,7 +2,11 @@
 import datetime
 import json
 from django import forms
-from django.core.urlresolvers import reverse
+try:
+    from django.core.urlresolvers import reverse
+except ImportError: # Django 1.11
+    from django.urls import reverse
+
 from django.forms import Widget
 from django.utils import formats
 from django.utils.html import format_html

+ 5 - 1
jet/dashboard/dashboard_modules/google_analytics_views.py

@@ -1,4 +1,8 @@
-from django.core.urlresolvers import reverse
+try:
+    from django.core.urlresolvers import reverse
+except ImportError: # Django 1.11
+    from django.urls import reverse
+
 from django.conf.urls import url
 from django.contrib import messages
 from django.shortcuts import redirect

+ 5 - 1
jet/dashboard/dashboard_modules/yandex_metrika.py

@@ -2,7 +2,11 @@
 import datetime
 import json
 from django import forms
-from django.core.urlresolvers import reverse
+try:
+    from django.core.urlresolvers import reverse
+except ImportError: # Django 1.11
+    from django.urls import reverse
+
 from django.forms import Widget
 from django.utils import formats
 from django.utils.html import format_html

+ 5 - 1
jet/dashboard/dashboard_modules/yandex_metrika_views.py

@@ -1,6 +1,10 @@
 from django.conf.urls import url
 from django.contrib import messages
-from django.core.urlresolvers import reverse
+try:
+    from django.core.urlresolvers import reverse
+except ImportError: # Django 1.11
+    from django.urls import reverse
+
 from django.http import HttpResponse
 from django.shortcuts import redirect
 from jet.dashboard.dashboard_modules.yandex_metrika import YandexMetrikaClient

+ 2 - 2
jet/dashboard/modules.py

@@ -4,7 +4,7 @@ from django.contrib.admin.models import LogEntry
 from django.db.models import Q
 from django.template.loader import render_to_string
 from django.utils.translation import ugettext_lazy as _
-from jet.utils import get_app_list, LazyDateTimeEncoder
+from jet.utils import get_app_list, LazyDateTimeEncoder, context_to_dict
 import datetime
 
 
@@ -148,7 +148,7 @@ class DashboardModule(object):
         pass
 
     def get_context_data(self):
-        context = self.context
+        context = context_to_dict(self.context)
         context.update({
             'module': self
         })

+ 5 - 1
jet/dashboard/views.py

@@ -1,6 +1,10 @@
 from django.contrib import messages
 from django.core.exceptions import ValidationError
-from django.core.urlresolvers import reverse
+try:
+    from django.core.urlresolvers import reverse
+except ImportError: # Django 1.11
+    from django.urls import reverse
+
 from django.forms.formsets import formset_factory
 from django.http import HttpResponseRedirect
 from django.views.decorators.http import require_POST, require_GET

+ 4 - 1
jet/filters.py

@@ -1,7 +1,10 @@
 from django.contrib.admin import RelatedFieldListFilter
 from django.utils.encoding import smart_text
 from django.utils.html import format_html
-from django.core.urlresolvers import reverse
+try:
+    from django.core.urlresolvers import reverse
+except ImportError: # Django 1.11
+    from django.urls import reverse
 
 try:
     from django.contrib.admin.utils import get_model_from_relation

+ 1 - 1
jet/management/commands/jet_side_menu_items_example.py

@@ -39,7 +39,7 @@ class Command(NoArgsCommand):
         self.stdout.write('JET_SIDE_MENU_ITEMS = [')
 
         for app in app_list:
-            self.stdout.write('    {\'app_label\': \'%s\', \'models\': [' % (
+            self.stdout.write('    {\'app_label\': \'%s\', \'items\': [' % (
                 app['app_label']
             ))
 

+ 1 - 1
jet/static/jet/css/_sidebar.scss

@@ -552,7 +552,7 @@
       color: $hover-link-color;
     }
 
-    body.menu-pinned & {
+    body.login &, body.menu-pinned & {
       display: none;
     }
 

File diff suppressed because it is too large
+ 0 - 0
jet/static/jet/css/themes/default/base.css


File diff suppressed because it is too large
+ 0 - 0
jet/static/jet/css/themes/default/base.css.map


File diff suppressed because it is too large
+ 0 - 0
jet/static/jet/css/themes/green/base.css


File diff suppressed because it is too large
+ 0 - 0
jet/static/jet/css/themes/green/base.css.map


File diff suppressed because it is too large
+ 0 - 0
jet/static/jet/css/themes/light-blue/base.css


File diff suppressed because it is too large
+ 0 - 0
jet/static/jet/css/themes/light-blue/base.css.map


File diff suppressed because it is too large
+ 0 - 0
jet/static/jet/css/themes/light-gray/base.css


File diff suppressed because it is too large
+ 0 - 0
jet/static/jet/css/themes/light-gray/base.css.map


File diff suppressed because it is too large
+ 0 - 0
jet/static/jet/css/themes/light-green/base.css


File diff suppressed because it is too large
+ 0 - 0
jet/static/jet/css/themes/light-green/base.css.map


File diff suppressed because it is too large
+ 0 - 0
jet/static/jet/css/themes/light-violet/base.css


File diff suppressed because it is too large
+ 0 - 0
jet/static/jet/css/themes/light-violet/base.css.map


File diff suppressed because it is too large
+ 0 - 0
jet/static/jet/css/vendor.css


+ 3 - 3
jet/templates/admin/base.html

@@ -36,7 +36,7 @@
 </head>
 {% load i18n %}
 
-<body class="{% if request.COOKIES.sidebar_pinned == 'true' %}menu-pinned {% endif %}{% if is_popup %}popup {% endif %}{% block bodyclass %}{% endblock %}"
+<body class="{% if request.COOKIES.sidebar_pinned != 'false' %}menu-pinned {% endif %}{% if is_popup %}popup {% endif %}{% block bodyclass %}{% endblock %}"
   data-admin-utc-offset="{% now "Z" %}">
 
 <!-- Container -->
@@ -202,7 +202,7 @@
                                             {{ app.label }}
                                         </a>
                                     </div>
-                                    {% for model in app.models %}
+                                    {% for model in app.items %}
                                         {% if model.has_perms %}
                                             <div>
                                                 <a{% if model.url %} href="{{ model.url }}"{% endif %} class="sidebar-link"{% if model.url_blank %} target="_blank"{% endif %}>
@@ -346,7 +346,7 @@
                                             </li>
                                         {% endif %}
 
-                                        {% for model in app.models %}
+                                        {% for model in app.items %}
                                             {% if model.has_perms %}
                                                 <li class="sidebar-popup-list-item{% if model.name %} model-{{ model.name }}{% endif %}{{ model.current|yesno:" current," }}">
                                                     <a{% if model.url %} href="{{ model.url }}"{% endif %} class="sidebar-popup-list-item-link"{% if model.url_blank %} target="_blank"{% endif %}>

+ 6 - 3
jet/templatetags/jet_tags.py

@@ -2,7 +2,11 @@ from __future__ import unicode_literals
 import json
 import os
 from django import template
-from django.core.urlresolvers import reverse
+try:
+    from django.core.urlresolvers import reverse
+except ImportError: # Django 1.11
+    from django.urls import reverse
+
 from django.forms import CheckboxInput, ModelChoiceField, Select, ModelMultipleChoiceField, SelectMultiple
 from django.contrib.admin.widgets import RelatedFieldWidgetWrapper
 from django.utils.formats import get_format
@@ -73,8 +77,7 @@ def jet_select2_lookups(field):
                 'data-ajax--url': reverse('jet:model_lookup')
             }
 
-            form = field.form
-            initial_value = form.data.get(field.name) if form.data != {} else form.initial.get(field.name)
+            initial_value = field.value()
 
             if hasattr(field, 'field') and isinstance(field.field, ModelMultipleChoiceField):
                 if initial_value:

+ 5 - 1
jet/tests/test_dashboard.py

@@ -1,5 +1,9 @@
 from django.contrib.auth.models import User
-from django.core.urlresolvers import reverse
+try:
+    from django.core.urlresolvers import reverse
+except ImportError: # Django 1.11
+    from django.urls import reverse
+
 from django.test import TestCase, Client
 from jet.dashboard.dashboard import Dashboard
 from jet.dashboard.modules import LinkList, RecentActions

+ 10 - 6
jet/tests/test_tags.py

@@ -1,7 +1,11 @@
 from django import forms
-from django.core.urlresolvers import reverse
+try:
+    from django.core.urlresolvers import reverse
+except ImportError: # Django 1.11
+    from django.urls import reverse
+
 from django.test import TestCase
-from jet.templatetags.jet_tags import jet_select2_lookups, jet_next_object_url, jet_previous_object_url
+from jet.templatetags.jet_tags import jet_select2_lookups, jet_next_object, jet_previous_object
 from jet.tests.models import TestModel, SearchableTestModel
 from django.test.client import RequestFactory
 
@@ -72,7 +76,7 @@ class TagsTestCase(TestCase):
             'request': RequestFactory().get(expected_url),
         }
 
-        actual_url = jet_next_object_url(context)
+        actual_url = jet_next_object(context)['url']
 
         self.assertEqual(actual_url, expected_url)
 
@@ -92,7 +96,7 @@ class TagsTestCase(TestCase):
             'request': RequestFactory().get(changelist_url),
         }
 
-        actual_url = jet_previous_object_url(context)
-        expected_url = None
+        previous_object = jet_previous_object(context)
+        expected_object = None
 
-        self.assertEqual(actual_url, expected_url)
+        self.assertEqual(previous_object, expected_object)

+ 5 - 1
jet/tests/test_views.py

@@ -1,6 +1,10 @@
 import json
 from django.contrib.auth.models import User
-from django.core.urlresolvers import reverse
+try:
+    from django.core.urlresolvers import reverse
+except ImportError: # Django 1.11
+    from django.urls import reverse
+
 from django.test import TestCase, Client
 from jet.dashboard.modules import LinkList
 from jet.models import Bookmark

+ 32 - 11
jet/utils.py

@@ -1,5 +1,6 @@
 import datetime
 import json
+from django.template import Context
 from django.utils import translation
 from jet import settings
 from jet.models import PinnedApplication
@@ -13,7 +14,11 @@ except ImportError:
         pass
 from django.core.serializers.json import DjangoJSONEncoder
 from django.http import HttpResponse
-from django.core.urlresolvers import reverse, resolve, NoReverseMatch
+try:
+    from django.core.urlresolvers import reverse, resolve, NoReverseMatch
+except ImportError: # Django 1.11
+    from django.urls import reverse, resolve, NoReverseMatch
+
 from django.contrib.admin import AdminSite
 from django.utils.encoding import smart_text
 from django.utils.text import capfirst
@@ -21,11 +26,14 @@ from django.contrib import messages
 from django.utils.encoding import force_text
 from django.utils.functional import Promise
 from django.contrib.admin.options import IncorrectLookupParameters
-from django.core import urlresolvers
 from django.contrib import admin
 from django.utils.translation import ugettext_lazy as _
 from django.utils.text import slugify
-from collections import OrderedDict
+
+try:
+    from collections import OrderedDict
+except ImportError:
+    from ordereddict import OrderedDict  # Python 2.6
 
 
 class JsonResponse(HttpResponse):
@@ -173,7 +181,7 @@ def get_model_queryset(admin_site, model, request, preserved_filters=None):
     model_admin = admin_site._registry.get(model)
 
     try:
-        changelist_url = urlresolvers.reverse('%s:%s_%s_changelist' % (
+        changelist_url = reverse('%s:%s_%s_changelist' % (
             admin_site.name,
             model._meta.app_label,
             model._meta.model_name
@@ -349,8 +357,8 @@ def get_menu_items(context):
             if 'label' in data:
                 item['label'] = data['label']
 
-            if 'models' in data:
-                item['models'] = list(map(lambda x: get_menu_item_app_model(app_label, x), data['models']))
+            if 'items' in data:
+                item['items'] = list(map(lambda x: get_menu_item_app_model(app_label, x), data['items']))
 
             if 'url' in data:
                 item['url'] = get_menu_item_url(data['url'], original_app_list)
@@ -382,7 +390,7 @@ def get_menu_items(context):
 
                 models_dict[app_label][model['object_name']] = model
 
-            app['models'] = []
+            app['items'] = []
 
         app_list = []
 
@@ -398,21 +406,24 @@ def get_menu_items(context):
 
                 for model_label in models:
                     if model_label == '__all__':
-                        app['models'] = models_dict[app_label].values()
+                        app['items'] = models_dict[app_label].values()
                         break
                     elif model_label in models_dict[app_label]:
                         model = models_dict[app_label][model_label]
-                        app['models'].append(model)
+                        app['items'].append(model)
 
                 app_list.append(app)
     else:
-        app_list = original_app_list.values()
+        def map_item(item):
+            item['items'] = item['models']
+            return item
+        app_list = map(map_item, original_app_list.values())
 
     current_found = False
 
     for app in app_list:
         if not current_found:
-            for model in app['models']:
+            for model in app['items']:
                 if not current_found and model.get('url') and context['request'].path.startswith(model['url']):
                     model['current'] = True
                     current_found = True
@@ -426,3 +437,13 @@ def get_menu_items(context):
                 app['current'] = False
 
     return app_list
+
+
+def context_to_dict(context):
+    if isinstance(context, Context):
+        flat = {}
+        for d in context.dicts:
+            flat.update(d)
+        context = flat
+
+    return context

+ 7 - 0
setup.py

@@ -13,10 +13,17 @@ def read(fname):
 
 def get_install_requires():
     install_requires = ['Django']
+
     try:
         import importlib
     except ImportError:
         install_requires.append('importlib')
+
+    try:
+        from collections import OrderedDict
+    except ImportError:
+        install_requires.append('ordereddict')
+
     return install_requires
 
 setup(

Some files were not shown because too many files changed in this diff