Django之stark组件
现在让我说啥是stark组件,我也说不清楚。反正从今天讲的知识来看,今天完成的就是自己写一个模块,这个模块包含了admin后台管理工具的一些比较好用的功能,我们把它提炼出来,也就是相当于自己写一个admin后台管理工具,但这个工具不叫admin了,叫stark。既然是模仿admin的实现功能,所以整个执行过程和admin是一样的。首先我们得建一个stark包,即一个stark文件夹,在这里,我们也可以新建一个APP叫stark也行。
第一步,启动所有APP下的stark.py文件
每当我们创建一个新的APP时,文件夹下都会有一个admin.py的文件,这是django帮你做的,但现在的后台管理工具换成stark了,所以我们首先要在每个APP下新建一个stark.py的文件。怎么才能让django启动的时候,也把每个APP下的stark.py文件执行呢????我们知道哈,admin是在它的__init__.py文件写了
def autodiscover():
autodiscover_modules('admin', register_to=site)
django项目启动的时候会执行每个APP下的apps.py文件,我们可以把这句代码写stark文件夹下的apps.py文件里,这样就能保证django项目启动后会执行这一句代码,然后通过这句代码,执行每个APP下的stark.py文件,这样我们就启动了每个APP下的stark.py文件。
apps.py
from django.apps import AppConfig
from django.utils.module_loading import autodiscover_modules class StarkConfig(AppConfig):
name = 'stark'
def ready(self):
autodiscover_modules('stark')
第二步,该写stark的源码了,完成注册功能
我们在stark文件夹下新建一个service文件夹用来放源码。
sites.py源码文件
class ModelStark():
def __init__(self,model):
self.model=model class StarkSite():
def __init__(self):
self._registry={}
def register(self,model,stark_class=None):
stark_class = stark_class or ModelStark
self._registry[model] = stark_class(model)
site=StarkSite()
我们可以给app01下的Book表和Publish注册
app01下的stark.py
from stark.service.sites import site,ModelStark
from app01.models import *
class BookConfig(ModelStark):
list_dispaly = ['name','price']
site.register(Book,BookConfig)
site.register(Publish)
这样就完成注册了
第三步,实现url的二级分发
urls.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),
]
然后再去源码里添加内容
sites.py
class ModelStark():
def get_urls(self):
temp=[
path('',self.list_view),
path('add/',self.add_view),
re_path('(\d+)/delete/',self.delete_view),
re_path('(\d+)/edit/',self.edit_view),
]
return temp
@property
def urls(self):
return self.get_urls(), None, None class StarkSite():
def get_urls(self):
temp=[]
for model,config_obj in self._registry.items():
model_name = model._meta.model_name
app_label = model._meta.app_label
temp.append(path('%s/%s/'%(app_label,model_name),config_obj.urls))
return temp @property
def urls(self):
return self.get_urls(),None, None
这个二级分发的想法是很巧秒的,一级分发在StarkSite类里面定义的,二级分发是在配置类里面定义的。调用视图函数的对象变成了配置类对象。在注册模型类的时候,我们就说了,我们可以给每个模型类定制一个配置类,然后去继承ModelStark,从而每个模型类可以重新定义自己的变量,也可以使用ModelStark类的变量,这样变的很灵活。到此,stark的主要功能就写完了,接下来我只需对每个视图写上逻辑和自己定制templates模板,就完成了,说的很简单,但其实并不是那么简单。
第四步,首先写一个查询的视图函数和模板
我们把视图定义成Modelstark的一个方法,直接用模型类对象就可以调用。我们首先在stark文件下新建一个templates文件夹,django在找模板时,会现在全局找,然后再一个一个APP下找,所以我们不用修改任何设置就行。静态文件也是一个道理,如bootstrap文件和jQuery文件,我们直接在stark文件下新建一个static文件夹,然后放在里面就行,它也是先在全局找,然后再一个一个APP下找。我们为什么要把这些文件放在stark文件下,只是为了我们写的这个stark模块,以后用在任何项目里都可以,只需要把stark文件夹拷走就行了。
sites.py
class ModelStark:
list_dispaly=['__str__']
def _checkbox(self,obj=None):
if obj:
return mark_safe('<input type="checkbox" name="check" vlaue="%s">'%obj.pk)
return '选择'
def _delete(self,obj=None):
if obj:
model_name = self.model._meta.model_name
app_label = self.model._meta.app_label
return mark_safe('<a href="/stark/%s/%s/%s/delete">删除</a>'%(app_label,model_name,obj.pk))
return '删除'
def _edit(self,obj=None):
if obj:
model_name = self.model._meta.model_name
app_label = self.model._meta.app_label
return mark_safe('<a href="/stark/%s/%s/%s/edit">编辑</a>' % (app_label, model_name, obj.pk))
return '编辑' def get_new_list_display(self):
new_list_display=self.list_dispaly
new_list_display.insert(0,self._checkbox)
new_list_display.append(self._edit)
new_list_display.append(self._delete)
return new_list_display
def list_view(self,request):
queryset=self.model.objects.all()
header=[]
for field in self.get_new_list_display():
if field=='__str__':
header.append(self.model._meta.model_name.upper())
elif callable(field):
header.append(field())
else:
header.append(self.model._meta.get_field(field).verbose_name)
data=[]
for obj in queryset:
tr=[]
for field in self.list_dispaly:
if callable(field):
val=field(obj)
else:
val=getattr(obj,field)
tr.append(val)
data.append(tr)
return render(request,'list_view.html',locals())
list_view.html
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Title</title>
<link rel="stylesheet" href="/static/css/bootstrap.css">
<script src="/static/js/bootstrap.js"></script>
<script src="/static/jquery-3.3.1.js"></script>
</head>
<body>
<div class="container">
<div class="title">所有书籍</div>
<table class="table table-bordered table-hover table-striped">
<thead>
<tr>
{% for foo in header %}
<td>{{ foo }}</td>
{% endfor %}
</tr>
</thead>
<tbody id="tbody">
{% for book in data %}
<tr>
{% for foo in book %}
<td>{{ foo }}</td>
{% endfor %}
</tr>
{% endfor %}
</tbody>
</table>
</div>
</body>
</html>
学习stark第二天,今天完成的功能不多,主要是url的反向解析,利用ModelForm完成了添加和编辑页面的搭建和数据的校验,在这还是不得不说ModelForm真心地好用,开发这个东西的大佬确实用了心的,感谢大佬。最后写了一个删除函数。
一、实现url的反向解析
在前面的博客,我们就讲了反向解析的好处,在这就扯它的好处了,直接上代码。
sites.py
二级分发的代码稍有改动
self.model_name = model._meta.model_name
self.app_label = model._meta.app_label
self.app_model=(self.app_label,self.model_name)
def get_urls(self):
temp=[
path('',self.list_view,name='%s_%s'%self.app_model),
path('add/',self.add_view,name='%s_%s_add'%self.app_model),
re_path('(\d+)/delete/',self.delete_view,name='%s_%s_delete'%self.app_model),
re_path('(\d+)/edit/',self.edit_view,name='%s_%s_edit'%self.app_model),
]
return temp
写四个方法,通过反向解析分别拿到四个视图函数对应的路径,
def get_list_url(self):
return reverse('%s_%s'%self.app_model)
def get_add_url(self):
return reverse('%s_%s_add'%self.app_model)
def get_delete_url(self,obj):
return reverse('%s_%s_delete'%self.app_model,args=(obj.pk,))
def get_edit_url(self,obj):
return reverse('%s_%s_edit'%self.app_model,args=(obj.pk,))
以后哪里用到相关路径,直接调用方法就行了,如下:
def _delete(self,obj=None):
if obj:
return mark_safe('<a url="%s" class="delete btn btn-primary">删除</a>'%self.get_delete_url(obj))
return '删除'
def _edit(self,obj=None):
if obj:
return mark_safe('<a href="%s">编辑</a>' % self.get_edit_url(obj))
return '编辑'
二、完成添加和编辑页面和数据校验
首先得创建一个ModelForm类,为了灵活性,即用户可以自定义这个类,我们模仿了配置类写法。首先我们在默认的配置中写了一个方法,可以拿到我写的默认用的ModelForm类,然后用户可以在自定义的配置类里面重写这个方法,在放下重新定义一个ModelForm类,它就会覆盖默认的ModelForm类,从而用自定义的。
sites.py
def get_model_form_class(self):
class Model_Form(forms.ModelForm):
class Meta:
model=self.model
fields='__all__'
return Model_Form
app01用户的自定义配置类里可以重写这方法:
stark.py
from stark.service.sites import site,ModelStark
from app01.models import *
from django import forms
from django.forms import widgets as wid
class BookConfig(ModelStark):
list_dispaly = ['name','price']
list_display_link = ['name']
def get_model_form_class(self):
class Model_Form(forms.ModelForm):
class Meta:
model=self.model
fields='__all__'
widgets={'pub_date':wid.DateInput(attrs={'type':'date'})}
error_messages={'name':{'required':'不能为空','max_length':'小于15个字符'},
'price':{'required':'不能为空'},
'publish': {'required': '不能为空'},
'pub_date': {'required': '不能为空'},
'author': {'required': '不能为空'}}
return Model_Form
site.register(Book,BookConfig)
site.register(Publish)
sites.py
def list_view(self,request):
url=request.path
add_url=self.get_add_url()
zw_name=self.zw_name
queryset=self.model.objects.all()
header=[]
for field in self.get_new_list_display():
if field=='__str__':
header.append(self.model_name.upper())
elif callable(field):
header.append(field())
else:
header.append(self.model._meta.get_field(field).verbose_name)
data=[]
for obj in queryset:
tr=[]
for field in self.get_new_list_display():
if callable(field):
val=field(obj)
else:
val=getattr(obj,field)
if field in self.list_display_link:
val=mark_safe('<a href="%s">%s</a>'%(self.get_edit_url(obj),val))
tr.append(val)
data.append(tr)
return render(request,'list_view.html',locals())
def add_view(self,request):
url=request.path
pre_url=request.GET.get('next')
zw_name=self.zw_name
model_form=self.get_model_form_class() #在这调用的时候,应为APP01用户的自定义配置类下重新写了这方法,所以就调用自己写的,所以得到的ModelForm类就是自己定义的类,这样app01用户就可以控制ModelForm渲染标签的样式,还可以要校验的规则等
if request.method=='GET':
form=model_form()
return render(request,'add_view.html',locals())
else:
form=model_form(request.POST)
if form.is_valid():
form.save()
return redirect(pre_url)
return render(request,'add_view.html',locals())
def delete_view(self,request,num):
self.model.objects.filter(pk=num).first().delete()
return redirect(self.get_list_url())
def edit_view(self,request,num):
obj=self.model.objects.all().filter(pk=num).first()
zw_name = self.zw_name
model_form=self.get_model_form_class()
if request.method=='GET':
form=model_form(instance=obj)
return render(request,'add_view.html',locals())
form=model_form(request.POST,instance=obj)
if form.is_valid():
form.save()
return redirect(self.get_list_url())
return render(request,'edit_view.html',locals())
add.html
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Title</title>
<link rel="stylesheet" href="/static/css/bootstrap.css">
<script src="/static/jquery-3.3.1.js"></script>
<script src="/static/js/bootstrap.js"></script>
<style>
.outer{
margin-top: 200px;
}
.title{
margin-bottom: 50px;
}
ul{
list-style: none;
margin: 0;
padding: 0;
}
.left{
position: fixed;
left: 0;
top: 20px;
}
</style>
</head>
<body>
<div class="left">
<div class="panel panel-primary">
<div class="panel-heading">
<h3 class="panel-title">操作栏</h3>
</div>
<div class="panel-body">
<div>
<a href="{% url 'app01_publish_add' %}?next={{ url }}" class="">添加出版社</a>
</div>
<div>
<a href="" class="">添加作者</a>
</div>
</div>
</div>
</div>
<div class="container outer">
<div class="row">
<div class="col-md-6 col-md-offset-3">
<div style="color: blue;font-size: 50px;text-align: center;font-family: 华文隶书" class="title">添加{{ zw_name }}</div>
<form action="" method="post" novalidate>
{% csrf_token %}
{% for field in form %}
<div class="form-group ">
<label for="id_{{ field.name }}">{{ field.label }}</label>
{{ field }}<span style="color: red">{{field.errors }}</span>
</div>
{% endfor %}
<button class="submit btn btn-info pull-right">提交</button>
</form>
</div>
</div>
</div>
<script>
$('input,select').addClass('form-control');
</script>
</body>
</html>
edit.html
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Title</title>
<link rel="stylesheet" href="/static/css/bootstrap.css">
<script src="/static/jquery-3.3.1.js"></script>
<script src="/static/js/bootstrap.js"></script>
<style>
.outer{
margin-top: 200px;
}
.title{
margin-bottom: 50px;
}
ul{
list-style: none;
margin: 0;
padding: 0;
}
.left{
position: fixed;
left: 0;
top: 20px;
}
</style>
</head>
<body>
<div class="left">
<div class="panel panel-primary">
<div class="panel-heading">
<h3 class="panel-title">操作栏</h3>
</div>
<div class="panel-body">
<div>
<a href="{% url 'app01_publish_add' %}?next={{ url }}" class="">添加出版社</a>
</div>
<div>
<a href="" class="">添加作者</a>
</div>
</div>
</div>
</div>
<div class="container outer">
<div class="row">
<div class="col-md-6 col-md-offset-3">
<div style="color: blue;font-size: 50px;text-align: center;font-family: 华文隶书" class="title">编辑{{ zw_name }}</div>
<form action="" method="post" novalidate>
{% csrf_token %}
{% for field in form %}
<div class="form-group ">
<label for="id_{{ field.name }}">{{ field.label }}</label>
{{ field }}<span style="color: red">{{field.errors }}</span>
</div>
{% endfor %}
<button class="submit btn btn-info pull-right">提交</button>
</form>
</div>
</div>
</div>
<script>
$('input,select').addClass('form-control');
</script>
</body>
</html>
删除是没有页面的,用的是一个confirm确定框做的,写在了查询页面的js里面
list.html
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Title</title>
<link rel="stylesheet" href="/static/css/bootstrap.css">
<script src="/static/js/bootstrap.js"></script>
<script src="/static/jquery-3.3.1.js"></script>
<style>
.title{
width: 100%;
height: 50px;
background-color:#337ab7;
color: yellow;
font-size: 40px;
line-height: 50px;
margin-bottom: 20px;
}
</style>
</head>
<body>
<div class="title">{{ zw_name }}列表</div>
<div class="container">
<div class="panel panel-default">
<div class="panel-heading"><a href="{{ add_url }}?next={{ url }}" class="btn btn-info add">添加{{ zw_name }}</a></div>
<table class="table table-bordered table-hover table-striped">
<thead>
<tr>
{% for foo in header %}
<td>{{ foo }}</td>
{% endfor %}
</tr>
</thead>
<tbody id="tbody">
{% for book in data %}
<tr>
{% for foo in book %}
<td>{{ foo }}</td>
{% endfor %}
</tr>
{% endfor %}
</tbody>
</table>
</div>
</div>
<script>
$('.delete').click(function () {
var url=$(this).attr('url');
var status=confirm('确定删除?');
if (status){
location.href=url;}
})
</script>
</body>
</html>
第二天就写到这里,明天继续。。。。。。。
Django之stark组件的更多相关文章
- 【django之stark组件】
一.需求 仿照django的admin,开发自己的stark组件.实现类似数据库客户端的功能,对数据进行增删改查. 二.实现 1.在settings配置中分别注册这三个app # Applicatio ...
- django 之 stark组件
----------------------------------------------------------------烦恼没完没了,内心动荡不安,呜呼哀哉. 一.有个特殊的需求,需要用sta ...
- Django之stark组件的使用和总结
Stark组件的使用 组件的字段 list_display=[] 需要显示的字段 list_display_links=[] #需要链接编辑字段 Stark_Model_Form=[] #设置Mode ...
- Django之stark组件1
stark组件 stark组件是根据Django admin为原型写的一个组件,能够让我们告别增删改查.stark组件是可插拔试的组件, 移植性强,而且只用配置文件就能够得到想要的数据 一.stark ...
- Django之stark组件2
action批量处理功能 用户可以自定义批量处理功能,但是默认的有批量删除功能. ***思路*** 1,定义一个列表用来装action的类名并extend用户自定义列表 2.循环该列表获取到函数名(用 ...
- stark组件之pop操作【模仿Django的admin】
一.先看下什么django的admin的pop到底是个什么东西 其实就是这么一个东西, a.在添加页面,在一对多和多对多的项后加了一个+号 b.点击这个加号,会弹出对应的添加 页面,在新的添加 c.添 ...
- stark组件之批量操作【模仿Django的admin】
一.先看下django的admin是如何实现批量操作 首先在配置类中定义一个函数 然后我们为这个函数对象设置一个属性,这个属性主要用来显示在select标签中显示的文本内容 最后把函数对象放到一个ac ...
- stark组件之搜索【模仿Django的admin】
一.先看下django的admin是如何做搜索功能的 配置一个search_fields的列表就可以实现搜索的功能 class testbook(admin.ModelAdmin): # 第一步,定义 ...
- stark组件之路由分发【模仿Django的admin】
一.先看下django的admin是如何进行路由分发的 1.先看下django的admin的url路径有哪些 其实很简单,假如有一个书籍表,那么每张表对应四个url,增.删.改.查 查看的url ht ...
随机推荐
- UVaLive 5760 Alice and Bob (博弈 + 记忆化搜索)
题意:有 n 堆石子,有两种操作,一种是从一堆中拿走一个,另一种是把两堆合并起来,Alice 先拿,谁不能拿了谁输,问谁胜. 析:某些堆石子数量为 1 是特殊,石子数量大于 1 个的都合并起来,再拿, ...
- Jmeter安装与实例
安装步骤: 安装环境:Windows7 安装包:JDK安装包:Jmeter安装包: 环境变量配置:变量名JAVA_HOME 值:jdk的安装路径 变量名CLASSPATH值:.;%JAV ...
- java 项目的路径详情
title: 项目下的路径问题tags:grammar_cjkRuby: true--- 在javaee的项目中,存取文件,解析xml和properties文件,以及项目中的文件,都需要获取路径,常用 ...
- .net core mvc发布项目到IIS上出现500错误
如题,我把.net core mvc项目以应用程序方式挂到IIS默认网站下,结果出现了如下错误:HTTP Error 500.0 - ANCM In-Process Handler Load Fail ...
- 4.ASP.NET MVC 5.0 视图之模型绑定
大家好,这篇文章,我将向大家介绍ASP.NET MVC的模型视图绑定,ASP.MVC的模型绑定分为两种:一种是动态绑定[Dynamic Binding];还有一种就是强类型绑定[Strongly ty ...
- JavaSE核心之一:Date类、Calendar类、Math类、枚举;
1.Date类 1) java.util.Date类用于封装日期及时间信息,一般仅用它显示某个日期,不对他作任何操作处理,作处理用Calendar类,计算方便. 2) Date 类本质上拥有一个lon ...
- SVN 客户端 TortoiseSVN 的安装和使用
关于 参考博客:TortoiseSVN新人使用指南 TortoiseSVN 是一个 Apache Subversion(SVN)客户端,实现为Windows外壳扩展.它直观且易于使用,因为它不需要Su ...
- 通过用jQuery写一个页面,我学到了什么
概述 前几天面试,hr发来一个测试文件,让我做做看.我一看,其实就是根据PSD需求写一个页面,再加上一些互动效果即可. 刚好我之前学了切图,jquery等知识还没练手过,于是高兴的答应了. 最后花了3 ...
- 小游戏——金庸奇侠传(JAVA,对面向对象的进一步了解)
金庸群侠传 游戏运行界面: 游戏实现源码: package game.logic; //人类 public class Person { public String name; //名字 Pers ...
- python实现线性排序算法-计数排序
计数排序假定输入元素的每一个都是介于0到k之间的整数,此处K为某个整数,当k=O(n)时,计数排序的运行时间为O(n) 它的基本思想是:根据每个输入元素x确定小于x的元素个数,根据这个信息把x直接放到 ...