如何重写Django Admin的save_model方法和get_queryset方法
在Django基础(19): Django Admin管理后台详解(上)中小编我介绍了如何创建superuser,如何自定义数据表的显示选项(list_display, list_filter, list_per_page, list_editable, ordering),如何更好地显示单对多(raw_id_fields)和多对多关系(filter_horizontal),如何使用Inlines显示多张数据表在同一页面上。今天我们来看下django admin的一些高级技巧,比如如何重写django admin的save方法和get_queryset方法。
重写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,欢迎关注。
大江狗
2018.11.3