Django基础:Django admin管理后台详解(中)如何自定义list

Django基础(19): Django Admin管理后台详解(上)

中小编我介绍了如何创建superuser,如何自定义数据表的显示选项(list_display, list_filter, list_per_page, list_editable, ordering),如何更好地显示单对多(raw_id_fields)和多对多关系(filter_horizontal),如何使用Inlines显示多张数据表在同一页面上。今天我们来看下django admin的一些高级技巧,比如如何自定义list_display和list_filter, 以及如何重写django admin的save方法和get_queryset方法。

自定义list_display

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

#blog/admin.py

classArticleAdmin(admin.ModelAdmin):

'''设置列表可显示的字段'''

list_display = ('title','author','status','mod_date','show_tags')

'''展示tags'''

defshow_tags(self,obj):

tag_list = []

fortaginobj.tags.all():

tag_list.append(tag.name)

return','.join(tag_list)

show_tags.short_description ='标签'#设置表头

显示效果如下:

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

#blog/admin.py

fromdjango.contribimportadmin

from.modelsimportArticle,Category,Tag

fromdjango.utils.htmlimportformat_html

# Register your models here.

classArticleAdmin(admin.ModelAdmin):

'''设置列表可显示的字段'''

list_display = ('title','author','status','mod_date','show_tags')

'''展示tags'''

defshow_tags(self,obj):

tag_list = []

tags = obj.tags.all()

iftags:

fortagintags:

tag_list.append(tag.name)

return','.join(tag_list)

else:

returnformat_html(

'文章{}无标签',

obj.id,)

'''设置表头'''

show_tags.short_description ='标签'#设置表头

admin.site.register(Article,ArticleAdmin)

改进的展示效果如下:

注意:

在本例中我们手动编写了需要显示的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方法返回符合查询条件的查询数据集。

fromdjango.contribimportadmin

from.modelsimportArticle,Category,Tag

classTitleKeywordFilter(admin.SimpleListFilter):

#右侧栏人为可读的标题

title ='标题关键词'

#在url中显示的参数名,如?keyword=xxx.

parameter_name ='keyword'

"""

自定义需要筛选的参数元组.

"""

deflookups(self,request,model_admin):

return(

('python','含python文章'),

('django','含django文章'),

)

defqueryset(self,request,queryset):

"""

调用self.value()获取url中的参数, 然后筛选所需的queryset.

"""

ifself.value() =='python':

returnqueryset.filter(title__icontains='python')

ifself.value() =='django':

returnqueryset.filter(title__icontains='django')

classArticleAdmin(admin.ModelAdmin):

'''设置过滤选项'''

list_filter = ('status',TitleKeywordFilter,'pub_date',)

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

注意:

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

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

重写Django admin的save_model方法

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

fromdjango.contribimportadmin

classArticleAdmin(admin.ModelAdmin):

defsave_model(self,request,obj,form,change):

obj.author = request.user

super().save_model(request,obj,form,change)

# album/forms.py

fromdjangoimportforms

from.modelsimportAlbum

classAlbumForm(forms.ModelForm):

classMeta:

model = Album

exclude = []

zip = forms.FileField(required=False)

# album/admin.py

importos

importuuid

importzipfile

fromdjango.contribimportadmin

fromdjango.core.files.baseimportContentFile

from.modelsimportAlbum,AlbumImage

from.formsimportAlbumForm

@admin.register(Album)

classAlbumModelAdmin(admin.ModelAdmin):

form = AlbumForm

prepopulated_fields = {'slug': ('title',)}

list_display = ('title','thumb')

list_filter = ('create_date',)

defsave_model(self,request,obj,form,change):

ifform.is_valid():

album = form.save()

ifform.cleaned_data['zip']is not None:

zip = zipfile.ZipFile(form.cleaned_data['zip'])

forfilenameinsorted(zip.namelist()):

file_name = os.path.basename(filename)

if notfile_name:

continue

data = zip.read(filename)

contentfile = ContentFile(data)

img = AlbumImage()

img.album = album

filename ='.jpg'.format(album.slug[:8],str(uuid.uuid4())[-13:])

img.alt = filename

img.image.save(filename,contentfile)

img.thumb.save('thumb-'.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

fromdjango.contribimportadmin

fromdjango.contrib.auth.modelsimportUser

fromdjango.contrib.auth.adminimportUserAdmin

from.modelsimportUserProfile

admin.site.unregister(User)

classUserProfileInline(admin.StackedInline):

model = UserProfile

exclude = ["uid","join_date","mod_date"]

classUserAdmin(UserAdmin):

inlines = [UserProfileInline,]

defsave_model(self,request,obj,form,change):

ifform.is_valid():

user = form.save()

user_profile = UserProfile()

user_profile.user = user

user_profile.save()

super().save_model(request,obj,form,change)

classUserProfileAdmin(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方法,我们可以控制所需要获取的对象。比如下例中,我们先对用户进行判定,如果用户是超级用户就展示所有文章,如果不是超级用户,我们仅展示用户自己所发表的文章。

小结

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

大江狗

2018.11.3

  • 发表于:
  • 原文链接:https://kuaibao.qq.com/s/20181103G0S6NA00?refer=cp_1026
  • 腾讯「云+社区」是腾讯内容开放平台帐号(企鹅号)传播渠道之一,根据《腾讯内容开放平台服务协议》转载发布内容。

扫码关注云+社区

领取腾讯云代金券