Xadmin组件构建之filter、pop

一、filter

1 app01\Xadmin.py

#定义我们自己的样式
class BookConfig(ModelXadmin):

    list_filter=["title","publish","authors",]

2 Xadmin\service\Xadmin.py

# 构建表头数据和构建表单数据本应该放在ModelXadminl类下面list_view视图函数中,但数据太多放在里面会会显得杂乱
# 这里定义一个类专门用来在页面展示数据,把ModelXadminl类中的self以及list_view函数中的data_list和request三个参数传过来
class show_list(object):
    def __init__(self, config, data_list, request):
        self.config = config  # config代表传过来的self, self.config就是ModelXadminl类中的实例化对象self
        self.data_list = data_list
        self.request = request

    def get_filter_linktags(self):
        print("list_filter",self.config.list_filter)    # list_filter ['title','publish', 'authors']
        link_dic={}
        import copy

        for filter_field in self.config.list_filter:  # list_filter ['title',publish', 'authors']
            params = copy.deepcopy(self.request.GET)   #第一次访问book页面时:<QueryDict: {}>
            print("111",params)

            cid=self.request.GET.get(filter_field,0) #默认取0,

            print("filter_field:",filter_field)   # filter_field: publish
            filter_field_obj=self.config.model._meta.get_field(filter_field) #由字符串取模型表中的字段对象
            print("filter_field_obj:",filter_field_obj)   #filter_field_obj: app01.Book.publish
            print(type(filter_field_obj))  #<class 'django.db.models.fields.related.ForeignKey'>

            #filter_field_obj.rel 打印的是:<ManyToOneRel: app01.book>          取该字段当前的模型表
            #filter_field_obj.rel.to 打印的是:<class 'app01.models.Publish'>   取该字段相关联的模型表
            # print("rel...",filter_field_obj.rel.to.objects.all())              #取该字段相关联的模型表中的所有数据
            #rel... <QuerySet [<Publish: 苹果出版社>, <Publish: 光子出版社>]>
            if isinstance(filter_field_obj,ForeignKey) or isinstance(filter_field_obj,ManyToManyField):
                data_list=filter_field_obj.rel.to.objects.all() #一对一、多对多字段

            else:  #普通字段
                data_list=self.config.model.objects.all().values("pk",filter_field)
                print("data_list:",data_list)  # <QuerySet [{'pk': 1, 'title': '北京折叠'},……]}

            temp=[]

            # 处理 全部标签
            if params.get(filter_field):
                del params[filter_field]
                temp.append("<a href='?%s'>全部</a>"%params.urlencode())
            else:
                temp.append("<a  class='active' href='#'>全部</a>")

            # 处理 数据标签
            for obj in data_list:  #循环字段关联模型表中的数据<QuerySet [<Publish: 苹果出版社>, <Publish: 光子出版社>]>
                if isinstance(filter_field_obj, ForeignKey) or isinstance(filter_field_obj, ManyToManyField):
                    pk=obj.pk
                    text=str(obj)
                    params[filter_field] = pk  # params里面没有filter_field+"__id"键,添加键值,有键,覆盖其值
                else:# data_list= [{"pk":1,"title":"go"},....]
                    print("========")
                    pk=obj.get("pk")
                    text=obj.get(filter_field)
                    params[filter_field] =text

                print(params)  #第一次循环打印结果:<QueryDict: {'publish': [1]}>

                _url=params.urlencode()    #把{'publish': [1]}转成publish=1

                if cid==str(pk) or cid==text:
                    link_tag = "<a class='active' href='?%s'>%s</a>" % (_url, text)  # 路径前没加/,从当前访问的路径开始拼接

                else:
                    link_tag = "<a href='?%s'>%s</a>" % (_url, text)

                temp.append(link_tag)
            link_dic[filter_field]=temp
        print("link_dic:",link_dic)
        #link_dic: {'publish': ["<a  class='active' href='#'>全部</a>",
                 # "<a href='?publish__id=1'>苹果出版社</a>", "<a href='?publish__id=2'>光子出版社</a>"],
                 # 'authors': ["<a  class='active' href='#'>全部</a>",
                 # "<a href='?authors__id=1'>xiaohei</a>", "<a href='?authors__id=2'>xiaobai</a>"]}

        return link_dic
  # 定义每张表的配置类样式
class ModelXadmin(object):
    list_filter=[]
def get_filter_condition(self,request): filter_condition=Q() #默认查询方式为且 for filter_field,val in request.GET.items(): #获取filter查询的键值条件 if filter_field in self.list_filter: #如果传的键在定义的列表里面,防止把page=1这样的键值对传过来 filter_condition.children.append((filter_field,val)) #注意里面接收的是一个元组 return filter_condition # (AND: ('publish', '2'), ('authors', '1')) # 查看视图 def list_view(self, request): # 这里注册用哪个样式类(默认、自定义),self就是谁 model_name = self.model._meta.model_name # 获取表名 # 获取filter构建Q对象 print("999", request.GET) # 999 <QueryDict: {'page': ['1'], 'authors': ['1'], 'publish': ['2']}> print("777", request.GET.items) # 777 <bound method MultiValueDict._iteritems of <QueryDict: {'page': ['1'], 'authors': ['1'], 'publish': ['2']}>> filter_condition=self.get_filter_condition(request) print("888",filter_condition) #888 (AND: ('authors', '1'), ('publish', '2')) 处理之后的filter查询条件 # 筛选获取当前表所有数据 data_list = self.model.objects.all().filter(search_connection).filter(filter_condition) # <QuerySet [<Book: 北京折叠>, …… ]> # 按照showlist展示数据 showlist = show_list(self, data_list, request) # 实例化一个对象showlist,并传三个参数,其中self是当前ModelXadmin的实例化对象 # 把self传给show_list类后,它里面的__init__进行接收(上面show_list函数用来接收self的参数是config) # 构建一个增加url路径 add_url = self.get_add_url() return render(request, 'list_view.html', locals())

 3 list_view.html

       <div class="col-md-3">
            <!--如果list_filter有值说明是用户自定义的,展示除来-->
            {% if showlist.config.list_filter %}
                <div class="filter">
                    <h4 style="">Filter</h4>
                    {% for filter_field,linktags in showlist.get_filter_linktags.items %}
                        <div class="well">  <!-- class="well"为加面板-->
                            <p>By {{ filter_field.upper }}</p>
                            {% for link in linktags %}
                                <p>{{ link|safe }}</p>
                            {% endfor %}
                        </div>
                    {% endfor %}
                </div>
            {% endif %}
        </div>
<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>Title</title>
    <link rel="stylesheet" href="/static/bootstrap/css/bootstrap.min.css">
    <script src="/static/jquery-3.3.1.js"></script>

    <style>
        .filter a {
            text-decoration: none;
        {# 去除a标签默认样式的下划线 #} color: grey;
        }

        .active {
            color: blue !important;
        }
    </style>
</head>
<body>

<h3>查看{{ model_name }}数据</h3>

<div class="container">
    <div class="row">
        <div class="col-lg-9">
            <!--添加数据-->
            <a href="{{ add_url }}" class="btn btn-primary">添加数据</a>
            <!--搜索数据开始-->
            {% if showlist.config.search_fields %}
                <form action="" class="pull-right">
                    <input type="text" name="q" value="{{ showlist.config.key_word }}">
                    <button>submit</button>
                </form>
            {% endif %}
            <!--搜索数据结束-->

            <!--添加form表单是为了在点击Go时确定发送数据的范围(不包括上面submit里面的内容)-->
            <form action="" method="post">
                {% csrf_token %}
                <!--action操作开始-->
                <select name="action" id="" style="width: 200px;padding: 5px 8px;display: inline-block">
                    <option value="">---------------</option>
                    {% for item in showlist.get_action_list %}
                        <option value="{{ item.name }}">{{ item.desc }}</option>
                    {% endfor %}
                </select>
                <button type="submit" class="btn btn-info">Go</button>
                <!--action操作结束-->

                <!--表格数据开始-->
                <table class="table table-bordered table-striped">
                    <thead>
                    <tr>
                        {% for item in showlist.get_header %}
                            <th>{{ item }}</th>
                        {% endfor %}
                    </tr>
                    </thead>
                    <tbody>
                    {% for data in showlist.get_body %}
                        <tr>
                            {% for item in data %}
                                <td>{{ item }}</td>
                            {% endfor %}
                        </tr>
                    {% endfor %}

                    </tbody>
                </table>
                <!--表格数据结束-->

                <!--分页开始-->
                <nav class="pull-right">
                    <ul class="pagination">
                        {{ showlist.pagination.page_html|safe }}
                    </ul>
                </nav>
                <!--分页结束-->
            </form>
        </div>

        <!--filter开始-->
        <div class="col-md-3">
            <!--如果list_filter有值说明是用户自定义的,展示除来-->
            {% if showlist.config.list_filter %}
                <div class="filter">
                    <h4 style="">Filter</h4>
                    {% for filter_field,linktags in showlist.get_filter_linktags.items %}
                        <div class="well">  <!-- class="well"为加面板-->
                            <p>By {{ filter_field.upper }}</p>
                            {% for link in linktags %}
                                <p>{{ link|safe }}</p>
                            {% endfor %}
                        </div>
                    {% endfor %}
                </div>
            {% endif %}
        </div>
        <!--filter结束-->
    </div>
</div>

<script>

    //给表头复选框加上点击事件(点击表头复选框,下面框全部选中,反之全部取消)
    $("#choice").click(function () {

        if ($(this).prop("checked")) {    //prop() 方法用于设置或返回被选元素的属性和值
            $(".choice_item").prop("checked", true)
        } else {
            $(".choice_item").prop("checked", false)
        }

    })
</script>

</body>
</html>
list_view.html

 二、pop

如何识别Form字段中一对多或者多对多字段

1 Xadmin\service\Xadmin.py

# 定义每张表的配置类样式
class ModelXadmin(object):# 增加视图
    def add_view(self, request):
        model_name = self.model._meta.model_name  # 获取表名
        ModelFormDemo = self.get_modelform_class()  # 获取modelform类变量
        form = ModelFormDemo()  # 实例化一个对象form


        # 循环form下每一个字段,如果为一对多或多对多字段为其添加两个属性is_pop=True、
        # url = _url + "?pop_res_id=id_%s" % bfield.name
        #为了在form.html中识别一对多或多对多form字段,为其添加一个+号进行pop操作
        for bfield in form:
            from django.forms.boundfield import BoundField
            print(bfield.field,type(bfield.field))  # 字段对象、字段类型
            print("name",bfield.name)  # 字段名(字符串)


            from django.forms.models import ModelChoiceField
            if isinstance(bfield.field,ModelChoiceField): #如果form字段是一对多或多对多类型
                bfield.is_pop=True   #为form字段加属性,以便之后取出判断是否为一对多或多对多字段

                print("===>",bfield.field.queryset.model)    #一对多或者多对多字段的关联模型表
                                                             #===> <class 'app01.models.Publish'>
                                                             #===> <class 'app01.models.Author'>

                related_model_name = bfield.field.queryset.model._meta.model_name
                related_app_label = bfield.field.queryset.model._meta.app_label

                _url = reverse("%s_%s_add" % (related_app_label, related_model_name))
                bfield.url = _url + "?pop_res_id=id_%s" % bfield.name
                #为bfield加属性 url = _url + "?pop_res_id=id_%s" % bfield.name

        if request.method == "POST":
            form = ModelFormDemo(request.POST)
            if form.is_valid():
                obj=form.save()  #返回的当前插入的那一条记录

                pop_res_id=request.GET.get("pop_res_id")
                if pop_res_id: #说明是子窗口:执行完父窗口函数后自行关闭
                    res = {"pk": obj.pk, "text": str(obj), "pop_res_id": pop_res_id}
                    return render(request, "pop.html", {"res": res})

                else:#正常查看页面:返回展示页面
                    return redirect(self.get_list_url())

        return render(request, 'add_view.html', locals())

2 templates\form.html

<div class="container">
    <div class="row">
        <div class="col-md-6 col-xs-8 col-md-offset-3">
            <form action="" method="post" novalidate>
                {% csrf_token %}
                {% for field in form %}
                    <div style="position: relative">
                        <label for="">{{ field.label }}</label>
                        {{ field }} <span class="error pull-right">{{ field.errors.0 }}</span>
                        {% if field.is_pop %} <!--如果是一对多或多对多字段,为其后添加一个+号进行pop操作-->
                            <!--position: absolute是为了绝对定位脱离文档流,把+号放到input标签之后-->
                            <a onclick="pop('{{ field.url }}')" style="position: absolute;right: -30px;top: 20px"><span
                                    style="font-size: 28px">+</span></a>
                        {% endif %}
                    </div>
                {% endfor %}
                <button type="submit" class="btn btn-default pull-right">提交</button>
            </form>
        </div>
    </div>
</div>

<script>
    function pop(url) {
        window.open(url, "", "width=600,height=400,top=100,left=100")
    }
</script>

 pop.html

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>Title</title>
    <meta name="viewport" content="width=device-width, initial-scale=1">
    <script src="/static/jquery-3.3.1.js"></script>

</head>
<body>

<script>
    window.opener.pop_response('{{ res.pk }}',"{{ res.text }}",'{{ res.pop_res_id }}'); //23 hui id_publish
    window.close()
</script>


</body>
</html>

 

posted @ 2020-06-02 23:03  zh_小猿  阅读(102)  评论(0)    收藏  举报