zoukankan      html  css  js  c++  java
  • Xadmin相关实现

    一,保存搜索条件(Save search conditions)

    kept conditions(保存条件)的应用场景比较常见,在我们查询时,可以实现多条件的筛选查询,比如:在淘宝上,选择了其中的一个条件后,我们可以继续选择其他的一些过滤条件,然后就可以实现多条件的查询。

    那么代码时怎么实现的呢?

    我们在用户提交查询条件请求时,一般都是GET请求,在相关路径后拼上?条件的形式,在Django中,我们在后端接收是通过request.GET这个属性得到所有的条件,在Django中,request.GET是一个QueryDict类型的数据。<QueryDict: {}>

    #访问一个请求
    print(request.GET,type(request.GET))
    
    #<QueryDict: {}> <class 'django.http.request.QueryDict'>

    我们点击它的源码,看看这个QueryDict的内部都做了些什么(from django.http.request import QueryDict)

    class QueryDict(MultiValueDict):
    
        _mutable = True
        _encoding = None
    
        def __init__(self, query_string=None, mutable=False, encoding=None):
            super(QueryDict, self).__init__()
            if not encoding:
                encoding = settings.DEFAULT_CHARSET
            self.encoding = encoding
            query_string = query_string or ''
    
    
            self._mutable = mutable

    这是截取的QueryDict中的一部分,在QueryDict中,有一个属性_mutable,这个值是True时,表示QueryDict可以被修改,否则表示无法被修改,通过源码可以看出,QueryDict在实例一个对象时,self._mutable是False,是不可修改的

    而request.GET是一个QueryDict的实例,所以,request.GET是不可以被修改的

    如果我们要修改或者添加request.GET中的值时,我们必须设置一个属性值request.GET._mutable=True,这样就可以修改了。

    那我们如何保存搜索条件呢,要保存搜索条件,我们首先要得到用户访问时携带的条件,那这个条件怎么获取呢?

    在Django中,提供了一个方法用于或者请求的字符串格式的搜索条件,这个方法就是:request.GET.urlencode()

    比如:

    #我们访问路径:http://127.0.0.1:8000/Xadmin/demo/order/?id=1&page=2
    
    #得到的是这个
    print(request.GET,request.GET.urlencode())
    <QueryDict: {'id': ['1'], 'page': ['2']}>    id=1&page=2
       

    通过这个方法我们可以把获取的这个参数拼接到相应路径后面。

    如果我们对request.GET这个参数修改时,我们最好是copy一个同样的数据,而不是在原数据上进行修改,这样可以保证数据的安全性,因为在其他地方,我们肯定还会使用这个request.GET这个参数。

    注意:在copy时,我们不需要设置_mutable=True这个参数就可以修改,因为Django已经为我们想到了这个情况,在QueryDict的源码中,对这两个copy有重写,默认的参数就是mutable=True,所以我们使用copy后,可以直接对原数据修改。

     def __copy__(self):
            result = self.__class__('', mutable=True, encoding=self.encoding)
            for key, value in six.iterlists(self):
                result.setlist(key, value)
            return result
    
        def __deepcopy__(self, memo):
            result = self.__class__('', mutable=True, encoding=self.encoding)
            memo[id(self)] = result
            for key, value in six.iterlists(self):
                result.setlist(copy.deepcopy(key, memo), copy.deepcopy(value, memo))
            return result

    所以,我们在取到这个值时,直接对这个值进行copy,然后在做修改

    params = request.GET
    import copy 
    params = copy.deepcopy(params)   #可以直接对这个值做修改
    
    params["要修改的值"] = value

    二、搜索框功能的实现(search_fields)

    相关实现的视图函数部分

    #配置类的实现代码
    from django.db.models import Q
    from django.shortcuts import HttpResponse, render
    
    # 配置类
    class ModelXadmin(object):
        # 配置类中默认的搜索内容
        search_fields = []
    
        def __init__(self, model, site):
            self.model = model
            self.site = site
    
            self.model_name = ""
            self.app_name = ""
    
        # 将搜索的实现单独的封装到一个函数中
        def get_search_condition(self, request):
            # 实例化一个Q对象
            search_condition = Q()
            # 设置多条件搜索的关系为或
            search_condition.connector = "or"
            # print("search_fields", self.search_fields)  # ["title","price"]
            # 获取输入的搜索内容
            key_word = request.GET.get('q')
            if key_word:   # 对搜索内容判断,如果为空,models.XXX.objects.filter()时 会报错
                for search_field in self.search_fields:
                    search_condition.children.append((search_field + "__icontains", key_word))
    
            return search_condition
    
        # 查看视图函数
        def list_view(self, request):
            """
            self.model: 用户访问哪张表,self.model就是谁
            data_list: 查看表下的数据
            :param request:
            :return:
            """
            search_condition = self.get_search_condition(request)
            data_list = self.model.objects.filter(search_condition)
            model_name = self.model._meta.model_name
            return render(request, 'list_view.html', locals())

     备注:示例化Q对象值为空时,filter(Q对象) 等同于all()

    相关实现的模板部分

    {% if search_fields %}
                            <form action="" class="form-inline">
                                <div style="margin-bottom: 20px;" class="input-group">
                                    <input type="text" class="form-control" placeholder="请输入内容" name="q">
                                    <span class="input-group-btn">
                                        <button class="btn btn-info" type="submit">搜索</button>
                                    </span>
                                </div>
                            </form>
     {% endif %}

    三、批量操作的实现(actions)

    批量操作中相关视图函数的实现

    1. 实现的一个基本思路:

    1. 首先,在默认的基础配置类中一定要有一个可设置批量操作的属性,并规定一个实现的方式,自定义的一个格式。
    2. 在后端做一个基础的处理,如果用户为配置该属性,则默认实现批量删除的功能,如果用户自定义了,则优先显示用户自定义的属性,再在最后展示默认的批量删除操作
    3. 构建在前端的展示方式:使用select的下拉框展示option中value为批量操作的方法名(函数名)(备注:因为在前端中不能使用__name__这个属性,所以在后端构建),文本内容为对这个方法的描述内容
    4. 将所有的有效控件放置在一个form表单中,发送post请求,(有效控件包括:select标签中的option标签:所有的批量操作的方法action,表单中的每行的checkbox,代表该行数据的pk值)
    5. 取到对应的方法名(字符串形式的)和对应的数据,通过反射执行该方法,进行相应批量操作
    6. 返回处理结果,或者直接展示当前页面

    2.具体实现代码:

    class ModelXadmin(object):
        actions=[]
    
        # 默认的批量删除操作,默认出入两个参数:1.request  2.包含所有执行批量操作数据的queryset集
        def patch_delete(self,request,queryset):
            queryset.delete()
    
        # 方法的描述内容
        patch_delete.short_description="批量删除"
    
        # action的基础处理  优先展示用户的配置信息
        def get_actions(self):
            temp=[]
            temp.extend(self.actions)
            temp.append(self.patch_delete)
    
            return temp
    
        def __init__(self,model,site):
    
            self.model=model
            self.site=site
    
            self.model_name=""
            self.app_name=""

    构建展示方式:

    class ShowList(object):
        def __init__(self,config,request,data_list):
    
            self.config=config
            self.data_list=data_list
            self.request=request
            
            # 封装的展示类中
            self.actions=self.config.get_actions()
    
        # 处理前端的展示信息 构建一个包含每一个action字典的列表
        def new_actions(self):
            temp=[]
            for action in self.actions:
                temp.append({
                    "name":action.__name__,
                    "desc":action.short_description
                })
    
            return temp

    批量操作中相关模板部分的实现

    <form action="" method="post">
                   {% csrf_token %}
                    <div>
                        <select name="action" id="" class="form-control" style="display: inline-block; 300px">
                            <option value="">---------------</option>
    
                            {% for action_dict in show_list.new_actions %}
                                <option value="{{ action_dict.name }}">{{ action_dict.desc }}</option>
                            {% endfor %}
    
                        </select>
                        <input type="submit" value="Go" class="btn btn-warning">
    
                    </div>
                    <table class="table table-bordered table-striped">
                    <thead>
                        <tr>
                             {% for item in show_list.get_header %}
                             <th>{{ item }}</th>
                             {% endfor %}
    
                        </tr>
                    </thead>
                    <tbody>
                          {% for new_data in show_list.get_body %}
                              <tr>
                                  {% for item in new_data %}
                                     <td>{{ item }}</td>
                                  {% endfor %}
                              </tr>
                          {% endfor %}
    
                    </tbody>
                </table>
    
                </form>

    发送post请求后,后端处理:

    class ModelXadmin(object):
        def __init__(self, model, site):
            self.model = model
            self.site = site
            
        def list_view(self, request):
            """
            self.model: 用户访问哪张表,self.model就是谁
            data_list: 查看表下的数据
            ShowList(self,data_list)  # self: 当前查看表的配置类对象
            :param request:
            :return:
            """
    
            if request.method == "POST":  # action
                action = request.POST.get("action")
                selected_pk_list = request.POST.getlist("selected")  # 多选框时使用getlist方法接收所有的值
                queryset = self.model.objects.filter(pk__in=selected_pk_list)  # 过滤符合条件的所有的数据
                action = getattr(self, action)  # 通过反射获取结果
                ret = action(request, queryset)
                
                return ret

    四、过滤器的实现(list_filter)

     1.过滤器实现的基本思路:

    1. 基础配置类中配置过滤的相关属性,以及适用方式(输入要过滤的字段即可,一般输入FK字段,M2M字段,其他字段没有意义)
    2. 处理配置信息,构建展示方式(后端生成a标签,实现保存搜索条件和相应条件对应的对象显示突出,即用户点那个那个条件变色)
    3. 接收过滤对象,后端进行筛选数据,同search_fields

    2.具体的代码实现:

    class ModelXadmin(object):
        list_filter=[]

    构建展示内容:

    from copy import deepcopy
    from django.utils.safestring import mark_safe
    
    
    class ShowList(object):
        def __init__(self,config,request,data_list):
    
            self.config=config
            self.data_list=data_list
            self.request=request
    
            # list_filter
            self.list_filter=self.config.list_filter  # ["publish","auhtors]
    
        def get_filter_link_tags(self):
            
            # 构建的最终方式:link_tags={"publish":["a标签","a标签"],"author":["a标签","a标签"]}
            link_tags={}
            
            # 循环每一个过滤条件
            for filter_field in self.list_filter: # ["publish","auhtors]
    
                # 保存过滤条件  不要对原数据修改  保证数据的安全性
                params = deepcopy(self.request.GET)  # {"authors":2}
                
                # 构建方式中a标签的href是:?过滤字段名 = 选中对象的pk值
                # 获取当前选中的对象  注意:必须从原数据中取,原数据是不变的  deepcopy的数据是可变的
                current_id=self.request.GET.get(filter_field)
                # print("current_id",current_id)
    
                # 获取当前字段对象
                filter_field_obj=self.config.model._meta.get_field(filter_field)
    
                # 获取当前字段对象对应的所有的数据
                related_data_list=filter_field_obj.rel.to.objects.all()
                temp=[]
                for obj in related_data_list:
                    params[filter_field]=obj.pk  # 保存搜索条件用
                    _url=params.urlencode()  # 获取字符串形式的参数
                    # 如果当前对象 = 搜索条件中的对象  做特殊展示
                    if current_id==str(obj.pk):  # 备注:obj.pk 是数字类型,一定要做类型转换
                         s="<a class='item' href='?%s'>%s</a>"%(_url,str(obj))
                    else:
                        s = "<a href='?%s'>%s</a>" % (_url, str(obj))
    
                    temp.append(mark_safe(s))
    
                link_tags[filter_field]=temp
    
            return link_tags

    对用户选择的过滤条件做筛选处理

    class ModelXadmin(object):
    
        def list_view(self, request):
            
            filter_condition = Q()
        
            for filter_field, val in request.GET.items():
                # 报存搜索条件处理时,因为还会有其他的干扰条件,必须过滤掉,比如:分页的page参数  搜索框的q参数等
                if filter_field not in ["page", "q"]:
                    filter_condition.children.append((filter_field, val))
            
            data_list = self.model.objects.filter(filter_condition)
            

    五、其他功能实现(展示字段的实现、展示字段可点击跳转的实现、增删改查)

    from django.shortcuts import HttpResponse,render,redirect
    from django.urls import reverse
    from django.utils.safestring import mark_safe
    
    
    class ShowList(object):
        def __init__(self,config,request,data_list):
    
            self.config=config
            self.data_list=data_list
            self.request=request
    
            # 分页器组件相关配置
            current_page=request.GET.get("page")
            all_count=self.data_list.count()
            pagination=Pagination(current_page,all_count,request.GET)
            self.pagination=pagination
            self.page_data_list=self.data_list[pagination.start:pagination.end]
    
        def get_header(self):
            # 处理表头
            # header_list=["ID","书籍名称","出版社"]
            header_list = []
    
            for field in self.config.new_list_display():  # [check,"nid","title","publish",edit,delete]
                if isinstance(field, str):
                    if field == "__str__":
                        val = self.config.model._meta.model_name.upper()
                    else:
                        field_obj = self.config.model._meta.get_field(field)
                        val = field_obj.verbose_name
    
                else:
                    val = field(self.config, is_header=True)  # 获取表头,传is_header=True
    
                header_list.append(val)
    
    
            return header_list
    
        def get_body(self):
    
            # 处理表单数据
            new_data_list = []
            for obj in self.page_data_list:  # data_list [book_obj,book_obj2,...]
    
                temp = []
                for field in self.config.new_list_display():  # ["nid","title","publish","authors"]
                    if isinstance(field, str):
                        try:
                            from django.db.models.fields.related import ManyToManyField
    
                            field_obj = self.config.model._meta.get_field(field)
    
                            if isinstance(field_obj, ManyToManyField):
                                t = []
                                for i in getattr(obj, field).all():
                                    t.append(str(i))
                                val = ",".join(t)
                            else:
                                if field in self.config.list_display_link:
                                    edit_url = self.config.get_edit_url(obj)
                                    val = mark_safe("<a href='%s'>%s</a>" % (edit_url, getattr(obj, field)))
                                else:
                                    val = getattr(obj, field)
    
                        except Exception as e:
                            val = getattr(obj, field)
    
                    else:
                        val = field(self.config, obj)
    
                    temp.append(val)
    
                new_data_list.append(temp)
    
            '''
            new_data_list=[
                ["北京折叠",122,<a href=''>编辑</a>,<a href=''>删除</a>],
                ["三体", 222,<a href=''>编辑</a>,<a href=''>删除</a>],
            ]
            '''
    
            return new_data_list
    
    
    
    class ModelXadmin(object):
        list_display=["__str__",]
        list_display_link=[]
    
        model_form_class=None
    
        def __init__(self,model,site):
    
            self.model=model
            self.site=site
    
            self.model_name=""
            self.app_name=""
    
        # 选择按钮  编辑 删除
        def edit(self, obj=None, is_header=False):
            if is_header:
                return "操作"
            _url=self.get_edit_url(obj)
            return mark_safe("<a href='%s'>编辑</a>" % _url)
    
        def delete(self, obj=None, is_header=False):
    
            if is_header:
                return "操作"
    
            _url=self.get_delete_url(obj)
    
            return mark_safe("<a href='%s'>删除</a>"%_url)
    
        def checkbox(self, obj=None, is_header=False):
            if is_header:
                return "选择"
    
            return mark_safe("<input type='checkbox' name='selected' value='%s'>"%obj.pk)
    
        # 反向解析当前表的增删改查的url
        def get_edit_url(self,obj):
            # 反向解析:url
            url_name = "%s_%s_change" % (self.app_name, self.model_name)
            # http://127.0.0.1:8008/Xadmin/app01/book/(d+)/change
            _url = reverse(url_name, args=(obj.pk,))
            # return mark_safe("<a href='%s/change/'>编辑</a>"%obj.pk)
            return _url
    
        def get_list_url(self):
            # 反向解析:url
            url_name = "%s_%s_list" % (self.app_name, self.model_name)
            _url = reverse(url_name)
            return _url
    
        def get_add_url(self):
            # 反向解析:url
            url_name = "%s_%s_add" % (self.app_name, self.model_name)
            _url = reverse(url_name)
            return _url
    
        def get_delete_url(self,obj):
            # 反向解析:url
            url_name = "%s_%s_delete" % (self.app_name, self.model_name)
            _url = reverse(url_name, args=(obj.pk,))
            return _url
    
        # 构建新的展示列表,默认加入选择按钮  编辑 删除
        def new_list_display(self):
            temp=[]
    
            temp.append(ModelXadmin.checkbox)
            temp.extend(self.list_display)
            if not self.list_display_link:
                 temp.append(ModelXadmin.edit)
            temp.append(ModelXadmin.delete)
    
            return temp
    
        # 查看视图函数
        def list_view(self, request):
            """
            self.model: 用户访问哪张表,self.model就是谁
            data_list: 查看表下的数据
            ShowList(self,data_list)  # self: 当前查看表的配置类对象
            :param request:
            :return:
            """
    
            data_list = self.model.objects.all()
    
            show_list=ShowList(self,request,data_list)
            add_url = self.get_add_url()
            model_name = self.model._meta.model_name
    
            return render(request, 'list_view.html',locals())
    
        # 获取modelForm类
        def get_model_form_class(self):
            if self.model_form_class:
                return self.model_form_class
            else:
                # 使用Django中ModelForm实现展示所有字段
                from django.forms import ModelForm
                class DemoModelForm(ModelForm):
                    class Meta:
                        model=self.model
                        fields="__all__"
                return DemoModelForm
    
        # 添加视图函数
        def add_view(self, request):
            DemoModelForm = self.get_model_form_class()
            if request.method=="POST":
                form=DemoModelForm(request.POST)
                if form.is_valid():
                    form.save()
                    return redirect(self.get_list_url())
                else:
                    return render(request, 'add_view.html', locals())
    
            form=DemoModelForm
    
            return render(request, 'add_view.html',locals())
    
        # 编辑视图函数
        def change_view(self, request, id):
            edit_obj=self.model.objects.get(pk=id)
            DemoModelForm=self.get_model_form_class()
    
            if request.method=="POST":
                form=DemoModelForm(request.POST,instance=edit_obj)
                if form.is_valid():
                    form.save()
                    return redirect(self.get_list_url())
                else:
                    return render(request, 'change_view.html', locals())
            form=DemoModelForm(instance=edit_obj)
            return render(request, 'change_view.html',locals())
    
        # 删除视图函数
        def delete_view(self, request, id):
            list_url = self.get_list_url()
    
            if request.method=="POST":
                self.model.objects.filter(pk=id).delete()
                return redirect(list_url)
    
            return render(request, 'delete_view.html',{"list_url":list_url})

    六、添加页面中特殊字段的小窗口的动态添加的实现

     1.实现的基本思路:

    1. 如何在一对多、多对多或者一对一的字段后显示添加按钮
    2. 如何弹出一个窗口
    3. 如何将新窗口中添加的数据绑定到老窗口

    基本思路的相关实现:如何辨别这个字段是一个普通的字段还是一个特殊的字段?Django中我们可以循环每一个字段对象,判断是不是这个类型的对象即可,以ModelForm为例,我们使用ModelForm示例一个对象,form_obj=ModelForm(),循环这个对象,一个元素就是一个字段对象,print(type(field))  得到的是一个BoundField的对象:<class 'django.forms.boundfield.BoundField'>

    from django.forms import ModelForm
    
    form_obj = ModelForm()
    
    for field in form_obj:
         print(type(field))
    
    结果为:
    <class 'django.forms.boundfield.BoundField'>
    <class 'django.forms.boundfield.BoundField'>
    <class 'django.forms.boundfield.BoundField'>
    <class 'django.forms.boundfield.BoundField'>
    ... # 每一个字段对象都是一个boundfield对象。

      显而易见,boundfield对原本的字段对象做了封装 ,我们打开它的源码,看看都有哪些可用的东西:

    class BoundField(object):
        "A Field plus data"
        def __init__(self, form, field, name):
            self.form = form
            self.field = field
            self.name = name
            self.html_name = form.add_prefix(name)
            self.html_initial_name = form.add_initial_prefix(name)
            self.html_initial_id = form.add_initial_prefix(self.auto_id)
            if self.field.label is None:
                self.label = pretty_name(name)
            else:
                self.label = self.field.label
            self.help_text = field.help_text or ''
    
        @property
        def auto_id(self):
            """
            Calculates and returns the ID attribute for this BoundField, if the
            associated Form has specified auto_id. Returns an empty string otherwise.
            """
            auto_id = self.form.auto_id
            if auto_id and '%s' in force_text(auto_id):
                return force_text(auto_id) % self.html_name
            elif auto_id:
                return self.html_name
            return ''
    
        @property
        def id_for_label(self):
            """
            Wrapper around the field widget's `id_for_label` method.
            Useful, for example, for focusing on this field regardless of whether
            it has a single widget or a MultiWidget.
            """
            widget = self.field.widget
            id_ = widget.attrs.get('id') or self.auto_id
            return widget.id_for_label(id_)

    这是截取的一段源码:我们可以利用的有一个对象属性:self.field = field,还有一个静态方法:auto_id 它们有什么作用呢 ?print一下就知道了:

    for boundfield in form_obj:
        print(boundfield.field)
        print(boundfield.auto_id)
    
    结果为:
    <django.forms.fields.CharField object at 0x000002AED2E33BA8>
    id_title
    <django.forms.fields.CharField object at 0x000002AED2E33A58>
    id_desc
    <django.forms.models.ModelChoiceField object at 0x000002AED2E33B00>
    id_publish
    <django.forms.models.ModelMultipleChoiceField object at 0x000002AED2E33B38>
    id_authors

    由此可见:boundfield.field  返回的是该字段的对象       boundfield.auto_id 返回一个 id_字段名      这种格式的字符串  也就是对应生成HTML标签时的id值

    回到正题,我们的目的是通过代码过滤出这些特殊的一对多、多对多或者一对一字段,那怎么实现呢:直接判断是不是这个类型,额外的一个知识点就是,这几个特殊的字段在ModelForm中都是ModelChoiceField的对象,或者继承这个类的对象,我们判断是不是这个类的对象,就可以实现对这个的多虑,

    代码实现:

    from django.forms.models import ModelChoiceField

    for boundfield in form_obj: if isinstance(boundfield.field,ModelChoiceField): # 如果是这个类的对象,就给这个对象设置一个区分其他对象的属性,比如: boundfield.is_field = True # obj.is_field=值

    HTML的实现:

    {% for field in form_obj %}
        <label for="">{{ field.label }}</label>
        {% if field.is_field %}
            <p>{{ field }} <span style="font-size:32px;font-weight: 500">+添加按钮在此</span></p>
          {% else %}
            <p>{{ field }}</p>
        {% endif %}
        <span>{{ field.errors.0 }}</span>
    {% endfor %}

    这样我们就实现了第一步,那接下来第二步:打开一个窗口

    基本思路:点击添加按钮,触发一个打开一个新窗口的事件:

    那么这个新窗口的路径是什么?应该是我们点击那个字段,就打开那个字段相对应的添加页面,怎么实现呢?还是回到上面,我们循环每一个字段时,如果为特殊的字段,就获取这个字段所对应的模型表,和所在的app,然后通过反向解析url,来或者当前这个字段的添加url,然后添加到这个对象的属性中,直接在模板语言中获取。

    备注:这里有个小问题,那就是,如果我们没有将当前字段所在的模型表注册时,就不应该显示可添加按钮,同时,在反向解析url时,就会报错,因为没有注册,就没有这个模型类的相关url,也就没有反向解析这个东西了。处理很简单,加一个判断,那怎么判断?一个思路是,判断当前字段所在的模型表,在不在注册的字典中  site._registry,首先,怎么获取当前字段所在模型表:一个知识点:取得字段对象,有一个queryset的属性,这个queryset会获取到当前字段在数据库中所有的数据,在.model 就可以或者当前表,这个方法只对这些特殊字段可用!!!

     for boundfield in form_obj:
                if isinstance(boundfield.field,ModelChoiceField):
                    print(boundfield.field.queryset.model)
    
    输出结果为:
    
    <class 'blogs.models.Publisher'>
    <class 'blogs.models.Author'>

    我们通过这个结果: ._meta.app_label  获得当前字段所在app的字符串名,   ._meta.model_name  得到当前字段所在的模型表的字符串名。

    通过这个模型类,判断在不在注册的模型字典中:

    视图函数的相关实现:

            for boundfield in form_obj:
                if isinstance(boundfield.field,ModelChoiceField):
              # 如果在注册表中,就添加属性
    if boundfield.field.queryset.model in site._registry: boundfield.is_field = True ret = boundfield.field.queryset.model._meta root = '%s_%s_add'%(ret.app_label,ret.model_name) root_url = reverse(root) boundfield.root_url = root_url+"?pop_back_id=%s"%boundfield.auto_id  
                    # 拼接一个参数,用于将添加的数据添加到老窗口中,服务于第三步

    HTML页面的相关实现:

    <form action="" method="post" novalidate>
        {% csrf_token %}
    
    
        {% for filed in form %}
        <div class="form-group" style="position: relative">
            <label for="">{{ filed.label }}</label>
            {{ filed }} <span>{{ filed.errors.0 }}</span>
    
            {% if filed.is_field %}
                      <a onclick="pop('{{ filed.root_url }}')"><span style="font-size: 22px;color: #1b6d85;
                      position: absolute;top: 27px;right:-23px"
    >+</span></a> {% endif %} </div> {% endfor %} <input type="submit" class="btn btn-default"> </form> <script> function pop(url) { window.open(url,"","wdith=600,height=300,top=200,left=200") } </script>

    第三步的实现:提交添加数据后,数据创建成功后我们可以获取到一个新数据的obj,然后通过判断路径中有没有参数pop_back_id对应的值,上面设置路径时,添加的一个区分的一个标记。如果有值,表示是小窗口提交的值,否则表示正常的一个添加页面,如果是正常的添加页面,不做其他处理,如果是小窗口提交的数据,我们可以返回一个关闭窗口的HTML代码,同时可以向老窗口传参,将pk值,str(obj),以及是哪个字段发起的添加,就把数据添加到哪个数据下的标识:

    视图函数部分:

    def add_view(self, request):
    
            DemoModelForm = self.get_model_form_class()
            if request.method=="POST":
                form=DemoModelForm(request.POST)
                if form.is_valid():
                    obj=form.save()
    
                    pop_back_id=request.GET.get("pop_back_id")
    
                    if pop_back_id:   # 小窗口
    
                        text=str(obj)
                        return render(request,"pop.html",locals())
    
                    else: # 大窗口
                        return redirect(self.get_list_url())

    HTML部分:

    pop.html 页面:用于关闭这个小窗口,同时向老窗口传值

    <!DOCTYPE html>
    <html lang="en">
    <head>
        <meta charset="UTF-8">
        <title>Title</title>
    </head>
    <body>
    
    
    <script>
    
        window.opener.handle_pop("{{ obj.pk }}","{{ text }}","{{ pop_back_id }}");
    
        window.close()
    
    </script>
    
    </body>
    </html>

    原添加HTML页面执行对应的添加数据函数

    <script>
        
        function handle_pop(pk,text,pop_id) {
    
              var $option=$("<option>"); // <option></option>  创建一个option标签
              $option.html(text);  // 添加文本值
              $option.attr("value",pk);   
              $option.attr("selected","selected");  // 添加默认选中的效果
              $("#"+pop_id).append($option);  // 将这个数据添加到对应的标签中
    
        }
        
    </script>

    总结:

    from django.conf.urls import url
    from django.shortcuts import HttpResponse,render,redirect
    from django.urls import reverse
    from django.utils.safestring import mark_safe
    from Xadmin.utils.page import Pagination
    from django.db.models import Q
    class ShowList(object):
        def __init__(self,config,request,data_list):
    
            self.config=config
            self.data_list=data_list
            self.request=request
    
    
            # 分页器组件相关配置
            current_page=request.GET.get("page")
            all_count=self.data_list.count()
            pagination=Pagination(current_page,all_count,request.GET)
            self.pagination=pagination
            self.page_data_list=self.data_list[pagination.start:pagination.end]
    
            self.actions=self.config.get_actions()
    
            # list_filter
    
            self.list_filter=self.config.list_filter  # ["publish","auhtors]
    
        def get_filter_link_tags(self):
    
            #link_tags={"publish":["a","a"],"author":["a","a"]}
    
            link_tags={}
    
            from copy import deepcopy
    
    
            for filter_field in self.list_filter: # ["publish","auhtors]
    
                params = deepcopy(self.request.GET)  # {"authors":2}
    
                current_id=self.request.GET.get(filter_field)
                print("current_id",current_id)
    
    
    
                filter_field_obj=self.config.model._meta.get_field(filter_field)
    
                related_data_list=filter_field_obj.rel.to.objects.all()
                temp=[]
                for obj in related_data_list:
                    params[filter_field]=obj.pk
    
                    _url=params.urlencode()
    
    
                    if current_id==str(obj.pk):
                         s="<a class='item' href='?%s'>%s</a>"%(_url,str(obj))
                    else:
                        s = "<a href='?%s'>%s</a>" % (_url, str(obj))
    
                    temp.append(mark_safe(s))
    
                link_tags[filter_field]=temp
    
            return link_tags
    
    
    
    
    
        def new_actions(self):
            temp=[]
            for action in self.actions:
                temp.append({
                    "name":action.__name__,
                    "desc":action.short_description
                })
    
            print("temp",temp)
    
            return temp
    
        def get_header(self):
            # 处理表头
            # header_list=["ID","书籍名称","出版社"]
            header_list = []
    
            for field in self.config.new_list_display():  # [check,"nid","title","publish",edit,delete]
                if isinstance(field, str):
                    if field == "__str__":
                        val = self.config.model._meta.model_name.upper()
                    else:
                        field_obj = self.config.model._meta.get_field(field)
                        val = field_obj.verbose_name
    
                else:
                    val = field(self.config, is_header=True)  # 获取表头,传is_header=True
    
                header_list.append(val)
    
    
            return header_list
    
        def get_body(self):
    
            # 处理表单数据
            new_data_list = []
            for obj in self.page_data_list:  # data_list [book_obj,book_obj2,...]
    
                temp = []
                for field in self.config.new_list_display():  # ["nid","title","publish","authors"]
                    if isinstance(field, str):
                        try:
                            from django.db.models.fields.related import ManyToManyField
    
                            field_obj = self.config.model._meta.get_field(field)
    
                            if isinstance(field_obj, ManyToManyField):
                                t = []
                                for i in getattr(obj, field).all():
                                    t.append(str(i))
                                val = ",".join(t)
                            else:
                                if field in self.config.list_display_link:
                                    edit_url = self.config.get_edit_url(obj)
                                    val = mark_safe("<a href='%s'>%s</a>" % (edit_url, getattr(obj, field)))
                                else:
                                    val = getattr(obj, field)
    
                        except Exception as e:
                            val = getattr(obj, field)
    
                    else:
                        val = field(self.config, obj)
    
                    temp.append(val)
    
                new_data_list.append(temp)
    
            '''
            new_data_list=[
                ["北京折叠",122,<a href=''>编辑</a>,<a href=''>删除</a>],
                ["三体", 222,<a href=''>编辑</a>,<a href=''>删除</a>],
            ]
            '''
    
            return new_data_list
    
    
    class ModelXadmin(object):
        list_display=["__str__",]
        list_display_link=[]
        search_fields=[]
        actions=[]
        list_filter=[]
    
    
    
        def patch_delete(self,request,queryset):
            queryset.delete()
    
        patch_delete.short_description="批量删除"
    
        def get_actions(self):
            temp=[]
            temp.extend(self.actions)
            temp.append(self.patch_delete)
    
            return temp
    
    
    
        model_form_class=None
    
        def __init__(self,model,site):
    
            self.model=model
            self.site=site
    
            self.model_name=""
            self.app_name=""
    
        # 选择按钮  编辑 删除
        def edit(self, obj=None, is_header=False):
            if is_header:
                return "操作"
            _url=self.get_edit_url(obj)
            return mark_safe("<a href='%s'>编辑</a>" % _url)
    
        def delete(self, obj=None, is_header=False):
    
            if is_header:
                return "操作"
    
            _url=self.get_delete_url(obj)
    
            return mark_safe("<a href='%s'>删除</a>"%_url)
    
        def checkbox(self, obj=None, is_header=False):
            if is_header:
                return "选择"
    
            return mark_safe("<input type='checkbox' name='selected' value='%s'>"%obj.pk)
    
        # 反向解析当前表的增删改查的url
        def get_edit_url(self,obj):
            # 反向解析:url
            url_name = "%s_%s_change" % (self.app_name, self.model_name)
            # http://127.0.0.1:8008/Xadmin/app01/book/(d+)/change
            _url = reverse(url_name, args=(obj.pk,))
            # return mark_safe("<a href='%s/change/'>编辑</a>"%obj.pk)
            return _url
    
        def get_list_url(self):
            # 反向解析:url
            url_name = "%s_%s_list" % (self.app_name, self.model_name)
            _url = reverse(url_name)
            return _url
    
        def get_add_url(self):
            # 反向解析:url
            url_name = "%s_%s_add" % (self.app_name, self.model_name)
            _url = reverse(url_name)
            return _url
    
        def get_delete_url(self,obj):
            # 反向解析:url
            url_name = "%s_%s_delete" % (self.app_name, self.model_name)
            _url = reverse(url_name, args=(obj.pk,))
            return _url
    
        # 构建新的展示列表,默认加入选择按钮  编辑 删除
        def new_list_display(self):
            temp=[]
    
            temp.append(ModelXadmin.checkbox)
            temp.extend(self.list_display)
            if not self.list_display_link:
                 temp.append(ModelXadmin.edit)
            temp.append(ModelXadmin.delete)
    
            return temp
    
    
        def get_search_condition(self,request):
    
            search_condition = Q()
            search_condition.connector = "or"
            print("search_fields", self.search_fields)  # ["title","price"]
    
            key_word = request.GET.get('q')
            if key_word:
                for search_field in self.search_fields:
                    search_condition.children.append((search_field + "__icontains", key_word))
    
    
            return search_condition
    
        # 查看视图函数
        def list_view(self, request):
            """
            self.model: 用户访问哪张表,self.model就是谁
            data_list: 查看表下的数据
            ShowList(self,data_list)  # self: 当前查看表的配置类对象
            :param request:
            :return:
            """
    
    
            if request.method=="POST":# action
                print(request.POST)
                action=request.POST.get("action")
                selected_pk_list=request.POST.getlist("selected")
                queryset=self.model.objects.filter(pk__in=selected_pk_list)
                action=getattr(self,action)
                ret=action(request,queryset)
    
                return ret
    
            search_condition=self.get_search_condition(request)
    
            filter_condition=Q()
    
            for filter_field,val in request.GET.items():
                if  filter_field not in ["page","q"]:
                     filter_condition.children.append((filter_field,val))
    
    
            data_list = self.model.objects.filter(filter_condition).filter(search_condition)
    
            show_list=ShowList(self,request,data_list)
            add_url = self.get_add_url()
            model_name = self.model._meta.model_name
    
            return render(request, 'list_view.html', {"model_name":model_name,"add_url":add_url,"show_list":show_list})
    
        # 获取modelForm类
        def get_model_form_class(self):
    
            if self.model_form_class:
    
                return self.model_form_class
    
            else:
    
                from django.forms import ModelForm
    
                class DemoModelForm(ModelForm):
                    class Meta:
                        model=self.model
                        fields="__all__"
    
    
                return DemoModelForm
    
        # 添加视图函数
        def add_view(self, request):
    
            DemoModelForm = self.get_model_form_class()
            if request.method=="POST":
                form=DemoModelForm(request.POST)
                if form.is_valid():
                    form.save()
                    return redirect(self.get_list_url())
                else:
                    return render(request, 'add_view.html', locals())
    
            form=DemoModelForm
    
            return render(request, 'add_view.html',locals())
    
        # 编辑视图函数
        def change_view(self, request, id):
            edit_obj=self.model.objects.get(pk=id)
            DemoModelForm=self.get_model_form_class()
    
            if request.method=="POST":
                form=DemoModelForm(request.POST,instance=edit_obj)
                if form.is_valid():
                    form.save()
                    return redirect(self.get_list_url())
                else:
                    return render(request, 'change_view.html', locals())
            form=DemoModelForm(instance=edit_obj)
            return render(request, 'change_view.html',locals())
    
        # 删除视图函数
        def delete_view(self, request, id):
            list_url = self.get_list_url()
    
            if request.method=="POST":
                self.model.objects.filter(pk=id).delete()
                return redirect(list_url)
    
    
            class Per():
                def __init__(self):
                    pass
    
    
            return render(request, 'delete_view.html',{"list_url":list_url})
    
    
        def get_urls2(self):
            temp = []
    
            self.app_name =  self.model._meta.app_label  # "app01"
            self.model_name = self.model._meta.model_name  # "book"
            temp.append(url(r"^$", self.list_view,name="%s_%s_list"%(self.app_name,self.model_name)))
            temp.append(url(r"^add/$", self.add_view,name="%s_%s_add"%(self.app_name,self.model_name)))
            temp.append(url(r"^(d+)/change/$", self.change_view,name="%s_%s_change"%(self.app_name,self.model_name)))
            temp.append(url(r"^(d+)/delete/$", self.delete_view,name="%s_%s_delete"%(self.app_name,self.model_name)))
    
            return temp
    
        # 路由二级分发
        @property
        def urls2(self):
            return self.get_urls2(), None, None
    
    
    class XadminSite(object):
    
        def __init__(self, name='admin'):
            self._registry = {}
    
    
        def get_urls(self):
    
            print(self._registry)  # {Book:modelAdmin(Book),.......}
    
            temp = []
            for model, admin_class_obj in self._registry.items():
                # 获取当前循环的model的字符串与所在app的字符串
                app_name = model._meta.app_label     #  "app01"
                model_name = model._meta.model_name  # "book"
    
                temp.append(url(r'^{0}/{1}/'.format(app_name, model_name),admin_class_obj.urls2), )
    
    
                '''
                url(r"app01/book",ModelXadmin(Book,site).urls2)
                url(r"app01/publish",ModelXadmin(Publish,site).urls2)
                url(r"app02/order",ModelXadmin(Order,site).urls2)
                
                '''
            return temp
    
        @property
        def urls(self):
            return self.get_urls(),None,None
    
        def register(self, model, admin_class=None, **options):
            if not admin_class:
                     admin_class = ModelXadmin
    
            self._registry[model] = admin_class(model, self) # {Book:ModelAdmin(Book),Publish:ModelAdmin(Publish)}
    
    site=XadminSite()
    后端逻辑处理
    <!DOCTYPE html>
    <html lang="en">
    <head>
        <meta charset="UTF-8">
        <title>Title</title>
        <link rel="stylesheet" href="/static/bootstrap/css/bootstrap.min.css">
        <link rel="stylesheet" href="/static/css/list_view.css">
    </head>
    <body>
    
    <h3>查看{{ model_name }}数据</h3>
    
    <div class="container">
        <div class="row">
    
            <div class="col-md-9">
                <a href="{{ add_url }}" class="btn btn-info add_btn">添加</a>
                {% if show_list.config.search_fields %}
                  <form action="" class="pull-right" method="get">
                    <input style="display: inline-block; 300px" type="text" name="q" class="form-control"><input type="submit" value="search" class="btn btn-success">
                  </form>
                {% endif %}
    
                <form action="" method="post">
                   {% csrf_token %}
                    <div>
                        <select name="action" id="" class="form-control" style="display: inline-block; 300px">
                            <option value="">---------------</option>
    
                            {% for action_dict in show_list.new_actions %}
                                <option value="{{ action_dict.name }}">{{ action_dict.desc }}</option>
                            {% endfor %}
    
                        </select>
                        <input type="submit" value="Go" class="btn btn-warning">
    
                    </div>
                    <table class="table table-bordered table-striped">
                    <thead>
                        <tr>
                             {% for item in show_list.get_header %}
                             <th>{{ item }}</th>
                             {% endfor %}
    
                        </tr>
                    </thead>
                    <tbody>
                          {% for new_data in show_list.get_body %}
                              <tr>
                                  {% for item in new_data %}
                                     <td>{{ item }}</td>
                                  {% endfor %}
                              </tr>
                          {% endfor %}
    
                    </tbody>
                </table>
    
                </form>
    
                <div class="pull-right"> {{ show_list.pagination.page_html|safe }}</div>
            </div>
    
    
            <div class="col-md-3">
                {% for filter_filed,link_tag_list in show_list.get_filter_link_tags.items %}
                  <p>By {{ filter_filed.upper }}</p>
                  {% for link_tag in link_tag_list %}
                  <p>{{ link_tag }}</p>
                  {% endfor %}
    
                {% endfor %}
    
            </div>
        </div>
    
    </div>
    
    
    
    </body>
    </html>
    前端展示页面
  • 相关阅读:
    mysq,oraclel复杂SQL操作汇总
    mysql查询速度慢的分析和解决
    mysql存储引擎介绍
    mysql数据库函数之left()、right()、substring()、substring_index()
    消息中间件之ActiveMQ
    正则表达式过滤url请求
    http请求常见错误状态码
    Ehcache缓存数据
    eclipse运用经验
    MySQL 触发器trigger
  • 原文地址:https://www.cnblogs.com/zhaopanpan/p/9147079.html
Copyright © 2011-2022 走看看