crm02 stark组件(自定义Django-admin)之运行流程

项目目录结构:                    

   

 

 

 

 

配置文件加载 setting.py  会从上至下加载每个app的app.py文件

 

第一步:循环加载执行所有已经注册的app中的stark.py文件

###### app01/app.py ###### 
from django.apps import AppConfig

class App01Config(AppConfig):
    name = 'app01'


###### app02/app.py ###### 
from django.apps import AppConfig

class App02Config(AppConfig):
    name = 'app02'


###### stark/app.py ###### 
from django.apps import AppConfig

from django.utils.module_loading import autodiscover_modules
class StarkConfig(AppConfig):
    name = 'stark'
    def ready(self):  #ready方法在加载的时候会执行
        autodiscover_modules('stark') # 扫描加载所有已经注册的app中的stark.py文件

 

第二步: 执行代码

###### app01/stark.py ######
from stark.service.sites import site,ModelStark
from .models import *
from django.utils.safestring import mark_safe

class BookConfig(ModelStark):

    #自定义列
    def show_authors(self,obj=None,header=False):
        if header:
            return "作者信息"
        return " ".join([author.name for author in obj.authors.all()])


    list_display=["title","price","state","publish",show_authors]


site.register(Book,BookConfig)
site.register(Publish)
print(site._registry)

###### app02/stark.py ######
print("234")

 

第三步 所有的app中的stark.py里面 都是调用 stark/service/sites.py 的site

这里应用的是一个单例模式,对于StarkSite类的一个单例模式,执行的每一个app中的每一个site都是同一个实例对象

 

第四步 执行register方法(代码在第二步里面)

site.register(Book,BookConfig)
site.register(Publish)
class StarkSite:
    '''
    stark全局类
    '''
    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)

 

思考:在每一个app的admin .py中加上

print(site._registry) # 执行结果{模型对象:配置类对象(模型类名)}? 
{<class 'app01.models.Book'>: <app01.stark.BookConfig object at 0x0000015EC8F21B38>, <class 'app01.models.Publish'>: <stark.service.sites.ModelStark object at 0x0000015EC8F21E10>}

 

到这里,注册结束!

 

 

 

<5> admin的URL配置

urlpatterns = [
    url(r'^admin/', admin.site.urls),
]
复制代码
class AdminSite(object):
    
     def get_urls(self):
        from django.conf.urls import url, include
      
        urlpatterns = []

        # Add in each model's views, and create a list of valid URLS for the
        # app_index
        valid_app_labels = []
        for model, model_admin in self._registry.items():
            urlpatterns += [
                url(r'^%s/%s/' % (model._meta.app_label, model._meta.model_name), include(model_admin.urls)),
            ]
            if model._meta.app_label not in valid_app_labels:
                valid_app_labels.append(model._meta.app_label)

      
        return urlpatterns

    @property
    def urls(self):
        return self.get_urls(), 'admin', self.name
复制代码

<6>  url()方法的扩展应用

复制代码
from django.shortcuts import HttpResponse
def test01(request):
    return HttpResponse("test01")

def test02(request):
    return HttpResponse("test02")

urlpatterns = [
    url(r'^admin/', admin.site.urls),
    url(r'^yuan/', ([
                    url(r'^test01/', test01),
                    url(r'^test02/', test02),

                    ],None,None)),

]
复制代码

扩展优化

 

 

 

 

全代码01版

# -*- coding: utf-8 -*-
# @Time    : 2019/4/12 11:51
# @Author  : xxx
# @Email   : xxx@admin.com
# @File    : sites.py
# @Software: PyCharm
from django.urls import path,re_path
from django.shortcuts import HttpResponse,render
from app01.models import *
from django.utils.safestring import mark_safe
from django.urls import reverse

class ModelStark(object):
    '''
    默认配置类
    '''
    list_display=("__str__",)
    def __init__(self,model):  #self  当前访问模型表对应的配置类对象 eg:<app01.stark.BookConfig object at 0x0000019BEBC88710>
        self.model=model  #self.model   当前访问模型表  eg:<class 'app01.models.Book'>
        self.model_name = self.model._meta.model_name #模型表的名字  eg:book
        self.app_label = self.model._meta.app_label #app的名字 eg:app01

    # 反向解析当前访问表的增删改查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, header=False):
        if header:
            return mark_safe("<input type='checkbox'>")
        return mark_safe("<input type='checkbox'>")

    def show_delbtn(self, obj=None, header=False):
        if header:
            return "删除"

        return mark_safe("<a href='%s'>删除</a>"%self.get_delete_url(obj))

    def show_editbtn(self, obj=None, header=False):
        if header:
            return "编辑"
        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: 当前访问模型表对应的配置类对象
        self.model: 当前访问模型表
        :param request:
        :return:
        '''

        queryset=self.model.objects.all()
        print(queryset) #eg:<QuerySet [<Book: python>, <Book: java>]>
        print(self.list_display) # eg:['title', 'price', 'state', 'publish', <function BookConfig.show_authors at 0x0000016D70D74840>]
        print(self.get_new_list_display())
        #[<function ModelStark.show_checkbox at 0x00000267120451E0>, 'title', 'price', 'state', 'publish', <function BookConfig.show_authors at 0x0000026711D14840>, <function ModelStark.show_editbtn at 0x00000267120452F0>, <function ModelStark.show_delbtn at 0x0000026712045268>]
        #   构建表头
        header_list=[]
        for field_or_func in self.get_new_list_display():
            if callable(field_or_func):
                val=field_or_func(self,header=True)
                header_list.append(val)
                #header_list.append(field_or_func.__name__)
            else:
                 if field_or_func=="__str__":
                     val=self.model._meta.model_name.upper()
                     print(val)
                 else:
                      field_obj = self.model._meta.get_field(field_or_func)
                      val=field_obj.verbose_name
                 header_list.append(val)

        #  构建展示数据
        new_data=[]
        for obj in queryset:
            temp=[]

            for field_or_func in self.get_new_list_display(): #
                if callable(field_or_func):
                    #  field:方法
                    val=field_or_func(self,obj)
                else:
                    try:
                        #  field:字符串
                        from django.db.models.fields.related import ManyToManyField
                        field_obj=self.model._meta.get_field(field_or_func)
                        #  判断是否多对多字段
                        if type(field_obj)==ManyToManyField:
                            raise Exception("list_display不能是多对多字段!")
                        #  判断字段是否 拥有choices属性
                        if field_obj.choices:
                            val=getattr(obj,"get_%s_display"%field_or_func)()
                        else:
                            val=getattr(obj,field_or_func)
                    except Exception as e:
                        #val=obj
                        val=getattr(obj,field_or_func)()
                        #如果没有自定义配置类  就会采用默认配置类ModelStark  此时就会getattr(obj,__str__)()   obj是模型类

                temp.append(val)
            new_data.append(temp)


        print("new_data",new_data)
        #new_data [["<input type='checkbox'>", 'python', Decimal('123.00'), '已出版', <Publish: 苹果出版设>, 'alex egon', "<a href='/stark/app01/book/1/change/'>编辑</a>", "<a href='/stark/app01/book/1/delete/'>删除</a>"],
        #  ["<input type='checkbox'>", 'java', Decimal('234.00'), '已出版', <Publish: 橘子出版设>, 'egon', "<a href='/stark/app01/book/2/change/'>编辑</a>", "<a href='/stark/app01/book/2/delete/'>删除</a>"]]


        '''
        目标数据结构
        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_urls(self):




        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:
    '''
    stark全局类
    '''
    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):
        #  动态为注册的模型类创建增删改查URL
        temp = []
        # {Book:ModelAdmin(Book),Publish:ModelAdmin(Publish)}
        for model, config_obj in self._registry.items():
            print("---->", model, config_obj)
            model_name = model._meta.model_name
            app_label = model._meta.app_label
            temp.append(
                path("%s/%s/" % (app_label,model_name),config_obj.get_urls)
            )

        '''
           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)
        
        '''

        return temp

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

site = StarkSite()
stark/service/sites.py
# -*- coding: utf-8 -*-
# @Time    : 2019/4/12 11:47
# @Author  : xxx
# @Email   : xxx@admin.com
# @File    : stark.py
# @Software: PyCharm

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

class BookConfig(ModelStark):

    def show_authors(self,obj=None,header=False):
        if header:
            return "作者信息"
        return " ".join([author.name for author in obj.authors.all()])


    list_display=["title","price","state","publish",show_authors]


site.register(Book,BookConfig)
site.register(Publish)
print(site._registry)
app01/stark.py
from django.db import models

# Create your models here.

class Author(models.Model):
    nid = models.AutoField(primary_key=True)
    name=models.CharField( max_length=32)
    age=models.IntegerField()
    def __str__(self):
        return self.name

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)
    title = models.CharField( max_length=32,verbose_name="书籍名称")
    # publishDate=models.DateField()
    price=models.DecimalField(max_digits=5,decimal_places=2,verbose_name="价格")
    state=models.IntegerField(choices=[(1,"已出版"),(2,"未出版社")],default=1)
    # 与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
app01/models.py
from django.contrib import admin
from django.urls import path
from stark.service.sites import site

urlpatterns = [
    path('admin/', admin.site.urls),
    path('stark/', site.urls),

]
urls.py
<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>Title</title>
    <link rel="stylesheet" href="/static/bootstrap/css/bootstrap.css">
</head>
<body>
<h3>查看数据</h3>

<div class="container">
   <div class="row">
       <table class="table table-striped">
    <thead>
         <tr>
             {% for item in header_list %}
                <th>{{ item }}</th>
              {% endfor %}
         </tr>

    </thead>
    <tbody>
          {% for item in new_data %}
          <tr>
              {% for info in item %}
              <td>{{ info }}</td>
              {% endfor %}
          </tr>
          {% endfor %}


    </tbody>
</table>
   </div>
</div>

</body>

</html>
stark/templates/stark/list_view.html
# -*- coding: utf-8 -*-
# @Time    : 2019/4/12 11:48
# @Author  : xxx
# @Email   : xxx@admin.com
# @File    : stark.py
# @Software: PyCharm
print("234")
app02/stark.py

 

 

全代码02版 增删改查  ModelForm

# -*- coding: utf-8 -*-
# @Time    : 2019/4/12 11:51
# @Author  : xxx
# @Email   : xxx@admin.com
# @File    : sites.py
# @Software: PyCharm
from django.urls import path,re_path
from django.shortcuts import HttpResponse,render,redirect
from app01.models import *
from django.utils.safestring import mark_safe
from django.core.exceptions import FieldDoesNotExist
from django.urls import reverse



class ModelStark(object):
    '''
    默认配置类
    '''
    list_display=("__str__",)
    list_display_links = []
    model_form_class=None

    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):
        # 反向解析当前表的删除的URL
        add_url = reverse("%s_%s_add" % (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, header=False):
        if header:
            return mark_safe("<input type='checkbox'>")
        return mark_safe("<input type='checkbox'>")

    def show_delbtn(self, obj=None, header=False):
        if header:
            return "删除"

        return mark_safe("<a href='%s'>删除</a>"%self.get_delete_url(obj))

    def show_editbtn(self, obj=None, header=False):
        if header:
            return "编辑"
        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: 当前访问模型表对应的配置类对象
        self.model: 当前访问模型表
        :param request:
        :return:
        '''

        queryset=self.model.objects.all()
        print(queryset)
        print(self.list_display) # ["title","price"]

        #   构建表头

        header_list=[]
        for field_or_func in self.get_new_list_display():
            if callable(field_or_func):
                val=field_or_func(self,header=True)
                header_list.append(val)
                #header_list.append(field_or_func.__name__)
            else:
                 if field_or_func=="__str__":
                     val=self.model._meta.model_name.upper()
                     print(val)
                 else:
                      field_obj = self.model._meta.get_field(field_or_func)
                      val=field_obj.verbose_name
                 header_list.append(val)

        #  构建展示数据
        new_data=[]
        for obj in queryset:
            temp=[]

            for field_or_func in self.get_new_list_display(): # ["title","price","state","publish",show_authors]
                if callable(field_or_func):
                    #  field:方法
                    val=field_or_func(self,obj)
                else:
                    try:
                        #  field:字符串
                        from django.db.models.fields.related import ManyToManyField
                        field_obj=self.model._meta.get_field(field_or_func)
                        #  判断是否多对多字段
                        if type(field_obj)==ManyToManyField:
                            raise Exception("list_display不能是多对多字段!")
                        #  判断字段是否 拥有choices属性
                        if field_obj.choices:
                            val=getattr(obj,"get_%s_display"%field_or_func)()
                        else:
                            val=getattr(obj,field_or_func)
                            if field_or_func in  self.list_display_links:
                                val=mark_safe("<a href='%s'>%s</a>"%(self.get_change_url(obj),val))

                    except FieldDoesNotExist as e:
                        #val=obj
                        val=getattr(obj,field_or_func)()

                temp.append(val)
            new_data.append(temp)


        print("new_data",new_data) # [['python', Decimal('123.00')], ['java', Decimal('234.00')]]


        '''
        目标数据结构
        new_data=[
            ["python",123],
            ["java",234]
        ]
        '''


        #  相关变量
        table_name=self.model._meta.verbose_name
        add_url=self.get_add_url()



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

    def get_model_form(self):
        from django.forms import ModelForm
        class BaseModelForm(ModelForm):
            class Meta:
                model = self.model
                fields = "__all__"

        return self.model_form_class or BaseModelForm

    def add_view(self,request):
        BaseModelForm=self.get_model_form()

        if  request.method=="GET":
            form_obj=BaseModelForm()
            return render(request,"stark/add_view.html",locals())
        else:
            form_obj = BaseModelForm(request.POST)
            if form_obj.is_valid():
                form_obj.save()
                return redirect(self.get_list_url())
            else:
                return render(request, "stark/add_view.html", locals())

    def change_view(self,request,id):
        BaseModelForm=self.get_model_form()
        edit_obj=self.model.objects.filter(pk=id).first()
        if  request.method=="GET":
            form_obj=BaseModelForm(instance=edit_obj)
            return render(request,"stark/change_view.html",locals())
        else:
            form_obj = BaseModelForm(request.POST,instance=edit_obj)
            if form_obj.is_valid():
                form_obj.save()
                return redirect(self.get_list_url())
            else:
                return render(request, "stark/change_view.html", locals())



    def delete_view(self,request, id):

        if request.method=="POST":
            self.model.objects.filter(pk=id).delete()
            return redirect(self.get_list_url())

        list_url=self.get_list_url()
        return render(request,"stark/delete_view.html",locals())

    @property
    def get_urls(self):

        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:
    '''
    stark全局类
    '''
    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):
        #  动态为注册的模型类创建增删改查URL
        temp = []
        # {Book:ModelAdmin(Book),Publish:ModelAdmin(Publish)}
        for model, config_obj in self._registry.items():
            print("---->", model, config_obj)
            model_name = model._meta.model_name
            app_label = model._meta.app_label
            temp.append(
                path("%s/%s/" % (app_label,model_name),config_obj.get_urls)
            )

        '''
           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)
        
        '''

        return temp

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

site = StarkSite()
stark/service/sites.py
# -*- coding: utf-8 -*-
# @Time    : 2019/4/12 11:47
# @Author  : xxx
# @Email   : xxx@admin.com
# @File    : stark.py
# @Software: PyCharm

from stark.service.sites import site,ModelStark
from .models import *
from django.utils.safestring import mark_safe
from django.forms import ModelForm
class BookModelForm(ModelForm):
    class Meta:
        model=Book
        fields="__all__"
        error_messages={
           "title":{ "required":" 该字段不能为空!"}
        }


class BookConfig(ModelStark):

    def show_authors(self,obj=None,header=False):
        if header:
            return "作者信息"
        return " ".join([author.name for author in obj.authors.all()])


    list_display=["title","price","state","publish",show_authors]
    list_display_links=["title","price"]

    model_form_class=BookModelForm

site.register(Book,BookConfig)
site.register(Publish)
print(site._registry)
app01/stark.py
<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>Title</title>
    <link rel="stylesheet" href="/static/bootstrap/css/bootstrap.css">
</head>
<body>
<h3>查看数据</h3>

<div class="container">
   <div class="row">
       <a href="{{ add_url }}" class="btn btn-primary">添加{{ table_name }}</a>
       <table class="table table-striped">
    <thead>
         <tr>
             {% for item in header_list %}
                <th>{{ item }}</th>
              {% endfor %}
         </tr>

    </thead>
    <tbody>
          {% for item in new_data %}
          <tr>
              {% for info in item %}
              <td>{{ info }}</td>
              {% endfor %}
          </tr>
          {% endfor %}


    </tbody>
</table>
   </div>
</div>

</body>

</html>
stark/templates/stark/list_view.html
<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>Title</title>
     <link rel="stylesheet" href="/static/bootstrap/css/bootstrap.css">
    <style>
        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;
        }

        .error{
            color: red;
        }
    </style>
</head>
<body>

<h3> 添加数据</h3>
{% include "stark/form.html" %}




</body>
</html>
stark/templates/stark/add_view.html
<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>Title</title>
     <link rel="stylesheet" href="/static/bootstrap/css/bootstrap.css">
    <style>
        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;
        }

        .error{
            color: red;
        }
    </style>
</head>
<body>
<h3>编辑数据</h3>

{% include "stark/form.html" %}

</body>
</html>
stark/templates/stark/change_view.html
<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>Title</title>
    <link rel="stylesheet" href="/static/bootstrap/css/bootstrap.css">

</head>
<body>

<form action="" method="post">
     {% csrf_token %}
     <a href="{{ list_url }}" class="btn btn-warning">取消</a>
     <input class="btn btn-danger" type="submit" value="确认删除">
</form>


</body>
</html>
stark/templates/stark/delete_view.html
<div class="container">
    <div class="row">
        <div class="col-md-8">
            <form action="" method="post" novalidate>
                {% csrf_token %}

                {% for field in form_obj %}
                   <div class="form-group">
                       <label for="">{{ field.label }}</label>
                       {{ field }}  <span class="error pull-right">{{ field.errors.0 }}</span>
                   </div>
                {% endfor %}

                <button class="btn btn-default">submit</button>


            </form>
        </div>
    </div>
</div>
stark/templates/stark/form.html

 

 

全代码03版 增删改查  ModelForm 分页 筛选框

# -*- coding: utf-8 -*-
# @Time    : 2019/4/12 11:51
# @Author  : xxx
# @Email   : xxx@admin.com
# @File    : sites.py
# @Software: PyCharm
from django.urls import path,re_path
from django.shortcuts import HttpResponse,render,redirect
from app01.models import *
from django.utils.safestring import mark_safe
from django.core.exceptions import FieldDoesNotExist
from django.urls import reverse

class ShowList(object):
    def __init__(self,request,config_obj,queryset):
        self.config_obj=config_obj  #当前模型配置类对象  eg:BookConfig(Book) 默认是ModelStark(Book)
        self.queryset=queryset #符合条件queryset字典数据记录
        self.request=request #请求
        self.pager_queryset=self.get_pager_queryset()  #类实例化时执行分页函数 获取当前页的数据

    def get_pager_queryset(self):

        from stark.utils.page import Pagination
        current_page = self.request.GET.get("page", 1)
        self.pagination = Pagination(self.request, current_page, self.queryset, per_page_num=self.config_obj.per_page_num or 5)
        # per_page_num=self.config_obj.per_page_num or 5 eg:模型配置类里面(BookConfig(Book))自定义下标个数 没有定义就用5
        queryset = self.queryset[self.pagination.start:self.pagination.end] #获取当前页的所有数据切片

        return queryset

    def get_header(self):
        '''
        获取表头
        :return:
        '''
        #   构建表头

        header_list = []
        for field_or_func in self.config_obj.get_new_list_display():
            if callable(field_or_func): #如果时可调用的函数
                val = field_or_func(self.config_obj, header=True)  #eg: def show_delbtn(self,obj=None, header=False):pass  ---self =self.config_obj,obj=None,header=True
                header_list.append(val)
                # header_list.append(field_or_func.__name__)
            else:
                if field_or_func == "__str__":  #如果没有自定义list_display  则默认只有三个函数列  +
                    val = self.config_obj.model._meta.model_name.upper() #eg:BookConfig(Book).Book._meta.model_name.upper() = 'book'.upper()
                    print(val)
                else:
                    field_obj = self.config_obj.model._meta.get_field(field_or_func)  #得到模型类的每个字段类的实例对象
                    val = field_obj.verbose_name #当field_obj有verbose_name属性时则用verbose_name ,无则用字段类的实例对象名(字段名) eg:price、state、publish
                header_list.append(val)

        return header_list

    def get_body(self):
        '''
        获取标体
        :return:
        '''
        #  构建展示数据
        new_data = []
        for obj in self.pager_queryset:
            temp = []

            for field_or_func in self.config_obj.get_new_list_display():  # ["title","price","state","publish",show_authors]
                if callable(field_or_func):
                    #  field:方法
                    val = field_or_func(self.config_obj, obj)  #obj为queryset字段的对象
                else:
                    try:
                        #  field:字符串
                        from django.db.models.fields.related import ManyToManyField
                        field_obj = self.config_obj.model._meta.get_field(field_or_func)
                        #  判断是否多对多字段
                        if type(field_obj) == ManyToManyField:
                            raise Exception("list_display不能是多对多字段!")
                        #  判断字段是否 拥有choices属性
                        if field_obj.choices:
                            val = getattr(obj, "get_%s_display" % field_or_func)()  #模型类(有一个eg:get_state_display(self)方法来处理拥有choices属性的字段)实例对象
                        else:
                            val = getattr(obj, field_or_func)
                            if field_or_func in self.config_obj.list_display_links:  #模型配置类自定义list_display_links  #可以链接到编辑页面
                                val = mark_safe("<a href='%s'>%s</a>" % (self.config_obj.get_change_url(obj), val))

                    except FieldDoesNotExist as e:
                        # val=obj
                        val = getattr(obj, field_or_func)()

                temp.append(val)
            new_data.append(temp)

        print("new_data", new_data)

        '''
        new_data [["<input type='checkbox'>", "<a href='/stark/app01/book/1/change/'>python180</a>", "<a href='/stark/app01/book/1/change/'>123.00</a>", '已出版', <Publish: 苹果出版设>, 'alex egon', "<a href='/stark/app01/book/1/change/'>编辑</a>", "<a href='/stark/app01/book/1/delete/'>删除</a>"], 
        ["<input type='checkbox'>", "<a href='/stark/app01/book/2/change/'>java</a>", "<a href='/stark/app01/book/2/change/'>234.00</a>", '已出版', <Publish: 橘子出版设>, 'egon', "<a href='/stark/app01/book/2/change/'>编辑</a>", "<a href='/stark/app01/book/2/delete/'>删除</a>"],
         ["<input type='checkbox'>", "<a href='/stark/app01/book/4/change/'>挤奶龙爪手123</a>", "<a href='/stark/app01/book/4/change/'>180.00</a>", '已出版', <Publish: 苹果出版设>, 'egon', "<a href='/stark/app01/book/4/change/'>编辑</a>", "<a href='/stark/app01/book/4/delete/'>删除</a>"]]
        目标数据结构
        new_data=[
            ["python",123],
            ["java",234]
        ]
        '''

        return new_data

class ModelStark(object):
    '''
    默认配置类
    '''
    list_display=("__str__",)  # 自定义显示哪些字段
    list_display_links = []  #自定义 哪些字段 可以链接编辑
    model_form_class=None #自定义model form模型表单类
    per_page_num=None #分页 自定义每页显示的数据记录条数
    search_fields=[] #自定义可以筛选框可以采用哪些值筛选
    search_val=None #用户输入的筛选框的值

    def __init__(self,model):
        self.model=model  # 模型类 eg: Book、Publish等

        # self.model._meta 默认生成的表名 应用名.模型类名小写 eg:   app01.book
        self.model_name = self.model._meta.model_name#    self.model._meta.model_name <---->Book._meta.model_name <------>模型类名小写 book
        self.app_label = self.model._meta.app_label#app名 eg: self.model._meta.app_label = app01

    # 反向解析当前访问表的增删改查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):
        # 反向解析当前表的删除的URL
        add_url = reverse("%s_%s_add" % (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, header=False):
        if header:
            return mark_safe("<input type='checkbox'>")  #将字符串转换成前端html标签
        return mark_safe("<input type='checkbox'>")

    def show_delbtn(self, obj=None, header=False):
        if header:
            return "删除"

        return mark_safe("<a href='%s'>删除</a>"%self.get_delete_url(obj))

    def show_editbtn(self, obj=None, header=False):
        if header:
            return "编辑"
        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 get_search_condition(self,val):
        # val = request.GET.get("q")  #获取前端页面搜索框里面 name为q的values
        # print(val)

        from django.db.models import Q
        q = Q()
        # if val:  #如果用户在搜索框 输入值
        self.search_val = val
        q.connector = "or"  #实现或操作  并
        for field in self.search_fields:  # ["title","price"] #在app01/stark.py可以自定义 搜索的数据库的字段
            print(field)
            # queryset=queryset.filter(Q(title__contains=val)|Q(price__contains=val))
            q.children.append((field + "__contains", val))#把条件追加进去
        # else:
        #     self.search_val=None
        #     # print(q) #(AND: )  没有用户搜索值时

        return q  #把搜索条件返回 eg:   相当于 q = Q(title__contains=val)|Q(price__contains=val)




    #  视图函数 查
    def list_view(self,request):
        '''
        self: 当前访问模型表对应的配置类对象
        self.model: 当前访问模型表
        :param request:
        :return:
        '''
        queryset=self.model.objects.all()  #获取

        # search过滤
        val = request.GET.get("q")
        if val:  #判断是有筛选框传值
            queryset=queryset.filter(self.get_search_condition(val)) #调用过滤类方法 传值request
        else:
            self.search_val = None  #将上次筛选框的值清空

        show_list=ShowList(request,self,queryset)  #self  eg: BookConfig(ModelStark)实例对象

        #  相关变量
        table_name=self.model._meta.verbose_name  #模型表 verbose_name 注释
        add_url=self.get_add_url() #获取添加数据的urls

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



#判断app01/stark.py是否有传了model_form_class(自定义模型表单类) 没有就使用BaseModelForm  用在增、改两个视图
    def get_model_form(self):
        from django.forms import ModelForm
        class BaseModelForm(ModelForm):
            class Meta:
                model = self.model
                fields = "__all__"

        return self.model_form_class or BaseModelForm

    #  视图函数 增
    def add_view(self,request):
        BaseModelForm=self.get_model_form()

        if  request.method=="GET":
            form_obj=BaseModelForm()
            return render(request,"stark/add_view.html",locals())
        else:
            form_obj = BaseModelForm(request.POST)
            if form_obj.is_valid():
                form_obj.save()
                return redirect(self.get_list_url())
            else:
                return render(request, "stark/add_view.html", locals())

    #  视图函数 改
    def change_view(self,request,id):
        BaseModelForm=self.get_model_form()
        edit_obj=self.model.objects.filter(pk=id).first()
        if  request.method=="GET":
            form_obj=BaseModelForm(instance=edit_obj)
            return render(request,"stark/change_view.html",locals())
        else:
            form_obj = BaseModelForm(request.POST,instance=edit_obj)
            if form_obj.is_valid():
                form_obj.save()
                return redirect(self.get_list_url())
            else:
                return render(request, "stark/change_view.html", locals())

    #  视图函数 删
    def delete_view(self,request, id):

        if request.method=="POST":
            self.model.objects.filter(pk=id).delete()
            return redirect(self.get_list_url())

        list_url=self.get_list_url()
        return render(request,"stark/delete_view.html",locals())



    @property
    def get_urls(self):

        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:
    '''
    stark全局类
    '''
    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):
        #  动态为注册的模型类创建增删改查URL
        temp = []
        # {Book:ModelAdmin(Book),Publish:ModelAdmin(Publish)}
        for model, config_obj in self._registry.items():
            print("---->", model, config_obj)
            model_name = model._meta.model_name
            app_label = model._meta.app_label
            temp.append(
                path("%s/%s/" % (app_label,model_name),config_obj.get_urls)
            )

        '''
           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)
        
        '''

        return temp

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

site = StarkSite()
stark/service/sites.py
# -*- coding: utf-8 -*-
# @Time    : 2019/4/12 11:47
# @Author  : xxx
# @Email   : xxx@admin.com
# @File    : stark.py
# @Software: PyCharm

from stark.service.sites import site,ModelStark
from .models import *
from django.utils.safestring import mark_safe
from django.forms import ModelForm


#自定义模型表单类
class BookModelForm(ModelForm):
    class Meta:
        model=Book
        fields="__all__"
        error_messages={
           "title":{ "required":" 该字段不能为空!"}
        }


#自定义模型配置类
class BookConfig(ModelStark):
        #自定义函数列
    def show_authors(self,obj=None,header=False):
        if header:
            return "作者信息"
        return " ".join([author.name for author in obj.authors.all()])

#自定义显示字段
    list_display=["title","price","state","publish",show_authors]
#自定义链接字段
    list_display_links=["title","price"]
#自定义模型表单类的值传给 模型配置类实例对象
    model_form_class=BookModelForm
# 自定义分页 :每页显示的数据条数
    per_page_num=3
 # 自定义筛选字段
    search_fields=["title","price"]

#注册模型类
site.register(Book,BookConfig)
site.register(Publish)
# print(site._registry)
app01/stark.py
from django.contrib import admin
from django.urls import path
from stark.service.sites import site

urlpatterns = [
    path('admin/', admin.site.urls),
    path('stark/', site.urls),

]
urls.py
<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>Title</title>
    <link rel="stylesheet" href="/static/bootstrap/css/bootstrap.css">
</head>
<body>
<h3>查看数据</h3>

<div class="container">

   <div class="row">

       <div class="panel panel-info">
      <div class="panel-heading">
        <h3 class="panel-title"></h3>
      </div>
      <div class="panel-body">
        <a href="{{ add_url }}" class="btn btn-primary">添加{{ table_name }}</a>
          {% if show_list.config_obj.search_fields %} {# 注释:如果模型配置类里面没有配置search_fields,就不显示搜索框 #}
              <form action="" method="get">
                  <div class="input-group pull-right" style="width: 400px">
                      <input value="{{ show_list.config_obj.search_val|default:'' }}" type="text" name="q" class="form-control" placeholder="Search for...">
                      <span class="input-group-btn">
                        <button class="btn btn-default">Search!</button>

                      </span>
                    </div>
              </form>
          {% endif %}

      </div>
    </div>


       <table class="table table-striped">
            <thead>
                 <tr>
                     {% for item in show_list.get_header %}
                        <th>{{ item }}</th>
                      {% endfor %}
                 </tr>

            </thead>
            <tbody>
                  {% for item in show_list.get_body %}
                  <tr>
                      {% for info in item %}
                      <td>{{ info }}</td>
                      {% endfor %}
                  </tr>
                  {% endfor %}


            </tbody>
       </table>

       <div class="pull-right">
           {{ show_list.pagination.page_html|safe }}
       </div>

   </div>
</div>

</body>

</html>
stark/templates/stark/list_view.html
class Pagination(object):

    def __init__(self, request, current_page, all_data, per_page_num=None, max_page_count=11):
        """
        封装分页相关数据
        :param current_page: 当前页
        :param all_count:    分页数据中的数据总条数
        :param per_page_num: 每页显示的数据条数
        :param max_page_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 = len(all_data)
        self.per_page_num = per_page_num

        # 总页码
        all_pager, tmp = divmod(self.all_count, per_page_num)
        if tmp:
            all_pager += 1
        self.all_pager = all_pager
        self.max_page_count = max_page_count
        self.max_page_count_half = int((max_page_count - 1) / 2)

        self.request = request

        #
        import copy
        self.params = copy.deepcopy(self.request.GET)

    '''
     all_count=100    per_page_num =10 

     current_page        start    end
          1                 0      10
          2                 10     20
          3                 20     30


      all_count=100    per_page_num =8

     current_page        start    end
          1                 0      8
          2                 8      16
          3                 16     24  

     表达式:    start= (current_page-1)*per_page_num    end =current_page* per_page_num

    '''

    @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.max_page_count:
            pageRange = range(1, self.all_pager + 1)
        # 总页码  > 11
        else:
            # 当前页如果小于页面上最多显示(11-1)/2个页码
            if self.current_page <= self.max_page_count_half:
                pageRange = range(1, self.max_page_count + 1)
            # 当前页大于5
            else:
                # 页码翻到最后
                if (self.current_page + self.max_page_count_half) > self.all_pager:
                    pageRange = range(self.all_pager - self.max_page_count + 1, self.all_pager + 1)
                else:
                    pageRange = range(self.current_page - self.max_page_count_half,
                                      self.current_page + self.max_page_count_half + 1)

        # 构建分页页码的html
        page_html_list = []
        self.params["page"] = 1
        first_page = '<nav aria-label="Page navigation"><ul class="pagination"><li><a href="?%s">首页</a></li>' % (
        self.params.urlencode(),)
        page_html_list.append(first_page)
        self.params["page"] = self.current_page - 1
        if self.current_page <= 1:
            prev_page = '<li class="disabled"><a href="#">上一页</a></li>'
        else:
            prev_page = '<li><a href="?%s">上一页</a></li>' % (self.params.urlencode(),)

        page_html_list.append(prev_page)
        # self.params: ["page":9,"a":1,"b":2]
        for i in pageRange:
            self.params["page"] = i
            if i == self.current_page:
                temp = '<li class="active"><a href="?%s">%s</a></li>' % (self.params.urlencode(), i,)
            else:
                temp = '<li><a href="?%s">%s</a></li>' % (self.params.urlencode(), i,)
            page_html_list.append(temp)

        self.params["page"] = self.current_page + 1
        if self.current_page >= self.all_pager:
            next_page = '<li class="disabled"><a href="#">下一页</a></li>'
        else:
            next_page = '<li><a href="?%s">下一页</a></li>' % (self.params.urlencode(),)
        page_html_list.append(next_page)

        self.params["page"] = self.all_pager
        last_page = '<li><a href="?%s">尾页</a></li> </ul></nav>' % (self.params.urlencode())
        page_html_list.append(last_page)

        return ''.join(page_html_list)
stark/utils/page.py

 

posted @ 2019-06-28 10:02  冥想心灵  阅读(198)  评论(0编辑  收藏  举报