604 lines
21 KiB
Python
604 lines
21 KiB
Python
|
import copy
|
||
|
import functools
|
||
|
import datetime
|
||
|
import decimal
|
||
|
from functools import update_wrapper
|
||
|
from inspect import getfullargspec
|
||
|
|
||
|
from django import forms
|
||
|
from django.apps import apps
|
||
|
from django.conf import settings
|
||
|
from django.contrib import messages
|
||
|
from django.contrib.auth import get_permission_codename
|
||
|
from django.core.exceptions import ValidationError
|
||
|
from django.core.serializers.json import DjangoJSONEncoder
|
||
|
from django.urls.base import reverse
|
||
|
from django.http import HttpResponse
|
||
|
from django.template import Context, Template
|
||
|
from django.template.response import TemplateResponse
|
||
|
from django.utils import six
|
||
|
from django.utils.decorators import method_decorator, classonlymethod
|
||
|
from django.utils.encoding import force_text, smart_text, smart_str
|
||
|
from django.utils.functional import Promise
|
||
|
from django.utils.http import urlencode
|
||
|
from django.utils.itercompat import is_iterable
|
||
|
from django.utils.safestring import mark_safe
|
||
|
from django.utils.text import capfirst
|
||
|
from django.utils.translation import ugettext as _
|
||
|
from django.views.decorators.csrf import csrf_protect
|
||
|
from django.views.generic import View
|
||
|
from collections import OrderedDict
|
||
|
from xadmin.util import static, json, vendor, sortkeypicker
|
||
|
|
||
|
from xadmin.models import Log
|
||
|
|
||
|
csrf_protect_m = method_decorator(csrf_protect)
|
||
|
|
||
|
|
||
|
class IncorrectPluginArg(Exception):
|
||
|
pass
|
||
|
|
||
|
|
||
|
def get_content_type_for_model(obj):
|
||
|
from django.contrib.contenttypes.models import ContentType
|
||
|
return ContentType.objects.get_for_model(obj, for_concrete_model=False)
|
||
|
|
||
|
|
||
|
def filter_chain(filters, token, func, *args, **kwargs):
|
||
|
if token == -1:
|
||
|
return func()
|
||
|
else:
|
||
|
def _inner_method():
|
||
|
fm = filters[token]
|
||
|
fargs = getfullargspec(fm)[0]
|
||
|
if len(fargs) == 1:
|
||
|
# Only self arg
|
||
|
result = func()
|
||
|
if result is None:
|
||
|
return fm()
|
||
|
else:
|
||
|
raise IncorrectPluginArg(u'Plugin filter method need a arg to receive parent method result.')
|
||
|
else:
|
||
|
return fm(func if fargs[1] == '__' else func(), *args, **kwargs)
|
||
|
return filter_chain(filters, token - 1, _inner_method, *args, **kwargs)
|
||
|
|
||
|
|
||
|
def filter_hook(func):
|
||
|
tag = func.__name__
|
||
|
func.__doc__ = "``filter_hook``\n\n" + (func.__doc__ or "")
|
||
|
|
||
|
@functools.wraps(func)
|
||
|
def method(self, *args, **kwargs):
|
||
|
|
||
|
def _inner_method():
|
||
|
return func(self, *args, **kwargs)
|
||
|
|
||
|
if self.plugins:
|
||
|
filters = [(getattr(getattr(p, tag), 'priority', 10), getattr(p, tag))
|
||
|
for p in self.plugins if callable(getattr(p, tag, None))]
|
||
|
filters = [f for p, f in sorted(filters, key=lambda x:x[0])]
|
||
|
return filter_chain(filters, len(filters) - 1, _inner_method, *args, **kwargs)
|
||
|
else:
|
||
|
return _inner_method()
|
||
|
return method
|
||
|
|
||
|
|
||
|
def inclusion_tag(file_name, context_class=Context, takes_context=False):
|
||
|
def wrap(func):
|
||
|
@functools.wraps(func)
|
||
|
def method(self, context, nodes, *arg, **kwargs):
|
||
|
_dict = func(self, context, nodes, *arg, **kwargs)
|
||
|
from django.template.loader import get_template, select_template
|
||
|
cls_str = str if six.PY3 else basestring
|
||
|
if isinstance(file_name, Template):
|
||
|
t = file_name
|
||
|
elif not isinstance(file_name, cls_str) and is_iterable(file_name):
|
||
|
t = select_template(file_name)
|
||
|
else:
|
||
|
t = get_template(file_name)
|
||
|
|
||
|
_dict['autoescape'] = context.autoescape
|
||
|
_dict['use_l10n'] = context.use_l10n
|
||
|
_dict['use_tz'] = context.use_tz
|
||
|
_dict['admin_view'] = context['admin_view']
|
||
|
|
||
|
csrf_token = context.get('csrf_token', None)
|
||
|
if csrf_token is not None:
|
||
|
_dict['csrf_token'] = csrf_token
|
||
|
nodes.append(t.render(_dict))
|
||
|
|
||
|
return method
|
||
|
return wrap
|
||
|
|
||
|
|
||
|
class JSONEncoder(DjangoJSONEncoder):
|
||
|
|
||
|
def default(self, o):
|
||
|
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, Promise):
|
||
|
return force_text(o)
|
||
|
else:
|
||
|
try:
|
||
|
return super(JSONEncoder, self).default(o)
|
||
|
except Exception:
|
||
|
return smart_text(o)
|
||
|
|
||
|
|
||
|
class BaseAdminObject(object):
|
||
|
|
||
|
def get_view(self, view_class, option_class=None, *args, **kwargs):
|
||
|
opts = kwargs.pop('opts', {})
|
||
|
return self.admin_site.get_view_class(view_class, option_class, **opts)(self.request, *args, **kwargs)
|
||
|
|
||
|
def get_model_view(self, view_class, model, *args, **kwargs):
|
||
|
return self.get_view(view_class, self.admin_site._registry.get(model), *args, **kwargs)
|
||
|
|
||
|
def get_admin_url(self, name, *args, **kwargs):
|
||
|
return reverse('%s:%s' % (self.admin_site.app_name, name), args=args, kwargs=kwargs)
|
||
|
|
||
|
def get_model_url(self, model, name, *args, **kwargs):
|
||
|
return reverse(
|
||
|
'%s:%s_%s_%s' % (self.admin_site.app_name, model._meta.app_label,
|
||
|
model._meta.model_name, name),
|
||
|
args=args, kwargs=kwargs, current_app=self.admin_site.name)
|
||
|
|
||
|
def get_model_perm(self, model, name):
|
||
|
return '%s.%s_%s' % (model._meta.app_label, name, model._meta.model_name)
|
||
|
|
||
|
def has_model_perm(self, model, name, user=None):
|
||
|
user = user or self.user
|
||
|
return user.has_perm(self.get_model_perm(model, name)) or (name == 'view' and self.has_model_perm(model, 'change', user))
|
||
|
|
||
|
def get_query_string(self, new_params=None, remove=None):
|
||
|
if new_params is None:
|
||
|
new_params = {}
|
||
|
if remove is None:
|
||
|
remove = []
|
||
|
p = dict(self.request.GET.items()).copy()
|
||
|
arr_keys = list(p.keys())
|
||
|
for r in remove:
|
||
|
for k in arr_keys:
|
||
|
if k.startswith(r):
|
||
|
del p[k]
|
||
|
for k, v in new_params.items():
|
||
|
if v is None:
|
||
|
if k in p:
|
||
|
del p[k]
|
||
|
else:
|
||
|
p[k] = v
|
||
|
return '?%s' % urlencode(p)
|
||
|
|
||
|
def get_form_params(self, new_params=None, remove=None):
|
||
|
if new_params is None:
|
||
|
new_params = {}
|
||
|
if remove is None:
|
||
|
remove = []
|
||
|
p = dict(self.request.GET.items()).copy()
|
||
|
arr_keys = list(p.keys())
|
||
|
for r in remove:
|
||
|
for k in arr_keys:
|
||
|
if k.startswith(r):
|
||
|
del p[k]
|
||
|
for k, v in new_params.items():
|
||
|
if v is None:
|
||
|
if k in p:
|
||
|
del p[k]
|
||
|
else:
|
||
|
p[k] = v
|
||
|
return mark_safe(''.join(
|
||
|
'<input type="hidden" name="%s" value="%s"/>' % (k, v) for k, v in p.items() if v))
|
||
|
|
||
|
def render_response(self, content, response_type='json'):
|
||
|
if response_type == 'json':
|
||
|
response = HttpResponse(content_type="application/json; charset=UTF-8")
|
||
|
response.write(
|
||
|
json.dumps(content, cls=JSONEncoder, ensure_ascii=False))
|
||
|
return response
|
||
|
return HttpResponse(content)
|
||
|
|
||
|
def template_response(self, template, context):
|
||
|
return TemplateResponse(self.request, template, context)
|
||
|
|
||
|
def message_user(self, message, level='info'):
|
||
|
"""
|
||
|
Send a message to the user. The default implementation
|
||
|
posts a message using the django.contrib.messages backend.
|
||
|
"""
|
||
|
if hasattr(messages, level) and callable(getattr(messages, level)):
|
||
|
getattr(messages, level)(self.request, message)
|
||
|
|
||
|
def static(self, path):
|
||
|
return static(path)
|
||
|
|
||
|
def vendor(self, *tags):
|
||
|
return vendor(*tags)
|
||
|
|
||
|
def log(self, flag, message, obj=None):
|
||
|
log = Log(
|
||
|
user=self.user,
|
||
|
ip_addr=self.request.META['REMOTE_ADDR'],
|
||
|
action_flag=flag,
|
||
|
message=message
|
||
|
)
|
||
|
if obj:
|
||
|
log.content_type = get_content_type_for_model(obj)
|
||
|
log.object_id = obj.pk
|
||
|
log.object_repr = force_text(obj)
|
||
|
log.save()
|
||
|
|
||
|
|
||
|
class BaseAdminPlugin(BaseAdminObject):
|
||
|
|
||
|
def __init__(self, admin_view):
|
||
|
self.admin_view = admin_view
|
||
|
self.admin_site = admin_view.admin_site
|
||
|
|
||
|
if hasattr(admin_view, 'model'):
|
||
|
self.model = admin_view.model
|
||
|
self.opts = admin_view.model._meta
|
||
|
|
||
|
def init_request(self, *args, **kwargs):
|
||
|
pass
|
||
|
|
||
|
|
||
|
class BaseAdminView(BaseAdminObject, View):
|
||
|
""" Base Admin view, support some comm attrs."""
|
||
|
|
||
|
base_template = 'xadmin/base.html'
|
||
|
need_site_permission = True
|
||
|
|
||
|
def __init__(self, request, *args, **kwargs):
|
||
|
self.request = request
|
||
|
self.request_method = request.method.lower()
|
||
|
self.user = request.user
|
||
|
|
||
|
self.base_plugins = [p(self) for p in getattr(self,
|
||
|
"plugin_classes", [])]
|
||
|
|
||
|
self.args = args
|
||
|
self.kwargs = kwargs
|
||
|
self.init_plugin(*args, **kwargs)
|
||
|
self.init_request(*args, **kwargs)
|
||
|
|
||
|
@classonlymethod
|
||
|
def as_view(cls):
|
||
|
def view(request, *args, **kwargs):
|
||
|
self = cls(request, *args, **kwargs)
|
||
|
|
||
|
if hasattr(self, 'get') and not hasattr(self, 'head'):
|
||
|
self.head = self.get
|
||
|
|
||
|
if self.request_method in self.http_method_names:
|
||
|
handler = getattr(
|
||
|
self, self.request_method, self.http_method_not_allowed)
|
||
|
else:
|
||
|
handler = self.http_method_not_allowed
|
||
|
|
||
|
return handler(request, *args, **kwargs)
|
||
|
|
||
|
# take name and docstring from class
|
||
|
update_wrapper(view, cls, updated=())
|
||
|
view.need_site_permission = cls.need_site_permission
|
||
|
|
||
|
return view
|
||
|
|
||
|
def init_request(self, *args, **kwargs):
|
||
|
pass
|
||
|
|
||
|
def init_plugin(self, *args, **kwargs):
|
||
|
plugins = []
|
||
|
for p in self.base_plugins:
|
||
|
p.request = self.request
|
||
|
p.user = self.user
|
||
|
p.args = self.args
|
||
|
p.kwargs = self.kwargs
|
||
|
result = p.init_request(*args, **kwargs)
|
||
|
if result is not False:
|
||
|
plugins.append(p)
|
||
|
self.plugins = plugins
|
||
|
|
||
|
@filter_hook
|
||
|
def get_context(self):
|
||
|
return {'admin_view': self, 'media': self.media, 'base_template': self.base_template}
|
||
|
|
||
|
@property
|
||
|
def media(self):
|
||
|
return self.get_media()
|
||
|
|
||
|
@filter_hook
|
||
|
def get_media(self):
|
||
|
return forms.Media()
|
||
|
|
||
|
|
||
|
class CommAdminView(BaseAdminView):
|
||
|
|
||
|
base_template = 'xadmin/base_site.html'
|
||
|
menu_template = 'xadmin/includes/sitemenu_default.html'
|
||
|
|
||
|
site_title = getattr(settings, "XADMIN_TITLE", _(u"Django Xadmin"))
|
||
|
site_footer = getattr(settings, "XADMIN_FOOTER_TITLE", _(u"my-company.inc"))
|
||
|
|
||
|
global_models_icon = {}
|
||
|
default_model_icon = None
|
||
|
apps_label_title = {}
|
||
|
apps_icons = {}
|
||
|
|
||
|
def get_site_menu(self):
|
||
|
return None
|
||
|
|
||
|
@filter_hook
|
||
|
def get_nav_menu(self):
|
||
|
site_menu = list(self.get_site_menu() or [])
|
||
|
had_urls = []
|
||
|
|
||
|
def get_url(menu, had_urls):
|
||
|
if 'url' in menu:
|
||
|
had_urls.append(menu['url'])
|
||
|
if 'menus' in menu:
|
||
|
for m in menu['menus']:
|
||
|
get_url(m, had_urls)
|
||
|
get_url({'menus': site_menu}, had_urls)
|
||
|
|
||
|
nav_menu = OrderedDict()
|
||
|
|
||
|
for model, model_admin in self.admin_site._registry.items():
|
||
|
if getattr(model_admin, 'hidden_menu', False):
|
||
|
continue
|
||
|
app_label = model._meta.app_label
|
||
|
app_icon = None
|
||
|
model_dict = {
|
||
|
'title': smart_text(capfirst(model._meta.verbose_name_plural)),
|
||
|
'url': self.get_model_url(model, "changelist"),
|
||
|
'icon': self.get_model_icon(model),
|
||
|
'perm': self.get_model_perm(model, 'view'),
|
||
|
'order': model_admin.order,
|
||
|
}
|
||
|
if model_dict['url'] in had_urls:
|
||
|
continue
|
||
|
|
||
|
app_key = "app:%s" % app_label
|
||
|
if app_key in nav_menu:
|
||
|
nav_menu[app_key]['menus'].append(model_dict)
|
||
|
else:
|
||
|
# Find app title
|
||
|
app_title = smart_text(app_label.title())
|
||
|
if app_label.lower() in self.apps_label_title:
|
||
|
app_title = self.apps_label_title[app_label.lower()]
|
||
|
else:
|
||
|
app_title = smart_text(apps.get_app_config(app_label).verbose_name)
|
||
|
# find app icon
|
||
|
if app_label.lower() in self.apps_icons:
|
||
|
app_icon = self.apps_icons[app_label.lower()]
|
||
|
|
||
|
nav_menu[app_key] = {
|
||
|
'title': app_title,
|
||
|
'menus': [model_dict],
|
||
|
}
|
||
|
|
||
|
app_menu = nav_menu[app_key]
|
||
|
if app_icon:
|
||
|
app_menu['first_icon'] = app_icon
|
||
|
elif ('first_icon' not in app_menu or
|
||
|
app_menu['first_icon'] == self.default_model_icon) and model_dict.get('icon'):
|
||
|
app_menu['first_icon'] = model_dict['icon']
|
||
|
|
||
|
if 'first_url' not in app_menu and model_dict.get('url'):
|
||
|
app_menu['first_url'] = model_dict['url']
|
||
|
|
||
|
for menu in nav_menu.values():
|
||
|
menu['menus'].sort(key=sortkeypicker(['order', 'title']))
|
||
|
|
||
|
nav_menu = list(nav_menu.values())
|
||
|
nav_menu.sort(key=lambda x: x['title'])
|
||
|
|
||
|
site_menu.extend(nav_menu)
|
||
|
|
||
|
return site_menu
|
||
|
|
||
|
@filter_hook
|
||
|
def get_context(self):
|
||
|
context = super(CommAdminView, self).get_context()
|
||
|
|
||
|
if not settings.DEBUG and 'nav_menu' in self.request.session:
|
||
|
nav_menu = json.loads(self.request.session['nav_menu'])
|
||
|
else:
|
||
|
menus = copy.copy(self.get_nav_menu())
|
||
|
|
||
|
def check_menu_permission(item):
|
||
|
need_perm = item.pop('perm', None)
|
||
|
if need_perm is None:
|
||
|
return True
|
||
|
elif callable(need_perm):
|
||
|
return need_perm(self.user)
|
||
|
elif need_perm == 'super':
|
||
|
return self.user.is_superuser
|
||
|
else:
|
||
|
return self.user.has_perm(need_perm)
|
||
|
|
||
|
def filter_item(item):
|
||
|
if 'menus' in item:
|
||
|
before_filter_length = len(item['menus'])
|
||
|
item['menus'] = [filter_item(
|
||
|
i) for i in item['menus'] if check_menu_permission(i)]
|
||
|
after_filter_length = len(item['menus'])
|
||
|
if after_filter_length == 0 and before_filter_length > 0:
|
||
|
return None
|
||
|
return item
|
||
|
|
||
|
nav_menu = [filter_item(item) for item in menus if check_menu_permission(item)]
|
||
|
nav_menu = list(filter(lambda x: x, nav_menu))
|
||
|
|
||
|
if not settings.DEBUG:
|
||
|
self.request.session['nav_menu'] = json.dumps(nav_menu, cls=JSONEncoder, ensure_ascii=False)
|
||
|
self.request.session.modified = True
|
||
|
|
||
|
def check_selected(menu, path):
|
||
|
selected = False
|
||
|
if 'url' in menu:
|
||
|
chop_index = menu['url'].find('?')
|
||
|
if chop_index == -1:
|
||
|
selected = path.startswith(menu['url'])
|
||
|
else:
|
||
|
selected = path.startswith(menu['url'][:chop_index])
|
||
|
if 'menus' in menu:
|
||
|
for m in menu['menus']:
|
||
|
_s = check_selected(m, path)
|
||
|
if _s:
|
||
|
selected = True
|
||
|
if selected:
|
||
|
menu['selected'] = True
|
||
|
return selected
|
||
|
for menu in nav_menu:
|
||
|
check_selected(menu, self.request.path)
|
||
|
|
||
|
context.update({
|
||
|
'menu_template': self.menu_template,
|
||
|
'nav_menu': nav_menu,
|
||
|
'site_title': self.site_title,
|
||
|
'site_footer': self.site_footer,
|
||
|
'breadcrumbs': self.get_breadcrumb()
|
||
|
})
|
||
|
|
||
|
return context
|
||
|
|
||
|
@filter_hook
|
||
|
def get_model_icon(self, model):
|
||
|
icon = self.global_models_icon.get(model)
|
||
|
if icon is None and model in self.admin_site._registry:
|
||
|
icon = getattr(self.admin_site._registry[model],
|
||
|
'model_icon', self.default_model_icon)
|
||
|
return icon
|
||
|
|
||
|
@filter_hook
|
||
|
def get_breadcrumb(self):
|
||
|
return [{
|
||
|
'url': self.get_admin_url('index'),
|
||
|
'title': _('Home')
|
||
|
}]
|
||
|
|
||
|
|
||
|
class ModelAdminView(CommAdminView):
|
||
|
|
||
|
fields = None
|
||
|
exclude = None
|
||
|
ordering = None
|
||
|
model = None
|
||
|
remove_permissions = []
|
||
|
|
||
|
def __init__(self, request, *args, **kwargs):
|
||
|
self.opts = self.model._meta
|
||
|
self.app_label = self.model._meta.app_label
|
||
|
self.model_name = self.model._meta.model_name
|
||
|
self.model_info = (self.app_label, self.model_name)
|
||
|
|
||
|
super(ModelAdminView, self).__init__(request, *args, **kwargs)
|
||
|
|
||
|
@filter_hook
|
||
|
def get_context(self):
|
||
|
new_context = {
|
||
|
"opts": self.opts,
|
||
|
"app_label": self.app_label,
|
||
|
"model_name": self.model_name,
|
||
|
"verbose_name": force_text(self.opts.verbose_name),
|
||
|
'model_icon': self.get_model_icon(self.model),
|
||
|
}
|
||
|
context = super(ModelAdminView, self).get_context()
|
||
|
context.update(new_context)
|
||
|
return context
|
||
|
|
||
|
@filter_hook
|
||
|
def get_breadcrumb(self):
|
||
|
bcs = super(ModelAdminView, self).get_breadcrumb()
|
||
|
item = {'title': self.opts.verbose_name_plural}
|
||
|
if self.has_view_permission():
|
||
|
item['url'] = self.model_admin_url('changelist')
|
||
|
bcs.append(item)
|
||
|
return bcs
|
||
|
|
||
|
@filter_hook
|
||
|
def get_object(self, object_id):
|
||
|
"""
|
||
|
Get model object instance by object_id, used for change admin view
|
||
|
"""
|
||
|
# first get base admin view property queryset, return default model queryset
|
||
|
model = self.model
|
||
|
try:
|
||
|
object_id = model._meta.pk.to_python(object_id)
|
||
|
return model.objects.get(pk=object_id)
|
||
|
except (model.DoesNotExist, ValidationError):
|
||
|
return None
|
||
|
|
||
|
@filter_hook
|
||
|
def get_object_url(self, obj):
|
||
|
if self.has_change_permission(obj):
|
||
|
return self.model_admin_url("change", getattr(obj, self.opts.pk.attname))
|
||
|
elif self.has_view_permission(obj):
|
||
|
return self.model_admin_url("detail", getattr(obj, self.opts.pk.attname))
|
||
|
else:
|
||
|
return None
|
||
|
|
||
|
def model_admin_url(self, name, *args, **kwargs):
|
||
|
return reverse(
|
||
|
"%s:%s_%s_%s" % (self.admin_site.app_name, self.opts.app_label,
|
||
|
self.model_name, name), args=args, kwargs=kwargs)
|
||
|
|
||
|
def get_model_perms(self):
|
||
|
"""
|
||
|
Returns a dict of all perms for this model. This dict has the keys
|
||
|
``add``, ``change``, and ``delete`` mapping to the True/False for each
|
||
|
of those actions.
|
||
|
"""
|
||
|
return {
|
||
|
'view': self.has_view_permission(),
|
||
|
'add': self.has_add_permission(),
|
||
|
'change': self.has_change_permission(),
|
||
|
'delete': self.has_delete_permission(),
|
||
|
}
|
||
|
|
||
|
def get_template_list(self, template_name):
|
||
|
opts = self.opts
|
||
|
return (
|
||
|
"xadmin/%s/%s/%s" % (
|
||
|
opts.app_label, opts.object_name.lower(), template_name),
|
||
|
"xadmin/%s/%s" % (opts.app_label, template_name),
|
||
|
"xadmin/%s" % template_name,
|
||
|
)
|
||
|
|
||
|
def get_ordering(self):
|
||
|
"""
|
||
|
Hook for specifying field ordering.
|
||
|
"""
|
||
|
return self.ordering or () # otherwise we might try to *None, which is bad ;)
|
||
|
|
||
|
@filter_hook
|
||
|
def queryset(self):
|
||
|
"""
|
||
|
Returns a QuerySet of all model instances that can be edited by the
|
||
|
admin site. This is used by changelist_view.
|
||
|
"""
|
||
|
return self.model._default_manager.get_queryset()
|
||
|
|
||
|
def has_view_permission(self, obj=None):
|
||
|
view_codename = get_permission_codename('view', self.opts)
|
||
|
change_codename = get_permission_codename('change', self.opts)
|
||
|
|
||
|
return ('view' not in self.remove_permissions) and (self.user.has_perm('%s.%s' % (self.app_label, view_codename)) or
|
||
|
self.user.has_perm('%s.%s' % (self.app_label, change_codename)))
|
||
|
|
||
|
def has_add_permission(self):
|
||
|
codename = get_permission_codename('add', self.opts)
|
||
|
return ('add' not in self.remove_permissions) and self.user.has_perm('%s.%s' % (self.app_label, codename))
|
||
|
|
||
|
def has_change_permission(self, obj=None):
|
||
|
codename = get_permission_codename('change', self.opts)
|
||
|
return ('change' not in self.remove_permissions) and self.user.has_perm('%s.%s' % (self.app_label, codename))
|
||
|
|
||
|
def has_delete_permission(self, request=None, obj=None):
|
||
|
codename = get_permission_codename('delete', self.opts)
|
||
|
return ('delete' not in self.remove_permissions) and self.user.has_perm('%s.%s' % (self.app_label, codename))
|