Django-CRM项目学习(二)-模仿admin实现stark

开始今日份整理html

1.stark模块基本操做

1.1 stark模块的启动

保证django自动的加载每个app下的stark.py文件前端

  • 建立django项目,建立stark项目,start app stark
  • settings注册app
  • stark.app中的apps.py建立def(函数名必须是ready才会自动执行)
from django.utils.module_loading import autodiscover_modules

class StarkConfig(AppConfig):
    name = 'stark'
    def ready(self):
        autodiscover_modules('stark')

image

1.2 stark模块的注册

所谓注册就是仿照admin模块,对于注册的数据表进行记录,方便后面的url的增删改查java

仿照admin在stark下建立一个包services,并建立一个sites.py文件,代码以下python

from django.contrib import admin
from django.urls import path
from django.shortcuts import render,HttpResponse

class ModelStark(object):

    list_display =("__str__")

    def __init__(self,model):
        self.model = model

    def list_view(self, request):

        return render(request,'stark/list_view.html',locals())


    def add_view(self, request):
        return HttpResponse("add_view")

    def change_view(self, request, id):
        return HttpResponse("change_view")

    def delete_view(self, request, id):
        return HttpResponse("delete_view")

    @property
    def get_url(self,):
        temp = [
            path("", self.list_view),
            path("add/",self. add_view),
            path("(\d+)/change/", self.change_view),
            path("(\d+)/delete/", self.delete_view),
        ]
        return (temp, None, None)



class StarkSite:
    def __init__(self):
        self._registry ={}

    def register(self,model,admin_class=None,**options):
        admin_class = admin_class or ModelStark
        self._registry[model]=admin_class(model)

    def get_urls(self):
        temp =[]

        # 拿到已经注册的全部表
        for model,config_obj in self._registry.items():
            # 表名
            model_name = model._meta.model_name
            # 项目名
            model_label = model._meta.app_label

            temp.append(
                path("%s/%s/"%(model_label,model_name),config_obj.get_url)
            )
        return temp


    @property
    def urls(self):
        return self.get_urls(),None,None


site = StarkSite()

在app01中建立stark.py文件,并注册数据库

from stark.services.sites import site,ModelStark
from .models import Book,Publish,Author,Author_detail

# 分别注册书籍,出版社以及做者
site.register(Book)
site.register(Publish)
site.register(Author)

print(site._registry)

打印注册的列表,结果以下django

{<class 'app01.models.Publish'>: <stark.services.sites.ModelStark object at 0x04B66B50>, <class 'app01.models.Book'>: <stark.services.sites.ModelStark object at 0x04B669B0>, <class 'app01.models.Author'>: <stark.services.sites.ModelStark object at 0x04B66970>}

这样就注册成功了vim

1.3 URL的分发功能以及页面样式理解(很是重要)

为了自定义的URL。因此咱们才会有自定义页面,才会有配置类。app

(1)在site中StarkSite类中建立一个URLS(self)方法,用@property方式,静态方法ide

image

(2)将二级分发功能放在配置类模块中函数

image

(3)配置类中self以及self.model的区别(超级重要)

self:是配置类对象

image

self.model:数据表对象,其实就是数据表的数据

经过上面:便可理解为何在注册的时候有一个空字典,在每个表对象进行注册时,对每个表生成对应的配置类对象,若是一个表对象有本身的自定义样式,则会走本身自定义样式,无则会走默认样式。

这样就基本实现了url的分发功能,有一级也有二级分发。这块内容就是理解就会以为东西少,不理解则东西好多!,只须要记住

self是配置类,self.model就是数据表对象就能够了。

对于默认类,self为默认配置类,其中self.model为传入的表对象,展现则使用默认类中的样式。

对于自定义类,self为自定义类,其中self.model为传入的表对象,自定义类继承默认类,优先使用自定义定义的类方法。

以下图展现

TIM图片20190415193021TIM图片20190415193031

from django.contrib import admin
from django.urls import path
from django.shortcuts import render,HttpResponse

class ModelStark(object):

    list_display =("__str__")

    def __init__(self,model):
        self.model = model

    def list_view(self, request):

        return render(request,'stark/list_view.html',locals())


    def add_view(self, request):
        return HttpResponse("add_view")

    def change_view(self, request, id):
        return HttpResponse("change_view")

    def delete_view(self, request, id):
        return HttpResponse("delete_view")

    @property
    def get_url(self,):
        temp = [
            path("", self.list_view),
            path("add/",self. add_view),
            path("(\d+)/change/", self.change_view),
            path("(\d+)/delete/", self.delete_view),
        ]
        return (temp, None, None)


class StarkSite:
    def __init__(self):
        self._registry ={}

    def register(self,model,admin_class=None,**options):
        admin_class = admin_class or ModelStark
        self._registry[model]=admin_class(model)

    def get_urls(self):
        temp =[]

        # 拿到已经注册的全部表
        for model,config_obj in self._registry.items():
            # 表名
            model_name = model._meta.model_name
            # 项目名
            model_label = model._meta.app_label
            temp.append(
                path("%s/%s/"%(model_label,model_name),config_obj.get_url)
            )
        return temp
    @property
    def urls(self):
        return self.get_urls(),None,None


site = StarkSite()
stark核心代码

访问顺序

path("stark/app01/book",BookConfig(Book).list_view)
path("stark/app01/book/add",BookConfig(Book).add_view)      
path("stark/app01/publish",ModelAdmin(Publish).list_view)
path("stark/app01/publish/add",ModelAdmin(Publish).add_view)

2.stark基础页面

2.1 stark的数据展现

2.1.1 普通数据与一对多数据展现

2.1.1.1 基于自定义类的数据展现

普通数据就是相似于数据库中book表中的title,price,出版日期,做者等基础数据,django自然对一对多支持

#目标数据类型
        new_data=[
            ["python",123],
            ["java",234]
        ]

视图层以下

def list_view(self, request):
    """
    self:当前配置类
    self.model:当前访问的表数据
    :param request:
    :return:
    """
    # 当前要展现的数据表的数据
    querset = self.model.objects.all()
    print(querset)

    print(self.list_display)

    # 用于构建视图中的展现数据
    new_data = []
    for obj in querset:
        temp = []
        # 构建列表嵌套中的小列表,使用的是自定义配置列表中要展现的内容
        for field_or_info in self.list_display:
            vim = getattr(obj,field_or_info)
            temp.append(vim)

        new_data.append(temp)
    print('new_data', new_data)


    return render(request,'stark/list_view.html',locals())

模板层以下

image

对于Book表,是自定义展现配置类,展现以下图

image

2.1.1.2 基于默认类的数据展现

视图层以及模板层如上,不过须要注意的是

image

没有加逗号,django会默认认为是一个字符串,并不会以一个元祖来读取元素,最后致使报错

对于出版社展现以下,只会展现对象名,显示名称是因为模型类中有__str__方法

image

2.1.2 多对多数据的判断

在app01中添加展现列表中添加做者这个多对多字段

2.1.2.1 错误显示

添加多对多字段

image

模板展现

image

最后展现中,做者多对多数据显示为none,

2.1.2.2 多对多数据判断以及自定义错误报错

(1)知识补充:

使用名字访问一个model的实例:模型名._meta.get_field("publish")

展现模型类的的名字:模型名._meta.model_name

展现模型类对应的项目名称:模型名._meta.app_label

展现一个模型类对象的默认名称:模型对象.verbose_name

对于模型类中的参数,若是设置了verbose.name则会显示设置的名字,无则显示参数名称

(2)多对多的判断

对于多对多的判断,首先是导包,对于 list_display来讲,普通属性从self.list_display拿到的是字符串,一对多和多对多则是拿到的对象,拿到对象的类型判断是不是多对多,若是是多对多则报错并报错

image

对于多对多来讲,须要提示为多对多,须要使用自定义列,弹出错误,用于提示错误

image

2.1.3 模型类中choice数据的反射

数据的反射,例如book的出版状态只有已出版以及未出版,但在数据库中记录只有1和2,须要对1和2的内容取出并反射出具体的内容

注:这里主要使用的是”get_属性名_display方法”,这样最后在页面中展现就会变成以及出版或者是未出版,不在是数据库中1和2

2.1.4 模型类属性中的__str__处理方法

field_obj = self.model._meta.get_field(field_or_func) #获取模型对象
这句话,获取的是模型类中的方法,因为模型类中没有__str__方法,因此须要对其进行处理

2.1.5 自定义的展现多对多内容

2.1.5.1 APP内对stark进行自定义列设定

自定义函数,例如show_author,而后把函数名丢到list_display列表中

stites中获取返回值,而后加入到列表中传到前端

注:因为list_display中有字符串也有函数,因此须要用到callable来判断是都为函数名

这里的self 是类函数调用的方式.,由于原来类中须要穿参,如今增长一个参数而已,什么都行.可是最好是self

2.1.5.2 处理多对多内容

在callable 内容中传入obj对象,方便操做数据

在自定制配置类中,经过obj 获取对应的做者信息

2.2 表头数据的展现

2.2.1 默认配置类

非定制列 ,即只有 __str__,只须要返回到数据表名的大写便可

2.2.2 自定制配置类

若是是普通属性,则只须要丢到对应的head_list列表中便可,对于自定义列,传入为函数名的时候则须要对传入作判断

app中注册类书写

2.3 添加选择框,编辑以及删除到全部的展现页面中

2.3.1 在html中展现指定的内容

导入mark_safe包

from django.utils.safestring import mark_safe

能够是后台传入的标签内容不会被转化,直接成为前端代码

2.3.2 反向解析

导入包

from django.urls import reverse

定义类名以及表名

def __init__(self,model):

        self.model = model
        self.model_name = self.model._meta.model_name
        self.app_label = self.model._meta.app_label

视图层反向解析,设置name

@property
    def get_url(self,):
        # temp = [
        #     path("", self.list_view),
        #     path("add/",self. add_view),
        #     re_path("(\d+)/change/", self.change_view),
        #     re_path("(\d+)/delete/", self.delete_view),
        # ]
        temp = [
            path("", self.list_view, name="%s_%s_list" % (self.app_label, self.model_name)),
            path("add/", self.add_view, name="%s_%s_add" % (self.app_label, self.model_name)),
            re_path("(\d+)/change/", self.change_view, name="%s_%s_change" % (self.app_label, self.model_name)),
            re_path("(\d+)/delete/", self.delete_view, name="%s_%s_delete" % (self.app_label, self.model_name)),
        ]
        return (temp, None, None)

反向解析代码

# 反向解析当前访问表的增删改查URL
    def get_list_url(self):
        # 反向解析当前表的查询的URL
        list_url = reverse("%s_%s_list" % (self.app_label, self.model_name))
        return list_url

    def get_add_url(self, obj):
        # 反向解析当前表的添加的URL
        add_url = reverse("%s_%s_delete" % (self.app_label, self.model_name))
        return add_url

    def get_delete_url(self, obj):
        # 反向解析当前表的删除的URL
        delete_url = reverse("%s_%s_delete" % (self.app_label, self.model_name), args=(obj.pk,))
        return delete_url

    def get_change_url(self, obj):
        # 反向解析当前表的修改的URL
        change_url = reverse("%s_%s_change" % (self.app_label, self.model_name), args=(obj.pk,))
        return change_url

分析:反向解析名字为app名加表名,利用的是无名分组,注意无名分组为元祖传参,最后是三个默认列代码

# 三个默认列
    # 选择框
    def show_checkbox(self, obj=None, heade=False):
        if heade:
            return mark_safe("<input type='checkbox'>")
        return mark_safe("<input type='checkbox'>")

        # 删除框

    def show_delbtn(self, obj=None, heade=False):
        if heade:
            return '删除'
        # return mark_safe("<a href='stark/app01/book/%s/delete'>删除</a>" % obj.pk)
        return mark_safe("<a href='%s'>删除</a>" % self.get_delete_url(obj))
        # 编辑框

    def show_editbtn(self, obj=None, heade=False):
        if heade:
            return '编辑'
        # return mark_safe("<a href='stark/app01/book/%s/change'>编辑</a>" % obj.pk)
        return mark_safe("<a href='%s'>编辑</a>" % self.get_change_url(obj))

####同时构建新的list_display,若是须要在默认列表中都展现,须要设定新的list_display

    #  构建新的list_display

    def get_new_list_display(self):
        temp = []
        temp.extend(self.list_display)
        temp.append(ModelStark.show_editbtn)
        temp.append(ModelStark.show_delbtn)
        temp.insert(0, ModelStark.show_checkbox)

        return temp

属性说明:

  • self 为当前操做的模型配置类
  • obj=None 让默认对象的值为None,即当获取表头的时候不用传值
  • header=False  让默认的header 为False ,使调用数据的时候不用传值,不返回表头,只返回数据

获取表头中,是header =true 这样能够获取表头数据内容

完整site代码以下

from django.contrib import admin
from django.urls import path,re_path
from django.shortcuts import render,HttpResponse
from django.utils.safestring import mark_safe
from django.urls import reverse

class ModelStark(object):

    list_display =("__str__",)

    def __init__(self,model):

        self.model = model
        self.model_name = self.model._meta.model_name
        self.app_label = self.model._meta.app_label

    # 反向解析当前访问表的增删改查URL
    def get_list_url(self):
        # 反向解析当前表的查询的URL
        list_url = reverse("%s_%s_list" % (self.app_label, self.model_name))
        return list_url

    def get_add_url(self, obj):
        # 反向解析当前表的添加的URL
        add_url = reverse("%s_%s_delete" % (self.app_label, self.model_name))
        return add_url

    def get_delete_url(self, obj):
        # 反向解析当前表的删除的URL
        delete_url = reverse("%s_%s_delete" % (self.app_label, self.model_name), args=(obj.pk,))
        return delete_url

    def get_change_url(self, obj):
        # 反向解析当前表的修改的URL
        change_url = reverse("%s_%s_change" % (self.app_label, self.model_name), args=(obj.pk,))
        return change_url

    # 三个默认列
    # 选择框
    def show_checkbox(self, obj=None, heade=False):
        if heade:
            return mark_safe("<input type='checkbox'>")
        return mark_safe("<input type='checkbox'>")

        # 删除框

    def show_delbtn(self, obj=None, heade=False):
        if heade:
            return '删除'
        # return mark_safe("<a href='stark/app01/book/%s/delete'>删除</a>" % obj.pk)
        return mark_safe("<a href='%s'>删除</a>" % self.get_delete_url(obj))
        # 编辑框

    def show_editbtn(self, obj=None, heade=False):
        if heade:
            return '编辑'
        # return mark_safe("<a href='stark/app01/book/%s/change'>编辑</a>" % obj.pk)
        return mark_safe("<a href='%s'>编辑</a>" % self.get_change_url(obj))
    #  构建新的list_display

    def get_new_list_display(self):
        temp = []
        temp.extend(self.list_display)
        temp.append(ModelStark.show_editbtn)
        temp.append(ModelStark.show_delbtn)
        temp.insert(0, ModelStark.show_checkbox)

        return temp

    # 视图函数
    def list_view(self, request):
        """
        self:当前配置类
        selfmodel:当前访问的表数据
        :param request:
        :return:
        """
        # 当前访问表的数据
        querset = self.model.objects.all()
        print(querset)

        print(self.list_display)

        #用于展现头部文件
        header_list=[]
        for field_or_info in self.get_new_list_display():
            #判断是函数名或者是字符段
            if callable(field_or_info):
                vim=field_or_info(self,heade=True)
                header_list.append(vim)
            else:
                # 获取指定字段的对象属性,并拿出verbose_name属性
                if field_or_info=='__str__':
                    #若是只有默认装饰类,只有__str__,则拿出他的表名做为头
                    vim = self.model._meta.model_name.upper()
                else:
                    file_obj =self.model._meta.get_field(field_or_info)
                    vim = file_obj.verbose_name
                header_list.append(vim)



        #用于构建视图中的展现数据
        new_data=[]
        for obj in querset:
            temp=[]
            for field_or_info in self.get_new_list_display():
                # 判断是函数仍是字符段
                if callable(field_or_info):
                    vim = field_or_info(self,obj)
                else:
                    try:
                        from django.db.models.fields.related import ManyToManyField
                        info_obj=self.model._meta.get_field(field_or_info)
                        # 判断多对多字段
                        if type(info_obj)==ManyToManyField:
                            raise Exception("list_distplay 不能是多很少字段")
                        #判断是不是__str__
                        # if field_or_info=='__str':
                        #     vim=getattr(obj,field_or_info)()
                        # 判断是否有choices字段
                        if info_obj.choices:
                            vim = getattr(obj,'get_%s_display'%field_or_info)()
                        else:
                            vim =getattr(obj,field_or_info)
                    except Exception as e:
                        vim = getattr(obj, field_or_info)()
                temp.append(vim)
            new_data.append(temp)
        print('new_data',new_data)

        # 目标数据
        # new_data=[
        #     ["python",123],
        #     ["java",234]
        # ]


        return render(request,'stark/list_view.html',locals())


    def add_view(self, request):
        return HttpResponse("add_view")

    def change_view(self, request, id):
        return HttpResponse("change_view")

    def delete_view(self, request, id):
        return HttpResponse("delete_view")

    @property
    def get_url(self,):
        # temp = [
        #     path("", self.list_view),
        #     path("add/",self. add_view),
        #     re_path("(\d+)/change/", self.change_view),
        #     re_path("(\d+)/delete/", self.delete_view),
        # ]
        temp = [
            path("", self.list_view, name="%s_%s_list" % (self.app_label, self.model_name)),
            path("add/", self.add_view, name="%s_%s_add" % (self.app_label, self.model_name)),
            re_path("(\d+)/change/", self.change_view, name="%s_%s_change" % (self.app_label, self.model_name)),
            re_path("(\d+)/delete/", self.delete_view, name="%s_%s_delete" % (self.app_label, self.model_name)),
        ]
        return (temp, None, None)


class StarkSite:
    def __init__(self):
        self._registry ={}

    def register(self,model,admin_class=None,**options):
        admin_class = admin_class or ModelStark
        self._registry[model]=admin_class(model)

    def get_urls(self):
        temp =[]

        # 拿到已经注册的全部表
        for model,config_obj in self._registry.items():
            # 表名
            model_name = model._meta.model_name
            # 项目名
            model_label = model._meta.app_label
            temp.append(
                path("%s/%s/"%(model_label,model_name),config_obj.get_url)
            )
        return temp
    @property
    def urls(self):
        return self.get_urls(),None,None


site = StarkSite()
site代码

完整注册stark代码以下

from stark.services.sites import site,ModelStark
from .models import *
from django.utils.safestring import mark_safe

class BookConfig(ModelStark):

    def show_authors(self,obj=None,heade=False):
        if heade:
            return "做者信息"

        return " ".join([author.name for author in obj.author.all()])
    # # 选择框
    # def show_checkbox(self,obj=None,heade=False):
    #     if heade:
    #         return mark_safe("<input type='checkbox'>")
    #     return mark_safe("<input type='checkbox'>")
    #
    # # 删除框
    # def show_delbtn(self, obj=None, heade=False):
    #     if heade:
    #         return '删除'
    #     return mark_safe("<a href='stark/app01/book/%s/delete'>删除</a>"%obj.pk)
    #
    # # 编辑框
    # def show_editbtn(self, obj=None, heade=False):
    #     if heade:
    #         return '编辑'
    #     return mark_safe("<a href='stark/app01/book/%s/change'>编辑</a>" % obj.pk)

    list_display=["title","price","staes","publish",show_authors]
    # list_display=["title","price","staes","publish"]


site.register(Book,BookConfig)
site.register(Publish)
print(site._registry)
stark代码

 

a