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()
# -*- 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)
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
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), ]
<!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>
# -*- coding: utf-8 -*- # @Time : 2019/4/12 11:48 # @Author : xxx # @Email : xxx@admin.com # @File : stark.py # @Software: PyCharm print("234")
全代码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()
# -*- 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)
<!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>
<!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>
<!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>
<!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>
<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>
全代码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()
# -*- 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)
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), ]
<!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>
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)