本文源自 Reddit 上对我最近的一个帖子的评论:html
“问题是,我问到的每一个人都持反对意见,他们认为 admin 只限于超级用户,很不灵活而且是难以定制。”
—来自 Reddit 的 andybakgit
我如今要澄清这个误解。Django 的 admin 绝对是软件中的亮点,能够有效的加速你的开发。github
这里有一些我能想到的颇有用的 Django 的 admin 模块的窍门。数据库
(对于 Django admin 不太熟悉的人,先简单解释几个名词)django
Changeform 是能够编辑对象的页面。编程
Changelist 页面能够列出指定类型的对象。你能够指定过滤对象的条件及对对象的操做。点击 changelist 里的对象通常会跳转到对象的 changeform 页面。安全
为了让这些敲门更具可操做性,咱们使用了与真实问题几乎一致的场景。假设咱们有一个简单的网站,访客能够上传可爱的动物图片并进行评论。这是否是很流行呢?架构
虽然 Django admin 管理界面能够很是友好的用在 Django 项目的其它部分,它一样能够很容易用于其它像传统的数据库或具备一个可怕的的管理界面的网站。并且这也是评估 Django 是否会知足您的需求的最佳途径。app
你须要作的仅是:ide
在你的 Django 项目中创建一个新的应用,并确保你已经链接好传统数据库 ,经过 settings.py 文件中的 DATABASES 的设置。
将你的数据表定义为 Django 的模型。正如它的名字所表述的,manage.py inspectdb 是一个很是有用的命令:检测现有的数据库,并打印出自动生成的 Django 模型。
建立 admin.py 文件,并放在那里,唉,管理相关的。稍后将详细说明这个。
说到咱们的动物“的网站,是由进屎的脑壳写出来的,因此管理界面看起来像……你知道的,不是很好。为了解决这个问题,咱们经过几个 Django 模型重构了数据库结构,实现一个简单的管理界面:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 |
# models.py class Picture(models.Model): DOG = 1 CAT = 2 ANIMAL_KIND_CHOICES = ( (DOG, 'dog'), (CAT, 'cat'), )
title = models.CharField(max_length=200) author = models.ForeignKey(Author, related_name='pictures') animal_kind = models.IntegerField(choices=ANIMAL_KIND_CHOICES) photo = models.ImageField(upload_to='animals') is_promoted = models.BooleanField(default=False)
class Author(models.Model): name = models.CharField(max_length=100) email = models.EmailField()
class Comment(models.Model): author = models.ForeignKey(Author, related_name='comments') picture = models.ForeignKey(Picture, related_name='comments') comment = models.TextField() editors_note = models.TextField()
# admin.py class PictureAdmin(admin.ModelAdmin): list_display_fields = ('photo', 'animal_kind', 'author', 'is_promoted', )
class AuthorAdmin(admin.ModelAdmin): list_display_fields = ('name', 'email', )
class CommentAdmin(admin.ModelAdmin): list_display_fields = ('picture', 'author', ) |
不少人使用 Django admin 后台对指定字段进行筛选。要知道,把一个字段名放到 list_filter 列表里就能够了。同时它也很是容易地建立一个自定义过滤器!
假如最终你决定要推广全部有 100+ 的帖子的做者。可是,咱们如何区分它们?让咱们建立一个过滤器,并把它添加到咱们的变动列表。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 |
class ProductiveAuthorsFilter(admin.SimpleListFilter): parameter_name = 'is_productive' title = 'Productive author' YES, NO = 1, 0
# Number of comments for an author to be considered a productive one THRESHOLD = 100
def lookups(self, request, model_admin): return ( (self.YES, 'yes'), (self.NO, 'no'), )
def queryset(self, request, queryset): qs = queryset.annotate(Count('comments'))
# Note the syntax. This way we avoid touching the queryset if our # filter is not used at all. if self.value() == self.YES: return qs.filter(comments__count__gte=self.THRESHOLD) if self.value() == self.NO: return qs.filter(comments__count__lt=self.THRESHOLD)
return queryset
class PictureAdmin(admin.ModelAdmin): list_filters = [..., ProductiveAuthorsFilter] |
如今,咱们能够很容易地选出咱们的核心做者。那么咱们如何开始向他们推广呢?让咱们进入下一部分。
这但是内容管理者的天赐之物。还记得在每一个模型的列表顶部的“动做”工具栏不?咱们是否是很是方便的先选择一些图片,而后只需单击一下就“推广”给做者了?如今让咱们来实现它:
1 2 3 4 5 6 7 |
class PictureAdmin(admin.ModelAdmin): actions = ['promote', ]
def promote(self, request, queryset): queryset.update(is_promoted=True) self.message_user(request, 'The posts are promoted') promote.short_description = 'Promote the pictures' |
就是这样!不用再一个挨一个的打开每一个表单!另外,它很容易进一步增长咱们的动做,例如,添加一个过渡表单。关于这点,Django 文档 有段很是棒的讲解。
好吧,过滤器是很酷,但让咱们关注了一下就搜索工具。在几乎全部的安装我见过的搜索框是用来在一个模型中的字段搜索。可是,当你意识到它能够处理关系的 Django 搜索真正的亮点。所以,假设咱们但愿它在图片“的标题,做者姓名和注释”文本进行搜索。咱们如何作到这一点?
1 2 |
class PictureAdmin(admin.ModelAdmin): search_fields = ('title', 'author__name', 'comments__text', ) |
若是你的数据库是够大,不要忘记添加一些全文索引来增长搜索速度。
在站点查看一个对象的界面是很是普及的需求,默认状况下,你必须打开该对象的表单,而后点击按钮“在站点查看”。如下代码展现如何使此过程更容易一些:
1 2 3 4 5 6 7 8 |
class PictureAdmin(admin.ModelAdmin): list_fields = [..., 'object_link']
def object_link(self, item): url = item.get_absolute_url() return u'<a href={url}>open</a>'.format(url=url) object_link.short_description = 'View on site' object_link.allow_tags = True |
这段代码给列表中每一个对象都添加了“在站点查看”的连接。在此,咱们假定你的模型(Model)已经实现了get_absolute_url()方法。若是尚未 – 那如今就去实现 ,这将为你节省不少时间。你也可能会想将这个片断转移到一个 mixin,或公用的 admin 基类。
假设咱们须要给评论加一个编辑的备注。很天然,咱们但愿不须要对每条评论都去打开评论的changeform。要作到这点,咱们能够稍微修改一下ModelAdmin:
1 2 3 |
class CommentAdmin(admin.ModelAdmin): list_display_fields = ('picture', 'author', 'editors_note', ) list_editable = ('editors_note', ) |
这样就搞定了,如今打开评论列表,能够按照须要进行过滤,还能够在评论上即时添加备注。
每一个 changelist 最下方都有一行列出总数(total)。假设咱们须要把猫和狗的图片数量区分开来。这个功能须要的代码稍微多一些:咱们须要重载 changelist 和 html 模板(更多内容参考模板重载)。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 |
from django.contrib.admin.views.main import ChangeList
class PicturesChangeList(admin.ChangeList): def get_results(self, request): super(PicturesChangeList, self).get_results(request) totals = self.result_list.aggregate( dogs_count=Sum(Case(When(animal_kind=Picture.DOG, then=1), output_field=IntegerField())), cats_count=Sum(Case(When(animal_kind=Picture.CAT, then=1), output_field=IntegerField()))) self.totals = totals
class PictureAdmin(admin.ModelAdmin): def get_changelist(self, request): return PicturesChangeList |
模板的内容:
1 2 3 4 5 6 7 8 9 10 11 12 |
{% extends 'admin/change_list.html' %} {% block result_list %} {{ block.super }} <p> There are <strong> {{ cl.totals.dogs_count|default:'none' }} dogs and {{ cl.totals.cats_count|default:'none' }} cats </strong> on this page. </p> {% endblock %} |
啥意思?假设你的祖母打算瞅一眼这些可爱的图片,她站在你背后,以为 Django 的 admin 界面挺有意思。不过你能确定,她要是使用 admin 界面,恐怕一个按钮的点击就能毁掉整个网站。那么,咱们加上 grandma-proof™,这样就支持只读的 admin 界面(就是某人说的“数据浏览”):
1 2 3 4 5 6 7 8 9 |
class GrandmaProofAdmin(admin.ModelAdmin): def get_readonly_fields(self, request, obj=None): if request.user.username == 'granny': return [f.name for f in self.model._meta.fields] else: return super(GrandmaProofAdmin, self).get_readonly_fields(request, obj)
class PictureAdmin(GrandmaProofAdmin): ... |
如今你能够安全的把修改图片的权限放开给你的祖母,这样她就能浏览图片列表。要注意这个方案确定不能适用于全部使用场景,你还须要处理更多的状况。
有时候你须要在单个对象上执行特定的 action。‘actions’工具固然能够完成这个任务,不过过程会显得很麻烦:点击对象、选择 action、再点击一个按钮……确定有更便捷的方式,对吧?让咱们想办法只点击一次就所有搞定。
此次咱们要实现老祖母的另外一个宏达的想法。她但愿能给某些编辑发 email,告诉他们她喜欢的全部图片。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 |
class PictureAdmin(admin.ModelAdmin): list_fields = (..., 'mail_link', )
def mail_link(self, obj): dest = reverse('admin:myapp_pictures_mail_author', kwargs={'pk': obj.pk}) return '<a href="{url}">{title}</a>'.format(url=dest, title='send mail') mail_link.short_description = 'Show some love' mail_link.allow_tags = True
def get_urls(self): urls = [ url('^(?P<pk>\d+)/sendaletter/?$', self.admin_site.admin_view(self.mail_view), name='myapp_pictures_mail_author'), ] return urls + super(PictureAdmin, self).get_urls()
def mail_view(self, request, *args, **kwargs): obj = get_object_or_404(Picture, pk=kwargs['pk']) send_mail('Feel the granny\'s love', 'Hey, she loves your pet!', 'granny@yoursite.com', [obj.author.email]) self.message_user(request, 'The letter is on its way') return redirect(reverse('admin:myapp_picture_changelist')) |
希望她如今可以满意。如今每一个对象字段加上了一个连接,让她点一下就能够发送邮件。
Django admin (Django 也是如此) 最经常使用也是最有用的技巧是 select_related。呃,你已经都知道了?不就是把对象的名字传给 ModelAdmin 的 list_select_related 属性来实现相关对象的预加载嘛。可是,你知道你并无描述所有的相关对象吗?只须要设置成 True,Django 就能够自动预加载外部对象:
1 2 |
class PictureAdmin(admin.ModelAdmin): list_select_related = True |
问啊-定制化IT教育平台,牛人一对一服务,有问必答,开发编程社交头条 官方网站:www.wenaaa.com
QQ群290551701 汇集不少互联网精英,技术总监,架构师,项目经理!开源技术研究,欢迎业内人士,大牛及新手有志于从事IT行业人员进入!