zoukankan      html  css  js  c++  java
  • Django Admin管理后台详解2(转)

    转载:https://mp.weixin.qq.com/s?__biz=MjM5OTMyODA4Nw==&mid=2247484136&idx=1&sn=b3f983f56a8d6fe94b3c137db5b9a209&chksm=a73c62d0904bebc6709eacdcdff45bf5b1934036b9b884b8cbbe172c608c0f18336ae1e658a3&scene=178&cur_album_id=1351340083605061635#rd

    今天我们来看下django admin的一些高级技巧,比如如何自定义list_display和list_filter, 以及如何重写django admin的save方法和get_queryset方法。

    自定义list_display

     

    前文中我们已经介绍过django admin的list_display选项不能用于显示多对多的字段(如tags)。如果需要通过list_display选项显示多对多的字段或模型中原本不存在的字段或方法,我们需要新增自定义的list_play方法。

    我们现在ArticleAdmin里重新定义一个show_tags方法,该方法有2个变量self和obj。我们先调用obj.tags.all()获取某篇文章obj的所有tags,然后使用join方法将它们连接成字符串。由于show_tags这个名字作为表单的header读起来并不友好,我们可以通过设置该方法的short_description属性来设置表头显示为"标签".

    #blog/admin.py

    class ArticleAdmin(admin.ModelAdmin):
    
        '''设置列表可显示的字段'''
        list_display = ('title', 'author',  'status', 'mod_date', 'show_tags')
    
        '''展示tags'''
        def show_tags(self, obj):
            tag_list = []
            for tag in obj.tags.all():
                tag_list.append(tag.name)
            return ','.join(tag_list)
    
        show_tags.short_description = '标签'  # 设置表头

    显示效果如下:

    你注意到上面图片有个问题没有?当标签为空的时候,显示内容为空白。有的时候我们需要设置空白值(empty value)来更好地提示用户,有的时候我们还要以不同颜色显示提示信息。下面我们就要对admin.py做些改进,当文章没有标签时,我们以红色字体显示没有"没有标签"。在下面中我们使用了format_html方法对字符串添加了样式。

    #blog/admin.py

    from django.contrib import admin
    from .models import Article, Category, Tag
    from django.utils.html import format_html
    
    # Register your models here.
    
    class ArticleAdmin(admin.ModelAdmin):
    
        '''设置列表可显示的字段'''
        list_display = ('title', 'author',  'status', 'mod_date', 'show_tags')
    
        '''展示tags'''
        def show_tags(self, obj):
            tag_list = []
            tags = obj.tags.all()
            if tags:
                for tag in tags:
                    tag_list.append(tag.name)
                return ','.join(tag_list)
            else:
                return format_html(
                    '<span style="color:red;">文章{}无标签</span>',
                    obj.id,)
    
        '''设置表头'''
        show_tags.short_description = '标签'  # 设置表头
        
    admin.site.register(Article, ArticleAdmin)

    改进的展示效果如下:

    图片

    注意:

    • 尽管我们实现了在列表中显示多对多的字段,但在实际项目中我们并不建议这么做,因为这额外地增加了许多数据库查询的工作量。

    • 获取某篇文章obj所对应的tags正确方法是使用obj.tags.all()而不是obj.tags.all, 否则会出现tags is not iterable的错误。你需要了解,obj.tags.all方法仅限于在模板中使用,在admin或view中使用时,千万别忘了加括号。

    在本例中我们手动编写了需要显示的empty value。Django实际上是允许我们给所有的空字段设置显示名字的,我们只需要使用empty_value_display选项即可。我们还可以通过设置admin_order_field选项设置需要排序的字段。设置了admin_order_field选项的字段表头会出现一个小三角按钮,用户可以点击三角按钮实现正序或逆序排列。使用例子如下所示:

    empty_value_display = "空值"
    admin_order_field = ('title', 'mod_date')

    自定义list_filter

    自定义list_filter也是一个非常有用的Django技术,可以让用户快速找到自己需要查看或编辑的对象。在之前的案例中,我们的list_filter已经实现了按文章状态和发布时间对文章进行过滤。现在我们需要自定义一个过滤器,按文章标题所含的关键词(比如python, django)对所有文章进行过滤, 并显示在右边的过滤条件栏目里。

    完整代码如下。我们定义了一个TitleKeywordFilter类,该类继承了admin的SimpleListFilter类。我们设置了过滤器的标题和参数名(keyword), 并通过lookups方法定义了过滤参数元组,并调用queryset方法返回符合查询条件的查询数据集。

    from django.contrib import admin
    from .models import Article, Category, Tag
    
    class TitleKeywordFilter(admin.SimpleListFilter):
        #  右侧栏人为可读的标题
       title = '标题关键词'
    
        # 在url中显示的参数名,如?keyword=xxx.
        parameter_name = 'keyword'
    
        """
        自定义需要筛选的参数元组. 
        """
        def lookups(self, request, model_admin):
            return (
                ('python', '含python文章'),
                ('django', '含django文章'),
                 )
    
        def queryset(self, request, queryset):
            """
            调用self.value()获取url中的参数, 然后筛选所需的queryset.
            """
            if self.value() == 'python':
                return queryset.filter(title__icontains='python')
            if self.value() == 'django':
                return queryset.filter(title__icontains='django')
    
    
    class ArticleAdmin(admin.ModelAdmin):
    
        '''设置过滤选项'''
        list_filter = ('status', TitleKeywordFilter, 'pub_date', )

    展示效果如下所示, 是不是很帅?

    注意:

    • 自定义的Filter类的代码必需放在ModelAdmin类的前面,否则无法使用。

    • 自定义的Filter参数名parameter_name不要使用q和next,这两个参数已作为django admin的默认参数使用了。

    重写Django admin的save_model方法

    很多时候,我们需要重写Django自带的save_model方法。比如在文章创建时我们希望在后台自动添加作者,而不是允许用户自己选择作者是谁,我们可以选择在创建文章的表单里把作者隐藏,而在后台添加作者。如下所示:

    from django.contrib import admin
    
    class ArticleAdmin(admin.ModelAdmin):
        def save_model(self, request, obj, form, change):
            obj.author = request.user
            super().save_model(request, obj, form, change)

    在我们世界那么大,我想去看看。Django仿制微信朋友圈九宫格相册(1)一文中我们也展示了save_model方法的重写,该方法作用是允许用户在创建Album对象时,还上传一个zip文件包。上传后对zip文件包进行解压存储,并与每个Image对象想关联。

    # album/forms.py

    from django import forms
    from .models import Album
    
    
    class AlbumForm(forms.ModelForm):
        class Meta:
            model = Album
            exclude = []
    
        zip = forms.FileField(required=False)

    # album/admin.py

    import os
    import uuid
    import zipfile
    from django.contrib import admin
    from django.core.files.base import ContentFile
    from .models import Album, AlbumImage
    from .forms import AlbumForm
    
    
    @admin.register(Album)
    class AlbumModelAdmin(admin.ModelAdmin):
        form = AlbumForm
        prepopulated_fields = {'slug': ('title',)}
        list_display = ('title', 'thumb')
        list_filter = ('create_date',)
    
        def save_model(self, request, obj, form, change):
            if form.is_valid():
                album = form.save()
    
                if form.cleaned_data['zip'] is not None:
                    zip = zipfile.ZipFile(form.cleaned_data['zip'])
                    for filename in sorted(zip.namelist()):
    
                        file_name = os.path.basename(filename)
                        if not file_name:
                            continue
    
                        data = zip.read(filename)
                        contentfile = ContentFile(data)
    
                        img = AlbumImage()
                        img.album = album
                        filename = '{0}{1}.jpg'.format(album.slug[:8], str(uuid.uuid4())[-13:])
                        img.alt = filename
                        img.image.save(filename, contentfile)
    
                        img.thumb.save('thumb-{0}'.format(filename), contentfile)
                        img.save()
                    zip.close()
                super().save_model(request, obj, form, change)

    还记得我们Django 2.0 项目实战: 扩展Django自带User模型,实现用户注册与登录中对django的User模型做的扩展吗?我们新建了一个UserProfile模型,其与User是一对一的关系。我们现在希望在admin中创建一个User对象时,也同时创建一个UserProfile对象,这时我们就需要用到save_model方法的重写了。代码如下所示:

    #myaccount/admin.py

    from django.contrib import admin
    from django.contrib.auth.models import User
    from django.contrib.auth.admin import UserAdmin
    from .models import UserProfile
    
    admin.site.unregister(User)
    
    
    class UserProfileInline(admin.StackedInline):
        model = UserProfile
        exclude = ["uid", "join_date", "mod_date"]
    
    
    class UserAdmin(UserAdmin):
        inlines = [UserProfileInline, ]
    
        def save_model(self, request, obj, form, change):
            if form.is_valid():
                user = form.save()
                user_profile = UserProfile()
                user_profile.user = user
                user_profile.save()
    
            super().save_model(request, obj, form, change)
    
    
    class UserProfileAdmin(admin.ModelAdmin):
        list_display = ('user', 'avatar', 'org', 'join_date')
        exclude = []
        ordering = ('-join_date',)
    
    
    admin.site.register(User, UserAdmin)
    admin.site.register(UserProfile, UserProfileAdmin)

    展示效果如下。我们使用了Inlines使UserProfile与User展示在同一页面上。由于我们重写了save_model方法,这样可以自动在创建User时也创建UserProfile,避免了只创建User而未创建UserProfile的错误,这对1对1的关系非常重要。

    重写Django admin的get_queryset方法

    Django的admin默认会展示所有对象。通过重写get_queryset方法,我们可以控制所需要获取的对象。比如下例中,我们先对用户进行判定,如果用户是超级用户就展示所有文章,如果不是超级用户,我们仅展示用户自己所发表的文章。

    class ArticleAdmin(admin.ModelAdmin):
        def get_queryset(self, request):
            qs = super().get_queryset(request)
            if request.user.is_superuser:
                return qs
            return qs.filter(author=request.user)

    小结

    本文总结了django admin的一些高级技巧,比如如何自定义list_display和list_filter, 以及如何重写django admin的save方法和get_queryset方法。这些方法和技巧都非常有用,要熟练掌握哦。下文将是django admin介绍的最后一篇,小编我将介绍下如何美化django-admin,欢迎关注。

  • 相关阅读:
    关于slot标签的使用
    vue组件
    template和component的理解(待更正)
    input 的属性autocomplete
    关于render: h => h(App)和components: { App }的区别
    关于vue的简单知识
    获取页面跳转携带的参数
    关于跨域的简单想法(此想法是错误的,特此备注)
    关于jq建立类似与双向绑定的函数
    关于jq将一个页面引入另一个页面,类似与组件化的解决方案
  • 原文地址:https://www.cnblogs.com/wangbin2188/p/15560934.html
Copyright © 2011-2022 走看看