手写一个admin 组件------STARK
开一个新的项目,,建立一个stark 包, 在里面创建一个service包,在service 包里创建一个stark.py 文件,
配置好环境, makemigreations, migreate.
settings.py 配置:
STATIC_URL = '/static/'
STATICFILES_DIRS = [
os.path.join(BASE_DIR,'static'),
]
INSTALLED_APPS = [
'django.contrib.admin',
'django.contrib.auth',
'django.contrib.contenttypes',
'django.contrib.sessions',
'django.contrib.messages',
'django.contrib.staticfiles',
'app01.apps.App01Config',
'stark.apps.StarkConfig',
]
from django.contrib import admin
配置好后:
app01下的modles.py
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
from django.db import models # Create your models here. 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() # 与AuthorDetail建立一对一的关系 authorDetail=models.OneToOneField(to="AuthorDetail",on_delete=models.CASCADE) def __str__(self): return self.name class AuthorDetail(models.Model): nid = models.AutoField(primary_key=True) birthday=models.DateField() telephone=models.BigIntegerField() addr=models.CharField( max_length=64) def __str__(self): return str(self.telephone) class Publish(models.Model): nid = models.AutoField(primary_key=True) name=models.CharField( max_length=32) city=models.CharField( max_length=32) email=models.EmailField() def __str__(self): return self.name class Book(models.Model): nid = models.AutoField(primary_key=True,verbose_name="ID") title = models.CharField( max_length=32,verbose_name="名称") publishDate=models.DateField() price=models.DecimalField(max_digits=5,decimal_places=2,verbose_name="价格") # 与Publish建立一对多的关系,外键字段建立在多的一方 publish=models.ForeignKey(to="Publish",to_field="nid",on_delete=models.CASCADE,verbose_name="出版社") # 与Author表建立多对多的关系,ManyToManyField可以建在两个模型中的任意一个,自动创建第三张表 authors=models.ManyToManyField(to='Author',) def __str__(self): return self.title
app01创建一个的stark.py
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
# 完成注册功能 from app01 import models from stark.service.stark import site,ModelStark from django.utils.safestring import mark_safe from django import forms class BookModelForm(forms.ModelForm): class Meta: model=models.Book fields="__all__" error_messages={ "title":{"required":" 该字段不能为空"} } class BookConfig(ModelStark): def authors_display(self,obj=None,is_header=False): if is_header: return "作者" author_list=obj.authors.all() name_list=[author.name for author in author_list] return ','.join(name_list) list_display = ["title","price","publish",authors_display,] list_display_links=["title","price"] model_form_class=BookModelForm search_fields = ["title","price",] def patch_delete(self,request,queryset): queryset.delete() patch_delete.desc="批量删除" def patch_init(self,request,queryset): queryset.update(price=0) patch_init.desc="批量初始化" actions=[patch_delete,patch_init] list_filter=["publish","authors"] site.register(models.Book,BookConfig) site.register(models.Publish) site.register(models.Author) site.register(models.AuthorDetail)
day81项目文件夹下的url.py文件
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
from django.conf.urls import url from django.contrib import admin from app01 import views from stark.service.stark import site urlpatterns = [ url(r'^admin/', admin.site.urls),
stark包下的service包里的stark.py
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
from django.conf.urls import url from django.shortcuts import HttpResponse,redirect,render from django.utils.safestring import mark_safe from django.urls import reverse from stark.utils.page import Pagination from django.db.models import Q class Showlist(object): def __init__(self,conf_obj,queryset,request): self.conf_obj=conf_obj self.queryset=queryset self.request=request # 分页 current_page=self.request.GET.get("page") pagination=Pagination(current_page,self.queryset.count(),self.request.GET,per_page_num=2) self.pagination=pagination self.page_queryset=self.queryset[self.pagination.start:self.pagination.end] def get_header(self): # 处理表头 # header_list=["名称","价格","出版社"] header_list = [] for field_or_func in self.conf_obj.get_new_list_display(): # ["title","price","publish",delete_col] if isinstance(field_or_func, str): if field_or_func == "__str__": val = self.conf_obj.model._meta.model_name.upper() else: field_obj = self.conf_obj.model._meta.get_field(field_or_func) val = field_obj.verbose_name else: val = field_or_func(self.conf_obj, is_header=True) header_list.append(val) return header_list def get_body(self): # 处理表单数据 data_list = [] for obj in self.page_queryset: # [obj1,obj2,obj3] temp = [] for field_or_func in self.conf_obj.get_new_list_display(): # list_display = ["title","price","publish",delete_col] if isinstance(field_or_func, str): val = getattr(obj, field_or_func) if field_or_func in self.conf_obj.list_display_links: val = mark_safe("<a href='%s'>%s</a>" % (self.conf_obj.get_reverse_url("change", obj), val)) else: val = field_or_func(self.conf_obj, obj) temp.append(val) data_list.append(temp) print(data_list) return data_list def get_actions(self): temp=[] for func in self.conf_obj.actions:# [patch_delete,] temp.append({ "name":func.__name__, "desc":func.desc }) return temp # [{"name":"patch_delete","desc":"批量删除"},] def get_filter_links(self): print("self.conf_obj.list_filter",self.conf_obj.list_filter) # ['publish', 'authors'] links_dict={} for filter_field in self.conf_obj.list_filter: # ['publish', 'authors'] filter_field_obj=self.conf_obj.model._meta.get_field(filter_field) print(filter_field_obj) print(type(filter_field_obj)) from django.db.models.fields.related import ForeignKey print("rel",filter_field_obj.rel.to) queryset=filter_field_obj.rel.to.objects.all() print("queryset",queryset) temp=[] import copy params=copy.deepcopy(self.request.GET) # 渲染标签 current_filter_field_id=self.request.GET.get(filter_field) # all 的链接标签 params2 = copy.deepcopy(self.request.GET) if filter_field in params2: params2.pop(filter_field) all_link="<a href='?%s'>All</a>"%params2.urlencode() else: all_link = "<a href=''>All</a>" temp.append(all_link) for obj in queryset: params[filter_field]=obj.pk _url=params.urlencode() if current_filter_field_id==str(obj.pk): s = "<a class='item active' href='?%s'>%s</a>" % (_url, str(obj)) else: s="<a class='item' href='?%s'>%s</a>"%(_url,str(obj)) temp.append(s) links_dict[filter_field]=temp return links_dict class ModelStark(): # 默认配置类对象 def __init__(self,model): self.model=model self.model_name = self.model._meta.model_name self.app_label = self.model._meta.app_label self.app_model_name=(self.app_label,self.model_name) self.key_word="" list_display=["__str__"] list_display_links=[] model_form_class=[] actions=[] search_fields=[] list_filter=[] # 反向解析出增删改查的url # # 删除url # def get_delete_url(self,obj): # url_name = "%s_%s_delete" % self.app_model_name # _url = reverse(url_name, args=(obj.pk,)) # # return _url # # # 编辑url # def get_change_url(self, obj): # url_name = "%s_%s_change" % self.app_model_name # _url = reverse(url_name, args=(obj.pk,)) # # return _url # # # # 查看url # def get_list_url(self): # url_name = "%s_%s_list" % self.app_model_name # _url = reverse(url_name) # # return _url # # # 添加url # def get_add_url(self, obj): # url_name = "%s_%s_add" % self.app_model_name # _url = reverse(url_name, args=(obj.pk,)) # # return _url def get_reverse_url(self, type,obj=None): url_name = "%s_%s_%s" % (self.app_label,self.model_name,type) if obj: _url = reverse(url_name, args=(obj.pk,)) else: _url = reverse(url_name) return _url # 选择,删除,编辑按钮 def delete_col(self,obj=None,is_header=False): if is_header: return "删除" return mark_safe("<a href='%s'>删除</a>"%self.get_reverse_url("delete",obj)) def edit_col(self,obj=None,is_header=False): if is_header: return "编辑" return mark_safe("<a href='%s'>编辑</a>"%(self.get_reverse_url("change",obj))) def check_col(self,obj=None,is_header=False): if is_header: return "选择" return mark_safe("<input type='checkbox' name='selected_action' value='%s'>"%obj.pk) def get_new_list_display(self): new_list_display=[] new_list_display.extend(self.list_display) if not self.list_display_links: new_list_display.append(ModelStark.edit_col) new_list_display.append(ModelStark.delete_col) new_list_display.insert(0,ModelStark.check_col) return new_list_display def search_filter(self,request,queryset): # search 操作 key_word = request.GET.get("q") print(self.search_fields) # ["title","price"] self.key_word = "" if key_word: self.key_word=key_word search_condition = Q() search_condition.connector = "or" for field in self.search_fields: search_condition.children.append((field + "__icontains", key_word)) queryset = queryset.filter(search_condition) return queryset def filter_list(self,request,queryset): # filter 操作 filter_condition = Q() for key, val in request.GET.items(): # publish=2&authors=1 filter_condition.children.append((key, val)) if filter_condition: queryset=queryset.filter(filter_condition) return queryset def list_view(self,request): # action操作 if request.method=="POST": action=request.POST.get("action") pk_list=request.POST.getlist("selected_action") queryset=self.model.objects.filter(pk__in=pk_list) func=getattr(self,action) func(request,queryset) # 用户访问的模型表: self.model # print("self.model:", self.model) queryset = self.model.objects.all() # print("self.list_display", self.list_display) # ["nid","title","price","publish"] # search 操作 queryset=self.search_filter(request,queryset) queryset=self.filter_list(request,queryset) showlist=Showlist(self,queryset,request) # 获取添加url add_url=self.get_reverse_url("add") return render(request, "stark/list_view.html", locals()) def get_model_form_class(self): if self.model_form_class: return self.model_form_class else: from django import forms class ModelFormDemo(forms.ModelForm): class Meta: model = self.model fields = "__all__" return ModelFormDemo def add_view(self, request): """ if GET请求: GET请求: form = BookModelForm() form:渲染 if POST请求: form = BookModelForm(request.POST) form.is_valid() form.save() # 添加数据 create :param request: :return: """ ModelFormDemo=self.get_model_form_class() from django.forms.boundfield import BoundField from django.forms.models import ModelChoiceField if request.method=="GET": form=ModelFormDemo() for bfield in form : # print(type(bfield.field)) if isinstance(bfield.field,ModelChoiceField): bfield.is_pop=True filed_rel_model=self.model._meta.get_field(bfield.name).rel.to model_name=filed_rel_model._meta.model_name app_label=filed_rel_model._meta.app_label _url=reverse("%s_%s_add"%(app_label,model_name)) bfield.url=_url+"?pop_back_id="+bfield.auto_id return render(request,"stark/add_view.html",locals()) else: form=ModelFormDemo(request.POST) if form.is_valid(): obj=form.save() pop_back_id=request.GET.get("pop_back_id") if pop_back_id: pk=obj.pk text=str(obj) return render(request,"stark/pop.html",locals()) return redirect(self.get_reverse_url("list")) else: return render(request, "stark/add_view.html", locals()) def change_view(self, request,id): """ edit_book = Book.objects.get(pk=id) GET: form = BookModelForm(instance=edit_book) form:渲染 POST: form = BookModelForm(request.POST, instance=edit_book) form.is_valid form.save() # 更新数据 update :param request: :param id: :return: """ ModelFormDemo=self.get_model_form_class() edit_obj=self.model.objects.get(pk=id) if request.method=="GET": form=ModelFormDemo(instance=edit_obj) return render(request, "stark/change_view.html",locals()) else: form=ModelFormDemo(data=request.POST,instance=edit_obj) if form.is_valid(): form.save() return redirect(self.get_reverse_url("list")) else: return render(request, "stark/change_view.html", locals()) def delete_view(self, request,id): if request.method=="POST": self.model.objects.get(pk=id).delete() return redirect(self.get_reverse_url("list")) list_url=self.get_reverse_url("list") return render(request,"stark/delete_view.html",locals()) def get_urls(self): temp=[ url("^$",self.list_view,name="%s_%s_list"%(self.app_model_name)), url("^add/$",self.add_view,name="%s_%s_add"%(self.app_model_name)), url("^(\d+)/change/$",self.change_view,name="%s_%s_change"%(self.app_model_name)), url("^(\d+)/delete/$",self.delete_view,name="%s_%s_delete"%(self.app_model_name)), ] return temp @property def urls(self): return self.get_urls(),None,None class StarkSite(object): def __init__(self, name='admin'): self._registry = {} def register(self, model, admin_class=None, **options): if not admin_class: admin_class = ModelStark # 配置类 self._registry[model] = admin_class(model) # {Book:BookConfig(Book),Publish:ModelAdmin(Publish)} def get_urls(self): temp = [ ] for model_class, config_obj in self._registry.items(): print("===>", model_class, config_obj) model_name = model_class._meta.model_name app_label = model_class._meta.app_label print("===>", app_label, model_name,) temp.append(url(r'^%s/%s/' % (app_label, model_name),config_obj.urls)) ''' 创建url: url("app01/book/$",self.list_view,name="app01_book_list"), url("app01/book/add$",self.add_view,name="app01_book_add"), url("app01/book/(\d+)/change/$",self.change_view), url("app01/book/(\d+)/delete/$",self.delete_view), url("app01/publish/$",self.list_view,name="app01_publish_list"), url("app01/publish/add$",self.add_view,name="app01_publish_add"), url("app01/publish/(\d+)/change/$",self.change_view), url("app01/publish/(\d+)/delete/$",self.delete_view), ''' return temp @property def urls(self): return self.get_urls(),None,None site=StarkSite()
utils包里的page.py(分页)
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
""" 分页组件使用示例: obj = Pagination(request.GET.get('page',1),len(USER_LIST),request.path_info) page_user_list = USER_LIST[obj.start:obj.end] page_html = obj.page_html() return render(request,'index.html',{'users':page_user_list,'page_html':page_html}) """ class Pagination(object): def __init__(self,current_page,all_count,params,per_page_num=10,pager_count=11): """ 封装分页相关数据 :param current_page: 当前页 :param all_count: 数据库中的数据总条数 :param per_page_num: 每页显示的数据条数 :param base_url: 分页中显示的URL前缀 :param pager_count: 最多显示的页码个数 all_count;100 per_page_num:8 current_page start (current_page-1)*per_page_num end (current_page*per_page_num) 1 0 8 2 8 16 3 16 24 情况1:self.current_page-self.pager_count_half<1 2 3 5 pager_start=1 pager_end=self.pager_count+1 情况2: 8 9 10 11 12 13 14 15 16 17 18 15 20 25 pager_start = self.current_page-self.pager_count_half pager_end = self.current_page+self.pager_count_half+1 情况3:self.current_page+self.pager_count_half>self.all_pager 48 47 79 pager_start = self.all_pager-self.pager_count pager_end =self.all_pager+1 """ try: current_page = int(current_page) except Exception as e: current_page = 1 if current_page <1: current_page = 1 self.current_page = current_page self.all_count = all_count self.per_page_num = per_page_num # 总页码 all_pager, tmp = divmod(all_count, per_page_num) if tmp: all_pager += 1 self.all_pager = all_pager self.pager_count = pager_count self.pager_count_half = int((pager_count - 1) / 2) # 5 # import copy self.params=copy.deepcopy(params) @property def start(self): return (self.current_page - 1) * self.per_page_num @property def end(self): return self.current_page * self.per_page_num def page_html(self): # 如果总页码 < 11个: if self.all_pager <= self.pager_count: pager_start = 1 pager_end = self.all_pager + 1 # 总页码 > 11 else: # 当前页如果<=页面上最多显示11/2个页码 if self.current_page <= self.pager_count_half: pager_start = 1 pager_end = self.pager_count + 1 # 当前页大于5 else: # 页码翻到最后 if (self.current_page + self.pager_count_half) > self.all_pager: pager_start = self.all_pager - self.pager_count + 1 pager_end = self.all_pager + 1 else: pager_start = self.current_page - self.pager_count_half pager_end = self.current_page + self.pager_count_half + 1 ######################################################################### page_html_list = [] first_page = '<li><a href="?page=%s">首页</a></li>' % (1,) page_html_list.append(first_page) if self.current_page <= 1: prev_page = '<li class="disabled"><a href="#">上一页</a></li>' else: prev_page = '<li><a href="?page=%s">上一页</a></li>' % (self.current_page - 1,) page_html_list.append(prev_page) # self.params {"page":7,"xxx":123} for i in range(pager_start, pager_end): self.params["page"]=i if i == self.current_page: temp = '<li class="active"><a href="?page=%s">%s</a></li>' % (i, i,) else: temp = '<li><a href="?%s">%s</a></li>' % (self.params.urlencode(), i,) page_html_list.append(temp) if self.current_page >= self.all_pager: next_page = '<li class="disabled"><a href="#">下一页</a></li>' else: next_page = '<li><a href="?page=%s">下一页</a></li>' % (self.current_page + 1,) page_html_list.append(next_page) last_page = '<li><a href="?page=%s">尾页</a></li>' % (self.all_pager,) page_html_list.append(last_page) return ''.join(page_html_list)
stark包里的apps.py
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
from django.apps import AppConfig from django.utils.module_loading import autodiscover_modules class StarkConfig(AppConfig): name = 'stark' def ready(self): autodiscover_modules('stark')
前端页面:
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <title>Title</title> <meta name="viewport" content="width=device-width, initial-scale=1"> <link rel="stylesheet" href="/static/bootstrap-3.3.7/css/bootstrap.min.css"> <link rel="stylesheet" href="/static/css/commonn.css"> </head> <body> {% include "stark/class_form.html" %} <script> function show_option(pop_back_id,pk,text) { console.log(pop_back_id,pk,text); var option=document.createElement("option"); option.innerHTML=text; option.value=pk; option.selected="selected"; select=document.getElementById(pop_back_id); select.appendChild(option); } </script> </body> </html>
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <title>Title</title> <meta name="viewport" content="width=device-width, initial-scale=1"> <link rel="stylesheet" href="/static/bootstrap-3.3.7/css/bootstrap.min.css"> <link rel="stylesheet" href="/static/css/commonn.css"> </head> <body> {% include "stark/class_form.html" %} </body> </html>
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
<div class="container"> <div class="row"> <div class="col-md-6 col-md-offset-3"> <form action="" method="post" novalidate> {% csrf_token %} {% for field in form %} <div class="form-group field_region"> <label for="">{{ field.label }}</label> {{ field }} <span class="erorr pull-right">{{ field.errors.0 }}</span> {% if field.is_pop %} <span class="plus"><a onclick="add_option('{{ field.url }}')">+</a></span> {% endif %} </div> {% endfor %} <input type="submit" class="btn btn-default pull-right"> </form> </div> </div> </div> <script> function add_option(url) { window.open(url,"","height=500,width=800,top=100,left=100") } </script>
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <title>Title</title> <meta name="viewport" content="width=device-width, initial-scale=1"> </head> <body> <form action="" method="post"> {% csrf_token %} <input type="submit" value="确认删除"> <a href="{{ list_url }}">取消</a> </form> </body> </html>
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <title>Title</title> <meta name="viewport" content="width=device-width, initial-scale=1"> <link rel="stylesheet" href="/static/bootstrap-3.3.7/css/bootstrap.min.css"> <script src="/static/jquery-3.3.1.min.js"></script> <style> .table { margin-top: 20px; } .action { width: 40%; margin-top: 10px; } .item{ color: gray; } .active{ color: red; } </style> </head> <body> <h3>数据展示</h3> <div class="container"> <div class="row"> <div class="col-md-8 col-md-offset-1"> <a href="{{ add_url }}" class="btn btn-success">添加数据</a> {% if showlist.conf_obj.search_fields %} <form class="form-inline pull-right"> <div class="form-group"> <div class="input-group"> <input type="text" class="form-control" name="q" value="{{ showlist.conf_obj.key_word }}" placeholder="关键字"> </div> </div> <button type="submit" class="btn btn-info">Search</button> </form> {% endif %} <form action="" class="form-inline" method="post"> {% csrf_token %} <select class="form-control action" name="action"> <option>-----------------------</option> {% for func_dict in showlist.get_actions %} <option value="{{ func_dict.name }}">{{ func_dict.desc }}</option> {% endfor %} </select> <input type="submit" class="btn btn-warning" style="vertical-align: -8px"> <table cl class="table table-bordered table-striped table-hover"> <thead> <tr> {% for item in showlist.get_header %} <td>{{ item }}</td> {% endfor %} </tr> </thead> <tbody> {% for data in showlist.get_body %} <tr> {% for foo in data %} <td>{{ foo }}</td> {% endfor %} </tr> {% endfor %} </tbody> </table> </form> <div> <nav aria-label="..."> <ul class="pagination"> {{ showlist.pagination.page_html|safe }} </ul> </nav> </div> </div> <div class="col-md-3"> <div class="filter_region"> <div class="alert-info text-center">FILTER</div> {% for key,val in showlist.get_filter_links.items %} <div class="panel panel-default"> <div class="panel-heading">By {{ key|upper }}</div> <div class="panel-body"> {% for link in val %} <p>{{ link|safe }}</p> {% endfor %} </div> </div> {% endfor %} </div> </div> </div> </div> </body> </html>
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <title>Title</title> <meta name="viewport" content="width=device-width, initial-scale=1"> </head> <body> <h1>POP</h1> <script> window.opener.show_option("{{ pop_back_id }}",'{{ pk }}','{{ text }}'); window.close(); </script> </body> </html>