diff --git a/xadmin/filters.py b/xadmin/filters.py
index 9eba01e99..4e719bf1b 100644
--- a/xadmin/filters.py
+++ b/xadmin/filters.py
@@ -6,21 +6,21 @@
from django.utils import timezone
from django.template.loader import get_template
from django.template.context import Context
-from django.utils import six
+import six
from django.utils.safestring import mark_safe
-from django.utils.html import escape,format_html
+from django.utils.html import escape, format_html
from django.utils.text import Truncator
from django.core.cache import cache, caches
from xadmin.views.list import EMPTY_CHANGELIST_VALUE
-from xadmin.util import is_related_field,is_related_field2
+from xadmin.util import is_related_field, is_related_field2
import datetime
FILTER_PREFIX = '_p_'
SEARCH_VAR = '_q_'
from .util import (get_model_from_relation,
- reverse_field_path, get_limit_choices_to_from_path, prepare_lookup_value)
+ reverse_field_path, get_limit_choices_to_from_path, prepare_lookup_value)
class BaseFilter(object):
@@ -125,9 +125,9 @@ def __init__(self, field, request, params, model, admin_view, field_path):
self.context_params["%s_val" % name] = ''
arr = map(
- lambda kv: setattr(self, 'lookup_' + kv[0], kv[1]),
- self.context_params.items()
- )
+ lambda kv: setattr(self, 'lookup_' + kv[0], kv[1]),
+ self.context_params.items()
+ )
if six.PY3:
list(arr)
@@ -169,27 +169,27 @@ def choices(self):
('', _('All')),
('1', _('Yes')),
('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
@@ -222,10 +222,10 @@ class TextFieldListFilter(FieldFilter):
@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)
- )
+ isinstance(field, models.CharField)
+ and field.max_length > 20
+ or isinstance(field, models.TextField)
+ )
@manager.register
@@ -320,7 +320,7 @@ def choices(self):
yield {
'selected': self.date_params == param_dict,
'query_string': self.query_string(
- param_dict, [FILTER_PREFIX + self.field_generic]),
+ param_dict, [FILTER_PREFIX + self.field_generic]),
'display': title,
}
@@ -339,12 +339,12 @@ def test(cls, field, request, params, model, admin_view, field_path):
def __init__(self, field, request, params, model, model_admin, field_path):
other_model = get_model_from_relation(field)
- if hasattr(field, 'rel'):
- rel_name = field.rel.get_related_field().name
+ if hasattr(field, 'remote_field'):
+ rel_name = field.remote_field.get_related_field().name
else:
rel_name = other_model._meta.pk.name
- self.lookup_formats = {'in': '%%s__%s__in' % rel_name,'exact': '%%s__%s__exact' % rel_name}
+ self.lookup_formats = {'in': '%%s__%s__in' % rel_name, 'exact': '%%s__%s__exact' % rel_name}
super(RelatedFieldSearchFilter, self).__init__(
field, request, params, model, model_admin, field_path)
@@ -360,9 +360,9 @@ def __init__(self, field, request, params, model, model_admin, field_path):
other_model._meta.app_label, other_model._meta.model_name))
self.label = self.label_for_value(other_model, rel_name, self.lookup_exact_val) if self.lookup_exact_val else ""
self.choices = '?'
- if field.rel.limit_choices_to:
- for i in list(field.rel.limit_choices_to):
- self.choices += "&_p_%s=%s" % (i, field.rel.limit_choices_to[i])
+ if field.remote_field.limit_choices_to:
+ for i in list(field.remote_field.limit_choices_to):
+ self.choices += "&_p_%s=%s" % (i, field.remote_field.limit_choices_to[i])
self.choices = format_html(self.choices)
def label_for_value(self, other_model, rel_name, value):
@@ -390,12 +390,12 @@ def test(cls, field, request, params, model, admin_view, field_path):
def __init__(self, field, request, params, model, model_admin, field_path):
other_model = get_model_from_relation(field)
- if hasattr(field, 'rel'):
- rel_name = field.rel.get_related_field().name
+ if hasattr(field, 'remote_field'):
+ rel_name = field.remote_field.get_related_field().name
else:
rel_name = other_model._meta.pk.name
- self.lookup_formats = {'in': '%%s__%s__in' % rel_name,'exact': '%%s__%s__exact' %
+ self.lookup_formats = {'in': '%%s__%s__in' % rel_name, 'exact': '%%s__%s__exact' %
rel_name, 'isnull': '%s__isnull'}
self.lookup_choices = field.get_choices(include_blank=False)
super(RelatedFieldListFilter, self).__init__(
@@ -409,7 +409,7 @@ def __init__(self, field, request, params, model, model_admin, field_path):
def has_output(self):
if (is_related_field(self.field)
- and self.field.field.null or hasattr(self.field, 'rel')
+ and self.field.field.null or hasattr(self.field, 'remote_field')
and self.field.null):
extra = 1
else:
@@ -435,7 +435,7 @@ def choices(self):
'display': val,
}
if (is_related_field(self.field)
- and self.field.field.null or hasattr(self.field, 'rel')
+ and self.field.field.null or hasattr(self.field, 'remote_field')
and self.field.null):
yield {
'selected': bool(self.lookup_isnull_val),
@@ -445,81 +445,83 @@ def choices(self):
'display': EMPTY_CHANGELIST_VALUE,
}
+
@manager.register
class MultiSelectFieldListFilter(ListFieldFilter):
""" Delegates the filter to the default filter and ors the results of each
-
+
Lists the distinct values of each field as a checkbox
Uses the default spec for each
-
+
"""
template = 'xadmin/filters/checklist.html'
lookup_formats = {'in': '%s__in'}
- cache_config = {'enabled':False,'key':'quickfilter_%s','timeout':3600,'cache':'default'}
-
+ cache_config = {'enabled': False, 'key': 'quickfilter_%s', 'timeout': 3600, 'cache': 'default'}
+
@classmethod
def test(cls, field, request, params, model, admin_view, field_path):
return True
-
+
def get_cached_choices(self):
if not self.cache_config['enabled']:
return None
c = caches(self.cache_config['cache'])
- return c.get(self.cache_config['key']%self.field_path)
-
- def set_cached_choices(self,choices):
+ return c.get(self.cache_config['key'] % self.field_path)
+
+ def set_cached_choices(self, choices):
if not self.cache_config['enabled']:
return
c = caches(self.cache_config['cache'])
- return c.set(self.cache_config['key']%self.field_path,choices)
-
- def __init__(self, field, request, params, model, model_admin, field_path,field_order_by=None,field_limit=None,sort_key=None,cache_config=None):
- super(MultiSelectFieldListFilter,self).__init__(field, request, params, model, model_admin, field_path)
-
+ return c.set(self.cache_config['key'] % self.field_path, choices)
+
+ def __init__(self, field, request, params, model, model_admin, field_path, field_order_by=None, field_limit=None, sort_key=None, cache_config=None):
+ super(MultiSelectFieldListFilter, self).__init__(field, request, params, model, model_admin, field_path)
+
# Check for it in the cachce
- if cache_config is not None and type(cache_config)==dict:
+ if cache_config is not None and type(cache_config) == dict:
self.cache_config.update(cache_config)
-
+
if self.cache_config['enabled']:
self.field_path = field_path
choices = self.get_cached_choices()
if choices:
self.lookup_choices = choices
return
-
+
# Else rebuild it
- queryset = self.admin_view.queryset().exclude(**{"%s__isnull"%field_path:True}).values_list(field_path, flat=True).distinct()
+ queryset = self.admin_view.queryset().exclude(**{"%s__isnull" % field_path: True}).values_list(field_path, flat=True).distinct()
#queryset = self.admin_view.queryset().distinct(field_path).exclude(**{"%s__isnull"%field_path:True})
-
+
if field_order_by is not None:
# Do a subquery to order the distinct set
queryset = self.admin_view.queryset().filter(id__in=queryset).order_by(field_order_by)
-
- if field_limit is not None and type(field_limit)==int and queryset.count()>field_limit:
+
+ if field_limit is not None and type(field_limit) == int and queryset.count() > field_limit:
queryset = queryset[:field_limit]
-
- self.lookup_choices = [str(it) for it in queryset.values_list(field_path,flat=True) if str(it).strip()!=""]
+
+ self.lookup_choices = [str(it) for it in queryset.values_list(field_path, flat=True) if str(it).strip() != ""]
if sort_key is not None:
- self.lookup_choices = sorted(self.lookup_choices,key=sort_key)
-
+ self.lookup_choices = sorted(self.lookup_choices, key=sort_key)
+
if self.cache_config['enabled']:
- self.set_cached_choices(self.lookup_choices)
+ self.set_cached_choices(self.lookup_choices)
def choices(self):
- self.lookup_in_val = (type(self.lookup_in_val) in (tuple,list)) and self.lookup_in_val or list(self.lookup_in_val)
+ self.lookup_in_val = (type(self.lookup_in_val) in (tuple, list)) and self.lookup_in_val or list(self.lookup_in_val)
yield {
'selected': len(self.lookup_in_val) == 0,
- 'query_string': self.query_string({},[self.lookup_in_name]),
+ 'query_string': self.query_string({}, [self.lookup_in_name]),
'display': _('All'),
}
for val in self.lookup_choices:
yield {
'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]),}),
+ '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,
}
+
@manager.register
class AllValuesFieldListFilter(ListFieldFilter):
lookup_formats = {'exact': '%s__exact', 'isnull': '%s__isnull'}
diff --git a/xadmin/models.py b/xadmin/models.py
index da5145641..c65eca1fd 100644
--- a/xadmin/models.py
+++ b/xadmin/models.py
@@ -5,11 +5,11 @@
from django.conf import settings
from django.contrib.contenttypes.models import ContentType
from django.utils.translation import ugettext_lazy as _, ugettext
-from django.core.urlresolvers import NoReverseMatch, reverse
+from django.urls.base import 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
-
+from django.utils.encoding import smart_text
+from six import python_2_unicode_compatible
from django.db.models.signals import post_migrate
from django.contrib.auth.models import Permission
@@ -19,6 +19,7 @@
AUTH_USER_MODEL = getattr(settings, 'AUTH_USER_MODEL', 'auth.User')
+
def add_view_permissions(sender, **kwargs):
"""
This syncdb hooks takes care of adding a view permission too all our
@@ -35,7 +36,7 @@ def add_view_permissions(sender, **kwargs):
Permission.objects.create(content_type=content_type,
codename=codename,
name="Can view %s" % content_type.name)
- #print "Added view permission for %s" % content_type.name
+ # print "Added view permission for %s" % content_type.name
# check for all our view permissions after a syncdb
post_migrate.connect(add_view_permissions)
@@ -44,9 +45,9 @@ def add_view_permissions(sender, **kwargs):
@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)
+ user = models.ForeignKey(AUTH_USER_MODEL, on_delete=models.CASCADE, verbose_name=_(u"user"), blank=True, null=True)
url_name = models.CharField(_(u'Url Name'), max_length=64)
- content_type = models.ForeignKey(ContentType)
+ content_type = models.ForeignKey(ContentType, on_delete=models.CASCADE)
query = models.CharField(_(u'Query String'), max_length=1000, blank=True)
is_share = models.BooleanField(_(u'Is Shared'), default=False)
@@ -66,6 +67,7 @@ class Meta:
class JSONEncoder(DjangoJSONEncoder):
+
def default(self, o):
if isinstance(o, datetime.datetime):
return o.strftime('%Y-%m-%d %H:%M:%S')
@@ -84,7 +86,7 @@ def default(self, o):
@python_2_unicode_compatible
class UserSettings(models.Model):
- user = models.ForeignKey(AUTH_USER_MODEL, verbose_name=_(u"user"))
+ user = models.ForeignKey(AUTH_USER_MODEL, on_delete=models.CASCADE, verbose_name=_(u"user"))
key = models.CharField(_('Settings Key'), max_length=256)
value = models.TextField(_('Settings Content'))
@@ -104,7 +106,7 @@ class Meta:
@python_2_unicode_compatible
class UserWidget(models.Model):
- user = models.ForeignKey(AUTH_USER_MODEL, verbose_name=_(u"user"))
+ user = models.ForeignKey(AUTH_USER_MODEL, on_delete=models.CASCADE, verbose_name=_(u"user"))
page_id = models.CharField(_(u"Page"), max_length=256)
widget_type = models.CharField(_(u"Widget Type"), max_length=50)
value = models.TextField(_(u"Widget Params"))
@@ -186,4 +188,3 @@ def __str__(self):
def get_edited_object(self):
"Returns the edited object represented by this log entry"
return self.content_type.get_object_for_this_type(pk=self.object_id)
-
diff --git a/xadmin/plugins/__init__.py b/xadmin/plugins/__init__.py
index e570dfe67..d0611656f 100644
--- a/xadmin/plugins/__init__.py
+++ b/xadmin/plugins/__init__.py
@@ -1,34 +1,34 @@
PLUGINS = (
- 'actions',
- 'filters',
- 'bookmark',
- 'export',
- 'layout',
+ 'actions',
+ 'filters',
+ 'bookmark',
+ 'export',
+ 'layout',
'refresh',
'details',
- 'editable',
- 'relate',
- 'chart',
- 'ajax',
- 'relfield',
- 'inline',
- 'topnav',
- 'portal',
+ 'editable',
+ 'relate',
+ 'chart',
+ 'ajax',
+ 'relfield',
+ 'inline',
+ 'topnav',
+ 'portal',
'quickform',
- 'wizard',
- 'images',
- 'auth',
- 'multiselect',
- 'themes',
- 'aggregation',
- 'mobile',
+ 'wizard',
+ 'images',
+ 'auth',
+ 'multiselect',
+ 'themes',
+ 'aggregation',
+ # 'mobile',
'passwords',
- 'sitemenu',
- 'language',
+ 'sitemenu',
+ 'language',
'quickfilter',
'sortablelist',
- 'importexport'
+ 'importexport'
)
diff --git a/xadmin/plugins/actions.py b/xadmin/plugins/actions.py
index 02085bf33..01f69ac43 100644
--- a/xadmin/plugins/actions.py
+++ b/xadmin/plugins/actions.py
@@ -1,11 +1,11 @@
from collections import OrderedDict
-from django import forms
+from django import forms, VERSION as django_version
from django.core.exceptions import PermissionDenied
from django.db import router
from django.http import HttpResponse, HttpResponseRedirect
from django.template import loader
from django.template.response import TemplateResponse
-from django.utils import six
+import six
from django.utils.encoding import force_text
from django.utils.safestring import mark_safe
from django.utils.translation import ugettext as _, ungettext
@@ -19,6 +19,7 @@
from xadmin.views import BaseAdminPlugin, ListAdminView
from xadmin.views.base import filter_hook, ModelAdminView
+from xadmin import views
ACTION_CHECKBOX_NAME = '_selected_action'
checkbox = forms.CheckboxInput({'class': 'action-select'}, lambda value: False)
@@ -26,12 +27,15 @@
def action_checkbox(obj):
return checkbox.render(ACTION_CHECKBOX_NAME, force_text(obj.pk))
+
+
action_checkbox.short_description = mark_safe(
'')
action_checkbox.allow_tags = True
action_checkbox.allow_export = False
action_checkbox.is_column = False
+
class BaseActionView(ModelAdminView):
action_name = None
description = None
@@ -51,6 +55,13 @@ def init_action(self, list_view):
def do_action(self, queryset):
pass
+ def __init__(self, request, *args, **kwargs):
+ super().__init__(request, *args, **kwargs)
+ if django_version > (2, 0):
+ for model in self.admin_site._registry:
+ if not hasattr(self.admin_site._registry[model], 'has_delete_permission'):
+ setattr(self.admin_site._registry[model], 'has_delete_permission', self.has_delete_permission)
+
class DeleteSelectedAction(BaseActionView):
@@ -70,7 +81,7 @@ def delete_models(self, queryset):
n = queryset.count()
if n:
if self.delete_models_batch:
- self.log('delete', _('Batch delete %(count)d %(items)s.') % { "count": n, "items": model_ngettext(self.opts, n) })
+ self.log('delete', _('Batch delete %(count)d %(items)s.') % {"count": n, "items": model_ngettext(self.opts, n)})
queryset.delete()
else:
for obj in queryset:
@@ -86,12 +97,17 @@ def do_action(self, queryset):
if not self.has_delete_permission():
raise PermissionDenied
- using = router.db_for_write(self.model)
-
# Populate deletable_objects, a data structure of all related objects that
# will also be deleted.
- deletable_objects, model_count, perms_needed, protected = get_deleted_objects(
- queryset, self.opts, self.user, self.admin_site, using)
+
+ if django_version > (2, 1):
+ deletable_objects, model_count, perms_needed, protected = get_deleted_objects(
+ queryset, self.opts, self.admin_site)
+ else:
+ using = router.db_for_write(self.model)
+ deletable_objects, model_count, perms_needed, protected = get_deleted_objects(
+ queryset, self.opts, self.user, self.admin_site, using)
+
# The user has already confirmed the deletion.
# Do the deletion and return a None to display the change list view again.
diff --git a/xadmin/plugins/aggregation.py b/xadmin/plugins/aggregation.py
index 16114da8d..1e7553fad 100644
--- a/xadmin/plugins/aggregation.py
+++ b/xadmin/plugins/aggregation.py
@@ -1,5 +1,6 @@
from django.db.models import FieldDoesNotExist, Avg, Max, Min, Count, Sum
from django.utils.translation import ugettext as _
+from django.forms import Media
from xadmin.sites import site
from xadmin.views import BaseAdminPlugin, ListAdminView
@@ -61,9 +62,7 @@ def results(self, rows):
# Media
def get_media(self, media):
- media.add_css({'screen': [self.static(
- 'xadmin/css/xadmin.plugin.aggregation.css'), ]})
- return media
+ return media + Media(css={'screen': [self.static('xadmin/css/xadmin.plugin.aggregation.css'), ]})
site.register_plugin(AggregationPlugin, ListAdminView)
diff --git a/xadmin/plugins/bookmark.py b/xadmin/plugins/bookmark.py
index 3d612cec8..f6e0708e8 100644
--- a/xadmin/plugins/bookmark.py
+++ b/xadmin/plugins/bookmark.py
@@ -1,6 +1,6 @@
from django.contrib.contenttypes.models import ContentType
-from django.core.urlresolvers import reverse
+from django.urls.base import reverse
from django.db import transaction
from django.db.models import Q
from django.forms import ModelChoiceField
@@ -43,16 +43,16 @@ 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()
- ))
- ])
+ '%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
@@ -64,21 +64,22 @@ def get_context(self, context):
for bk in self.list_bookmarks:
title = bk['title']
params = dict([
- (FILTER_PREFIX + k, v)
- for (k, v) in bk['query'].items()
- ])
+ (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:
params[COL_LIST_VAR] = '.'.join(bk['cols'])
if 'search' in bk:
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()))
- ])
+ ])
url = list_base_url + '?' + bk_qs
selected = (current_qs == bk_qs)
@@ -174,7 +175,6 @@ def get_list_display(self):
list_display.remove('user')
return list_display
-
def has_change_permission(self, obj=None):
if not obj or self.user.is_superuser:
return True
@@ -222,12 +222,12 @@ 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()
- ]
+ [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/details.py b/xadmin/plugins/details.py
index 0b5ed919e..ba167fdc3 100644
--- a/xadmin/plugins/details.py
+++ b/xadmin/plugins/details.py
@@ -1,7 +1,7 @@
from django.utils.translation import ugettext as _
-from django.core.urlresolvers import reverse, NoReverseMatch
+from django.urls.base import reverse, NoReverseMatch
from django.db import models
from xadmin.sites import site
@@ -16,7 +16,7 @@ class DetailsPlugin(BaseAdminPlugin):
def result_item(self, item, obj, field_name, row):
if (self.show_all_rel_details or (field_name in self.show_detail_fields)):
rel_obj = None
- if hasattr(item.field, 'rel') and isinstance(item.field.rel, models.ManyToOneRel):
+ if hasattr(item.field, 'remote_field') and isinstance(item.field.remote_field, models.ManyToOneRel):
rel_obj = getattr(obj, field_name)
elif field_name in self.show_detail_fields:
rel_obj = obj
diff --git a/xadmin/plugins/editable.py b/xadmin/plugins/editable.py
index 1b49feb60..68a596004 100644
--- a/xadmin/plugins/editable.py
+++ b/xadmin/plugins/editable.py
@@ -2,6 +2,7 @@
from django.core.exceptions import PermissionDenied, ObjectDoesNotExist
from django.db import models, transaction
from django.forms.models import modelform_factory
+from django.forms import Media
from django.http import Http404, HttpResponse
from django.utils.encoding import force_text, smart_text
from django.utils.html import escape, conditional_escape
@@ -32,7 +33,7 @@ def init_request(self, *args, **kwargs):
return active
def result_item(self, item, obj, field_name, row):
- if self.list_editable and item.field and item.field.editable and (field_name in self.list_editable):
+ if self.list_editable and item.field and item.field.editable and (field_name in self.list_editable):
pk = getattr(obj, obj._meta.pk.attname)
field_label = label_for_field(field_name, obj,
model_admin=self.admin_view,
@@ -52,7 +53,12 @@ def result_item(self, item, obj, field_name, row):
# Media
def get_media(self, media):
if self.editable_need_fields:
- media = media + self.model_form.media + \
+
+ try:
+ m = self.model_form.media
+ except:
+ m = Media()
+ media = media + m +\
self.vendor(
'xadmin.plugin.editable.js', 'xadmin.widget.editable.css')
return media
@@ -75,7 +81,7 @@ def init_request(self, object_id, *args, **kwargs):
def get_new_field_html(self, f):
result = self.result_item(self.org_obj, f, {'is_display_first':
- False, 'object': self.org_obj})
+ False, 'object': self.org_obj})
return mark_safe(result.text) if result.allow_tags else conditional_escape(result.text)
def _get_new_field_html(self, field_name):
@@ -156,5 +162,6 @@ def post(self, request, object_id):
return self.render_response(result)
+
site.register_plugin(EditablePlugin, ListAdminView)
site.register_modelview(r'^(.+)/patch/$', EditPatchView, name='%s_%s_patch')
diff --git a/xadmin/plugins/export.py b/xadmin/plugins/export.py
index 8ccc0f4e8..12d94e262 100644
--- a/xadmin/plugins/export.py
+++ b/xadmin/plugins/export.py
@@ -5,7 +5,7 @@
from django.http import HttpResponse
from django.template import loader
-from django.utils import six
+import six
from django.utils.encoding import force_text, smart_text
from django.utils.html import escape
from django.utils.translation import ugettext as _
diff --git a/xadmin/plugins/filters.py b/xadmin/plugins/filters.py
index dc7e171d8..f9a308f6f 100644
--- a/xadmin/plugins/filters.py
+++ b/xadmin/plugins/filters.py
@@ -7,9 +7,10 @@
from django.core.exceptions import SuspiciousOperation, ImproperlyConfigured, ValidationError
from django.db import models
from django.db.models.fields import FieldDoesNotExist
-from django.db.models.sql.query import LOOKUP_SEP, QUERY_TERMS
+from django.db.models.constants import LOOKUP_SEP
+# from django.db.models.sql.constants import QUERY_TERMS
from django.template import loader
-from django.utils import six
+import six
from django.utils.encoding import smart_str
from django.utils.translation import ugettext as _
@@ -44,8 +45,8 @@ def lookup_allowed(self, lookup, value):
# Last term in lookup is a query term (__exact, __startswith etc)
# This term can be ignored.
- if len(parts) > 1 and parts[-1] in QUERY_TERMS:
- parts.pop()
+ # if len(parts) > 1 and parts[-1] in QUERY_TERMS:
+ # parts.pop()
# Special case -- foo__id__exact and foo__id queries are implied
# if foo has been specificially included in the lookup list; so
@@ -59,9 +60,9 @@ def lookup_allowed(self, lookup, value):
# Lookups on non-existants fields are ok, since they're ignored
# later.
return True
- if hasattr(field, 'rel'):
- model = field.rel.to
- rel_name = field.rel.get_related_field().name
+ if hasattr(field, 'remote_field'):
+ model = field.remote_field.to
+ rel_name = field.remote_field.get_related_field().name
elif is_related_field(field):
model = field.model
rel_name = model._meta.pk.name
@@ -158,7 +159,7 @@ def get_list_queryset(self, queryset):
# fix a bug by david: In demo, quick filter by IDC Name() cannot be used.
if isinstance(queryset, models.query.QuerySet) and lookup_params:
new_lookup_parames = dict()
- for k, v in lookup_params.iteritems():
+ for k, v in lookup_params.items():
list_v = v.split(',')
if len(list_v) > 0:
new_lookup_parames.update({k: list_v})
diff --git a/xadmin/plugins/images.py b/xadmin/plugins/images.py
index 1c92bca55..7fea2b3b1 100644
--- a/xadmin/plugins/images.py
+++ b/xadmin/plugins/images.py
@@ -42,13 +42,13 @@ class AdminImageWidget(forms.FileInput):
def __init__(self, attrs={}):
super(AdminImageWidget, self).__init__(attrs)
- def render(self, name, value, attrs=None):
+ def render(self, name, value, attrs=None, renderer=None):
output = []
if value and hasattr(value, "url"):
label = self.attrs.get('label', name)
output.append('
%s ' %
(value.url, label, value.url, _('Change:')))
- output.append(super(AdminImageWidget, self).render(name, value, attrs))
+ output.append(super(AdminImageWidget, self).render(name, value, attrs, renderer))
return mark_safe(u''.join(output))
diff --git a/xadmin/plugins/importexport.py b/xadmin/plugins/importexport.py
index 90fe10c47..507ae5e1e 100644
--- a/xadmin/plugins/importexport.py
+++ b/xadmin/plugins/importexport.py
@@ -63,7 +63,7 @@ class FooAdmin(object):
from django.contrib.admin.models import LogEntry, ADDITION, CHANGE, DELETION
from django.contrib.contenttypes.models import ContentType
from django.contrib import messages
-from django.core.urlresolvers import reverse
+from django.urls.base import reverse
from django.core.exceptions import PermissionDenied
from django.http import HttpResponseRedirect, HttpResponse
@@ -153,6 +153,7 @@ def get_import_formats(self):
class ImportView(ImportBaseView):
+
def get_media(self):
media = super(ImportView, self).get_media()
media = media + self.vendor('xadmin.plugin.importexport.css')
@@ -254,6 +255,7 @@ def post(self, request, *args, **kwargs):
class ImportProcessView(ImportBaseView):
+
@filter_hook
@csrf_protect_m
@transaction.atomic
diff --git a/xadmin/plugins/inline.py b/xadmin/plugins/inline.py
index 2fbe5b19d..fd4d18ac8 100644
--- a/xadmin/plugins/inline.py
+++ b/xadmin/plugins/inline.py
@@ -7,7 +7,7 @@
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
+import six
from django.utils.encoding import smart_text
from crispy_forms.utils import TEMPLATE_PACK
@@ -210,7 +210,7 @@ def instance_form(self, **kwargs):
rendered_fields = [i[1] for i in layout.get_field_names()]
layout.extend([f for f in instance[0]
- .fields.keys() if f not in rendered_fields])
+ .fields.keys() if f not in rendered_fields])
helper.add_layout(layout)
style.update_layout(helper)
@@ -227,10 +227,11 @@ def instance_form(self, **kwargs):
form.readonly_fields = []
inst = form.save(commit=False)
if inst:
+ meta_field_names = [field.name for field in inst._meta.get_fields()]
for readonly_field in readonly_fields:
value = None
label = None
- if readonly_field in inst._meta.get_all_field_names():
+ if readonly_field in meta_field_names:
label = inst._meta.get_field(readonly_field).verbose_name
value = smart_text(getattr(inst, readonly_field))
elif inspect.ismethod(getattr(inst, readonly_field, None)):
@@ -268,8 +269,8 @@ def has_change_permission(self):
opts = self.opts
if opts.auto_created:
for field in opts.fields:
- if field.rel and field.rel.to != self.parent_model:
- opts = field.rel.to._meta
+ if field.remote_field and field.remote_field.model != self.parent_model:
+ opts = field.remote_field.model._meta
break
codename = get_permission_codename('change', opts)
@@ -352,7 +353,7 @@ class Inline(Fieldset):
def __init__(self, rel_model):
self.model = rel_model
self.fields = []
- super(Inline,self).__init__(legend="")
+ super(Inline, self).__init__(legend="")
def render(self, form, form_style, context, template_pack=TEMPLATE_PACK, **kwargs):
return ""
@@ -471,6 +472,7 @@ def _get_detail_formset_instance(self, inline):
DetailAdminUtil, fake_admin_class, instance)
return formset
+
class DetailAdminUtil(DetailAdminView):
def init_request(self, obj):
diff --git a/xadmin/plugins/language.py b/xadmin/plugins/language.py
index 3814771e4..7c73b9d7d 100644
--- a/xadmin/plugins/language.py
+++ b/xadmin/plugins/language.py
@@ -14,6 +14,7 @@ def block_top_navmenu(self, context, nodes):
context['redirect_to'] = self.request.get_full_path()
nodes.append(loader.render_to_string('xadmin/blocks/comm.top.setlang.html', context=context))
+
class SetLangView(BaseAdminView):
def post(self, request, *args, **kwargs):
@@ -21,6 +22,6 @@ def post(self, request, *args, **kwargs):
del request.session['nav_menu']
return set_language(request)
-if settings.LANGUAGES and 'django.middleware.locale.LocaleMiddleware' in settings.MIDDLEWARE_CLASSES:
+if settings.LANGUAGES and 'django.middleware.locale.LocaleMiddleware' in settings.MIDDLEWARE:
site.register_plugin(SetLangNavPlugin, CommAdminView)
site.register_view(r'^i18n/setlang/$', SetLangView, 'set_language')
diff --git a/xadmin/plugins/multiselect.py b/xadmin/plugins/multiselect.py
index 38e0c917d..0681af8ab 100644
--- a/xadmin/plugins/multiselect.py
+++ b/xadmin/plugins/multiselect.py
@@ -9,7 +9,7 @@
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, DJANGO_11
+from xadmin.util import vendor
from xadmin.views import BaseAdminPlugin, ModelFormAdminView
@@ -37,10 +37,7 @@ def render(self, name, value, attrs=None, choices=()):
attrs['class'] += 'stacked'
if value is None:
value = []
- if DJANGO_11:
- final_attrs = self.build_attrs(attrs, extra_attrs={'name': name})
- else:
- final_attrs = self.build_attrs(attrs, name=name)
+ final_attrs = self.build_attrs(attrs, extra_attrs={'name': name})
selected_choices = set(force_text(v) for v in value)
available_output = []
diff --git a/xadmin/plugins/passwords.py b/xadmin/plugins/passwords.py
index ac7852714..a5f3eebe5 100644
--- a/xadmin/plugins/passwords.py
+++ b/xadmin/plugins/passwords.py
@@ -1,7 +1,7 @@
# coding=utf-8
from django.contrib.auth.forms import PasswordResetForm, SetPasswordForm
from django.contrib.auth.tokens import default_token_generator
-from django.contrib.auth.views import password_reset_confirm
+from django.contrib.auth.views import PasswordResetConfirmView as password_reset_confirm
from django.template.response import TemplateResponse
from django.utils.translation import ugettext as _
@@ -53,14 +53,17 @@ def post(self, request, *args, **kwargs):
else:
return self.get(request, form=form)
+
site.register_view(r'^xadmin/password_reset/$', ResetPasswordSendView, name='xadmin_password_reset')
+
class ResetLinkPlugin(BaseAdminPlugin):
def block_form_bottom(self, context, nodes):
reset_link = self.get_admin_url('xadmin_password_reset')
return '