zoukankan      html  css  js  c++  java
  • Xadmin组件的构建

    详情见:

    Xadmin组件构建之增删改查 

    Xadmin组件构建之分页、search查询与action批量操作

    Xadmin组件构建之filter、pop

    ----------------------------------------------------------------------------------------- 

    相关知识:url分发

    搭建一个类似admin功能的Xadmin组件

        'Xadmin.apps.XadminConfig',
        'app01.apps.App01Config',
        'app02.apps.App02Config',

     

    Xadmin实现流程:

    (1) 启动:在Django执行的这一刻把每一个叫Xadmin.py文件都加载一遍 

    #Xadmin/apps.py
    from django.apps import AppConfig
    from django.utils.module_loading import autodiscover_modules
    
    class XadminConfig(AppConfig):
        name = 'Xadmin'
    
        def ready(self):   #当加载这个类的时候,这个方法自动执行
            autodiscover_modules('Xadmin')  #通过此步设置,Django一启动就是扫描每一个叫Xadmin.py的文件

     (2) 注册、url的设计与构建表单数据 

    urls.py

    from Xadmin.service.Xadmin import site

    urlpatterns = [ url(r'^Xadmin/', site.urls), ]

    Xadmin/service/Xadmin.py 

    from django.conf.urls import url
    from django.shortcuts import HttpResponse, render, redirect
    from django.urls import reverse
    from django.db.models import Q
    from django.utils.safestring import mark_safe
    from django.db.models.fields.related import ManyToManyField,ForeignKey
    
    from Xadmin.utils.page import Pagination
    
    
    # 构建表头数据和构建表单数据本应该放在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
            # 分页
            data_count = self.data_list.count()
            current_page = int(self.request.GET.get("page", 1))
            base_path = self.request.path
    
            self.pagination = Pagination(current_page, data_count, base_path, self.request.GET, per_page_num=4, pager_count=11, )
            self.page_data = self.data_list[self.pagination.start:self.pagination.end]
    
            # actions
            self.actions=self.config.new_actions() # actions里面装的是函数,每个表都有默认的批量删除[patch_delete,]
    
        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)
                print("111",params)#第一次访问book页面时:<QueryDict: {}>
    
                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() #一对一、多对多字段
                    data_list = filter_field_obj.remote_field.model.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
    
    
        #定义一个函数,构建数据结构:temp=[ "name": "patch_init","desc":"批量初始化"]
        def get_action_list(self):
            temp=[]
            for action in self.actions:
               temp.append({
                   "name":action.__name__,          #取函数的名称:   "patch_init"
                   "desc":action.short_description   # 取函数的描述:"批量初始化"
               })
    
            return temp
    
        def get_header(self):
            # 构建表头数据
            header_list = []
            print("header", self.config.new_list_display())  # [check,"nid","title","publish","price",edit,delete]
    
            for field in self.config.new_list_display():
                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)
            print(header_list)  # ["<input id='choice' type='checkbox'>", ' 编号', '书籍名称', 'publish', 'price', '操作', '操作']
            return header_list
    
        def get_body(self):
            # 构建表单数据
            new_data_list = []
            for obj in self.page_data:  # data_list:<QuerySet [<Book: 北京折叠>, <Book: 三体>]>
                temp = []
                for field in self.config.new_list_display():  # ["__str__"] [check,"nid","title","publish","price","authors",edit,delete]
    
                    if isinstance(field, str):  # 判断字段是不是str类型
                        try:#为了捕捉__str__,防止报错
                            field_obj = self.config.model._meta.get_field(field) #拿模型表字段对象
                            # 判断是不是多对多字段(把authors取出来)
                            if isinstance(field_obj, ManyToManyField):
                                ret = getattr(obj, field).all()  #取出所有作者 :<QuerySet [<Author: xiaohei>, <Author: xiaobai>……]>
                                t=[]
                                for mobj in ret:  #千万记住多层循环的时候循环名称不要重复这里是obj和mobj
                                    t.append(str(mobj))
                                val=",".join(t)
                            else:
                                if field_obj.choices:#如果当前字段对象有choices属性
                                    #相当于把((1, '男'), (2, '女'))中的男或女拿出来
                                    val = getattr(obj, "get_"+field+"_display")
                                else:
                                    val = getattr(obj, field)  # 由字符串找对象的属性,相当于obj.field,也就是把'nid',title',''publish',price'拿出来了
                                if field in self.config.list_display_links:
                                    # "app01/userinfo/(d+)/change"
                                    _url = self.config.get_change_url(obj)
    
                                    val = mark_safe("<a href='%s'>%s</a>" % (_url, val))
                        except Exception as e:  # 如果出错误说明是__str__
                            val = getattr(obj, field)
    
                    else:
                        val = field(self.config, obj)  # 如果字段是函数,把当前处理的对象传给当前的函数(这一步为了拿到obj.pk,
                        # 然后拼接路径,比如我们在点击“编辑”的时候跳转到编辑界面,这里就用到了pk),执行后拿到返回结果
    
                    temp.append(val)
    
                new_data_list.append(temp)
    
            '''
            new_data_list=[
                [check,1,北京折叠",苹果出版社,<a href=''>编辑</a>,<a href=''>删除</a>],
                [check,2,"三体", 苹果出版社,<a href=''>编辑</a>,<a href=''>删除</a>],
            ]
            '''
            return new_data_list
    
    
    # 定义每张表的配置类样式
    class ModelXadmin(object):
        list_display = ["__str__", ]  # 走默认的样式类的话默认显示"__str__"内容
        list_display_links = []  # 控制点哪个字段进入编辑界面,如果里面添加'publish'则点击publish就能进入编辑界面
        modelform_class = []
        search_fields = []
        actions = []
        list_filter=[]
    
        # 定制action操作:批量删除--定义到父类中,面对所有的表都有这个默认操作
        def patch_delete(self, request, queryset):
            queryset.delete()
        patch_delete.short_description = "批量删除"
    
    
        def __init__(self, model, site):
    
            self.model = model
            self.site = site  # site为单例对象
    
        # url路径反向解析
        def get_change_url(self, obj):
            model_name = self.model._meta.model_name
            app_label = self.model._meta.app_label
    
            _url = reverse("%s_%s_change" % (app_label, model_name), args=(obj.pk,))
    
            return _url
    
        def get_delete_url(self, obj):
            model_name = self.model._meta.model_name
            app_label = self.model._meta.app_label
    
            _url = reverse("%s_%s_delete" % (app_label, model_name), args=(obj.pk,))
    
            return _url
    
        def get_add_url(self):
    
            model_name = self.model._meta.model_name
            app_label = self.model._meta.app_label
    
            _url = reverse("%s_%s_add" % (app_label, model_name))
    
            return _url
    
        def get_list_url(self):
    
            model_name = self.model._meta.model_name
            app_label = self.model._meta.app_label
    
            _url = reverse("%s_%s_list" % (app_label, model_name))
    
            return _url
    
        # 复选框、删除、编辑,定义在这里是为了在new_list_display函数中进行拼接
        # 在查看每一张表页面时都会出现这三项
        def check(self, obj=None, is_header=False):
            if is_header:
                return mark_safe("<input id='choice' type='checkbox'>")
            # 添加name='selected'_pk value='%s'是为批量操作actions做准备
            return mark_safe("<input class='choice_item' type='checkbox' name='selected_pk' value='%s'>"%obj.pk)
    
        def edit(self, obj=None, is_header=False):
            if is_header:
                return "操作"
            # 方案1:固定url
            # return mark_safe("<a href='/Xadmin/app01/book/%s/change/'>编辑</a>"%obj.pk)  #这样做把路径写死了
    
            # 方案2:拼接url
            # return mark_safe("<a href='%s/change/'>编辑</a>"%obj.pk) #比较safe方法,safe用在母版中的
            # %s前面没加/,当点击编辑的时候,按照当前页面的路径在后面把href里面的路径添加进去,这样做为了不把路径写死
    
            # 方案3:反向解析
    
            _url = self.get_change_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)
    
        #扩展list_display列表,配置每个表默认都有复选框、删除、编辑三个操作,而且固定它们的位置
        def new_list_display(self):
            temp = []
            temp.append(ModelXadmin.check)  # append只能加一个元素
            temp.extend(self.list_display)  # extend扩展一个列表进来
            if not self.list_display_links:
                temp.append(ModelXadmin.edit)
            temp.append(ModelXadmin.delete)
    
            return temp
    
        #扩展actions,配置每个表默认都有批量删除操作
        def new_actions(self):
            temp=[]
            temp.append(ModelXadmin.patch_delete)  #批量删除
            temp.extend(self.actions) #如果用户定制了自己的actions就用用户自定义的,没有就添加其父类ModelXadmin下actions空列表
    
            return temp
    
        # 获取serach的Q对象的函数
        def get_serach_conditon(self, request):
            key_word = request.GET.get("q", "")  # 第一次访问的时候查询框肯定没有值,用""表示默认为空
            self.key_word = key_word  # 这时候ModelXadmin的实例化对象里面就有了一个self.key_word这个值
            # 可以通过showlist.config.key_word获取
    
            search_connection = Q()  # Q查询:通过字符串查询
            if key_word:
                # self.search_fields    #["title","price"]
                search_connection.connector = "or"  # 通过这个参数可以将Q对象默认的and关系变成or
                for search_field in self.search_fields:
                    search_connection.children.append((search_field + "__contains", key_word))  # 模糊查询
            return search_connection
    
        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就是谁
            print("self.model", self.model)  # 用户访问的是哪张表,self.model就是哪张表,这就是urls跨类定义最大的意义所在
            # 这里首先要弄清楚self是什么,我们一层一层的找,self->list_view->get_urls2->urls2->admin_class_obj,
            # 所以这里self就相当于admin_class_obj,这里就要搞清楚admin_class_obj是默认样式类的实例化对象还是自己定义样式类的实例化对象
            # 以访问Book表为例(这里Book表使用自定义的样式类,以下都是以访问Book表为例),
            # 上面打印内容为,self.model <class 'app01.models.Book'>
            model_name = self.model._meta.model_name  # 获取表名
    
            if request.method == "POST":  # action
                print("POST:", request.POST)
                #POST: <QueryDict: {'csrfmiddlewaretoken': ['7eY8H0ZW1TS80cB9FPzgPX4AdQayMPqMQoSC0m4fq0dUhctGFyhWWux8Ubqza9i5'],
                                  # 'action': ['patch_init'], 'selected_pk': ['1', '5']}>
                action = request.POST.get("action")  # 'action': ['patch_init']
                selected_pk = request.POST.getlist("selected_pk")  #传过来的是一个列表,用getlist取值 'selected_pk': ['1', '5']
                action_func = getattr(self, action) #反射,相当于在自己所在的类(BookConfig)下找patch_init方法,自己类没有去父类找
                queryset = self.model.objects.filter(pk__in=selected_pk)  #查询当前选中的复选框对象 <QuerySet [<Book: 北京折叠>, <Book: 蓦然回首>]>
                ret = action_func(request, queryset)
    
                # return ret
    
            # 获取serach的Q对象
            search_connection = self.get_serach_conditon(request)
    
            # 获取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: 北京折叠>, …… ]>
            print("list_display:", self.list_display)  # self.list_display为列表中定义的哪几列, ['nid', 'title', 'publish', 'price']
    
            # 按照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())
    
        # 增加视图
        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())
    
        # 获取modelform类:
        def get_modelform_class(self):
            # 如果用户没有定制,就使用默认配置的ModelFormDemo
            if not self.modelform_class:
                from django.forms import ModelForm
                from django.forms import widgets as wid
                class ModelFormDemo(ModelForm):
                    class Meta:
                        model = self.model
                        fields = "__all__"
    
                return ModelFormDemo
            # 如果用户定制了自己的modelform就用用户自己的(app01/Xadmin.py)
            else:
                return self.modelform_class
    
        # 编辑视图
        def change_view(self, request, id):
            model_name = self.model._meta.model_name  # 获取表名
            ModelFormDemo = self.get_modelform_class()
            edit_obj = self.model.objects.filter(pk=id).first()  # 取出要编辑的对象
    
            if request.method == "POST":
                form = ModelFormDemo(request.POST, instance=edit_obj)  # 更新当前修改的数据
                if form.is_valid():
                    form.save()
                    return redirect(self.get_list_url())
                return render(request, 'add_view.html', locals())
    
            form = ModelFormDemo(instance=edit_obj)  # 传给instance,此时编辑页面就有值了
            return render(request, 'change_view.html', locals())
    
        # 删除视图
        def delete_view(self, request, id):
            model_name = self.model._meta.model_name  # 获取表名
            url = self.get_list_url()
            if request.method == "POST":
                self.model.objects.filter(pk=id).delete()
                return redirect(self.get_list_url())
    
            return render(request, "delete_view.html", locals())
    
        #默认的扩展url为空,用户可以自定义
        def extra_url(self):
            return []
    
        def get_urls2(self):
            temp = []
    
            app_label = self.model._meta.app_label
            model_name = self.model._meta.model_name
            # 以上取app名称和model名称是为了给以下路径起别名(因为每一个app下的model名都是不一样的),用于反向解析
            temp.append(url(r"^$", self.list_view, name="%s_%s_list" % (app_label, model_name)))
            temp.append(url(r"^add/$", self.add_view, name="%s_%s_add" % (app_label, model_name)))
            temp.append(url(r"^(d+)/change/$", self.change_view, name="%s_%s_change" % (app_label, model_name)))
            temp.append(url(r"^(d+)/delete/$", self.delete_view, name="%s_%s_delete" % (app_label, model_name)))
    
            # 用户自定义扩展的url
            temp.extend(self.extra_url())
    
            return temp
    
        @property
        def urls2(self):  # urls2跨类定义
            return self.get_urls2(), None, None
    
    
    # 定义一个全局类,用对创建单例对象  在这个类中设计了url以及完成了注册操作
    class XadminSite(object):
    
        def __init__(self, name='admin'):
            self._registry = {}
    
        def get_urls(self):
    
            print(self._registry)  # {Book:modelAdmin(Book),.......}
    
            temp = []
            # 循环在Django一启动时就执行
            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()  # 创建单例对象 

    app01/Xadmin.py

    from Xadmin.service.Xadmin import site,ModelXadmin
    from django.shortcuts import HttpResponse
    
    from django.forms import ModelForm
    from django.forms import widgets as wid
    
    from app01.models import *
    
    #为book表定制modelform
    class BookModelForm(ModelForm):
        class Meta:
            model = Book
            fields = "__all__"
    
            labels={
                "title":"书籍名称",
                "price":"价格",
                "publishDate":"出版日期"
            }  #编辑页面展示效果
            # widgets={  #应用这个然后页面就会显示样式,把浏览器的样式复制到母版中使用
            #     "title":wid.TextInput(attrs={"class":"form-control"})
            # }
    
    #定义我们自己的样式
    class BookConfig(ModelXadmin):
        list_display=["nid","title","publish","price","authors"] #定义查看页面有哪些字段
        list_display_links = ["title"]                 #定义点击哪个字段进入编辑页面
        modelform_class=BookModelForm                  #定义编辑页面使用我们自定义的
        search_fields=["title","price"]                #定义搜索框按照这两个字段进行筛选
    
    
        # 定制action操作
        def patch_init(self, request, queryset):
            print(queryset)
            # queryset就是我们选中的数据,这里把我们选中的数据全部更新为123
            queryset.update(price=123)
            return HttpResponse("批量初始化OK")
    
        # 为我们自定义的函数加上中文描述的属性
        patch_init.short_description = "批量初始化"
        # 最后把函数放入actions列表中
        actions = [patch_init]
    
        list_filter=["title","publish","authors",]
    
    site.register(Book,BookConfig)
    site.register(Publish)
    site.register(Author)
    site.register(AuthorDetail)

    app02/Xadmin.py

    from Xadmin.service.Xadmin import site,ModelXadmin
    from app02.models import *
    
    site.register(Order)
    
    class FoodConfig(ModelXadmin):
        list_display = ["id","title"]
    site.register(Food,FoodConfig)
    
    print("_registry",site._registry) #6个表 

    app01/models.py 

    from django.db import models
    
    class Author(models.Model):
        nid = models.AutoField(primary_key=True)
        name=models.CharField( max_length=32)
        age=models.IntegerField()
    
        # 与AuthorDetail建立一对一的关系
        authorDetail=models.OneToOneField(to="AuthorDetail",on_delete=models.CASCADE)
        def __str__(self):
            return self.name
    
    class AuthorDetail(models.Model):
    
        nid = models.AutoField(primary_key=True)
        # birthday=models.DateField()
        telephone=models.BigIntegerField()
        addr=models.CharField( max_length=64)
    
        def __str__(self):
            return str(self.telephone)
    
    class Publish(models.Model):
        nid = models.AutoField(primary_key=True)
        name=models.CharField( max_length=32)
        city=models.CharField( max_length=32)
        email=models.EmailField()
    
        def __str__(self):
            return self.name
    
    class Book(models.Model):
    
        nid = models.AutoField(primary_key=True,verbose_name=" 编号")  #定义在Xadmin查看页面中显示中文名称“编号”
        title = models.CharField( max_length=32)
        publishDate=models.DateField()
        price=models.DecimalField(max_digits=5,decimal_places=2)
    
        # 与Publish建立一对多的关系,外键字段建立在多的一方
        publish=models.ForeignKey(to="Publish",to_field="nid",on_delete=models.CASCADE)
        # 与Author表建立多对多的关系,ManyToManyField可以建在两个模型中的任意一个,自动创建第三张表
        authors=models.ManyToManyField(to='Author',)
    
        def __str__(self):
            return self.title 

    app02/models.py

    from django.db import models
    
    class Order(models.Model):
        title=models.CharField(max_length=32)
        def __str__(self):
            return self.title
    
    class Food(models.Model):
        title = models.CharField(max_length=32)
    
        def __str__(self):
            return self.title

    list_view.html 

    <!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=" 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> 

    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 %}
                           <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>

    add_view.html

    <!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>
            {#下面样式从浏览器复制而来,页面的标签都是input/select标签,让它们都有下面的样式#}
            input, select {
                display: block;
                width: 100%;
                height: 34px;
                padding: 6px 12px;
                font-size: 14px;
                line-height: 1.42857143;
                color: #555;
                background-color: #fff;
                background-image: none;
                border: 1px solid #ccc;
                border-radius: 4px;
                -webkit-box-shadow: inset 0 1px 1px rgba(0, 0, 0, .075);
                box-shadow: inset 0 1px 1px rgba(0, 0, 0, .075);
                -webkit-transition: border-color ease-in-out .15s, -webkit-box-shadow ease-in-out .15s;
                -o-transition: border-color ease-in-out .15s, box-shadow ease-in-out .15s;
                transition: border-color ease-in-out .15s, box-shadow ease-in-out .15s;
            }
            .error{
                color:red;
            }
    
        </style>
    </head>
    <body>
    <h3>添加{{ model_name }}数据</h3>
    
    {% include 'form.html' %}
    
    <script>
        function pop_response(pk,text,id) {
    
            console.log(pk,text,id);    //23 hui id_publish
    
            //选择哪一个select标签
            //option的文本值和value值
    
            var $option=$('<option>');     //创建一个option空标签<option></option>
            $option.html(text);   // 为标签加文本<option>text</option>
            $option.val(pk);             //<option value=pk>text</option>
            $option.attr("selected","selected") ;   //  <option value=111>text</option>
            $("#"+id).append($option)            //把构建的标签放到id=id标签中
    
        }
    </script>
    
    </body>
    </html>
    <!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>
            {#下面样式从浏览器复制而来,页面的标签都是input/select标签,让它们都有下面的样式#}
            input, select {
                display: block;
                width: 100%;
                height: 34px;
                padding: 6px 12px;
                font-size: 14px;
                line-height: 1.42857143;
                color: #555;
                background-color: #fff;
                background-image: none;
                border: 1px solid #ccc;
                border-radius: 4px;
                -webkit-box-shadow: inset 0 1px 1px rgba(0, 0, 0, .075);
                box-shadow: inset 0 1px 1px rgba(0, 0, 0, .075);
                -webkit-transition: border-color ease-in-out .15s, -webkit-box-shadow ease-in-out .15s;
                -o-transition: border-color ease-in-out .15s, box-shadow ease-in-out .15s;
                transition: border-color ease-in-out .15s, box-shadow ease-in-out .15s;
            }
            .error{
                color:red;
            }
    
        </style>
    </head>
    <body>
    <h3>编辑{{ model_name }}数据</h3>
    
    {% include 'form.html' %}
    </body>
    </html>
    change_view.html

    delete_view.py

    <!DOCTYPE html>
    <html lang="en">
    <head>
        <meta charset="UTF-8">
        <title>Title</title>
    </head>
    <body>
    <h3>删除{{ model_name }}数据</h3>
    
    <form action="" method="post">
        {% csrf_token %}
        <button>确认删除?</button>
        <a href="{{ url }}">取消</a>
    </form>
    
    </body>
    </html>

    pop.html

    <!DOCTYPE html>
    <html lang="zh-CN">
    <head>
        <meta charset="UTF-8">
        <title>Title</title>
        <meta name="viewport" content="width=device-width, initial-scale=1">
    </head>
    <body>
    
    <script>
    
        window.opener.pop_response('{{ res.pk }}',"{{ res.text }}",'{{ res.pop_res_id }}')
        window.close()
    </script>
    
    </body>
    </html> 
    """
    自定义分页组件,可以保存搜索条件
    
    """
    
    class Pagination(object):
        def __init__(self, current_page, all_count, base_url, params, per_page_num=8, pager_count=11, ):
            """
            封装分页相关数据
            :param current_page: 当前页
            :param all_count:    数据库中的数据总条数
            :param per_page_num: 每页显示的数据条数
            :param base_url: 分页中显示的URL前缀
            :param pager_count:  最多显示的页码个数
            """
            try:
                current_page = int(current_page)
            except Exception as e:
                current_page = 1
    
            if current_page < 1:
                current_page = 1
    
            self.current_page = current_page
    
            self.all_count = all_count
            self.per_page_num = per_page_num
    
            self.base_url = base_url
    
            # 总页码
            all_pager, tmp = divmod(all_count, per_page_num)
            if tmp:
                all_pager += 1
            self.all_pager = all_pager
    
            self.pager_count = pager_count  # 最多显示页码数
            self.pager_count_half = int((pager_count - 1) / 2)
    
            import copy
            params = copy.deepcopy(params)
            params._mutable = True
            self.params = params  # self.params : {"page":77,"title":"python","nid":1}
    
        @property
        def start(self):
            return (self.current_page - 1) * self.per_page_num
    
        @property
        def end(self):
            return self.current_page * self.per_page_num
    
        def page_html(self):
            # 如果总页码 < 11个:
            if self.all_pager <= self.pager_count:
                pager_start = 1
                pager_end = self.all_pager + 1
            # 总页码  > 11
            else:
                # 当前页如果<=页面上最多显示(11-1)/2个页码
                if self.current_page <= self.pager_count_half:
                    pager_start = 1
                    pager_end = self.pager_count + 1
    
                # 当前页大于5
                else:
                    # 页码翻到最后
                    if (self.current_page + self.pager_count_half) > self.all_pager:
                        pager_start = self.all_pager - self.pager_count + 1
                        pager_end = self.all_pager + 1
    
                    else:
                        pager_start = self.current_page - self.pager_count_half
                        pager_end = self.current_page + self.pager_count_half + 1
    
            page_html_list = []
            self.params["page"] = 1
            first_page = '<li><a href="%s?%s">首页</a></li>' % (self.base_url, self.params.urlencode(),)
            page_html_list.append(first_page)
    
            if self.current_page <= 1:
                prev_page = '<li class="disabled"><a href="#">上一页</a></li>'
            else:
                self.params["page"] = self.current_page - 1
                prev_page = '<li><a href="%s?%s">上一页</a></li>' % (self.base_url, self.params.urlencode(),)
    
            page_html_list.append(prev_page)
    
            # urlencode可以把key-value这样的键值对转换成我们想要的格式,返回的是a=1&b=2这样的字符串
    
            for i in range(pager_start, pager_end):
                #  self.params  : {"page":77,"title":"python","nid":1}
    
                self.params["page"] = i  # {"page":72,"title":"python","nid":1}
                if i == self.current_page:
                    temp = '<li class="active"><a href="%s?%s">%s</a></li>' % (self.base_url, self.params.urlencode(), i,)
                else:
                    temp = '<li><a href="%s?%s">%s</a></li>' % (self.base_url, self.params.urlencode(), i,)
                page_html_list.append(temp)
    
            if self.current_page >= self.all_pager:
                next_page = '<li class="disabled"><a href="#">下一页</a></li>'
            else:
                self.params["page"] = self.current_page + 1
                next_page = '<li><a href="%s?%s">下一页</a></li>' % (self.base_url, self.params.urlencode(),)
            page_html_list.append(next_page)
    
            self.params["page"] = self.all_pager
            last_page = '<li><a href="%s?%s">尾页</a></li>' % (self.base_url, self.params.urlencode(),)
            page_html_list.append(last_page)
    
            return ''.join(page_html_list)
    
    # class Pagination(object):
    #
    #     def __init__(self, data_num, current_page, url_prefix,params, per_page=10, max_show=3):
    #         """
    #         进行初始化.
    #         :param data_num: 数据总数
    #         :param current_page: 当前页
    #         :param url_prefix: 生成的页码的链接前缀
    #         :param per_page: 每页显示多少条数据
    #         :param max_show: 页面最多显示多少个页码
    #         """
    #         self.data_num = data_num
    #         self.per_page = per_page
    #         self.max_show = max_show
    #         self.url_prefix = url_prefix
    #
    #         # 计算出总页码数
    #         #divmod() 函数把除数和余数运算结果结合起来,返回一个包含商和余数的元组(a // b, a % b)。
    #         self.page_num, more = divmod(data_num, per_page)
    #         if more:
    #             self.page_num += 1
    #
    #         #对访问的当前页进行监控
    #         try:
    #             self.current_page = current_page
    #         except Exception as e:
    #             # 取不到或者页码数不是数字都默认展示第1页
    #             self.current_page = 1
    #             # 如果URL传过来的页码数是负数
    #         if self.current_page <= 0:
    #             self.current_page = 1
    #             # 如果URL传过来的页码数超过了最大页码数
    #         elif self.current_page > self.page_num:
    #             self.current_page = self.page_num  # 默认展示最后一页
    #
    #         # 页码数的一半 算出来
    #         self.half_show = max_show // 2
    #
    #         # 页码最左边显示多少
    #         if self.current_page - self.half_show <= 1: # 如果左边越界
    #             self.page_start = 1
    #             self.page_end = self.max_show
    #         elif self.current_page + self.half_show >= self.page_num:  # 如果右边越界
    #             self.page_end = self.page_num
    #             self.page_start = self.page_num - self.max_show
    #         else:
    #             #页面上展示的页码从哪儿开始
    #             self.page_start = self.current_page - self.half_show
    #             # 页面上展示的页码从哪儿结束
    #             self.page_end = self.current_page + self.half_show
    #
    #         import copy
    #         self.params=copy.deepcopy(params) # {'page': ['10'], 'name': ['xiaohei']}
    #
    #     @property
    #     def start(self):
    #         # 数据从哪儿开始切
    #         return (self.current_page - 1) * self.per_page
    #
    #     @property
    #     def end(self):
    #         # 数据切片切到哪儿
    #         return self.current_page * self.per_page
    #
    #     #自己拼接分页的HTML代码
    #     def page_html(self):
    #         # 生成页码
    #         l = []
    #         # 加一个首页
    #         l.append('<li><a href="{}?page=1">首页</a></li>'.format(self.url_prefix))
    #         # 加一个上一页
    #         if self.current_page == 1:
    #             l.append('<li class="disabled" ><a href="#">«</a></li>'.format(self.current_page))
    #         else:
    #             l.append('<li><a href="{}?page={}">«</a></li>'.format(self.url_prefix, self.current_page - 1))
    #
    #
    #         # {'page': ['10'], 'name': ['xiaohei']}
    #
    #         # urlencode可以把key-value这样的键值对转换成我们想要的格式,返回的是a=1&b=2这样的字符串
    #         print(self.params.urlencode())   # page=10&name=xiaohei
    #
    #         #控制页面上展示哪些页码(当前页码-最多展示页码的一半,当前页码+最多展示页码的一半)
    #         for i in range(self.page_start, self.page_end + 1):
    #             self.params["page"]=i # {'page': ['10'], 'name': ['xiaohei']}
    #             if i == self.current_page:
    #                 tmp = '<li class="active"><a href="{0}?page={1}">{1}</a></li>'.format(self.url_prefix, i)
    #             else:
    #                 tmp = '<li><a href="{0}?{1}">{2}</a></li>'.format(self.url_prefix, self.params.urlencode(),i)
    #             l.append(tmp)
    #
    #
    #         # 加一个下一页
    #         if self.current_page == self.page_num:
    #             l.append('<li class="disabled"><a href="#">»</a></li>'.format(self.current_page))
    #         else:
    #             l.append('<li><a href="{}?page={}">»</a></li>'.format(self.url_prefix, self.current_page + 1))
    #         # 加一个尾页
    #         l.append('<li><a href="{}?page={}">尾页</a></li>'.format(self.url_prefix, self.page_num))
    #         return "".join(l)
    Xadminutilspage.py

     Book表

     

     publihs表

      

     页面效果图:

       

  • 相关阅读:
    奔跑的绵羊js
    13.差分
    12.子矩阵的和
    11.前缀和
    10.高精度除法
    9.高精度乘法
    8.高精度减法
    7.高精度加法
    6.数的三次方根
    5.数的范围
  • 原文地址:https://www.cnblogs.com/zh-xiaoyuan/p/12872010.html
Copyright © 2011-2022 走看看