- Author:
- riccardodivirgilio
- Posted:
- November 18, 2010
- Language:
- Python
- Version:
- Not specified
- Score:
- 0 (after 0 ratings)
Ok let's descrive what i have done I subclassed the django admin to create a form that makes you choose if activate a delete and replace login inside your admin.
Then i have added a form with a modelChoiceField to make you select another model instance when you are selecting an istance to delete. If you select another instance the current instance will be replaced.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 | #ALL ADMIN IMPORTS
from django.contrib.admin.options import *
from django.utils.translation import ugettext_lazy as _, ugettext
from django import forms
from django.contrib.admin.util import NestedObjects
from django.db import models
from django.db.models.related import RelatedObject
from django.utils.functional import curry
class AdminReplacer(ModelAdmin):
#START PATCH
#START PATCH
#START PATCH
substitution_delete = True
substitution_custom_handlers = tuple()
def get_deletion_form(self, request, objs):
class DeletionForm(forms.Form):
substituted_object = forms.ModelChoiceField(
empty_label = ugettext("Do not replace, delete the following objects"),
label = ugettext("Object to replace"),
queryset = self.queryset(request).exclude(pk__in = [obj.pk for obj in objs]),
required = False,
)
return DeletionForm
def log_substitution(self, request, object, object_repr, substituted_object, substituted_object_repr):
"""
Log that an object has been successfully deleted. Note that since the
object is deleted, it might no longer be safe to call *any* methods
on the object, hence this method getting object_repr.
The default implementation creates an admin LogEntry object.
"""
from django.contrib.admin.models import LogEntry, DELETION
LogEntry.objects.log_action(
user_id = request.user.id,
content_type_id = ContentType.objects.get_for_model(self.model).pk,
object_id = object.pk,
object_repr = object_repr,
action_flag = DELETION,
change_message = u'Replaced with object with primary key %s "%s".' % (substituted_object.pk, substituted_object_repr)
)
def substitution_m2m_handler(self, request, deleted_object, substituted_object, accessor_name, related = None):
"""
Default behavoiur for handling incoming M2M fields
If a RelatedObject is defined, M2M relation in Inbound, otherwise it is outbound.
"""
manager = getattr(deleted_object, accessor_name)
if related:
"""
Default behaviour:
Incoming M2M relation will be preserved
"""
getattr(substituted_object, accessor_name).add(*manager.all())
return
"""
Default behaviour:
Exiting M2M objects will be deleted
"""
return
def substitution_handler(self, request, deleted_object, substituted_object, accessor_name, related):
"""
This is the default handler for a substitution, subclasses can define a new method.
Related is an instance of django.db.models.related.RelatedObject
Custom functions will receive (request, deleted_object, substituted_object, accessor_name)
Default behaviour:
Incoming ForeignKeys will be preserved,
Subclass this method to add a custom behaviour, return None to delete incoming objects.
"""
manager = getattr(deleted_object, accessor_name)
manager.all().update(**{related.field.name: substituted_object})
def get_substitution_handlers(self):
"""
The many-to-many version of get_fields_with_model().
"""
try:
return self._substitution_handlers_cache
except AttributeError:
self._fill_substitution_handlers_cache()
return self._substitution_handlers_cache
def _fill_substitution_handlers_cache(self):
cache = SortedDict()
accessor_names = [(related.get_accessor_name(), curry(self.substitution_handler, related = related))
for related in self.model._meta.get_all_related_objects()]
accessor_names += [(related.get_accessor_name(), curry(self.substitution_m2m_handler, related = related))
for related in self.model._meta.get_all_related_many_to_many_objects()]
accessor_names += [(field.name, self.substitution_m2m_handler)
for field in self.model._meta.many_to_many]
keys = [v for v, f in accessor_names]
for (accessor_name, f) in accessor_names:
for attr_name, handler_name in self.substitution_custom_handlers:
if not attr_name in keys:
raise KeyError, 'Accessor %s was not found. Choices are: %s' % (attr_name, ", ".join(keys))
if accessor_name == attr_name:
f = handler_name
if isinstance(handler_name, basestring):
f = getattr(self, handler_name)
break
if f:
cache[accessor_name] = f
self._substitution_handlers_cache = cache
def substitute_deleted_objects(self, request, deleted_objects, substituted_object):
"""
Move every related object to another instance
You may need to define a list of model to ignore for two reason:
1. moving object with unique columns may cause integrity error
2. you could have business reasons
"""
handlers = self.get_substitution_handlers().items()
for obj in deleted_objects:
for accessor_name, handler in handlers:
handler(request, obj, substituted_object, accessor_name)
#END PATCH
#END PATCH
#END PATCH
@csrf_protect_m
def delete_view(self, request, object_id, extra_context=None):
if not self.substitution_delete:
return super(AdminReplacer, self).delete_view(request, object_id, extra_context=extra_context)
"The 'delete' admin view for this model."
opts = self.model._meta
app_label = opts.app_label
obj = self.get_object(request, unquote(object_id))
if not self.has_delete_permission(request, obj):
raise PermissionDenied
if obj is None:
raise Http404(_('%(name)s object with primary key %(key)r does not exist.') % {'name': force_unicode(opts.verbose_name), 'key': escape(object_id)})
#START PATCH
#START PATCH
#START PATCH
substituted_object = deletion_form = None
if self.substitution_delete:
DeletionForm = self.get_deletion_form(request, (obj,))
deletion_form = DeletionForm(request.POST or None, initial = request.GET)
if deletion_form.is_valid():
substituted_object = deletion_form.cleaned_data["substituted_object"]
#END PATCH
#END PATCH
#END PATCH
# Populate deleted_objects, a data structure of all related objects that
# will also be deleted.
(deleted_objects, perms_needed) = get_deleted_objects((obj,), opts, request.user, self.admin_site)
if request.POST and deletion_form and deletion_form.is_valid(): # The user has already confirmed the deletion.
if perms_needed:
raise PermissionDenied
obj_display = force_unicode(obj)
#START PATCH
#START PATCH
#START PATCH
if self.substitution_delete and substituted_object:
self.substitute_deleted_objects(request, [obj], substituted_object)
self.log_substitution(request, obj, obj_display, substituted_object, force_unicode(substituted_object))
else:
self.log_deletion(request, obj, obj_display)
#END PATCH
#END PATCH
#END PATCH
obj.delete()
self.message_user(request, _('The %(name)s "%(obj)s" was deleted successfully.') % {'name': force_unicode(opts.verbose_name), 'obj': force_unicode(obj_display)})
if not self.has_change_permission(request, None):
return HttpResponseRedirect("../../../../")
return HttpResponseRedirect("../../")
context = {
"title": _("Are you sure?"),
"object_name": force_unicode(opts.verbose_name),
"object": obj,
"deletion_form":deletion_form,
"deleted_objects": deleted_objects,
"perms_lacking": perms_needed,
"opts": opts,
"root_path": self.admin_site.root_path,
"app_label": app_label,
}
context.update(extra_context or {})
context_instance = template.RequestContext(request, current_app=self.admin_site.name)
return render_to_response(self.delete_confirmation_template or [
"admin/%s/%s/delete_confirmation.html" % (app_label, opts.object_name.lower()),
"admin/%s/delete_confirmation.html" % app_label,
"admin/delete_confirmation.html"
], context, context_instance=context_instance)
---------------------
#HTML -> admin/delete_confirmation.html
{% extends "admin/base_site.html" %}
{% load i18n %}
{% block breadcrumbs %}
<div class="breadcrumbs">
<a href="../../../../">{% trans "Home" %}</a> ›
<a href="../../../">{{ app_label|capfirst }}</a> ›
<a href="../../">{{ opts.verbose_name_plural|capfirst }}</a> ›
<a href="../">{{ object|truncatewords:"18" }}</a> ›
{% trans 'Delete' %}
</div>
{% endblock %}
{% block content %}
{% if perms_lacking %}
<p>{% blocktrans with object as escaped_object %}Deleting the {{ object_name }} '{{ escaped_object }}' would result in deleting related objects, but your account doesn't have permission to delete the following types of objects:{% endblocktrans %}</p>
<ul>
{% for obj in perms_lacking %}
<li>{{ obj }}</li>
{% endfor %}
</ul>
{% else %}
<form action="" method="post">{% csrf_token %}
{{ deletion_form.as_p }}
<p>{% blocktrans with object as escaped_object %}Are you sure you want to delete the {{ object_name }} "{{ escaped_object }}"? All of the following related items will be deleted:{% endblocktrans %}</p>
<ul>{{ deleted_objects|unordered_list }}</ul>
<div>
<input type="hidden" name="post" value="yes" />
<input type="submit" value="{% trans "Yes, I'm sure" %}" />
</div>
</form>
{% endif %}
{% endblock %}
|
More like this
- Template tag - list punctuation for a list of items by shapiromatron 10 months, 3 weeks ago
- JSONRequestMiddleware adds a .json() method to your HttpRequests by cdcarter 11 months ago
- Serializer factory with Django Rest Framework by julio 1 year, 5 months ago
- Image compression before saving the new model / work with JPG, PNG by Schleidens 1 year, 6 months ago
- Help text hyperlinks by sa2812 1 year, 7 months ago
Comments
Please login first before commenting.