Django - Extending another apps ModelAdmin?

2019-04-19 10:21发布

Is there a way to extend another apps ModelAdmin?

I have a project that uses functionality offered by django.contrib.comments.

The CommentsAdmin ModelAdmin class has:
actions = ["flag_comments", "approve_comments", "remove_comments"]

I would like to extend the CommentsAdmin ModelAdmin in my project to include an action ban_user.

I've tried creating my own NewCommentsAdmin(CommentsAdmin) object in my admin.py file and registering it, but I get a notice 'AlreadyRegistered at /admin/' 'The model Comment is already registered'.

class NewCommentAdmin(CommentAdmin):
    actions = ['ban_user']

    def ban_user(self, request, queryset):
        pass

admin.site.register(Comment, NewCommentAdmin)

Is there a way to do this without modifying the original django.contrib.comments code?

4条回答
手持菜刀,她持情操
2楼-- · 2019-04-19 10:53

Have a look at https://github.com/kux/django-admin-extend

It offers some easy to use functions and decorators that implement the functionality you're requesting in a very flexible manner. The documentation does a pretty good job at explaining why using this approach is better than direct inheritance.

It also has support for injecting bidirectional many to many fields.

查看更多
仙女界的扛把子
3楼-- · 2019-04-19 11:00

I guess you have something like this at the top of your file:

from django.contrib.comments.admin import CommentAdmin

This import executes the registration of the model (at the very bottom of this admin file) again.

One idea that doesn't look very nice (I actually haven't tried it) could be:

from django.contrib.comments.models import Comment
from django.contrib import admin
from django.contrib.admin.sites import NotRegistered

# Try to unregister the Comment model 
# that was registered via the auto_discover method
try:
    admin.site.unregister(Comment)
except NotRegistered:
    pass

# Now we can load the CommentAdmin (which reregisters the admin model)
from django.contrib.comments.admin import CommentAdmin

# We have to unregister again:
try:
    admin.site.unregister(Comment)
except NotRegistered:
    pass

# Now your stuff...

I guess this could be done better but it should work. To make this approach work, the application that contains this file has to be after the comments application in INSTALLED_APPS.

Now to your class. I think if you write actions = ['ban_user'] you actually overwrite all the actions in the parent class. I think it is the easiest way to override the get_actions method:

class NewCommentAdmin(CommentAdmin):

    def get_actions(self, request):
        actions = super(NewCommentAdmin, self).get_actions(request)

        # Do some logic here based on request.user if you want 
        # to restrict the new action to certain users
        actions.append('ban_user')

        return actions

    def ban_user(self, request, queryset):
        pass

admin.site.register(Comment, NewCommentAdmin)

Hope that helps (or at least gives an idea) :)

查看更多
倾城 Initia
4楼-- · 2019-04-19 11:01

Here's how I do it in one project for the User model. In the admin.py for my app:

from django.contrib import admin
from django.contrib.auth.admin import UserAdmin
from django.contrib.auth.models import User

class MyUserAdmin(UserAdmin):
    # ...

admin.site.unregister(User)
admin.site.register(User, MyUserAdmin)
查看更多
Animai°情兽
5楼-- · 2019-04-19 11:01

Unregister the Comment model first.

查看更多
登录 后发表回答