python 全栈开发,Day117(popup,Model类的继承,crm业务开发)
昨日内容回顾
第一部分:权限相关 1. 权限基本流程 用户登录成功后获取权限信息,将【权限和菜单】信息写入到session。 以后用户在来访问,在中间件中进行权限校验。 为了提升用户体验友好度,在后台通过inclusion_tag动态生成一个二级菜单。 2. 使用权限 - 用户登陆:权限和菜单的初始化; init_permission - 配置中间件 - 配置白名单 - 配置session中使用到的key - load rbac - menu ,inclusion_tag 生成菜单 - filter,可以在if后做条件,粒度控制到按钮。 第二部分:stark组件 1. 如何使用 - 在app中编写 stark.py - 在stark.py中进行定制 - 默认配置: site.register(models.UserInfo) - 自定义配置: class UserConfig(StarkConfig): list_display = [] # 表格式列表上显示的字段 def get_list_display(): pass order_by = [] # 排序 action_list=[] # 批量操作 search_list = [] # 模糊搜索 list_filter = [] # 组合搜索 add_btn # 是否显示添加按钮 model_form_class # 自定义ModelForm def extra_url(self): # 自定义扩展 URL pass def get_urls(self): # 自定义URL pass def changelist_view(self,request): pass def add_view(self,request): pass def change_view(self,request): pass def del_view(self,request): pass site.register(models.UserInfo,UserConfig)
一、popup
什么是popup
popup英文翻译叫 弹出窗口。
弹窗是一个非常流行的对话框,弹窗可以覆盖在页面上展示。
弹窗可用于显示一段文本,图片,地图或其他内容。
注意:popup弹窗,是由浏览器生成的!
在前端里面的,有一个模态框。那是由Html生成的!跟popup弹框不一样!
window.open
popup实际上,是调用了js中的window.open方法
open() 方法用于打开一个新的浏览器窗口或查找一个已命名的窗口。
语法
window.open(URL,name,features,replace)
注意:如果name重名,只会打开一个
窗口特征(Window Features)
这里只列举,下面例子中,会用到的参数
status=yes|no|1|0 | 是否添加状态栏。默认是 yes。 |
height=pixels | 窗口文档显示区的高度。以像素计。 |
width=pixels | 窗口的文档显示区的宽度。以像素计。 |
toolbar=yes|no|1|0 | 是否显示浏览器的工具栏。默认是 yes。 |
resizable=yes|no|1|0 | 窗口是否可调节尺寸。默认是 yes。 |
其他更多参数,请参考链接:
http://www.w3school.com.cn/jsref/met_win_open.asp
在django admin中,就用到了popup。比如之前写的博客系统
点击加号
它弹出了一个网页框。注意:它是单页面的!不能像浏览器一样,在当前页面,新开一个窗口。
不能收藏!URL地址也不能修改!
举例:
新建一个项目,注意:django版本为1.11
修改urls.py,增加路径
from django.conf.urls import url from django.contrib import admin from app01 import views urlpatterns = [ url(r'^admin/', admin.site.urls), url(r'^index/', views.index), ]
修改views.py,增加视图函数
from django.shortcuts import render # Create your views here. def index(request): return render(request,"index.html")
在templates目录下,创建文件index.html
<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <title>Title</title> </head> <body> <h1>添加页面</h1> <form> <p><input type="text"></p> <p> <select id="city"> <option>北京</option> <option>上海</option> <option value="1">深圳</option> </select> <input type="button" value="+" onclick="popUp();"> </p> </form> <script> function popUp() { {#弹窗#} window.open("http://www.py3study.com/",'x1',"status=1, height=500, width=500, toolbar=0, resizable=0") } </script> </body> </html>
启动django项目,访问首页
点击加号按钮,弹窗页面。它是一个单页面!
弹窗的内容,可以定制吗?当然可以!
修改urls.py,增肌路径
urlpatterns = [ url(r'^admin/', admin.site.urls), url(r'^index/', views.index), url(r'^pop/', views.pop), ]
修改views.py,增肌视图函数
from django.shortcuts import render,HttpResponse # Create your views here. def index(request): return render(request,"index.html") def pop(request): if request.method == "GET": return render(request,"add_city.html") print(request.POST) return HttpResponse('添加成功')
在templates目录下,创建文件add_city.html
<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <title>Title</title> </head> <body> <h1>创建城市</h1> <form method="post"> {% csrf_token %} <div style="width: 200px;height: 300px;border: 1px solid #dddddd"> <input type="text" name="city"> <input type="submit" value="提交"> </div> </form> </body> </html>
刷新页面,重新点击加号按钮,效果如下:
点击提交之后,效果如下:
但是窗口并没有自动关闭!它应该自动关闭,并跳转到首页才对!
怎么让它自动关系呢?使用windows.close()
由于windows.close()是js代码,需要使用html文件来执行才行!
在templates目录下,创建文件pop_response.html
<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <title>正在关闭</title> </head> <body> <script> {#自执行函数#} (function () { window.close(); })() </script> </body> </html>
自执行函数,也就是能够自动立即执行的函数
看下面一段代码
(function () {
window.close();
})()
在js中声明函数,使用function关键字。函数执行,必须要加括号执行。
所以上面一段代码,就是声明之后,立刻被执行了!
还有一种用途,用于做隔离。比如多个js文件之间相互调用时!
修改views.py,渲染pop_response.html
from django.shortcuts import render,HttpResponse # Create your views here. def index(request): return render(request,"index.html") def pop(request): if request.method == "GET": return render(request,"add_city.html") print(request.POST) return render(request, 'pop_response.html')
重启django,刷新页面,重新添加一次。效果如下:
那么问题来了,添加之后的数据,要在下拉框中展示。要实时更新,怎么搞?
注意:拉下框中的数据,是来源于数据库的。所以即使添加成功了,要刷新页面,才能加载出来!
popup,它能记住是由哪个页面触发弹窗的。它能调用原始页面的数据!使用opener调用
测试一下
修改index.html,增加tx方法
<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <title>Title</title> </head> <body> <h1>添加页面</h1> <form> <p><input type="text"></p> <p> <select id="city"> <option>北京</option> <option>上海</option> <option value="1">深圳</option> </select> <input type="button" value="+" onclick="popUp();"> </p> </form> <script> {#测试函数#} function tx() { alert('xxx'); } function popUp() { {#弹窗#} window.open("/pop/",'x1',"status=1, height=500, width=500, toolbar=0, resizable=0") } </script> </body> </html>
修改pop_response.html,调用tx方法
<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <title>正在关闭</title> </head> <body> <script> {#自执行函数#} (function () { //调用tx方法 opener.tx(); window.close(); })() </script> </body> </html> (function () { window.close(); })()
刷新页面,重新添加一次,效果如下:
那么既然可以调用index.html的方法,就可以传值了
修改views.py,增加变量content,用来使用js传参给index.html
from django.shortcuts import render,HttpResponse # Create your views here. def index(request): return render(request,"index.html") def pop(request): if request.method == "GET": return render(request,"add_city.html") print(request.POST) # 假设数据已经添加成功了,这里要获取添加的id和title content = {'id':4,'title':'成都'} # 渲染页面,用来将参数传给index.html return render(request, 'pop_response.html',content)
修改pop_response.html,调用tx方法,并传参。
注意:参数是字符串,要用引号括起来
<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <title>正在关闭</title> </head> <body> <script> {#自执行函数#} (function () { //调用tx方法,参数必须是字符串 opener.tx('{{ id }}','{{ title }}'); window.close(); })() </script> </body> </html> (function () { window.close(); })()
修改index.html,接收参数后,操作DOM
<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <title>Title</title> </head> <body> <h1>添加页面</h1> <form> <p><input type="text"></p> <p> <select id="city"> <option value="1">北京</option> <option value="2">上海</option> <option value="3">深圳</option> </select> <input type="button" value="+" onclick="popUp();"> </p> </form> <script> {#测试函数#} function tx(cityId,cityTitle) { //创建option标签 var tag = document.createElement('option'); //设置value tag.value = cityId; // 设置text属性 tag.innerText = cityTitle; // 获取ID标签 var city = document.getElementById('city'); //最后一个位置添加option标签 city.appendChild(tag); } function popUp() { {#弹窗#} window.open("/pop/",'x1',"status=1, height=500, width=500, toolbar=0, resizable=0") } </script> </body> </html>
重启django,刷新页面,重新添加一次!效果如下:
注意:这里只是添加到浏览器了,并没有到数据库。所以刷新页面,数据会丢失!
django render传输的变量。变量只会在指定的文件渲染!如果这个文件包含了js,那么即使js使用{{ }} 语法,也不会渲染!
举例:
修改views.py,给index.html传一个id参数
from django.shortcuts import render,HttpResponse # Create your views here. def index(request): return render(request,"index.html") def pop(request): if request.method == "GET": return render(request,"add_city.html",{'id':'33'}) print(request.POST) # 假设数据已经添加成功了,这里要获取添加的id和title content = {'id':4,'title':'成都'} # 渲染页面,用来将参数传给index.html return render(request, 'pop_response.html',content)
在app01目录下,创建static目录,在此目录下创建test.js
alert('{{id}}');
修改index.html,添加h6标签,引入js文件
{% load staticfiles %} <!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <title>Title</title> {#测试js文件#} <script src="{% static 'test.js' %}"></script> </head> <body> <h1>添加页面</h1> <h6>'{{ id }}'</h6> <form> <p><input type="text"></p> <p> <select id="city"> <option value="1">北京</option> <option value="2">上海</option> <option value="3">深圳</option> </select> <input type="button" value="+" onclick="popUp();"> </p> </form> <script> {#测试函数#} function tx(cityId,cityTitle) { //创建option标签 var tag = document.createElement('option'); //设置value tag.value = cityId; // 设置text属性 tag.innerText = cityTitle; // 获取ID标签 var city = document.getElementById('city'); //最后一个位置添加option标签 city.appendChild(tag); } function popUp() { {#弹窗#} window.open("/pop/",'x1',"status=1, height=500, width=500, toolbar=0, resizable=0") } </script> </body> </html>
重启django,刷新页面
发现id没有被渲染出来
但是index.html渲染出来了
在window.open中,name重名,只会打开1个
如果定义了多个window.open,会被浏览器拦截
只会弹出第一个,后续的都会被拦截!
总结:
主页面: function xxxxxx(){ } window.open('url','name','.....') popup页面: opener.xxxxxx() // widown.close() 补充: js的自执行函数 用于做隔离: (function(jq){ jq.xxx })(jQuery)
二、Model类的继承
Django有三种继承的方式:
- 抽象基类:被用来继承的模型被称为
Abstract base classes
,将子类共同的数据抽离出来,供子类继承重用,它不会创建实际的数据表; - 多表继承:
Multi-table inheritance
,每一个模型都有自己的数据库表; - 代理模型:如果你只想修改模型的Python层面的行为,并不想改动模型的字段,可以使用代理模型。
注意!同Python的继承一样,Django也是可以同时继承两个以上父类的!
关于这3种继承的方式,详情请参考链接:
https://www.cnblogs.com/feixuelove1009/p/8420751.html
本文主要讲解 多表继承
多表继承
这种继承方式下,父类和子类都是独立自主、功能完整、可正常使用的模型,都有自己的数据库表,内部隐含了一个一对一的关系。
举例
修改models.py
from django.db import models # Create your models here. class Author(models.Model): # 作者 name=models.CharField(verbose_name="姓名",max_length=32) age=models.IntegerField(verbose_name="年龄") class AuthorDetail(Author): # 作者详情 gf=models.CharField(verbose_name="女朋友",max_length=32) tel=models.CharField(verbose_name="作者电话",max_length=32)
它等同于原生sql语句
CREATE TABLE "Author" ( "id" integer NOT NULL PRIMARY KEY, "name" varchar(32) NOT NULL, "age" int(11) NOT NULL ); CREATE TABLE "AuthorDetail" ( "author_ptr_id" integer NOT NULL PRIMARY KEY REFERENCES "Author" ("id"), "gf" varchar(32) NOT NULL, "tel" varchar(32) NOT NULL );
使用2个命令,生成表
python manage.py makemigrations
python manage.py migrate
使用Navicat打开数据库,查看表结构,并添加数据
author表
authordetail表
父类和子类都生成了单独的数据表,authordetail中存储了author的id,也就是通过OneToOneField链接在一起。继承关系通过表的JOIN操作来表示。在JPA中称作JOINED。这种方式下,每个表只包含类中定义的字段,不存在字段冗余,但是要同时操作子类和所有父类所对应的表。
author 里面的所有字段在 authordetail 中也是有效的,只不过数据保存在另外一张数据表当中。所以下面两个语句都是可以运行的:
models.Author.objects.filter(name='xiao') models.AuthorDetail.objects.filter(name='xiao')
修改urls.py
urlpatterns = [ url(r'^admin/', admin.site.urls), url(r'^index/', views.index), ]
修改views.py
from django.shortcuts import render,HttpResponse from app01 import models # Create your views here. def index(request): obj1 = models.Author.objects.filter(name='xiao').values() obj2 = models.AuthorDetail.objects.filter(name='xiao').values() print(obj1) print(obj2) return HttpResponse('ok')
重启django,访问首页
查看Pycharm控制台输出:
<QuerySet [{'age': 23, 'name': 'xiao', 'id': 1}]> <QuerySet [{'gf': '韩雪', 'author_ptr_id': 1, 'tel': '2345', 'age': 23, 'name': 'xiao', 'id': 1}]>
可以看出,authordetail 表打印出本表的字段以及父表author表的字段
如果你有一个 author,那么它同时也是一个 authordetail, 那么你可以使用子 model 的小写形式从 author 对象中获得与其对应的 authordetail 对象:
修改views.py
from django.shortcuts import render,HttpResponse from app01 import models # Create your views here. def index(request): obj1 = models.Author.objects.filter(name='xiao').first() print(obj1.authordetail) return HttpResponse('ok')
重启django,访问首页
查看Pycharm控制台输出:
AuthorDetail object
但是,如果上例中的 obj1 并不是 authordetail (比如它仅仅只是 author 对象,或者它是其他类的父类),那么在引用 p.authordetail 就会抛开RelatedObjectDoesNotExist: Author has no authordetail. 异常:
def index(request): obj1 = models.Author.objects.create(name='zhang',age='25') print(obj1.authordetail) return HttpResponse('ok')
也就是说,创建author实例的同时不会创建authordetail,但是创建authordetail实例的同时会创建author实例:
修改views.py
from django.shortcuts import render,HttpResponse from app01 import models # Create your views here. def index(request): obj1 = models.AuthorDetail.objects.create(name="zhang",age="25",gf="蒋婷婷",tel="2534") obj2 = models.Author.objects.filter(name="zhang").values() print(obj2) return HttpResponse('ok')
刷新页面,查看Pycharm控制体输出:
<QuerySet [{'age': 25, 'name': 'zhang', 'id': 2}]>
使用Navicat打开2个表
author表
authordetail表
三、crm业务开发
新建一个项目pro_crm,应用名为crm,注意:django版本为1.11
拷贝stark app
下载代码:
链接:https://pan.baidu.com/s/1fLOGH_3G7hPTvCYKX84UdQ 密码:m8rh
将里面的stark目录拷贝至 项目根目录中
注册stark app
修改settings.py,修改INSTALLED_APPS配置项
INSTALLED_APPS = [ 'django.contrib.admin', 'django.contrib.auth', 'django.contrib.contenttypes', 'django.contrib.sessions', 'django.contrib.messages', 'django.contrib.staticfiles', 'crm.apps.CrmConfig', 'stark.apps.StarkConfig', ]
使用stark组件
在目标app的根目录中创建stark.py
进入crm目录,创建stark.py
修改 crm-->models.py,增加表
# from rbac.models import UserInfo as RbacUserInfo from django.db import models class Department(models.Model): """ 部门表 """ title = models.CharField(verbose_name='部门名称', max_length=16) def __str__(self): return self.title class UserInfo(models.Model): """ 员工表 """ name = models.CharField(verbose_name='真实姓名', max_length=16) phone = models.CharField(verbose_name='手机号', max_length=32) gender_choices = ( (1,'男'), (2,'女'), ) gender = models.IntegerField(verbose_name='性别',choices=gender_choices,default=1) depart = models.ForeignKey(verbose_name='部门', to="Department") def __str__(self): return self.name class Course(models.Model): """ 课程表 如: Linux基础 Linux架构师 Python自动化 Python全栈 """ name = models.CharField(verbose_name='课程名称', max_length=32) def __str__(self): return self.name class School(models.Model): """ 校区表 如: 北京昌平校区 上海浦东校区 深圳南山校区 """ title = models.CharField(verbose_name='校区名称', max_length=32) def __str__(self): return self.title class ClassList(models.Model): """ 班级表 如: Python全栈 面授班 5期 10000 2017-11-11 2018-5-11 """ school = models.ForeignKey(verbose_name='校区', to='School') course = models.ForeignKey(verbose_name='课程名称', to='Course') semester = models.IntegerField(verbose_name="班级(期)") # 11 price = models.IntegerField(verbose_name="学费") start_date = models.DateField(verbose_name="开班日期") graduate_date = models.DateField(verbose_name="结业日期", null=True, blank=True) tutor = models.ForeignKey(verbose_name='班主任', to='UserInfo', related_name='classes',limit_choices_to={'depart__title':'教质部'}) teachers = models.ManyToManyField(verbose_name='任课老师', to='UserInfo', related_name='teach_classes',limit_choices_to={'depart_id__in':[6,7]}) memo = models.CharField(verbose_name='说明', max_length=256, blank=True, null=True) def __str__(self): return "{0}({1}期)".format(self.course.name, self.semester)
使用2个命令,生成表
python manage.py makemigrations
python manage.py migrate
修改crm-->stark.py,注册表Department
from stark.service.stark import site from crm import models site.register(models.Department)
配置路由信息
修改urls.py,增加stark路由
from django.conf.urls import url from django.contrib import admin from stark.service.stark import site urlpatterns = [ url(r'^admin/', admin.site.urls), # 导入stark组件的路由 url(r'^stark/', site.urls), ]
启动django项目,访问url: http://127.0.0.1:8000/stark/crm/department/list/
默认是没有数据的,需要添加!
由于时间关系,详细的步骤略...
完整代码,请参考
链接:https://pan.baidu.com/s/1K4YG5LY89aidRWK7j51DBg 密码:30jt
未完待续...