Issue
I've been overwriting django's delete_queryset in my app (mainly to provide custom bulk deletion).
I can't find the way to overwrite the "short_description" of this method.
For custom methods, you can easily use the short_description attribute, for instance :
class MyAdminModel(admin.ModelAdmin):
def dump_model(self, request, queryset):
pass
dump_model.short_description = "My description"
But this won't work :
class MyAdminModel(admin.ModelAdmin):
def delete_queryset(self, request, queryset):
pass
delete_queryset.short_description = "My description"
I've tried every method/attribute of the standard delete_queryset (within the __init__
method after a super()
call that is) without finding anything useful, though I might have missed something.
I've also gave it a try deleting the standard method using delattr (also in __init__
), though it seems a bit clumsy. It also fails, throwing an AttributeError :
class MyAdminModel(admin.ModelAdmin):
def __init__(self, *args, **kwargs):
super().__init__(*args, **kwargs)
delattr(self, "delete_queryset")
Solution
I finally figured it out.
It involves some explanations from this other question and this.
To sum it up (I don't understand it completly), it seems that the delete_selected
is the action called and that the delete_queryset
is the real function doing the job of the deletion. But as the delete_queryset
is defined outside of the model (and then defined globally on your app), you can't subclass it in your ModelAdmin.
I first tried to do something like this :
class MyAdminModel(admin.ModelAdmin):
def get_actions(self, request):
actions = super().get_actions(request)
function, name, _short_description = actions['delete_selected']
short_description = "I want this custom description"
function.short_description = short_description
return actions
But you can easily see that this will change the description globally.
So the trick would be instead to make a deep copy of the function and add it to the actions. I have been using this other answer to do the job.
So, this would at last give something like this :
from django.contrib import admin
from django.db import transaction
from .models import MyModel
import types
def copy_func(f, name=None):
'''
return a function with same code, globals, defaults, closure, and
name (or provide a new name)
'''
fn = types.FunctionType(f.__code__, f.__globals__, name or f.__name__,
f.__defaults__, f.__closure__)
# in case f was given attrs (note this dict is a shallow copy):
fn.__dict__.update(f.__dict__)
return fn
@admin.register(MyModel)
class MyAdminModel(admin.ModelAdmin):
def delete_queryset(self, request, queryset):
"""
You can there rewrite wathever you want in the original function, for
instance: here apply "delete" on each instance
"""
with transaction.atomic():
for obj in queryset:
obj.delete()
def get_actions(self, request):
actions = super().get_actions(request)
name = "delete_selected"
function, name, _short_description = actions[name]
my_custom_delete_selected = copy_func(function, name)
short_description = "Supprimer définitivement les signalements sélectionnés"
del actions[name]
actions[name] = (my_custom_delete_selected, name, short_description)
return actions
Edit :
This seems to work only if the name of the "delete_selected" function stays unchanged. Otherwise the confirmation form will return something like a "no chosen action" message instead of performing the deletion (the message might be a tad different, my django is using french messages).
Answered By - tgrandje
0 comments:
Post a Comment
Note: Only a member of this blog may post a comment.