在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_quer