168 lines
6.7 KiB
Python
168 lines
6.7 KiB
Python
from django import template
|
|
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
|
|
from django.utils.safestring import mark_safe
|
|
from django.utils.translation import ugettext as _
|
|
from xadmin.plugins.ajax import JsonErrorDict
|
|
from xadmin.sites import site
|
|
from xadmin.util import lookup_field, display_for_field, label_for_field, unquote, boolean_icon
|
|
from xadmin.views import BaseAdminPlugin, ModelFormAdminView, ListAdminView
|
|
from xadmin.views.base import csrf_protect_m, filter_hook
|
|
from xadmin.views.edit import ModelFormAdminUtil
|
|
from xadmin.views.list import EMPTY_CHANGELIST_VALUE
|
|
from xadmin.layout import FormHelper
|
|
|
|
|
|
class EditablePlugin(BaseAdminPlugin):
|
|
|
|
list_editable = []
|
|
|
|
def __init__(self, admin_view):
|
|
super(EditablePlugin, self).__init__(admin_view)
|
|
self.editable_need_fields = {}
|
|
|
|
def init_request(self, *args, **kwargs):
|
|
active = bool(self.request.method == 'GET' and self.admin_view.has_change_permission() and self.list_editable)
|
|
if active:
|
|
self.model_form = self.get_model_view(ModelFormAdminUtil, self.model).form_obj
|
|
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):
|
|
pk = getattr(obj, obj._meta.pk.attname)
|
|
field_label = label_for_field(field_name, obj,
|
|
model_admin=self.admin_view,
|
|
return_attr=False
|
|
)
|
|
|
|
item.wraps.insert(0, '<span class="editable-field">%s</span>')
|
|
item.btns.append((
|
|
'<a class="editable-handler" title="%s" data-editable-field="%s" data-editable-loadurl="%s">' +
|
|
'<i class="fa fa-edit"></i></a>') %
|
|
(_(u"Enter %s") % field_label, field_name, self.admin_view.model_admin_url('patch', pk) + '?fields=' + field_name))
|
|
|
|
if field_name not in self.editable_need_fields:
|
|
self.editable_need_fields[field_name] = item.field
|
|
return item
|
|
|
|
# Media
|
|
def get_media(self, media):
|
|
if self.editable_need_fields:
|
|
|
|
try:
|
|
m = self.model_form.media
|
|
except:
|
|
m = Media()
|
|
media = media + m +\
|
|
self.vendor(
|
|
'xadmin.plugin.editable.js', 'xadmin.widget.editable.css')
|
|
return media
|
|
|
|
|
|
class EditPatchView(ModelFormAdminView, ListAdminView):
|
|
|
|
def init_request(self, object_id, *args, **kwargs):
|
|
self.org_obj = self.get_object(unquote(object_id))
|
|
|
|
# For list view get new field display html
|
|
self.pk_attname = self.opts.pk.attname
|
|
|
|
if not self.has_change_permission(self.org_obj):
|
|
raise PermissionDenied
|
|
|
|
if self.org_obj is None:
|
|
raise Http404(_('%(name)s object with primary key %(key)r does not exist.') %
|
|
{'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':
|
|
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):
|
|
try:
|
|
f, attr, value = lookup_field(field_name, self.org_obj, self)
|
|
except (AttributeError, ObjectDoesNotExist):
|
|
return EMPTY_CHANGELIST_VALUE
|
|
else:
|
|
allow_tags = False
|
|
if f is None:
|
|
allow_tags = getattr(attr, 'allow_tags', False)
|
|
boolean = getattr(attr, 'boolean', False)
|
|
if boolean:
|
|
allow_tags = True
|
|
text = boolean_icon(value)
|
|
else:
|
|
text = smart_text(value)
|
|
else:
|
|
if isinstance(f.rel, models.ManyToOneRel):
|
|
field_val = getattr(self.org_obj, f.name)
|
|
if field_val is None:
|
|
text = EMPTY_CHANGELIST_VALUE
|
|
else:
|
|
text = field_val
|
|
else:
|
|
text = display_for_field(value, f)
|
|
return mark_safe(text) if allow_tags else conditional_escape(text)
|
|
|
|
@filter_hook
|
|
def get(self, request, object_id):
|
|
model_fields = [f.name for f in self.opts.fields]
|
|
fields = [f for f in request.GET['fields'].split(',') if f in model_fields]
|
|
defaults = {
|
|
"form": self.form,
|
|
"fields": fields,
|
|
"formfield_callback": self.formfield_for_dbfield,
|
|
}
|
|
form_class = modelform_factory(self.model, **defaults)
|
|
form = form_class(instance=self.org_obj)
|
|
|
|
helper = FormHelper()
|
|
helper.form_tag = False
|
|
helper.include_media = False
|
|
form.helper = helper
|
|
|
|
s = '{% load i18n crispy_forms_tags %}<form method="post" action="{{action_url}}">{% crispy form %}' + \
|
|
'<button type="submit" class="btn btn-success btn-block btn-sm">{% trans "Apply" %}</button></form>'
|
|
t = template.Template(s)
|
|
c = template.Context({'form': form, 'action_url': self.model_admin_url('patch', self.org_obj.pk)})
|
|
|
|
return HttpResponse(t.render(c))
|
|
|
|
@filter_hook
|
|
@csrf_protect_m
|
|
@transaction.atomic
|
|
def post(self, request, object_id):
|
|
model_fields = [f.name for f in self.opts.fields]
|
|
fields = [f for f in request.POST.keys() if f in model_fields]
|
|
defaults = {
|
|
"form": self.form,
|
|
"fields": fields,
|
|
"formfield_callback": self.formfield_for_dbfield,
|
|
}
|
|
form_class = modelform_factory(self.model, **defaults)
|
|
form = form_class(
|
|
instance=self.org_obj, data=request.POST, files=request.FILES)
|
|
|
|
result = {}
|
|
if form.is_valid():
|
|
form.save(commit=True)
|
|
result['result'] = 'success'
|
|
result['new_data'] = form.cleaned_data
|
|
result['new_html'] = dict(
|
|
[(f, self.get_new_field_html(f)) for f in fields])
|
|
else:
|
|
result['result'] = 'error'
|
|
result['errors'] = JsonErrorDict(form.errors, form).as_json()
|
|
|
|
return self.render_response(result)
|
|
|
|
|
|
site.register_plugin(EditablePlugin, ListAdminView)
|
|
site.register_modelview(r'^(.+)/patch/$', EditPatchView, name='%s_%s_patch')
|