• Xadmin控件的实现:〇三查询视图一——基础功能


    ------------恢复内容开始------------

    从现在开始我们要着手进行查询效果的实现

    数据展示

    直接显示QuerySet对象

    数据的展示是最最基础的功能,第一步我们就要实现如何获取到要显示的数据

     1 class ConfXadmin(object):
     2 
     3     list_display = ["__str__",]   #这里一定要是个元组或列表,如果少了逗号的时候会在后面的extend方法中把字符串打散
     4 
     5     #获取需要显示的字段
     6     def get_display_field(self):
     7         temp = []
     8         temp.extend(self.list_display)
     9         return temp
    10 
    11     def list_view(self,request):
    12         all_data = self.model.objects.all()
    13         
    14         show_data_list = []
    15 
    16         for model_obj in all_data:
    17             temp = []
    18 
    19             for field in self.get_display_field():
    20                 
    21                 val = getattr(model_obj,field)
    22                 temp.append(val)
    23 
    24             show_data_list.append(temp)
    25 
    26 
    27         return render(request,'list.html',locals())

    上面两个方法就是获取到所需数据的视图。来逐步分析一下

    第2行我们创建了一个list_display变量,放了一个字符'__str__’,这个对应的是我们在models.py里给每个ORM类定义的一个方法(像下面这段一样)。

    class Publisher(models.Model):
        id = models.AutoField(primary_key=True)
        name = models.CharField(null = False,max_length=16,verbose_name='名称')
    
        def __str__(self):
            return self.name

    具体的使用方法我们后面会讲到

    但是这里要做注意的是,list_display里必须是一个元组或者列表,如果用下面的方式定义是不行的

    list_display = ("__str__")

    注意,是少了逗号,这里有个比较有意思的地方,我们可以在terminal里试一试

    >>> a=[]
    >>> b=['abc']
    >>> c=('abc')
    >>> d=('abc',)
    >>> a.extend(b)
    >>> a
    ['abc']
    >>> a.extend(c)
    >>> a
    ['abc', 'a', 'b', 'c']
    >>> a.extend(d)
    >>> a
    ['abc', 'a', 'b', 'c', 'abc']

    c的数据类型是字符串,而不是tuple,所以会被整个打散添加到列表里。而b的数据类型就是个list,不存在这种问题。

    经过get_display_field()返回的值和我们在类里一开始定义的list_display是一样的,是因为有些功能我们后期要用到,所以先把函数放在这里。

    下面主要看一看list_view这个视图,

    获取数据是比较简单的,但是第12行的all_data是个QuerySet对象, QuerySet在视图中可以用for循环的方式来显示出来

    <div>
        {%for data in all_data%}
        <div>
         {{data}}
        </div>
        {%endfor%}
    </div>

    在上面一段html代码中,可以直接用循环的方式显示出我们获得的QuerySet里的每个object对象

     但是如果想要获得每个object里的字段,就不能简简单单的这样做了。

    默认的字段显示

    上面的代码从第19行开始是用一个嵌套的for循环来生成一个二维的列表

    model_obj是QuerySet里的每一个object对象,第二个for循环是把我们需要的字段内容从object里拿出来

     但是我们在display_list里只定义了一个__str__,所以只会显示一个书名。

    第二层的for循环就是把我们在display_list里制定的字段拿出来,放在列表里。主要是因为第一层的object是不可迭代的对象。在模板中就不能用for循环显示出来了。所以要用这种二维的列表才可以。

    假设我们需要显示出书籍的价格,只要修改一下前面定义的disp_list就可以

    list_display = ["__str__",'price']

    然后把模板里再加一层for循环

    <div>
        {%for data in show_data_list%}
        <div>
         {%for field in data%}
             {{field}}
         {%endfor%}
        </div>
        {%endfor%}
    </div>

    这样就能显示出来书籍的价格了

     但是这就有一个问题出来,在访问其他的数据库对象后,就会报错

    因为并不是所有的model里都有price这个字段的,所以这样做是不行的。需要我们队每一个要显示的数据库配一个独立的配置类。

    带有自定义配置的Model类注册

    还记得我们前面怎么完成的model类的注册么?在app下的Xadmin.py文件下

    site.register(models.Books)

    而我们在写site对应的XadminSite类的时候,指定了在没有自定义的配置类传入的时候是用默认的配置类的,也就是ConfXadmin直接生成的对象在site里的那个self._registry字典中。

    现在我们需要指定一些需要的字段显示在list视图里,

     1 from Xadmin.service.Xadmin import site
     2 from Xadmin.service.Xadmin import ConfXadmin
     3 from . import models
     4 from copy import deepcopy
     5 
     6 
     7 class BookConf(ConfXadmin):
     8     list_display = deepcopy(ConfXadmin.list_display)
     9     list_display.append('price')
    10 
    11 
    12 site.register(models.Books,BookConf)
    13 
    14 site.register(models.Publisher

    在Xadmin.py中我们重新定义了一个BookConf类,这个类还继承了ConfXadmin,那么就可以对原有的list_display变量重新追加一些需要显示的字段名。但是要注意的是获取原类的时候要用deepcopy,否则会影响到其他实例化的对象

    看一看下面的例子

     1 from copy import deepcopy
     2 class A():
     3     A_data = ['a']
     4     def a(self):
     5         print(self.A_data)
     6 
     7 
     8 class B(A):
     9     # A_data = deepcopy(A.A_data)
    10     A_data = A.A_data
    11     def b(self):
    12         self.A_data.append('in B')
    13 
    14 a1 = A()
    15 b = B()
    16 a2 = A()
    17 print('**********')
    18 a1.a()
    19 a2.a()
    20 print("**********")
    21 ##########输出##########
    22 **********
    23 ['a']
    24 ['a']
    25 **********
    26 ['a', 'in B']
    27 ['a', 'in B']
    28 b.b()
    29 a1.a()
    30 a2.a()
    类变量的深拷贝

    所以一定要用深拷贝!!!

    这样就好咯!忽略前端的简陋效果,只为了完成功能

    美化一下显示效果,加上bootstrap的资源,用表格的方式吧内容显示出来

     1 <!DOCTYPE html>
     2 <html lang="en">
     3 <head>
     4     <meta charset="UTF-8">
     5     <meta name="viewport" content="width=device-width, initial-scale=1.0">
     6     <title>Document</title>
     7     <link rel="stylesheet" href="/static/bootstrap/css/bootstrap.min.css">
     8     <script src="/static/jquery-3.2.1.min.js"></script>
     9 
    10 </head>
    11 <body>
    12 <h3>数据列表</h3>
    13 
    14 <div class="container">
    15     <div class="row">
    16         <div class="col-md-9">
    17             <table class="table table-bordered table-striped">
    18                 <thead>
    19                     <tr>
    20                         {%for item in head_list%}
    21                         <th>
    22                             {{item}}
    23                         </th>
    24                         {%endfor%}
    25                     </tr>
    26                 </thead>
    27                 <tbody>
    28                     {%for data in show_data_list%}
    29                     <tr>
    30                         {%for item in data%}
    31                         <td>{{item}}</td>
    32                         {%endfor%}
    33                     </tr>
    34                     {%endfor%}
    35                 </tbody>
    36             </table>
    37         </div> 
    38     </div>
    39 </div>
    40 </body>
    41 </html>
    表格显示的list.html

    添加功能性的字段

     有些时候我们需要添加一些新的功能,比方选择框CheckBox、修改、删除的a标签什么的,比方在每条记录后面加一个编辑的按钮可以进入编辑页面

    选择框

    选择框的实现比较简单

     1 class ConfXadmin(object):
     2 
     3     modelform_class = None
     4 
     5     list_display = ["__str__"]   #这里一定要是个元组,如果少了逗号的时候会在后面的extend方法中把字符串打散
     6 
     7 
     8     def __init__(self,model,site):
     9         self.model = model
    10         self.model_name = self.model._meta.model_name
    11         self.app_name = self.model._meta.app_label
    12 
    13     @property
    14     def urls2(self):
    15         return self.get_urls2(),None,None
    16 
    17     def get_urls2(self):
    18         temp = []
    19         #通过name设置反向解析
    20         temp.append(url(r'^$',self.list_view,name='{}_{}_list'.format(self.app_name,self.model_name)))
    21         temp.append(url(r'^add/$',self.add_view,name='{}_{}_add'.format(self.app_name,self.model_name)))
    22         temp.append(url(r'^(d+)/change/$',self.change_view,name='{}_{}_change'.format(self.app_name,self.model_name)))
    23         temp.append(url(r'^(d+)/delete/$',self.delete_view,name='{}_{}_delete'.format(self.app_name,self.model_name)))
    24         return temp
    25 
    26 
    27     def get_list_url(self):
    28         model_name = self.model._meta.model_name
    29         app_label = self.model._meta.app_label
    30         _url = reverse("%s_%s_list"%(app_label, model_name)) 
    31         return _url
    32 
    33     def get_modelform_class(self):
    34         if not self.modelform_class:
    35             from django.forms import ModelForm
    36 
    37             class ModelFormDemo(ModelForm):
    38                 class Meta:
    39                     model = self.model
    40                     fields = '__all__'
    41             
    42             return ModelFormDemo
    43         else:
    44             return self.modelform_class
    45         
    46     #选择按钮
    47     def select(self,obj):
    48         return mark_safe("<input type='checkbox'>")
    49 
    50     #获取需要显示的字段
    51     def get_display_field(self):
    52         temp = []
    53         temp.append(ConfXadmin.select)
    54         temp.extend(self.list_display)
    55         return temp
    56     
    57 
    58     def list_view(self,request):
    59         all_data = self.model.objects.all()
    60         
    61         show_data_list = []
    62 
    63         for model_obj in all_data:
    64             temp = []
    65 
    66             for field in self.get_display_field():
    67                 if callable(field):
    68                     print(field)
    69                     val = field(self,model_obj)
    70                 else:
    71                     val = getattr(model_obj,field)
    72                 temp.append(val)
    73 
    74             show_data_list.append(temp)
    75 
    76 
    77         return render(request,'list.html',locals())

    我们先定义一个函数,让他生成一个html标签的字符串,函数的返回值要用safe_mark的形式返回,也就是第46行的select函数。

    函数的传参不光给了个self,还有个obj,在这个函数中是用不到的,是因为在后面其他的一些按钮,比方删除和修改,是需要被编辑对象的id的,那就需要从当前循环的model_obj里的id的,所以在for循环里需要传model_obj给field。

    然后把get_display_field函数里面加上新的函数(第53行),在append的时候注意顺序,因为返回的temp是按照顺序渲染在页面上,所以必须按照需求顺序append。

    视图里的for循环改动的思路比较有意思,我们对field进行一下callabled判断,条件判断值为TRUE,说明当前的这个字段是个函数,否则就是在model的ORM类里定义的字段。这样出来的效果就是这样的

    编辑、删除按钮

    对每条记录进行编辑、删除是需要有个按钮的,可以按照下面的思路添加进去

     1     #删除按钮
     2     def edit(self,obj):
     3         _url = reverse("{}_{}_change".format(self.app_name,self.model_name),args=(obj.pk,))
     4         return mark_safe("<a href='{}' class = 'btn btn-info'>编辑</a>".format(_url))
     5 
     6     #删除按钮
     7     def remove(self,obj):
     8         _url = reverse("{}_{}_delete".format(self.app_name,self.model_name),args=(obj.pk,))
     9         return mark_safe("<a href='{}' class = 'btn btn-warning'>删除</a>".format(_url))
    10         
    11     #选择按钮
    12     def select(self,obj):
    13         return mark_safe("<input type='checkbox'>")
    14 
    15     #获取需要显示的字段
    16     def get_display_field(self):
    17         temp = []
    18         temp.append(ConfXadmin.select)
    19         temp.extend(self.list_display)
    20         temp.append(ConfXadmin.edit)
    21         temp.append(ConfXadmin.remove)
    22         return temp

    这里要注意的是 和选择按钮不同的是edit和remove的url反向解析涉及到了一个参数id,所以要用到args来赋值。其他的用法都差不太多了,直接看看效果

     注意看上面动图中的url,点击编辑的时候跳转的URL是带有相关的ID参数的。

    表格抬头显示

     经过前面的一系列的操作,我们已经成功的拿到数据然后按照表格的效果显示出来,可是字段的名称并没有显示出来。我们要获取每个字段的字段名,就要用一个循环来获取一个列表,下面的代码是list_view里的一部分,专门用来获取字段的标题

     1 #构建表头
     2 field_title_list = []
     3 for field in self.get_display_field():
     4     if callable(field):
     5         field_title = field(self,get_title=True)
     6     else:
     7 
     8         if field == '__str__':
     9             field_title = self.model_name.upper()
    10         else:
    11             field_title = self.model._meta.get_field(field).verbose_name
    12 
    13     field_title_list.append(field_title)

    其实很简单,先对我们定义的要显示的字段列表进行for循环,拿到每个字段,再对这个字段进行判定,如果是可调用的,就从函数获取,否则就是定义的字段名,如果就是"__str__"就直接用model的名称,否则就从字段里获取名称。

    这里需要对前面定义的select/edit/remove方法做一些修改,先看看是怎么修改的

     1 #删除按钮
     2 def edit(self,obj=None,get_title=False):
     3     if not get_title:
     4         _url = reverse("{}_{}_change".format(self.app_name,self.model_name),args=(obj.pk,))
     5         return mark_safe("<a href='{}' class = 'btn btn-info'>编辑</a>".format(_url))
     6     else:
     7         return '编辑'
     8 
     9 #删除按钮
    10 def remove(self,obj=None,get_title=False):
    11     if not get_title:
    12         _url = reverse("{}_{}_delete".format(self.app_name,self.model_name),args=(obj.pk,))
    13         return mark_safe("<a href='{}' class = 'btn btn-warning'>删除</a>".format(_url))
    14     else:
    15         return '删除'
    16 #选择按钮
    17 def select(self,obj=None,get_title=False):
    18     if not get_title:
    19         return mark_safe("<input type='checkbox'>")
    20     else:
    21         return mark_safe("<input type='checkbox' class = 'selected'>全选")

    修改的思路是一样的,主要是对传参这一块进行一些修改:

    先把obj给了个默认的值为空,然后定义了另一个形参:get_title,默认为FALSE,可以看出来函数有两个返回值,当get_title为真是,意思是函数的返回值为filed的字段名,否则就是返回一段html代码嵌到前端里。

    给obj和get_title赋默认值主要是为了简化了后面的代码。

    对了,对应的前端的模板也要加thead标签

     1 <div class="container">
     2     <div class="row">
     3         <div class="col-md-9">
     4             <h3>数据列表</h3>
     5             <table class="table table-bordered table-striped">
     6                 <thead>
     7                     <tr>
     8                         {%for item in field_title_list%}
     9                         <th>
    10                             {{item}}
    11                         </th>
    12                         {%endfor%}
    13                     </tr>
    14                 </thead>
    15                 <tbody>
    16                     {%for data in show_data_list%}
    17                     <tr>
    18                         {%for item in data%}
    19                         <td>{{item}}</td>
    20                         {%endfor%}
    21                     </tr>
    22                     {%endfor%}
    23                 </tbody>
    24             </table>
    25         </div> 
    26     </div>
    27 </div>
    list的表格部分html代码

    看看效果

     这样就完成了最基础的展示的视图,先到这里,后面我们在看看怎么实现分页、filter等效果

  • 相关阅读:
    前端高效开发必备的 js 库梳理
    前端进阶: css必知的几个底层知识和技巧
    Vue项目上线做的一些基本优化
    如何制作一个组件?论组件化思想
    15分钟带你了解前端工程师必知的javascript设计模式(附详细思维导图和源码)
    Promise的源码实现(完美符合Promise/A+规范)
    前端工程师不可不知的Nginx知识
    java EE应用概述
    javaweb学习——session和Cookie实现购物车功能
    javaweb学习——会话技术(二)
  • 原文地址:https://www.cnblogs.com/yinsedeyinse/p/13492640.html
Copyright © 2020-2023  润新知