10.18正式开发stark组件*(三)

时间:2022-04-02 21:52:00

2018-10-18 19:15:54

等这个stark组件做完了再上传到github上面,然后再整理博客!这就到周末啦!

因为models导入的时候出现bug,所以只有源码没有测试数据!

源码都有注释,已经很详细啦! 一步一步能看懂!

里面重要的思想就是 用类封装,组件用模块封装! 然后解耦!把重复的东西封装成类!面向对象编程!

看源码也许是一种享受!

越努力,与幸运!永远不要高估自己!

新增了 搜索框!和批量操作!

批量操作没有默认的删除可以迭代自行完成!批量操作需要用户自己定制函数

先放上用到的新的知识点!

# Q 查询的两种方式
# Book.objects.filter(Q(title="yuan")|Q(price=123))
#
# 第二种方式可以传入字符串
# q=Q()
# q.connection="or"
# q.children.append(("title","yuan"))
# q.children.append(("price",123)) # 模糊查找
# ret=self.model.objects.filter(title__startswith="py")
# ret=self.model.objects.filter(price__in=[12,34,56,78,222])
# ret=self.model.objects.filter(price__range=[10,100])
# ret=self.model.objects.filter(title__contains="o")
# ret=self.model.objects.filter(title__icontains="o")
# print(ret)

10.18正式开发stark组件*(三)

分页的组件(只要传好参数就可以直接用)

utils/page.py

import copy

# 自定义分页组件   注意参数
class Pagination(object):
def __init__(self, current_page, all_count, base_url, params, per_page_num=8, pager_count=11, ):
"""
封装分页相关数据
:param params: 接收url里面的参数也就是键值对?xx=2&xx=3&xx=4 类似于这样的
:param current_page: 当前页
:param all_count: 数据库中的数据总条数
:param per_page_num: 每页显示的数据条数
:param base_url: 分页中显示的URL前缀
:param pager_count: 最多显示的页码个数
""" try:
current_page = int(current_page)
except Exception as e:
current_page = 1 if current_page < 1:
current_page = 1
self.current_page = current_page
self.all_count = all_count
self.per_page_num = per_page_num
self.base_url = base_url
# 总页码
all_pager, tmp = divmod(all_count, per_page_num)
if tmp:
all_pager += 1
self.all_pager = all_pager
self.pager_count = pager_count # 最多显示页码数
self.pager_count_half = int((pager_count - 1) / 2)
params = copy.deepcopy(params)
params._mutable = True
self.params = params # self.params : {"page":77,"title":"python","nid":1} @property
def start(self):
return (self.current_page - 1) * self.per_page_num @property
def end(self):
return self.current_page * self.per_page_num def page_html(self):
# 如果总页码 < 11个:
if self.all_pager <= self.pager_count:
pager_start = 1
pager_end = self.all_pager + 1
# 总页码 > 11
else:
# 当前页如果<=页面上最多显示(11-1)/2个页码
if self.current_page <= self.pager_count_half:
pager_start = 1
pager_end = self.pager_count + 1
# 当前页大于5
else:
# 页码翻到最后
if (self.current_page + self.pager_count_half) > self.all_pager:
pager_start = self.all_pager - self.pager_count + 1
pager_end = self.all_pager + 1
else:
pager_start = self.current_page - self.pager_count_half
pager_end = self.current_page + self.pager_count_half + 1 page_html_list = []
self.params["page"] = 1
first_page = '<li><a href="%s?%s">首页</a></li>' % (self.base_url, self.params.urlencode(),)
page_html_list.append(first_page) if self.current_page <= 1:
prev_page = '<li class="disabled"><a href="#">上一页</a></li>'
else:
self.params["page"] = self.current_page - 1
prev_page = '<li><a href="%s?%s">上一页</a></li>' % (self.base_url, self.params.urlencode(),) page_html_list.append(prev_page) for i in range(pager_start, pager_end):
# self.params : {"page":77,"title":"python","nid":1}
# 既保留每次循环的page 有保留了后面参数的值
# urlencode()是内置方法 把键值拼成url ?page=1&xx=22 类似于这样的
# 这样做法仅仅变页码page 然后后面参数也就是条件不变
self.params["page"] = i # {"page":72,"title":"python","nid":1}
if i == self.current_page:
temp = '<li class="active"><a href="%s?%s">%s</a></li>' % (self.base_url, self.params.urlencode(), i,)
else:
temp = '<li><a href="%s?%s">%s</a></li>' % (self.base_url, self.params.urlencode(), i,)
page_html_list.append(temp)
if self.current_page >= self.all_pager:
next_page = '<li class="disabled"><a href="#">下一页</a></li>'
else:
self.params["page"] = self.current_page + 1
next_page = '<li><a href="%s?%s">下一页</a></li>' % (self.base_url, self.params.urlencode(),)
page_html_list.append(next_page)
self.params["page"] = self.all_pager
last_page = '<li><a href="%s?%s">尾页</a></li>' % (self.base_url, self.params.urlencode(),)
page_html_list.append(last_page) return ''.join(page_html_list) # class Pagination(object):
#
# def __init__(self, data_num, current_page, url_prefix,params, per_page=10, max_show=4):
# """
# 进行初始化.
# :param data_num: 数据总数
# :param current_page: 当前页
# :param url_prefix: 生成的页码的链接前缀
# :param per_page: 每页显示多少条数据
# :param max_show: 页面最多显示多少个页码
# """
# self.data_num = data_num
# self.per_page = per_page
# self.max_show = max_show
# self.url_prefix = url_prefix
#
# # 把页码数算出来
# self.page_num, more = divmod(data_num, per_page)
# if more:
# self.page_num += 1
#
# try:
# self.current_page = int(current_page)
# except Exception as e:
# self.current_page = 1
# # 如果URL传过来的页码数是负数
# if self.current_page <= 0:
# self.current_page = 1
# # 如果URL传过来的页码数超过了最大页码数
# elif self.current_page > self.page_num:
# self.current_page = self.page_num # 默认展示最后一页
#
# # 页码数的一半 算出来
# self.half_show = max_show // 2
#
# # 页码最左边显示多少
# if self.current_page - self.half_show <= 1:
# self.page_start = 1
# self.page_end = self.max_show
# elif self.current_page + self.half_show >= self.page_num: # 如果右边越界
# self.page_end = self.page_num
# self.page_start = self.page_num - self.max_show
# else:
# self.page_start = self.current_page - self.half_show
# # 页码最右边显示
# self.page_end = self.current_page + self.half_show
#
#
# import copy
# self.params=copy.deepcopy(params) # {"page":"12","title_startwith":"py","id__gt":"5"}
#
#
#
# @property
# def start(self):
# # 数据从哪儿开始切
# return (self.current_page - 1) * self.per_page
#
# @property
# def end(self):
# # 数据切片切到哪儿
# return self.current_page * self.per_page
#
# def page_html(self):
# # 生成页码
# l = []
# # 加一个首页
# l.append('<li><a href="{}?page=1">首页</a></li>'.format(self.url_prefix))
# # 加一个上一页
# if self.current_page == 1:
# l.append('<li class="disabled" ><a href="#">«</a></li>'.format(self.current_page))
# else:
# l.append('<li><a href="{}?page={}">«</a></li>'.format(self.url_prefix, self.current_page - 1))
#
#
#
# # {"page":"12","title_startwith":"py","id__gt":"5"} # "page=12&title_startwith=py&id__gt=5"
#
#
# print(self.params.urlencode())
# for i in range(self.page_start, self.page_end + 1):
# self.params["page"]=i # # {"page":"7","title_startwith":"py","id__gt":"5"} # "page=7&title_startwith=py&id__gt=5"
# if i == self.current_page:
# tmp = '<li class="active"><a href="{0}?page={1}">{1}</a></li>'.format(self.url_prefix, i)
# else:
# tmp = '<li><a href="{0}?{1}">{2}</a></li>'.format(self.url_prefix, self.params.urlencode(),i)
# l.append(tmp)
#
#
#
#
#
#
#
# # 加一个下一页
# if self.current_page == self.page_num:
# l.append('<li class="disabled"><a href="#">»</a></li>'.format(self.current_page))
# else:
# l.append('<li><a href="{}?page={}">»</a></li>'.format(self.url_prefix, self.current_page + 1))
# # 加一个尾页
# l.append('<li><a href="{}?page={}">尾页</a></li>'.format(self.url_prefix, self.page_num))
# return "".join(l)

app01/models.py

from django.db import models

# Create your models here.

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 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)
title = models.CharField( max_length=32)
publishDate=models.DateField()
price=models.DecimalField(max_digits=5,decimal_places=2) # 与Publish建立一对多的关系,外键字段建立在多的一方
publish=models.ForeignKey(to="Publish",to_field="nid",on_delete=models.CASCADE)
# 与Author表建立多对多的关系,ManyToManyField可以建在两个模型中的任意一个,自动创建第三张表
authors=models.ManyToManyField(to='Author',)
def __str__(self):
return self.title

server/server.py

from django.conf.urls import url
from django.shortcuts import render, redirect
from django.urls import reverse
from django.utils.safestring import mark_safe
from django.forms import ModelForm
from stark.utils.page import Pagination
from django.db.models import Q class ShowList(object):
# 这是一个配置类的对象初始化
def __init__(self, config, data_list, request):
self.config = config
self.data_list = data_list
self.request = request
# 分页
data_count = self.data_list.count()
current_page = int(self.request.GET.get("page", 1))
base_path = self.request.path
self.pagination = Pagination(current_page, data_count, base_path, self.request.GET, per_page_num=3, pager_count=11,)
self.page_data = self.data_list[self.pagination.start:self.pagination.end]
# actions 获取actions这个配置类的列表
self.actions = self.config.actions # [patch_init,] # 获取下拉框 用户配置的action_list
def get_action_list(self):
temp = []
for action in self.actions:
# [{"name":""patch_init,"desc":"批量初始化"}]
temp.append({
"name": action.__name__,
"desc": action.short_description
})
return temp # 构建表头
def get_header(self):
header_list = []
print("header", self.config.new_list_play())
# [checkbox,"pk","name","age",edit ,deletes] 【checkbox ,"__str__", edit ,deletes】
for field in self.config.new_list_play(): if callable(field):
# header_list.append(field.__name__)
val = field(self.config, header=True)
header_list.append(val) else:
if field == "__str__":
header_list.append(self.config.model._meta.model_name.upper())
else:
# header_list.append(field)
val = self.config.model._meta.get_field(field).verbose_name
header_list.append(val)
return header_list # 构建表单数据
def get_body(self):
new_data_list = []
for obj in self.page_data:
temp = []
for filed in self.config.new_list_play(): # ["__str__",] ["pk","name","age",edit]
if callable(filed):
val = filed(self.config, obj)
else:
val = getattr(obj, filed)
if filed in self.config.list_display_links:
# "app01/userinfo/(\d+)/change"
_url = self.config.get_change_url(obj) val = mark_safe("<a href='%s'>%s</a>" % (_url, val))
temp.append(val)
new_data_list.append(temp)
return new_data_list class ModelStark(object):
# 默认的list_play[]
list_display = ["__str__", ]
list_display_links = []
modelform_class = None
search_fields = []
actions = [] def __init__(self, model, site):
self.model = model
self.site = site # 配置表头: 删除 编辑,复选框
def edit(self, obj=None, header=False):
"""编辑"""
if header:
return "操作"
# return mark_safe("<a href='%s/change'>编辑</a>"%obj.pk)
_url = self.get_change_url(obj)
return mark_safe("<a href='%s'>编辑</a>" % _url) def deletes(self, obj=None, header=False):
"""删除"""
if header:
return "操作"
# return mark_safe("<a href='%s/change'>编辑</a>"%obj.pk)
_url = self.get_delete_url(obj)
return mark_safe("<a href='%s'>删除</a>" % _url) def checkbox(self, obj=None, header=False):
"""复选框"""
if header:
return mark_safe('<input id="choice" type="checkbox">')
# value的值不能写死,
return mark_safe('<input class="choice_item" type="checkbox" name="selected_pk" value="%s">' % obj.pk) # 获取配置类的表头信息
def get_modelform_class(self):
"""获取表的配置类"""
if not self.modelform_class:
# 如果表的配置类为空
class ModelFormDemo(ModelForm):
class Meta:
model = self.model
fields = "__all__"
labels = {
""
}
return ModelFormDemo
else:
return self.modelform_class # 添加的视图函数
def add_view(self, request):
ModelFormDemo = self.get_modelform_class()
if request.method == "POST":
form = ModelFormDemo(request.POST)
if form.is_valid():
form.save()
return redirect(self.get_list_url()) return render(request, "add_view.html", locals()) form = ModelFormDemo() return render(request, "add_view.html", locals()) # 删除的视图函数
def delete_view(self, request, id):
url = self.get_list_url()
if request.method == "POST":
self.model.objects.filter(pk=id).delete()
return redirect(url)
return render(request, "delete_view.html", locals()) # 编辑的视图函数
def change_view(self, request, id):
ModelFormDemo = self.get_modelform_class()
edit_obj = self.model.objects.filter(pk=id).first()
if request.method == "POST":
form = ModelFormDemo(request.POST, instance=edit_obj)
if form.is_valid():
form.save()
return redirect(self.get_list_url())
return render(request, "add_view.html", locals())
form = ModelFormDemo(instance=edit_obj)
return render(request, "change_view.html", locals()) # 搜索的视图函数
def get_serach_conditon(self, request):
key_word = request.GET.get("q", "")
self.key_word = key_word
search_connection = Q()
if key_word:
# self.search_fields # ["title","price"]
search_connection.connector = "or"
# 用Q的这种添加方法可以添加字符串
for search_field in self.search_fields:
# search_field+"__contains" ----> title__contains="o" 就是title字段里面包含字母o的
search_connection.children.append((search_field + "__contains", key_word))
return search_connection # 查看的视图函数
def list_view(self, request):
if request.method == "POST": # action
print("POST:", request.POST)
action = request.POST.get("action")
selected_pk = request.POST.getlist("selected_pk")
# 通过getattr()传入函数名的字符串和调用该函数的对象 拿到该函数的变量
action_func = getattr(self, action)
queryset = self.model.objects.filter(pk__in=selected_pk)
# 把request和queryset对象传给函数,然后执行,最后return给用户配置的那个函数
ret = action_func(request, queryset)
return ret
# 获取search的Q对象
search_connection = self.get_serach_conditon(request)
# 筛选获取当前表所有数据
data_list = self.model.objects.all().filter(search_connection) # 【obj1,obj2,....】
# 按这ShowList展示页面
showlist=ShowList(self, data_list, request)
# 构建一个查看URL
add_url = self.get_add_url()
return render(request, "list_view.html", locals()) # 获取用户配置类里面的list_play[]
def new_list_play(self):
temp = []
temp.append(ModelStark.checkbox)
temp.extend(self.list_display)
if not self.list_display_links:
temp.append(ModelStark.edit)
temp.append(ModelStark.deletes)
return temp """把url进行反向解析,解耦到各自的函数中,函数中直接返回了对应的url"""
# 获取修改页面的url
def get_change_url(self, obj):
model_name = self.model._meta.model_name
app_label = self.model._meta.app_label _url = reverse("%s_%s_change" % (app_label, model_name), args=(obj.pk,)) return _url # 获删除改页面的url
def get_delete_url(self, obj):
model_name = self.model._meta.model_name
app_label = self.model._meta.app_label _url = reverse("%s_%s_delete" % (app_label, model_name), args=(obj.pk,)) return _url # 获取添加页面的url
def get_add_url(self): model_name = self.model._meta.model_name
app_label = self.model._meta.app_label _url = reverse("%s_%s_add" % (app_label, model_name)) return _url # 获取查看页面的url
def get_list_url(self): model_name = self.model._meta.model_name
app_label = self.model._meta.app_label _url = reverse("%s_%s_list" % (app_label, model_name)) return _url # 二级url分发函数
def get_urls_2(self): temp = [] model_name = self.model._meta.model_name
app_label = self.model._meta.app_label temp.append(url(r"^add/", self.add_view, name="%s_%s_add" % (app_label, model_name)))
temp.append(url(r"^(\d+)/delete/", self.delete_view, name="%s_%s_delete" % (app_label, model_name)))
temp.append(url(r"^(\d+)/change/", self.change_view, name="%s_%s_change" % (app_label, model_name)))
temp.append(url(r"^$", self.list_view, name="%s_%s_list" % (app_label, model_name))) return temp @property
def urls_2(self):
print(self.model)
return self.get_urls_2(), None, None class StarkSite(object):
def __init__(self):
self._registry = {} def register(self, model, stark_class=None):
if not stark_class:
stark_class = ModelStark self._registry[model] = stark_class(model, self) # 一级分发url函数
def get_urls(self):
temp = []
for model, stark_class_obj in self._registry.items():
model_name = model._meta.model_name
app_label = model._meta.app_label
# 分发增删改查
temp.append(url(r"^%s/%s/" % (app_label, model_name), stark_class_obj.urls_2)) '''
url(r"^app01/userinfo/",UserConfig(Userinfo).urls_2),
url(r"^app01/book/",ModelStark(Book).urls_2), '''
return temp @property
def urls(self): return self.get_urls(), None, None # 创建stark的一个单例对象
site = StarkSite()

list.html

<!DOCTYPE html>
<html lang="zh-CN">
<head>
<meta charset="UTF-8">
<title>Title</title>
<meta name="viewport" content="width=device-width, initial-scale=1">
<link rel="stylesheet" href="/static/bootstrap/css/bootstrap.css">
<script src="/static/js/jquery-1.12.4.min.js"></script>
</head>
<body> <h4>数据列表</h4> <div class="container">
<div class="row">
<div class="col-md-9">
<a href="{{ add_url }}" class="btn btn-primary">添加数据</a> {% if showlist.config.search_fields %}
<form action="" class="pull-right">
<input type="text" name="q" value="{{ showlist.config.key_word }}"><button>submit</button>
</form>
{% endif %} <form action="" method="post">
{% csrf_token %}
<select name="action" id="" style="width: 200px;padding: 5px 8px;display: inline-block">
{% for item in showlist.get_action_list %}
<option value="">---------------</option>
<option value="{{ item.name }}">{{ item.desc }}</option>
{% endfor %} </select><button type="submit" class="btn btn-info">Go</button>
<table class="table table-bordered table-striped">
<thead>
<tr>
{% for item in showlist.get_header %}
<th>{{ item }}</th>
{% endfor %} </tr> </thead>
<tbody>
{% for data in showlist.get_body %} <tr>
{% for item in data %}
<td>{{ item }}</td>
{% endfor %} </tr>
{% endfor %} </tbody>
</table>
<nav class="pull-right">
<ul class="pagination">
{{ showlist.pagination.page_html|safe }}
</ul>
</nav> </form>
</div>
</div>
</div> <script> $("#choice").click(function () { if($(this).prop("checked")){
$(".choice_item").prop("checked",true)
}else {
$(".choice_item").prop("checked",false)
} }) </script>
</body>
</html>

app01/server.py

from stark.service.stark import site, ModelStark
from django.shortcuts import HttpResponse
from .models import *
from django.forms import ModelForm class BookModelForm(ModelForm):
class Meta:
model = Book
fields = "__all__"
labels = {
"title": "书籍名称",
"price": "价格"
} # Book表的配置类
class BookConfig(ModelStark):
# 自定义显示字段
list_display = ["title", "price", "publishDate"]
modelform_class = BookModelForm
# 自定义搜索字段
search_fields = ["title", "price"] # 自定义action函数,在下拉框中显示
def patch_init(self, request, queryset):
print(queryset, request)
queryset.update(price=123)
return HttpResponse("批量初始化OK") patch_init.short_description = "批量初始化"
actions = [patch_init] site.register(Book,BookConfig)
site.register(Publish)
site.register(Author)
site.register(AuthorDetail)

这个stark完完全仿照的admin !并且仿照的很成功!