diff --git a/.gitignore b/.gitignore
index cccac58f3..bae85af2f 100644
--- a/.gitignore
+++ b/.gitignore
@@ -43,3 +43,6 @@ xadmin/static/xadmin/bower_components/
.idea
*~
demo_app/data.db
+
+TAGS*
+*.swp
diff --git a/demo_app/app/adminx.py b/demo_app/app/adminx.py
index 3e3c50de4..43109d25b 100644
--- a/demo_app/app/adminx.py
+++ b/demo_app/app/adminx.py
@@ -1,6 +1,7 @@
+from __future__ import absolute_import
import xadmin
from xadmin import views
-from models import IDC, Host, MaintainLog, HostGroup, AccessRecord
+from .models import IDC, Host, MaintainLog, HostGroup, AccessRecord
from xadmin.layout import Main, TabHolder, Tab, Fieldset, Row, Col, AppendedText, Side
from xadmin.plugins.inline import Inline
from xadmin.plugins.batch import BatchChangeAction
@@ -9,12 +10,11 @@ class MainDashboard(object):
widgets = [
[
{"type": "html", "title": "Test Widget", "content": "
Welcome to Xadmin!
Join Online Group:
QQ Qun : 282936295
"},
- {"type": "chart", "model": "app.accessrecord", 'chart': 'user_count', 'params': {'_p_date__gte': '2013-01-08', 'p': 1, '_p_date__lt': '2013-01-29'}},
- {"type": "list", "model": "app.host", 'params': {
- 'o':'-guarantee_date'}},
+ {"type": "chart", "model": "app.accessrecord", "chart": "user_count", "params": {"_p_date__gte": "2013-01-08", "p": 1, "_p_date__lt": "2013-01-29"}},
+ {"type": "list", "model": "app.host", "params": {"o":"-guarantee_date"}},
],
[
- {"type": "qbutton", "title": "Quick Start", "btns": [{'model': Host}, {'model':IDC}, {'title': "Google", 'url': "http://www.google.com"}]},
+ {"type": "qbutton", "title": "Quick Start", "btns": [{"model": Host}, {"model":IDC}, {"title": "Google", "url": "http://www.google.com"}]},
{"type": "addform", "model": MaintainLog},
]
]
@@ -30,134 +30,160 @@ class BaseSetting(object):
class GlobalSetting(object):
global_search_models = [Host, IDC]
global_models_icon = {
- Host: 'fa fa-laptop', IDC: 'fa fa-cloud'
+ Host: "fa fa-laptop", IDC: "fa fa-cloud"
}
- menu_style = 'default'#'accordion'
+ menu_style = "default"#"accordion"
xadmin.sites.site.register(views.CommAdminView, GlobalSetting)
class MaintainInline(object):
model = MaintainLog
extra = 1
- style = 'accordion'
+ style = "accordion"
class IDCAdmin(object):
- list_display = ('name', 'description', 'create_time')
- list_display_links = ('name',)
+ list_display = ("name", "description", "create_time")
+ list_display_links = ("name",)
wizard_form_list = [
- ('First\'s Form', ('name', 'description')),
- ('Second Form', ('contact', 'telphone', 'address')),
- ('Thread Form', ('customer_id',))
- ]
+ ("First's Form", ("name", "description")),
+ ("Second Form", ("contact", "telphone", "address")),
+ ("Thread Form", ("customer_id",))
+ ]
- search_fields = ['name']
- relfield_style = 'fk-select'
+ search_fields = ["name"]
+ relfield_style = "fk-select"
reversion_enable = True
actions = [BatchChangeAction, ]
- batch_fields = ('contact', 'groups')
+ batch_fields = ("contact", "groups")
class HostAdmin(object):
def open_web(self, instance):
- return "Open" % instance.ip
+ return """Open""" % instance.ip
open_web.short_description = "Acts"
open_web.allow_tags = True
open_web.is_column = True
- list_display = ('name', 'idc', 'guarantee_date', 'service_type',
- 'status', 'open_web', 'description')
- list_display_links = ('name',)
+ list_display = (
+ "name", "idc", "guarantee_date", "service_type", "status", "open_web",
+ "description",
+ )
+ list_display_links = ("name",)
- raw_id_fields = ('idc',)
- style_fields = {'system': "radio-inline"}
+ raw_id_fields = ("idc",)
+ style_fields = {"system": "radio-inline"}
- search_fields = ['name', 'ip', 'description']
- list_filter = ['idc', 'guarantee_date', 'status', 'brand', 'model',
- 'cpu', 'core_num', 'hard_disk', 'memory', ('service_type',xadmin.filters.MultiSelectFieldListFilter)]
+ search_fields = ["name", "ip", "description"]
+ list_filter = [
+ "idc", "guarantee_date", "status", "brand", "model", "cpu", "core_num",
+ "hard_disk", "memory", (
+ "service_type",
+ xadmin.filters.MultiSelectFieldListFilter,
+ ),
+ ]
- list_quick_filter = ['service_type',{'field':'idc__name','limit':10}]
- list_bookmarks = [{'title': "Need Guarantee", 'query': {'status__exact': 2}, 'order': ('-guarantee_date',), 'cols': ('brand', 'guarantee_date', 'service_type')}]
-
- show_detail_fields = ('idc',)
+ list_quick_filter = ["service_type",{"field":"idc__name","limit":10}]
+ list_bookmarks = [{
+ "title": "Need Guarantee",
+ "query": {"status__exact": 2},
+ "order": ("-guarantee_date",),
+ "cols": ("brand", "guarantee_date", "service_type"),
+ }]
+
+ show_detail_fields = ("idc",)
list_editable = (
- 'name', 'idc', 'guarantee_date', 'service_type', 'description')
+ "name", "idc", "guarantee_date", "service_type", "description",
+ )
save_as = True
aggregate_fields = {"guarantee_date": "min"}
- grid_layouts = ('table', 'thumbnails')
+ grid_layouts = ("table", "thumbnails")
form_layout = (
Main(
TabHolder(
- Tab('Comm Fields',
- Fieldset('Company data',
- 'name', 'idc',
- description="some comm fields, required"
- ),
+ Tab(
+ "Comm Fields",
+ Fieldset(
+ "Company data", "name", "idc",
+ description="some comm fields, required",
+ ),
Inline(MaintainLog),
),
- Tab('Extend Fields',
- Fieldset('Contact details',
- 'service_type',
- Row('brand', 'model'),
- Row('cpu', 'core_num'),
- Row(AppendedText(
- 'hard_disk', 'G'), AppendedText('memory', "G")),
- 'guarantee_date'
- ),
+ Tab(
+ "Extend Fields",
+ Fieldset(
+ "Contact details",
+ "service_type",
+ Row("brand", "model"),
+ Row("cpu", "core_num"),
+ Row(
+ AppendedText("hard_disk", "G"),
+ AppendedText("memory", "G")
+ ),
+ "guarantee_date"
+ ),
),
+ ),
),
- ),
Side(
- Fieldset('Status data',
- 'status', 'ssh_port', 'ip'
- ),
+ Fieldset("Status data", "status", "ssh_port", "ip"),
+ )
)
- )
inlines = [MaintainInline]
reversion_enable = True
- data_charts = {
- "host_service_type_counts": {'title': u"Host service type count", "x-field": "service_type", "y-field": ("service_type",),
- "option": {
- "series": {"bars": {"align": "center", "barWidth": 0.8,'show':True}},
- "xaxis": {"aggregate": "count", "mode": "categories"},
- },
- },
- }
+ data_charts = {"host_service_type_counts": {
+ "title": u"Host service type count",
+ "x-field": "service_type",
+ "y-field": ("service_type",),
+ "option": {
+ "series": {
+ "bars": {
+ "align": "center",
+ "barWidth": 0.8,
+ "show": True,
+ },
+ },
+ "xaxis": {
+ "aggregate": "count",
+ "mode": "categories",
+ },
+ },
+ }}
class HostGroupAdmin(object):
- list_display = ('name', 'description')
- list_display_links = ('name',)
+ list_display = ("name", "description")
+ list_display_links = ("name",)
- search_fields = ['name']
- style_fields = {'hosts': 'checkbox-inline'}
+ search_fields = ["name"]
+ style_fields = {"hosts": "checkbox-inline"}
class MaintainLogAdmin(object):
list_display = (
- 'host', 'maintain_type', 'hard_type', 'time', 'operator', 'note')
- list_display_links = ('host',)
+ "host", "maintain_type", "hard_type", "time", "operator", "note")
+ list_display_links = ("host",)
- list_filter = ['host', 'maintain_type', 'hard_type', 'time', 'operator']
- search_fields = ['note']
+ list_filter = ["host", "maintain_type", "hard_type", "time", "operator"]
+ search_fields = ["note"]
form_layout = (
Col("col2",
- Fieldset('Record data',
- 'time', 'note',
- css_class='unsort short_label no_title'
+ Fieldset("Record data",
+ "time", "note",
+ css_class="unsort short_label no_title"
),
span=9, horizontal=True
),
Col("col1",
- Fieldset('Comm data',
- 'host', 'maintain_type'
+ Fieldset("Comm data",
+ "host", "maintain_type"
),
- Fieldset('Maintain details',
- 'hard_type', 'operator'
+ Fieldset("Maintain details",
+ "hard_type", "operator"
),
span=3
)
@@ -172,20 +198,20 @@ def avg_count(self, instance):
avg_count.allow_tags = True
avg_count.is_column = True
- list_display = ('date', 'user_count', 'view_count', 'avg_count')
- list_display_links = ('date',)
+ list_display = ("date", "user_count", "view_count", "avg_count")
+ list_display_links = ("date",)
- list_filter = ['date', 'user_count', 'view_count']
+ list_filter = ["date", "user_count", "view_count"]
actions = None
- aggregate_fields = {"user_count": "sum", 'view_count': "sum"}
+ aggregate_fields = {"user_count": "sum", "view_count": "sum"}
refresh_times = (3, 5, 10)
data_charts = {
- "user_count": {'title': u"User Report", "x-field": "date", "y-field": ("user_count", "view_count"), "order": ('date',)},
- "avg_count": {'title': u"Avg Report", "x-field": "date", "y-field": ('avg_count',), "order": ('date',)},
- "per_month": {'title': u"Monthly Users", "x-field": "_chart_month", "y-field": ("user_count", ),
+ "user_count": {"title": u"User Report", "x-field": "date", "y-field": ("user_count", "view_count"), "order": ("date",)},
+ "avg_count": {"title": u"Avg Report", "x-field": "date", "y-field": ("avg_count",), "order": ("date",)},
+ "per_month": {"title": u"Monthly Users", "x-field": "_chart_month", "y-field": ("user_count", ),
"option": {
- "series": {"bars": {"align": "center", "barWidth": 0.8,'show':True}},
+ "series": {"bars": {"align": "center", "barWidth": 0.8,"show":True}},
"xaxis": {"aggregate": "sum", "mode": "categories"},
},
},
diff --git a/demo_app/app/models.py b/demo_app/app/models.py
index 32fef1113..5f2ddcc74 100644
--- a/demo_app/app/models.py
+++ b/demo_app/app/models.py
@@ -1,6 +1,7 @@
from django.db import models
from django.contrib.auth.models import Group
from django.conf import settings
+from django.utils.encoding import python_2_unicode_compatible
AUTH_USER_MODEL = getattr(settings, 'AUTH_USER_MODEL', 'auth.User')
@@ -22,7 +23,7 @@
('mix', u"Mix"),
)
-
+@python_2_unicode_compatible
class IDC(models.Model):
name = models.CharField(max_length=64)
description = models.TextField()
@@ -35,7 +36,7 @@ class IDC(models.Model):
create_time = models.DateField(auto_now=True)
- def __unicode__(self):
+ def __str__(self):
return self.name
class Meta:
@@ -43,6 +44,7 @@ class Meta:
verbose_name_plural = verbose_name
+@python_2_unicode_compatible
class Host(models.Model):
idc = models.ForeignKey(IDC)
name = models.CharField(max_length=64)
@@ -72,7 +74,7 @@ class Host(models.Model):
administrator = models.ForeignKey(AUTH_USER_MODEL, verbose_name="Admin")
- def __unicode__(self):
+ def __str__(self):
return self.name
class Meta:
@@ -80,6 +82,7 @@ class Meta:
verbose_name_plural = verbose_name
+@python_2_unicode_compatible
class MaintainLog(models.Model):
host = models.ForeignKey(Host)
maintain_type = models.CharField(max_length=32)
@@ -88,7 +91,7 @@ class MaintainLog(models.Model):
operator = models.CharField(max_length=16)
note = models.TextField()
- def __unicode__(self):
+ def __str__(self):
return '%s maintain-log [%s] %s %s' % (self.host.name, self.time.strftime('%Y-%m-%d %H:%M:%S'),
self.maintain_type, self.hard_type)
@@ -97,6 +100,7 @@ class Meta:
verbose_name_plural = verbose_name
+@python_2_unicode_compatible
class HostGroup(models.Model):
name = models.CharField(max_length=32)
@@ -108,10 +112,11 @@ class Meta:
verbose_name = u"Host Group"
verbose_name_plural = verbose_name
- def __unicode__(self):
+ def __str__(self):
return self.name
+@python_2_unicode_compatible
class AccessRecord(models.Model):
date = models.DateField()
user_count = models.IntegerField()
@@ -121,5 +126,5 @@ class Meta:
verbose_name = u"Access Record"
verbose_name_plural = verbose_name
- def __unicode__(self):
+ def __str__(self):
return "%s Access Record" % self.date.strftime('%Y-%m-%d')
diff --git a/demo_app/demo/settings.py b/demo_app/demo/settings.py
index 87b76b1d3..ba583a1bc 100644
--- a/demo_app/demo/settings.py
+++ b/demo_app/demo/settings.py
@@ -2,13 +2,18 @@
import sys
import os.path
+from django.utils import six
-reload(sys)
-sys.setdefaultencoding('utf-8')
-gettext = lambda s: s
+if six.PY2 and sys.getdefaultencoding()=='ascii':
+ import imp
+ imp.reload(sys)
+ sys.setdefaultencoding('utf-8')
+
+from django.utils.translation import ugettext_lazy as _
PROJECT_ROOT = os.path.join(
os.path.realpath(os.path.dirname(__file__)), os.pardir)
+# PROJECT_ROOT = os.path.dirname(os.path.dirname(os.path.abspath(__file__)))
DEBUG = True
@@ -30,7 +35,7 @@
}
# Hosts/domain names that are valid for this site; required if DEBUG is False
# See https://docs.djangoproject.com/en/1.5/ref/settings/#allowed-hosts
-ALLOWED_HOSTS = '*'
+ALLOWED_HOSTS = ['*']
# Local time zone for this installation. Choices can be found here:
# http://en.wikipedia.org/wiki/List_of_tz_zones_by_name
@@ -43,8 +48,8 @@
LANGUAGE_CODE = 'en-us'
LANGUAGES = (
- ('en', gettext('English')),
- ('zh-hans', gettext('Chinese')),
+ ('en', _('English')),
+ ('zh-hans', _('Chinese')),
)
SITE_ID = 1
@@ -98,10 +103,12 @@
SECRET_KEY = '5=!nss_+^nvyyc_j(tdcf!7(_una*3gtw+_8v5jaa=)j0g^d_2'
MIDDLEWARE_CLASSES = (
- 'django.middleware.common.CommonMiddleware',
+ 'django.middleware.security.SecurityMiddleware',
'django.contrib.sessions.middleware.SessionMiddleware',
+ 'django.middleware.common.CommonMiddleware',
'django.middleware.csrf.CsrfViewMiddleware',
'django.contrib.auth.middleware.AuthenticationMiddleware',
+ 'django.contrib.auth.middleware.SessionAuthenticationMiddleware',
'django.contrib.messages.middleware.MessageMiddleware',
'django.middleware.locale.LocaleMiddleware',
# Uncomment the next line for simple clickjacking protection:
@@ -123,12 +130,15 @@
'OPTIONS': {
'context_processors': [
'django.template.context_processors.i18n',
+ 'django.template.context_processors.media',
+ 'django.template.context_processors.static',
+ 'django.template.context_processors.tz',
'django.template.context_processors.debug',
'django.template.context_processors.request',
'django.contrib.auth.context_processors.auth',
'django.contrib.messages.context_processors.messages',
],
- 'debug':DEBUG
+ 'debug': DEBUG,
},
},
]
diff --git a/requirements.txt b/requirements.txt
index d49b05b72..cb5940a30 100644
--- a/requirements.txt
+++ b/requirements.txt
@@ -2,4 +2,6 @@ django~=1.9.0
django-crispy-forms~=1.6.0
django-reversion~=2.0.0
django-formtools==1.0
+future==0.15.2
httplib2==0.9.2
+six==1.10.0
diff --git a/setup.py b/setup.py
index c9a504580..63a93debb 100644
--- a/setup.py
+++ b/setup.py
@@ -40,6 +40,7 @@
"Programming Language :: JavaScript",
'Programming Language :: Python',
"Programming Language :: Python :: 2.7",
+ "Programming Language :: Python :: 3.4",
"Topic :: Internet :: WWW/HTTP",
"Topic :: Internet :: WWW/HTTP :: Dynamic Content",
"Topic :: Software Development :: Libraries :: Python Modules",
diff --git a/tests/runtests.py b/tests/runtests.py
index 3efe90c17..5f8677b25 100755
--- a/tests/runtests.py
+++ b/tests/runtests.py
@@ -1,4 +1,5 @@
#!/usr/bin/env python
+from __future__ import print_function
import os
import shutil
import sys
@@ -6,6 +7,7 @@
import django
from django.apps import AppConfig,apps
+from django.utils.encoding import smart_text
TEST_ROOT = os.path.realpath(os.path.dirname(__file__))
@@ -32,9 +34,13 @@
def get_test_modules():
modules = []
for f in os.listdir(RUNTESTS_DIR):
- if (f.startswith('__init__') or
- f.startswith('.') or
- f.startswith('sql') or not os.path.isdir(os.path.join(RUNTESTS_DIR, f))):
+ if (
+ f.startswith('__init__')
+ or f.startswith('__pycache__')
+ or f.startswith('.')
+ or f.startswith('sql')
+ or not os.path.isdir(os.path.join(RUNTESTS_DIR, f))
+ ):
continue
modules.append(f)
return modules
@@ -81,7 +87,6 @@ def setup(verbosity, test_labels):
# Load all the test model apps.
test_labels_set = set([label.split('.')[0] for label in test_labels])
test_modules = get_test_modules()
-
for module_name in test_modules:
module_label = module_name
# if the module was named on the command line, or
@@ -103,7 +108,7 @@ def teardown(state):
# so that it will successfully remove temp trees containing
# non-ASCII filenames on Windows. (We're assuming the temp dir
# name itself does not contain non-ASCII characters.)
- shutil.rmtree(unicode(TEMP_DIR))
+ shutil.rmtree(smart_text(TEMP_DIR))
# Restore the old settings.
for key, value in state.items():
setattr(settings, key, value)
diff --git a/tests/xtests/site/apps.py b/tests/xtests/site/apps.py
index 1ab4a5c31..37777566c 100644
--- a/tests/xtests/site/apps.py
+++ b/tests/xtests/site/apps.py
@@ -1,8 +1,10 @@
#!/usr/bin/env python
#coding:utf-8
import sys
-if sys.getdefaultencoding()=='ascii':
- reload(sys)
+from django.utils import six
+if six.PY2 and sys.getdefaultencoding()=='ascii':
+ import imp
+ imp.reload(sys)
sys.setdefaultencoding('utf-8')
from django.apps import AppConfig
diff --git a/tests/xtests/site/tests.py b/tests/xtests/site/tests.py
index db5ccb81a..808997f40 100644
--- a/tests/xtests/site/tests.py
+++ b/tests/xtests/site/tests.py
@@ -1,10 +1,11 @@
+from __future__ import absolute_import
from django.http import HttpResponse
from base import BaseTest
from xadmin.sites import AdminSite
from xadmin.views import BaseAdminView, BaseAdminPlugin, ModelAdminView, filter_hook
-from models import ModelA
+from .models import ModelA
class ModelAAdmin(object):
diff --git a/tests/xtests/view_base/adminx.py b/tests/xtests/view_base/adminx.py
index 7db81e35f..b75fc8f8a 100644
--- a/tests/xtests/view_base/adminx.py
+++ b/tests/xtests/view_base/adminx.py
@@ -1,6 +1,7 @@
+from __future__ import absolute_import
from xadmin.sites import AdminSite
from xadmin.views import BaseAdminView, CommAdminView, ListAdminView
-from models import ModelA, ModelB
+from .models import ModelA, ModelB
site = AdminSite('views_base')
diff --git a/tests/xtests/view_base/apps.py b/tests/xtests/view_base/apps.py
index 6398388ca..5b59ac85d 100644
--- a/tests/xtests/view_base/apps.py
+++ b/tests/xtests/view_base/apps.py
@@ -1,8 +1,10 @@
#!/usr/bin/env python
#coding:utf-8
import sys
-if sys.getdefaultencoding()=='ascii':
- reload(sys)
+from django.utils import six
+if six.PY2 and sys.getdefaultencoding()=='ascii':
+ import imp
+ imp.reload(sys)
sys.setdefaultencoding('utf-8')
from django.apps import AppConfig
diff --git a/tests/xtests/view_base/tests.py b/tests/xtests/view_base/tests.py
index a34da38cb..a779341f4 100644
--- a/tests/xtests/view_base/tests.py
+++ b/tests/xtests/view_base/tests.py
@@ -1,11 +1,12 @@
+from __future__ import absolute_import
from django.contrib.auth.models import User
from base import BaseTest
from xadmin.views import BaseAdminView, BaseAdminPlugin, ModelAdminView, ListAdminView
-from models import ModelA, ModelB
-from adminx import site, ModelAAdmin, TestBaseView, TestCommView, TestAView, OptionA
+from .models import ModelA, ModelB
+from .adminx import site, ModelAAdmin, TestBaseView, TestCommView, TestAView, OptionA
class BaseAdminTest(BaseTest):
diff --git a/tests/xtests/view_base/urls.py b/tests/xtests/view_base/urls.py
index 5f76a1c80..a6a3601ec 100644
--- a/tests/xtests/view_base/urls.py
+++ b/tests/xtests/view_base/urls.py
@@ -1,5 +1,6 @@
+from __future__ import absolute_import
from django.conf.urls import patterns, include
-from adminx import site
+from .adminx import site
urlpatterns = patterns('',
(r'', include(site.urls)),
diff --git a/xadmin/adminx.py b/xadmin/adminx.py
index a22630107..e7a66e0ff 100644
--- a/xadmin/adminx.py
+++ b/xadmin/adminx.py
@@ -1,5 +1,6 @@
+from __future__ import absolute_import
import xadmin
-from models import UserSettings, Log
+from .models import UserSettings, Log
from xadmin.layout import *
from django.utils.translation import ugettext_lazy as _, ugettext
diff --git a/xadmin/filters.py b/xadmin/filters.py
index 6656578c3..9eba01e99 100644
--- a/xadmin/filters.py
+++ b/xadmin/filters.py
@@ -1,10 +1,12 @@
+from __future__ import absolute_import
from django.db import models
from django.core.exceptions import ImproperlyConfigured
-from django.utils.encoding import smart_unicode
+from django.utils.encoding import smart_text
from django.utils.translation import ugettext_lazy as _
from django.utils import timezone
from django.template.loader import get_template
from django.template.context import Context
+from django.utils import six
from django.utils.safestring import mark_safe
from django.utils.html import escape,format_html
from django.utils.text import Truncator
@@ -17,7 +19,7 @@
FILTER_PREFIX = '_p_'
SEARCH_VAR = '_q_'
-from util import (get_model_from_relation,
+from .util import (get_model_from_relation,
reverse_field_path, get_limit_choices_to_from_path, prepare_lookup_value)
@@ -45,8 +47,10 @@ def query_string(self, new_params=None, remove=None):
return self.admin_view.get_query_string(new_params, remove)
def form_params(self):
- return self.admin_view.get_form_params(
- remove=map(lambda k: FILTER_PREFIX + k, self.used_params.keys()))
+ arr = map(lambda k: FILTER_PREFIX + k, self.used_params.keys())
+ if six.PY3:
+ arr = list(arr)
+ return self.admin_view.get_form_params(remove=arr)
def has_output(self):
"""
@@ -120,14 +124,20 @@ def __init__(self, field, request, params, model, admin_view, field_path):
else:
self.context_params["%s_val" % name] = ''
- map(lambda kv: setattr(
- self, 'lookup_' + kv[0], kv[1]), self.context_params.items())
+ arr = map(
+ lambda kv: setattr(self, 'lookup_' + kv[0], kv[1]),
+ self.context_params.items()
+ )
+ if six.PY3:
+ list(arr)
def get_context(self):
context = super(FieldFilter, self).get_context()
context.update(self.context_params)
- context['remove_url'] = self.query_string(
- {}, map(lambda k: FILTER_PREFIX + k, self.used_params.keys()))
+ obj = map(lambda k: FILTER_PREFIX + k, self.used_params.keys())
+ if six.PY3:
+ obj = list(obj)
+ context['remove_url'] = self.query_string({}, obj)
return context
def has_output(self):
@@ -158,22 +168,28 @@ def choices(self):
for lookup, title in (
('', _('All')),
('1', _('Yes')),
- ('0', _('No'))):
+ ('0', _('No')),
+ ):
yield {
- 'selected': self.lookup_exact_val == lookup and not self.lookup_isnull_val,
- 'query_string': self.query_string({
- self.lookup_exact_name: lookup,
- }, [self.lookup_isnull_name]),
- 'display': title,
- }
+ 'selected': (
+ self.lookup_exact_val == lookup
+ and not self.lookup_isnull_val
+ ),
+ 'query_string': self.query_string(
+ {self.lookup_exact_name: lookup},
+ [self.lookup_isnull_name],
+ ),
+ 'display': title,
+ }
if isinstance(self.field, models.NullBooleanField):
yield {
- 'selected': self.lookup_isnull_val == 'True',
- 'query_string': self.query_string({
- self.lookup_isnull_name: 'True',
- }, [self.lookup_exact_name]),
- 'display': _('Unknown'),
- }
+ 'selected': self.lookup_isnull_val == 'True',
+ 'query_string': self.query_string(
+ {self.lookup_isnull_name: 'True'},
+ [self.lookup_exact_name],
+ ),
+ 'display': _('Unknown'),
+ }
@manager.register
@@ -192,7 +208,7 @@ def choices(self):
}
for lookup, title in self.field.flatchoices:
yield {
- 'selected': smart_unicode(lookup) == self.lookup_exact_val,
+ 'selected': smart_text(lookup) == self.lookup_exact_val,
'query_string': self.query_string({self.lookup_exact_name: lookup}),
'display': title,
}
@@ -201,11 +217,15 @@ def choices(self):
@manager.register
class TextFieldListFilter(FieldFilter):
template = 'xadmin/filters/char.html'
- lookup_formats = {'in': '%s__in','search': '%s__contains'}
+ lookup_formats = {'in': '%s__in', 'search': '%s__contains'}
@classmethod
def test(cls, field, request, params, model, admin_view, field_path):
- return (isinstance(field, models.CharField) and field.max_length > 20) or isinstance(field, models.TextField)
+ return (
+ isinstance(field, models.CharField)
+ and field.max_length > 20
+ or isinstance(field, models.TextField)
+ )
@manager.register
@@ -408,7 +428,7 @@ def choices(self):
}
for pk_val, val in self.lookup_choices:
yield {
- 'selected': self.lookup_exact_val == smart_unicode(pk_val),
+ 'selected': self.lookup_exact_val == smart_text(pk_val),
'query_string': self.query_string({
self.lookup_exact_name: pk_val,
}, [self.lookup_isnull_name]),
@@ -494,7 +514,7 @@ def choices(self):
}
for val in self.lookup_choices:
yield {
- 'selected': smart_unicode(val) in self.lookup_in_val,
+ 'selected': smart_text(val) in self.lookup_in_val,
'query_string': self.query_string({self.lookup_in_name: ",".join([val]+self.lookup_in_val),}),
'remove_query_string': self.query_string({self.lookup_in_name: ",".join([v for v in self.lookup_in_val if v != val]),}),
'display': val,
@@ -535,7 +555,7 @@ def choices(self):
if val is None:
include_none = True
continue
- val = smart_unicode(val)
+ val = smart_text(val)
yield {
'selected': self.lookup_exact_val == val,
'query_string': self.query_string({self.lookup_exact_name: val},
diff --git a/xadmin/models.py b/xadmin/models.py
index 32ef94f27..da5145641 100644
--- a/xadmin/models.py
+++ b/xadmin/models.py
@@ -8,7 +8,7 @@
from django.core.urlresolvers import NoReverseMatch, reverse
from django.core.serializers.json import DjangoJSONEncoder
from django.db.models.base import ModelBase
-from django.utils.encoding import python_2_unicode_compatible, smart_text, smart_unicode
+from django.utils.encoding import python_2_unicode_compatible, smart_text
from django.db.models.signals import post_migrate
from django.contrib.auth.models import Permission
@@ -40,6 +40,8 @@ def add_view_permissions(sender, **kwargs):
# check for all our view permissions after a syncdb
post_migrate.connect(add_view_permissions)
+
+@python_2_unicode_compatible
class Bookmark(models.Model):
title = models.CharField(_(u'Title'), max_length=128)
user = models.ForeignKey(AUTH_USER_MODEL, verbose_name=_(u"user"), blank=True, null=True)
@@ -55,7 +57,7 @@ def url(self):
base_url = base_url + '?' + self.query
return base_url
- def __unicode__(self):
+ def __str__(self):
return self.title
class Meta:
@@ -65,10 +67,10 @@ class Meta:
class JSONEncoder(DjangoJSONEncoder):
def default(self, o):
- if isinstance(o, datetime.date):
- return o.strftime('%Y-%m-%d')
- elif isinstance(o, datetime.datetime):
+ if isinstance(o, datetime.datetime):
return o.strftime('%Y-%m-%d %H:%M:%S')
+ elif isinstance(o, datetime.date):
+ return o.strftime('%Y-%m-%d')
elif isinstance(o, decimal.Decimal):
return str(o)
elif isinstance(o, ModelBase):
@@ -77,9 +79,10 @@ def default(self, o):
try:
return super(JSONEncoder, self).default(o)
except Exception:
- return smart_unicode(o)
+ return smart_text(o)
+@python_2_unicode_compatible
class UserSettings(models.Model):
user = models.ForeignKey(AUTH_USER_MODEL, verbose_name=_(u"user"))
key = models.CharField(_('Settings Key'), max_length=256)
@@ -91,7 +94,7 @@ def json_value(self):
def set_json(self, obj):
self.value = json.dumps(obj, cls=JSONEncoder, ensure_ascii=False)
- def __unicode__(self):
+ def __str__(self):
return "%s %s" % (self.user, self.key)
class Meta:
@@ -99,6 +102,7 @@ class Meta:
verbose_name_plural = _('User Settings')
+@python_2_unicode_compatible
class UserWidget(models.Model):
user = models.ForeignKey(AUTH_USER_MODEL, verbose_name=_(u"user"))
page_id = models.CharField(_(u"Page"), max_length=256)
@@ -126,13 +130,15 @@ def save(self, *args, **kwargs):
except Exception:
pass
- def __unicode__(self):
+ def __str__(self):
return "%s %s widget" % (self.user, self.widget_type)
class Meta:
verbose_name = _(u'User Widget')
verbose_name_plural = _('User Widgets')
+
+@python_2_unicode_compatible
class Log(models.Model):
action_time = models.DateTimeField(
_('action time'),
diff --git a/xadmin/plugins/actions.py b/xadmin/plugins/actions.py
index f05c1a180..02085bf33 100644
--- a/xadmin/plugins/actions.py
+++ b/xadmin/plugins/actions.py
@@ -5,7 +5,8 @@
from django.http import HttpResponse, HttpResponseRedirect
from django.template import loader
from django.template.response import TemplateResponse
-from django.utils.encoding import force_unicode
+from django.utils import six
+from django.utils.encoding import force_text
from django.utils.safestring import mark_safe
from django.utils.translation import ugettext as _, ungettext
from django.utils.text import capfirst
@@ -24,7 +25,7 @@
def action_checkbox(obj):
- return checkbox.render(ACTION_CHECKBOX_NAME, force_unicode(obj.pk))
+ return checkbox.render(ACTION_CHECKBOX_NAME, force_text(obj.pk))
action_checkbox.short_description = mark_safe(
'')
action_checkbox.allow_tags = True
@@ -102,9 +103,9 @@ def do_action(self, queryset):
return None
if len(queryset) == 1:
- objects_name = force_unicode(self.opts.verbose_name)
+ objects_name = force_text(self.opts.verbose_name)
else:
- objects_name = force_unicode(self.opts.verbose_name_plural)
+ objects_name = force_text(self.opts.verbose_name_plural)
if perms_needed or protected:
title = _("Cannot delete %(name)s") % {"name": objects_name}
@@ -227,6 +228,8 @@ def get_actions(self):
# get_action might have returned None, so filter any of those out.
actions = filter(None, actions)
+ if six.PY3:
+ actions = list(actions)
# Convert the actions into a OrderedDict keyed by name.
actions = OrderedDict([
@@ -242,7 +245,7 @@ def get_action_choices(self):
tuple (name, description).
"""
choices = []
- for ac, name, description, icon in self.actions.itervalues():
+ for ac, name, description, icon in self.actions.values():
choice = (name, description % model_format_dict(self.opts), icon)
choices.append(choice)
return choices
diff --git a/xadmin/plugins/ajax.py b/xadmin/plugins/ajax.py
index f6e21c3d6..b09bea21b 100644
--- a/xadmin/plugins/ajax.py
+++ b/xadmin/plugins/ajax.py
@@ -1,7 +1,7 @@
from collections import OrderedDict
from django.forms.utils import ErrorDict
from django.utils.html import escape
-from django.utils.encoding import force_unicode
+from django.utils.encoding import force_text
from xadmin.sites import site
from xadmin.views import BaseAdminPlugin, ListAdminView, ModelFormAdminView, DetailAdminView
@@ -27,7 +27,7 @@ def get_list_display(self,list_display):
def get_result_list(self, response):
av = self.admin_view
base_fields = self.get_list_display(av.base_list_display)
- headers = dict([(c.field_name, force_unicode(c.text)) for c in av.result_headers(
+ headers = dict([(c.field_name, force_text(c.text)) for c in av.result_headers(
).cells if c.field_name in base_fields])
objects = [dict([(o.field_name, escape(str(o.value))) for i, o in
diff --git a/xadmin/plugins/auth.py b/xadmin/plugins/auth.py
index fb42fe12d..1ab6f6110 100644
--- a/xadmin/plugins/auth.py
+++ b/xadmin/plugins/auth.py
@@ -9,6 +9,7 @@
from django.utils.decorators import method_decorator
from django.http import HttpResponseRedirect
from django.utils.html import escape
+from django.utils.encoding import smart_text
from django.utils.translation import ugettext as _
from django.views.decorators.debug import sensitive_post_parameters
from django.forms import ModelMultipleChoiceField
@@ -198,7 +199,7 @@ def get_context(self):
helper.include_media = False
self.form.helper = helper
context.update({
- 'title': _('Change password: %s') % escape(unicode(self.obj)),
+ 'title': _('Change password: %s') % escape(smart_text(self.obj)),
'form': self.form,
'has_delete_permission': False,
'has_change_permission': True,
diff --git a/xadmin/plugins/batch.py b/xadmin/plugins/batch.py
index b6030773b..b322af98e 100644
--- a/xadmin/plugins/batch.py
+++ b/xadmin/plugins/batch.py
@@ -5,7 +5,7 @@
from django.core.exceptions import PermissionDenied
from django.forms.models import modelform_factory
from django.template.response import TemplateResponse
-from django.utils.encoding import force_unicode
+from django.utils.encoding import force_text
from django.utils.safestring import mark_safe
from django.utils.translation import ugettext as _, ugettext_lazy
from xadmin.layout import FormHelper, Layout, Fieldset, Container, Col
@@ -129,9 +129,9 @@ def do_action(self, queryset):
self.form_obj.helper = helper
count = len(queryset)
if count == 1:
- objects_name = force_unicode(self.opts.verbose_name)
+ objects_name = force_text(self.opts.verbose_name)
else:
- objects_name = force_unicode(self.opts.verbose_name_plural)
+ objects_name = force_text(self.opts.verbose_name_plural)
context = self.get_context()
context.update({
diff --git a/xadmin/plugins/bookmark.py b/xadmin/plugins/bookmark.py
index da1c39ebd..3d612cec8 100644
--- a/xadmin/plugins/bookmark.py
+++ b/xadmin/plugins/bookmark.py
@@ -1,22 +1,23 @@
-from django.template import loader
-from django.core.urlresolvers import reverse
-from django.utils.translation import ugettext_lazy as _
-from django.utils.decorators import method_decorator
-from django.views.decorators.csrf import csrf_protect
from django.contrib.contenttypes.models import ContentType
+from django.core.urlresolvers import reverse
from django.db import transaction
from django.db.models import Q
from django.forms import ModelChoiceField
from django.http import QueryDict
+from django.template import loader
+from django.utils.decorators import method_decorator
+from django.utils.encoding import smart_text
+from django.utils.translation import ugettext_lazy as _
+from django.views.decorators.csrf import csrf_protect
+from xadmin.filters import FILTER_PREFIX, SEARCH_VAR
+from xadmin.plugins.relate import RELATE_PREFIX
from xadmin.plugins.utils import get_context_dict
from xadmin.sites import site
from xadmin.views import ModelAdminView, BaseAdminPlugin, ListAdminView
from xadmin.views.list import COL_LIST_VAR, ORDER_VAR
from xadmin.views.dashboard import widget_manager, BaseWidget, PartialBaseWidget
-from xadmin.filters import FILTER_PREFIX, SEARCH_VAR
-from xadmin.plugins.relate import RELATE_PREFIX
from xadmin.models import Bookmark
@@ -41,9 +42,17 @@ def get_context(self, context):
bookmarks = []
- current_qs = '&'.join(['%s=%s' % (k, v) for k, v in sorted(
- filter(lambda i: bool(i[1] and (i[0] in (COL_LIST_VAR, ORDER_VAR, SEARCH_VAR) or i[0].startswith(FILTER_PREFIX)
- or i[0].startswith(RELATE_PREFIX))), self.request.GET.items()))])
+ current_qs = '&'.join([
+ '%s=%s' % (k, v)
+ for k, v in sorted(filter(
+ lambda i: bool(i[1] and (
+ i[0] in (COL_LIST_VAR, ORDER_VAR, SEARCH_VAR)
+ or i[0].startswith(FILTER_PREFIX)
+ or i[0].startswith(RELATE_PREFIX)
+ )),
+ self.request.GET.items()
+ ))
+ ])
model_info = (self.opts.app_label, self.opts.model_name)
has_selected = False
@@ -54,8 +63,10 @@ def get_context(self, context):
# local bookmarks
for bk in self.list_bookmarks:
title = bk['title']
- params = dict(
- [(FILTER_PREFIX + k, v) for (k, v) in bk['query'].items()])
+ params = dict([
+ (FILTER_PREFIX + k, v)
+ for (k, v) in bk['query'].items()
+ ])
if 'order' in bk:
params[ORDER_VAR] = '.'.join(bk['order'])
if 'cols' in bk:
@@ -64,7 +75,10 @@ def get_context(self, context):
params[SEARCH_VAR] = bk['search']
def check_item(i):
return bool(i[1]) or i[1] == False
- bk_qs = '&'.join(['%s=%s' % (k, v) for k, v in sorted(filter(check_item, params.items()))])
+ bk_qs = '&'.join([
+ '%s=%s' % (k, v)
+ for k, v in sorted(filter(check_item, params.items()))
+ ])
url = list_base_url + '?' + bk_qs
selected = (current_qs == bk_qs)
@@ -188,7 +202,7 @@ def setup(self):
self.bookmark = bookmark
if not self.title:
- self.title = unicode(bookmark)
+ self.title = smart_text(bookmark)
req = self.make_get_request("", data.items())
self.list_view = self.get_view_class(
@@ -207,9 +221,13 @@ def context(self, context):
context['result_headers'] = [c for c in list_view.result_headers(
).cells if c.field_name in base_fields]
- context['results'] = [[o for i, o in
- enumerate(filter(lambda c:c.field_name in base_fields, r.cells))]
- for r in list_view.results()]
+ context['results'] = [
+ [o for i, o in enumerate(filter(
+ lambda c: c.field_name in base_fields,
+ r.cells
+ ))]
+ for r in list_view.results()
+ ]
context['result_count'] = list_view.result_count
context['page_url'] = self.bookmark.url
diff --git a/xadmin/plugins/chart.py b/xadmin/plugins/chart.py
index 36907da65..e2b0a649a 100644
--- a/xadmin/plugins/chart.py
+++ b/xadmin/plugins/chart.py
@@ -1,22 +1,21 @@
+import calendar
import datetime
import decimal
-import calendar
-from django.template import loader
-from django.http import HttpResponseNotFound
from django.core.serializers.json import DjangoJSONEncoder
-from django.http import HttpResponse
-from django.utils.encoding import smart_unicode
from django.db import models
+from django.http import HttpResponse, HttpResponseNotFound
+from django.template import loader
from django.utils.http import urlencode
+from django.utils.encoding import force_text, smart_text
from django.utils.translation import ugettext_lazy as _, ugettext
from xadmin.plugins.utils import get_context_dict
from xadmin.sites import site
from xadmin.views import BaseAdminPlugin, ListAdminView
from xadmin.views.dashboard import ModelBaseWidget, widget_manager
-from xadmin.util import lookup_field, label_for_field, force_unicode, json
+from xadmin.util import lookup_field, label_for_field, json
@widget_manager.register
@@ -77,7 +76,7 @@ def default(self, o):
try:
return super(JSONEncoder, self).default(o)
except Exception:
- return smart_unicode(o)
+ return smart_text(o)
class ChartsPlugin(BaseAdminPlugin):
@@ -124,7 +123,7 @@ def get(self, request, name):
self.y_fields = (
y_fields,) if type(y_fields) not in (list, tuple) else y_fields
- datas = [{"data":[], "label": force_unicode(label_for_field(
+ datas = [{"data":[], "label": force_text(label_for_field(
i, self.model, model_admin=self))} for i in self.y_fields]
self.make_result_list()
diff --git a/xadmin/plugins/editable.py b/xadmin/plugins/editable.py
index 00c4c8ae0..1b49feb60 100644
--- a/xadmin/plugins/editable.py
+++ b/xadmin/plugins/editable.py
@@ -3,7 +3,7 @@
from django.db import models, transaction
from django.forms.models import modelform_factory
from django.http import Http404, HttpResponse
-from django.utils.encoding import force_unicode, smart_unicode
+from django.utils.encoding import force_text, smart_text
from django.utils.html import escape, conditional_escape
from django.utils.safestring import mark_safe
from django.utils.translation import ugettext as _
@@ -71,7 +71,7 @@ def init_request(self, object_id, *args, **kwargs):
if self.org_obj is None:
raise Http404(_('%(name)s object with primary key %(key)r does not exist.') %
- {'name': force_unicode(self.opts.verbose_name), 'key': escape(object_id)})
+ {'name': force_text(self.opts.verbose_name), 'key': escape(object_id)})
def get_new_field_html(self, f):
result = self.result_item(self.org_obj, f, {'is_display_first':
@@ -92,7 +92,7 @@ def _get_new_field_html(self, field_name):
allow_tags = True
text = boolean_icon(value)
else:
- text = smart_unicode(value)
+ text = smart_text(value)
else:
if isinstance(f.rel, models.ManyToOneRel):
field_val = getattr(self.org_obj, f.name)
diff --git a/xadmin/plugins/export.py b/xadmin/plugins/export.py
index 860755640..8ccc0f4e8 100644
--- a/xadmin/plugins/export.py
+++ b/xadmin/plugins/export.py
@@ -1,10 +1,12 @@
-import StringIO
+import io
import datetime
import sys
+from future.utils import iteritems
from django.http import HttpResponse
from django.template import loader
-from django.utils.encoding import force_unicode, smart_unicode
+from django.utils import six
+from django.utils.encoding import force_text, smart_text
from django.utils.html import escape
from django.utils.translation import ugettext as _
from django.utils.xmlutils import SimplerXMLGenerator
@@ -75,7 +77,7 @@ def _get_objects(self, context):
rows = context['results']
return [dict([
- (force_unicode(headers[i].text), self._format_value(o)) for i, o in
+ (force_text(headers[i].text), self._format_value(o)) for i, o in
enumerate(filter(lambda c:getattr(c, 'export', False), r.cells))]) for r in rows]
def _get_datas(self, context):
@@ -83,19 +85,19 @@ def _get_datas(self, context):
new_rows = [[self._format_value(o) for o in
filter(lambda c:getattr(c, 'export', False), r.cells)] for r in rows]
- new_rows.insert(0, [force_unicode(c.text) for c in context['result_headers'].cells if c.export])
+ new_rows.insert(0, [force_text(c.text) for c in context['result_headers'].cells if c.export])
return new_rows
def get_xlsx_export(self, context):
datas = self._get_datas(context)
- output = StringIO.StringIO()
+ output = io.BytesIO()
export_header = (
self.request.GET.get('export_xlsx_header', 'off') == 'on')
model_name = self.opts.verbose_name
book = xlsxwriter.Workbook(output)
sheet = book.add_worksheet(
- u"%s %s" % (_(u'Sheet'), force_unicode(model_name)))
+ u"%s %s" % (_(u'Sheet'), force_text(model_name)))
styles = {'datetime': book.add_format({'num_format': 'yyyy-mm-dd hh:mm:ss'}),
'date': book.add_format({'num_format': 'yyyy-mm-dd'}),
'time': book.add_format({'num_format': 'hh:mm:ss'}),
@@ -125,14 +127,14 @@ def get_xlsx_export(self, context):
def get_xls_export(self, context):
datas = self._get_datas(context)
- output = StringIO.StringIO()
+ output = io.BytesIO()
export_header = (
self.request.GET.get('export_xls_header', 'off') == 'on')
model_name = self.opts.verbose_name
book = xlwt.Workbook(encoding='utf8')
sheet = book.add_sheet(
- u"%s %s" % (_(u'Sheet'), force_unicode(model_name)))
+ u"%s %s" % (_(u'Sheet'), force_text(model_name)))
styles = {'datetime': xlwt.easyxf(num_format_str='yyyy-mm-dd hh:mm:ss'),
'date': xlwt.easyxf(num_format_str='yyyy-mm-dd'),
'time': xlwt.easyxf(num_format_str='hh:mm:ss'),
@@ -164,7 +166,8 @@ def _format_csv_text(self, t):
if isinstance(t, bool):
return _('Yes') if t else _('No')
t = t.replace('"', '""').replace(',', '\,')
- if isinstance(t, basestring):
+ cls_str = str if six.PY3 else basestring
+ if isinstance(t, cls_str):
t = '"%s"' % t
return t
@@ -187,17 +190,17 @@ def _to_xml(self, xml, data):
self._to_xml(xml, item)
xml.endElement("row")
elif isinstance(data, dict):
- for key, value in data.iteritems():
+ for key, value in iteritems(data):
key = key.replace(' ', '_')
xml.startElement(key, {})
self._to_xml(xml, value)
xml.endElement(key)
else:
- xml.characters(smart_unicode(data))
+ xml.characters(smart_text(data))
def get_xml_export(self, context):
results = self._get_objects(context)
- stream = StringIO.StringIO()
+ stream = io.StringIO()
xml = SimplerXMLGenerator(stream, "utf-8")
xml.startDocument()
@@ -230,7 +233,7 @@ def get_response(self, response, context, *args, **kwargs):
# View Methods
def get_result_list(self, __):
if self.request.GET.get('all', 'off') == 'on':
- self.admin_view.list_per_page = sys.maxint
+ self.admin_view.list_per_page = sys.maxsize
return __()
def result_header(self, item, field_name, row):
diff --git a/xadmin/plugins/filters.py b/xadmin/plugins/filters.py
index 03e4f7831..0ee332650 100644
--- a/xadmin/plugins/filters.py
+++ b/xadmin/plugins/filters.py
@@ -1,4 +1,5 @@
import operator
+from future.utils import iteritems
from xadmin import widgets
from xadmin.plugins.utils import get_context_dict
@@ -8,6 +9,7 @@
from django.db.models.fields import FieldDoesNotExist
from django.db.models.sql.query import LOOKUP_SEP, QUERY_TERMS
from django.template import loader
+from django.utils import six
from django.utils.encoding import smart_str
from django.utils.translation import ugettext as _
@@ -15,6 +17,7 @@
from xadmin.sites import site
from xadmin.views import BaseAdminPlugin, ListAdminView
from xadmin.util import is_related_field
+from functools import reduce
class IncorrectLookupParameters(Exception):
@@ -74,7 +77,7 @@ def lookup_allowed(self, lookup, value):
def get_list_queryset(self, queryset):
lookup_params = dict([(smart_str(k)[len(FILTER_PREFIX):], v) for k, v in self.admin_view.params.items()
if smart_str(k).startswith(FILTER_PREFIX) and v != ''])
- for p_key, p_val in lookup_params.iteritems():
+ for p_key, p_val in iteritems(lookup_params):
if p_val == "False":
lookup_params[p_key] = False
use_distinct = False
@@ -128,7 +131,7 @@ def get_list_queryset(self, queryset):
if spec and spec.has_output():
try:
new_qs = spec.do_filte(queryset)
- except ValidationError, e:
+ except ValidationError as e:
new_qs = None
self.admin_view.message_user(_("Filtering error: %s") % e.messages[0], 'error')
if new_qs is not None:
@@ -138,21 +141,23 @@ def get_list_queryset(self, queryset):
self.has_filters = bool(self.filter_specs)
self.admin_view.filter_specs = self.filter_specs
- self.admin_view.used_filter_num = len(
- filter(lambda f: f.is_used, self.filter_specs))
+ obj = filter(lambda f: f.is_used, self.filter_specs)
+ if six.PY3:
+ obj = list(obj)
+ self.admin_view.used_filter_num = len(obj)
try:
for key, value in lookup_params.items():
use_distinct = (
use_distinct or lookup_needs_distinct(self.opts, key))
- except FieldDoesNotExist, e:
+ except FieldDoesNotExist as e:
raise IncorrectLookupParameters(e)
try:
queryset = queryset.filter(**lookup_params)
except (SuspiciousOperation, ImproperlyConfigured):
raise
- except Exception, e:
+ except Exception as e:
raise IncorrectLookupParameters(e)
query = self.request.GET.get(SEARCH_VAR, '')
@@ -189,10 +194,16 @@ def construct_search(field_name):
# Media
def get_media(self, media):
- if bool(filter(lambda s: isinstance(s, DateFieldListFilter), self.filter_specs)):
+ arr = filter(lambda s: isinstance(s, DateFieldListFilter), self.filter_specs)
+ if six.PY3:
+ arr = list(arr)
+ if bool(arr):
media = media + self.vendor('datepicker.css', 'datepicker.js',
'xadmin.widget.datetime.js')
- if bool(filter(lambda s: isinstance(s, RelatedFieldSearchFilter), self.filter_specs)):
+ arr = filter(lambda s: isinstance(s, RelatedFieldSearchFilter), self.filter_specs)
+ if six.PY3:
+ arr = list(arr)
+ if bool(arr):
media = media + self.vendor(
'select.js', 'select.css', 'xadmin.widget.select.js')
return media + self.vendor('xadmin.plugin.filters.js')
diff --git a/xadmin/plugins/inline.py b/xadmin/plugins/inline.py
index cf4629156..2fbe5b19d 100644
--- a/xadmin/plugins/inline.py
+++ b/xadmin/plugins/inline.py
@@ -7,6 +7,8 @@
from django.template import loader
from django.template.loader import render_to_string
from django.contrib.auth import get_permission_codename
+from django.utils import six
+from django.utils.encoding import smart_text
from crispy_forms.utils import TEMPLATE_PACK
from xadmin.layout import FormHelper, Layout, flatatt, Container, Column, Field, Fieldset
@@ -113,10 +115,11 @@ def get_attrs(self):
def replace_field_to_value(layout, av):
if layout:
+ cls_str = str if six.PY3 else basestring
for i, lo in enumerate(layout.fields):
if isinstance(lo, Field) or issubclass(lo.__class__, Field):
layout.fields[i] = ShowField(av, *lo.fields, **lo.attrs)
- elif isinstance(lo, basestring):
+ elif isinstance(lo, cls_str):
layout.fields[i] = ShowField(av, lo)
elif hasattr(lo, 'get_field_names'):
replace_field_to_value(lo, av)
@@ -229,7 +232,7 @@ def instance_form(self, **kwargs):
label = None
if readonly_field in inst._meta.get_all_field_names():
label = inst._meta.get_field(readonly_field).verbose_name
- value = unicode(getattr(inst, readonly_field))
+ value = smart_text(getattr(inst, readonly_field))
elif inspect.ismethod(getattr(inst, readonly_field, None)):
value = getattr(inst, readonly_field)()
label = getattr(getattr(inst, readonly_field), 'short_description', readonly_field)
diff --git a/xadmin/plugins/multiselect.py b/xadmin/plugins/multiselect.py
index 29426a7fe..4fec7f2be 100644
--- a/xadmin/plugins/multiselect.py
+++ b/xadmin/plugins/multiselect.py
@@ -6,7 +6,7 @@
from django.db.models import ManyToManyField
from django.forms.utils import flatatt
from django.template import loader
-from django.utils.encoding import force_unicode
+from django.utils.encoding import force_text
from django.utils.html import escape, conditional_escape
from django.utils.safestring import mark_safe
from xadmin.util import vendor
@@ -25,9 +25,9 @@ def __init__(self, verbose_name, is_stacked, attrs=None, choices=()):
super(SelectMultipleTransfer, self).__init__(attrs, choices)
def render_opt(self, selected_choices, option_value, option_label):
- option_value = force_unicode(option_value)
+ option_value = force_text(option_value)
return u'' % (
- escape(option_value), conditional_escape(force_unicode(option_label))), bool(option_value in selected_choices)
+ escape(option_value), conditional_escape(force_text(option_label))), bool(option_value in selected_choices)
def render(self, name, value, attrs=None, choices=()):
if attrs is None:
@@ -39,14 +39,14 @@ def render(self, name, value, attrs=None, choices=()):
value = []
final_attrs = self.build_attrs(attrs, name=name)
- selected_choices = set(force_unicode(v) for v in value)
+ selected_choices = set(force_text(v) for v in value)
available_output = []
chosen_output = []
for option_value, option_label in chain(self.choices, choices):
if isinstance(option_label, (list, tuple)):
available_output.append(u'