# 3、Django后台基础用法

### **1、注册表**

表必须在admin.py中注册后才能在界面上显示，所以注册表时第一步，一般代码如下：

```
from django.contrib import admin
from djangoTestApp.models import Student,Score#导入表

# Register your models here.

admin.site.register(Student)
admin.site.register(Score)
```

上面代码把之前建的3张表都注册了

### **2、列表中显示列（list\_display）**

之前讲过，就是在模型管理中使用list\_display显示

```
from django.contrib import admin
from djangoTestApp.models import Student,Score

# Register your models here.
class StudentAdmin(admin.ModelAdmin):
    list_display = ('NameNo','Sex','Age',)class ScoreAdmin(admin.ModelAdmin):
    list_display = ('No','Name','Course','Score')

admin.site.register(Student,StudentAdmin)
admin.site.register(Score,ScoreAdmin)
```

上面创建的样式类需要继承admin.ModelAdmin类，名称一般都是表名+Admin

一般情况下，如果有相同的名称时，list\_display按照下面顺序解释元素：

★模型中表的字段

★callabled类型（自定义 函数）

★ModelAdmin属性

★模型属性

### **3、编辑时显示列（fields）**

即在增加or编辑界面显示fields中的字段，如下为当前Student表在编辑界面展示的字段：

![](/files/9QCiYDarYigbm3co0ujd)

上面左图为增加界面，右图为修改界面，显示了Student的4个字段

在admin.py中加上fields字段只显示学号、姓名和年龄，如下：

```
class StudentAdmin(admin.ModelAdmin):
    list_display = ('NameNo','Sex','Age')
    fields = ('No','Name','Age')#编辑界面只显示No、Name和Age
```

这样在编辑界面只有这三个字段，少了‘性别’：

![](/files/fsMTPgKTGQdW5iRxQnRr)

fields还有调整列的顺序的功能，比如把No和Name对调一下

```
class StudentAdmin(admin.ModelAdmin):
    list_display = ('NameNo','Sex','Age')
    fields = ('Name','No','Age',)#No和Name顺序对调
```

在界面上显示如下：

![](/files/CT9S0tAUtOVvZNDAqQNk)

如上图，学号和姓名已经调整了位置。

fields还有使字段分布在同一行的功能，直到一行满了为止。比如说要把姓名和学号放到一排，那么可以如下：

```
class StudentAdmin(admin.ModelAdmin):
    list_display = ('NameNo','Sex','Age')
    fields = (('Name','No',),'Age',)#Name和No一组，在同一行显示
```

界面上如下图所示：

![](/files/51bQ2YZBOqK3z6P71lIU)

### **4、编辑时不显示列（exclude）**

同fields字段相反，在exclude中的字段将不会显示。使用时不要把fields和exclude同时使用

同上，如果不需要显示Sex字段，也可以直接写成如下，效果是一样的

```
class StudentAdmin(admin.ModelAdmin):
    list_display = ('NameNo','Sex','Age')
    #fields = ('No','Name','Age',)
    exclude = ('Sex',)#记得元祖需要在后面加个逗号，不然会报错
```

### **5、编辑时页面布局（fieldsets）**

比如说把No和Name作为‘基本信息’字段集，把Age和Sex作为‘高级信息’字段集，代码可以做如下调整

```
class StudentAdmin(admin.ModelAdmin):
    list_display = ('NameNo','Sex','Age')
    #fields = (('Name','No',),'Age',)
    #exclude = ('Sex',)
    fieldsets = (
        ('基本信息',{
                'fields':('No','Name',),
            },
        ),
        ('高级信息',{
                'fields':('Sex','Age',),
            }
        )
    )
```

说明下fields和fieldsets不能同时存在，系统会报错导致打不开。

上面代码在界面上展示样式如下：

如果不需要‘基本信息’这一行，可以在fieldsets中把‘基本信息’修改为None：

```
class StudentAdmin(admin.ModelAdmin):
    list_display = ('NameNo','Sex','Age')
    #fields = (('Name','No',),'Age',)
    #exclude = ('Sex',)
    fieldsets = (
        (None,{#不显示
                'fields':('No','Name',),
            },
        ),
        ('高级信息',{'fields':('Sex','Age',),
            }
        )
    )
```

界面展示如下：

![](/files/hziNM2EVg5sFOz5142nD)

也可以把不重要的信息折叠起来，这时会用到字段集的参数classes，如下把高级信息折叠起来：

```
class StudentAdmin(admin.ModelAdmin):
    list_display = ('NameNo','Sex','Age')
    #fields = (('Name','No',),'Age',)
    #exclude = ('Sex',)
    fieldsets = (
        (None,{
                'fields':('No','Name',),
            },
        ),
        ('高级信息',{
                'classes':('collapse',),#把‘高级信息’默认折叠起来
                'fields':('Sex','Age',),
            }
        )
    )
```

在界面上展示如下，可以通过点击‘show’展开：

![](/files/MdHVxKUyZUR4OoOaAvYF)

classes还有另外一个样式：wide，即字段填写有更大的控件。

字段集还有属性description，即描述，如果带有此属性，则会显示在 字段集下方，如下：

```
class StudentAdmin(admin.ModelAdmin):
    list_display = ('NameNo','Sex','Age')
    #fields = (('Name','No',),'Age',)
    #exclude = ('Sex',)
    fieldsets = (
        (None,{
                'fields':('No','Name',),
            },
        ),
        ('高级信息',{
                'classes':('collapse',),
                'description':'这里是高级信息的内容',#描述的内容
                'fields':('Sex','Age',),
            }
        )
    )
```

### **6、编辑时样式字段（form）**

可以通过django.forms来定义不同的样式，然后在模型管理中调用这个样式，如下在admin.py中定义一个样式

```
from django import forms#导入样式类

class StudentForm(forms.ModelForm):
    class Meta:
        exclude = ('Sex',)
```

上面代码就是让表Student不显示Age这个列，然后在StudentAdmin类中调用这个样式

```
class StudentAdmin(admin.ModelAdmin):
    list_display = ('NameNo','Sex','Age')
    #fields = (('Name','No',),'Age',)
    #exclude = ('Sex',)
    form = StudentForm#调用自定义样式，之前的fieldsets注释掉
```

上面这样调用和在模型管理中调用exclude效果是一样的，但若模型管理中也有exclude，那么优先使用模型管理中的。

这个规则同样适用于其他样式比如fields、fieldsets等

### **7、字段样式（format\_html）**

在前面学习过字段聚合显示，这里的字段样式和之前差不多，比如把性别按照male和female分别显示蓝色和红色，具体操作如下

▲在models.py的Student表中增加如下代码：

```
from django.utils.html import format_html

class Student(models.Model):
    No = models.CharField('学号',max_length = 10)
    Name = models.CharField('姓名',max_length = 20)
    Sex = models.CharField('性别',max_length = 1,choices = SEX_CHOICE,default = 'M')
    Age = models.IntegerField('年龄')

    def colored(self):#定义显示的颜色，male显示蓝色，female显示红色
        if self.Sex == 'M':
            html =  '<span style="color: #0000FF;">{}</span>'
        else:
            html ='<span style="color: #FF0000;">{}</span>'
        return format_html(
            html,
            self.Sex,
        )
    colored.short_description = '性别'
    Sex_color = colored
```

▲在admin.py中修改展示的列

```
class StudentAdmin(admin.ModelAdmin):
    list_display = ('NameNo','Sex_color','Age',)
```

这时刷新页面，展示效果如下：

![](/files/mL3lKb5S6DEXTiY4IVE9)

### **8、非表字段可排序（admin\_order\_field）**

通过字段样式设置的字段，在展示后无法排序（即无法通过点击字段名排序），这里可以通过指定字段来排序

（多个字段组合也可以指定，通过property装饰的字段都不能参与排序）

在models.py中增加一行如下代码：

```
class Student(models.Model):
    No = models.CharField('学号',max_length = 10)
    Name = models.CharField('姓名',max_length = 20)
    Sex = models.CharField('性别',max_length = 1,choices = SEX_CHOICE,default = 'M')
    Age = models.IntegerField('年龄')
    #Color = models.CharField(max_length = 6,default='FF0000')

    def colored(self):
        if self.Sex == 'M':
            html =  '<span style="color: #0000FF;">{}</span>'
        else:
            html ='<span style="color: #FF0000;">{}</span>'
        return format_html(
            html,
            #self.Color,
            self.Sex,
        )
    colored.short_description = '性别'
    colored.admin_order_field = 'Sex'  # 指定Sex可以参与排序
    Sex_color = colored
```

```
colored.admin_order_field = '-Sex'#指定Sex可以参与排序，先反序
```

### **9、字段链接到编辑界面（list\_display\_links）**

在没有list\_display\_links字段时，默认情况list\_displapy中第一个字段具有链接到编辑界面，如果需要其他字段链接到编辑界面，则可以使用此字段，如下设置

```
class StudentAdmin(admin.ModelAdmin):
    def Age(self,obj):
        return  'xxx'
    list_display = ('NameNo','Sex_color','Age',)
    list_display_links = ('Age',)#设置年龄字段可链接到编辑界面
```

刷新界面即可看到Age字段上的数据可以点击进入编辑界面：

![](/files/bXvEdKRWRQgrFUt6P8PN)

当然也可以设置所有字段都不可以点击进入编辑页面，只要设置为None即可

```
class StudentAdmin(admin.ModelAdmin):
    def Age(self,obj):
        return  'xxx'
    list_display = ('NameNo','Sex_color','Age',)
    list_display_links = None#所有字段都不可点击进入编辑页面
```

### **10、列表可编辑字段（list\_editable）**

list\_editable允许表字段可以在列表中直接进行编辑，也可以一次编辑多条数据，如下：

```
class StudentAdmin(admin.ModelAdmin):
    list_display = ('NameNo','Sex_color','Age',)
    list_display_links = None
    list_editable = ('Age',)#年龄字段在列表界面可直接编辑
```

刷新界面后，样式如下：

![](/files/fx5GEBAUPO1ajnsf73UW)

可以看到年龄字段可以直接进行编辑，无需再到编辑界面一个个编辑。

使用list\_editable有几个限制：

★list\_editable中字段必须在list\_display中

★list\_editable中字段不能在list\_display\_links中，如果没有list\_display\_links则不能为list\_display的第一个字段（也就是可链接到编辑界面的字段，无法在列表页直接编辑）

★list\_editable不能设置非模型中表字段，也就是必须为原始表中的字段才可以设置

### **11、过滤器（list\_filter）**

显示在列表右侧的过滤器，算是快捷的过滤方法，一般用作boolean或者有限的值，如下

```
class StudentAdmin(admin.ModelAdmin):
    list_display = ('NameNo','Sex_color','Age',)
    list_filter = ('Age',)#设置Age为过滤列
    list_display_links = None
    list_editable = ('Age',)
```

刷新页面后如下所示：

![](/files/9udPKiYBwl80j08bQ8pQ)

说明：非模型中原始表的列不能作为过滤对象

list\_filter还有种写法如下，感觉效果是一样的：

```
list_filter = (('Sex',admin.ChoicesFieldListFilter,),)
```

字段Sex是Choice类型，所以也可以这么写，当然还有其他类型如：RelatedFieldListFilter、BooleanFieldListFilter、ChoicesFieldListFilter、DateFieldListFilter等

### **12、最多显示个数（list\_max\_show\_all）**

在这个版本没看出来作用

### **13、每页显示条数（list\_per\_page）**

我们设置list\_per\_page=4（**默认100**），那么Student中的6条数据会显示为俩页

```
class StudentAdmin(admin.ModelAdmin):
    list_display = ('NameNo','Sex_color','Age',)
    list_filter = ('Age',)
    list_per_page = 4#每页显示4条数据
    list_max_show_all = 2
    list_display_links = None
    list_editable = ('Age',)
```

刷新页面后显示如下：

![](/files/Yhm2mLT7xMmBSRKausVB)

### **14、按照列排序（ordering）**

这个是进入表之后的排序，如果不设置，则默认按照数据录入顺序排列

```
class StudentAdmin(admin.ModelAdmin):
    list_display = ('NameNo','Sex','Age',)
    list_filter = ('Age',)
    ordering = ('-Age',)#年龄按照从大到小排列
    list_per_page = 10
    list_max_show_all = 2
    list_display_links = None
    list_editable = ('Age',)
```

### **15、编辑时显示为只读（readonly\_fields）**

即在编辑界面，设置的字段为只读，不可编辑。对非模型表原始字段也生效，若readonly\_fields设置了非模型字段，则会在编辑界面显示，否则不显示

```
class StudentAdmin(admin.ModelAdmin):
    list_display = ('NameNo','Sex_color','Age',)
    readonly_fields = ('Sex','NameNo')#设置性别、姓名（学号）为只读
```

刷新界面后 显示如下：

说明：在列表界面设置可编辑（list\_editable）不冲突，俩边都可以自行设置为是否可编辑

### **16、保存为新记录并停留在编辑界面（save\_as）**

默认save\_as=False，此时编辑界面有三个按钮：保存并新增、保存继续编辑、保存并跳转到列表页。

修改为True之后，保存并新增选项会修改为保存为新纪录并跳转到新纪录的编辑界面，如下：

```
class StudentAdmin(admin.ModelAdmin):
    list_display = ('NameNo','Sex_color','Age',)
    save_as = True#保存为新纪录
```

操作如下：![](/files/AqDiG7g85e5Kr57iLIFg)

### **17、保存为新纪录并跳转到列表页（save\_as\_continue）**

默认值为True，即保存并新增按钮会跳转到编辑页面，若为False则跳转到列表页面（只针对sava\_as=True时生效）

```
class StudentAdmin(admin.ModelAdmin):
    list_display = ('NameNo','Sex_color','Age',)
    save_as_continue = False#跳转到列表页面
    save_as = True
```

### **18、在编辑界面顶部设置保存、删除按钮（save\_on\_top）**

默认为False，也就是在编辑界面保存、删除按钮都是在底部。若设置为True，保存、删除按钮也会存在顶部（底部按钮仍在）

```
class StudentAdmin(admin.ModelAdmin):
    list_display = ('NameNo','Sex_color','Age',)
    save_on_top = True#设置删除、保存按钮在顶部
```

### **19、搜索框（search\_fields）**

默认是没有搜索框的，需要设置。在search\_fields中的字段（必须为模型表字段）必须在list\_display中存在（不一定为模型表字段），如下：

```
class StudentAdmin(admin.ModelAdmin):
    list_display = ('NameNo','Sex_color','Age')
    search_fields = ('Name',)#搜索姓名字段
```

默认情况搜索框是按照queryset中icontains进行查询，若需要精确查询等，可以在字段上加对应的条件，如，需要精确查询姓名：

```
search_fields = ('Name__exact',)#双下划线，精确查询姓名
```

其他基本和queryset中filter类似，具体可以查看[**filter()方法**](https://www.cnblogs.com/watertaro/p/10579397.html)

### **20、是否在搜索框右侧显示列表总数（show\_full\_result\_count）**

默认为True，即显示总数，如下所示：

若设置为False

```
class StudentAdmin(admin.ModelAdmin):
    list_display = ('NameNo','Sex_color','Age')
    show_full_result_count = False  #设置不显示总数，只显示‘show all’
```

则如下效果：

和总数一样，show all也可以点击，然后显示所有记录

### **21、设置列表页中的列表可排序（sortable\_by）**

默认情况下模型表字段都可以排序，之前admin\_order\_field字段是让非模型表字段可排序。

这里的sortable\_by的的优先级高于admin\_order\_field。

若sortable\_by设置为空集，则所有字段都不可以排序。sortable\_by可以把所有字段（包括非模型表字段）设置为可排序

如设置NameNo设置为可排序：

```
class StudentAdmin(admin.ModelAdmin):
    list_display = ('NameNo','Sex_color','Age')
    sortable_by = ('NameNo',)#设置NameNo字段可排序
```

这时其他字段都不可以排序。

### **22、编辑页面右上角链接（view\_on\_site）**

默认True，使用get\_absolute\_url来显示（若没有则不显示）。若为False，则不会显示。

当然也可以自定义view\_on\_site为callable，这样就可以显示对应返回的url，如下：

```
class StudentAdmin(admin.ModelAdmin):
    list_display = ('NameNo','Sex_color','Age')
    def view_on_site(self, obj):
        return 'http://www.cnblogs.com/watertaro'#在编辑界面右上角显示主页
```

刷新页面编辑界面如下：

![](/files/QbSd7D5j9oEMPd1gQqua)

### **23、按时间搜索（date\_hierarchy）**

只针对date或datetime类型数据，会在search\_fields和Action之间出现一个按照时间搜索的插件

在models.py中增加一张表Books：

```
class Books(models.Model):
    No = models.CharField('编号',max_length = 10)
    Name = models.CharField('书名',max_length = 20)
    CreateTime = models.DateTimeField('创建时间',auto_now_add=True)
    UpdateTime = models.DateTimeField('更新时间',auto_now=True)

    def __str__(self):
        return 'No:' + self.No + ';TeacherName:' + self.Name + ';CreateTime:' + str(self.CreateTime)
```

加完表之后在命令行中执行：

```
python manage.py makemigrations#生成表改动文件
python manage.py migrate#修改表
```

在admin.py中增加如下代码：

```
from djangoTestApp.models import Student,Score,Books#导入Books模型

class BooksAdmin(admin.ModelAdmin):#增加后台操作
    list_display = ('No','Name','CreateTime','UpdateTime',)
    date_hierarchy = 'CreateTime'
    search_fields = ('Name',)

admin.site.register(Books,BooksAdmin)#注册表
```

此时刷新界面进入Bookss可以看到这边多了个如下框中的插件：

![](/files/8fekjOydKVvxPRzYbLx1)

### 24、Django 之 fieldsets

django的admin.py在配置后台编辑页面的时候，如下展示的时候，整个信息都是在一个块里面，这时候如果用到表单分隔，把录入字段凤城几个字段集，这样展示起来就更方便了

![](/files/rd7P13QeWn1pO67gp5ro)

fieldsets就是用来实现这种表格切割的。具体的代码实现如下：

```python
# 表列太多，用 fieldsets,分隔一下，字段少的，使用元组放在一行， <admin.py>
fieldsets = [
    (None, {'fields': [ ('apply_id', 'apply_user', 'apply_city'),('apply_iphone','apply_email','apply_position'),]}),
    ('第一轮面试记录', {'fields':[('first_interview','first_learning_ability','first_professional_competency'),'first_advantage']}),
    ('第二轮面试记录', {'fields': ['two_interview',('two_learning_ability','two_professional_competency',),]}),
    ('HR面试记录', {'fields': [('hr_advantage','hr_disadvantage'),'hr_result','hr_interviewer_user','hr_remark']}),
]
```

![](/files/LkHlB5AhYLXtxGYPCYIw)

### 25、Django 添加列表页自定义按钮

**APP下的 model.py**

```python
from django.db import models
from django.core import validators
from django.utils.html import format_html


class Account(models.Model):
    email = models.EmailField("账户邮箱", unique=True, db_index=True,validators=[validators.EmailValidator('邮箱格式不对！')])
    api_key = models.CharField("账户API_kEY", unique=True, max_length=50)

    class Meta:
        verbose_name = "Cloudflare账户"
        verbose_name_plural = verbose_name
        db_table = "cf_account"

        # 下面是添加的自定义内容，为啥不用button,那是因为还需要写 js, 我比较懒
    def data_update(self):
        button_html = '<a href="#" class="el-button el-button--mini"><i class="el-icon-refresh"></i><span></span>更新数据</a>'
        return format_html(button_html)

    data_update.short_description = '在线更新'
```

**APP下的 admin.py**

```python
''''''
class AccountAdmin(admin.ModelAdmin):
    list_display = ('email', 'api_key','data_update')      # 这里把自定义的 data_update 添加进来
admin.site.register(Account, AccountAdmin)
```

![](/files/StLMBvsMyItI0JM7n0JP)

> 参考： <https://www.cnblogs.com/wanghong1994/p/13666113.html>

### 26、Django添加按钮-actions(集成钉钉权限控制)

```python
pip install DingtalkChatbot
```

例如集成钉钉

setting.py

```python
# 钉钉群的 WEB_HOOK， 用于发送钉钉消息
DINGTALK_WEB_HOOK = "https://oapi.dingtalk.com/robot/send?access_token=xxxxxx"
```

dingtalk.py

```python
from dingtalkchatbot.chatbot import DingtalkChatbot
from django.conf import settings


def send(message, at_mobiles=[]):
    # 引用 settings里面配置的钉钉群消息通知的WebHook地址:
    webhook = settings.DINGTALK_WEB_HOOK

    # 初始化机器人小丁, # 方式一：通常初始化方式
    xiaoding = DingtalkChatbot(webhook)

    # 方式二：勾选“加签”选项时使用（v1.5以上新功能）
    # xiaoding = DingtalkChatbot(webhook, secret=secret)

    # Text消息@所有人
    xiaoding.send_text(msg=('面试通知: \n%s' % message), at_mobiles=at_mobiles)
```

admin.py

```python
'''略'''
from .dingtalk import send

class ApplicantAdmin(admin.ModelAdmin):
'''''略'''''
#  ==========================================================
    # 钉钉发送
    def notify_interviewer(self, request, queryset):
        applicant = ""
        first_interviewer_user = ""
        two_interviewer_user = ""
        for obj in queryset:
            applicant = obj.apply_user
            first_interviewer_user = obj.first_interviewer_user.username
            two_interviewer_user = obj.two_interviewer_user.username

        # 这里的消息发送到钉钉， 或者通过 Celery 异步发送到钉钉
        send(
            "候选人: %s\n初始面试官: %s \n复试面试官：%s\n亲爱的面试官，请准备好面试！" % (applicant, first_interviewer_user, two_interviewer_user))
        # send_dingtalk_message.delay("候选人 %s 进入面试环节，亲爱的面试官，请准备好面试： %s" % (candidates, interviewers) )
        # messages.add_message(request, messages.INFO, '已经成功发送面试通知')

    notify_interviewer.short_description = '钉钉通知'
    notify_interviewer.allowed_permissions = ('notify',)       # 设置绑定的权限

    def has_notify_permission(self, request):                  # 判断是有这个权限， 写法： has_权限_permission
        opts = self.opts
        return request.user.has_perm('%s.%s' % (opts.app_label, "notify"))

# ==========================================================
```

model.py

```python
    class Meta:
        verbose_name = '应聘者'
        verbose_name_plural = verbose_name

        permissions = [
            ('notify', 'notify interviewer for candidate review')
        ]
```

![](/files/bqny0VrK0vKWBOpvb2eh)


---

# Agent Instructions: Querying This Documentation

If you need additional information that is not directly available in this page, you can query the documentation dynamically by asking a question.

Perform an HTTP GET request on the current page URL with the `ask` query parameter:

```
GET https://close.gitbook.io/yun-wei-bi-ji/python/django/3django-hou-tai-ji-chu-yong-fa.md?ask=<question>
```

The question should be specific, self-contained, and written in natural language.
The response will contain a direct answer to the question and relevant excerpts and sources from the documentation.

Use this mechanism when the answer is not explicitly present in the current page, you need clarification or additional context, or you want to retrieve related documentation sections.
