day15
本节内容介绍
上节作业讲解(让行进入编辑模式,批量编辑)
CSS之特殊内容补充
CSS内容补充之伪类
伪类实例:返回顶部终极版
CSS内容补充之无法被覆盖
jQuery插件
jQuery插件之验证图片轮番和图标
jQuery插件之jQueryUI和EasyUI
jQuery插件之BootStrap介绍
一.上节作业讲解
思路
1.先把table画出来
2.全选反选取消
3. 标识:是否进入编辑模式
id='c1' class = "edit" 未进入编辑模式
id='c1' class = "edit doing" 进入编辑模式
判断一个标签是否包含一个样式用hasClass()
$('#c1').hasClass('doing')
4.当前行进入编辑模式
RowIntoEdit(tr)/RowOutEdit() //tr是当前行的选择器
一行数据,不一定每一个td都是可编辑的.所以在td上要有一个属性标示是否需要编辑.
还有,不是所有的td编辑时都使用input[type='text'],肯定有的是select有的是text.所以每一个td上还需要有一个属性表示编辑的类型,是select或者text
普通内容 => input
如果是input的话,还简单些,直接获取td里的text,然后放倒input里的value
<td id='xxx'>123</td>
t = $(xxx).text()
创建input标签,并且将标签的值设置成t
$(xxx).val(t)
所以对于普通的,td里的text就是数据源
选择 => select
对于那种选择的,数据源去哪里拿?
select数据源去哪里拿?首先数据源不能保存在本地,因为后端的数据经常更新.
有两种方式:
1.通过ajax向服务端发送一个请求,服务端反回给一个数据源.这样又多了一次请求.
2.我们知道html是客户端浏览器访问服务端端口时反回的,所以我们想服务端在反回的时候,能不能在js中声明一个全局变量,当我们需要用的时候直接使用这个全局变量就可以获取数据源了.
分析:第一种方式是数据源是在需要时通过ajax从服务端获取.
第二种方式是当用户一开始访问请求的时候,就把数据源放倒JS里的一个全局变量,用的时候不需要再去服务端进行请求了.
我们使用第二种方式,我们这里没有后台,所以就在页面直接模拟出来一个全局变量里有些值.(如果有后台,全局变量的存在也是必须的,至于里面的数据是通过数据库还是通过ajax获取的都可以.)
全局变量:
STATU_DICT={
1:'在线',
2:'下线',
}
需求列出来了,就两个要求:
1. td是否可以编辑
2. 要以什么类型方式进行编辑.
所以我要对tr行里的元素td进行循环
判断td里是否有一个edit 属性为true,如果是true则可以进行编辑
判断td里是否有一个edit-type ,它的值如果edit-type = 'input'就用普通的,
如果edit-type = 'select'就用select方式编辑,这里要注意了,我们要向select的数据源进行取值,当然我们可以在RowIntoEdit()函数中写死,也可以作为参数出传入,
写死肯定不好,作为参数靠谱些.我们会想,那么我们直接在调用函数时把变量传入.反正是全局变量.
对没错,是可以传入变量,但是编程理念不应该这么做,我们要把函数想象成一个没有思想的工具,
假如它是一个DVD一体机,有好几张dvd唱片,你要使用dvd播放dvd时,你要动手把dvd唱片找到,然后放入到DVD,这个流程才对.
你不能直接告诉dvd,去播放"周杰伦"的唱片.这就相当于你直接点播放按钮,并对dvd机喊了句"周杰伦".
所以我们要在td里在设置一个属性global-key = 'STATUS_DICT',标示数据源从哪个全局变量去取.
5.退出编辑模式
<td> input</td>
<td> select</td>
实例代码:(还包含了按住ctrl键选择select时批量操作!)代码必看
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Title</title>
<style>
.edit-mode{
padding:10px;
}
.editing{
padding:10px;
background-color: #ffd00f;
}
</style>
</head>
<body>
<div style="padding: 20px">
<input type="button" value="全选" onclick="CheckAll('#edit_mode','#tb1');"/>
<input type="button" value="反选" onclick="CheckReverse('#edit_mode','#tb1');"/>
<input type="button" value="取消" onclick="CheckCancel('#edit_mode','#tb1');"/>
<a id="edit_mode" class="edit-mode" href="javascript:void(0)" onclick="EditMode(this,'#tb1')" >进入编辑模式</a>
</div>
<table border="1">
<thead>
<tr>
<th>选择</th>
<th edit="true">主机</th>
<th>端口</th>
<th>状态</th>
</tr>
</thead>
<tbody id="tb1">
<tr>
<td><input type="checkbox"></td>
<td edit="true" edit-type="input">v1</td>
<td>p1</td>
<td edit="true" edit-type="select" set-val="1" global-key="STATUS_DICT">在线</td>
</tr>
<tr>
<td><input type="checkbox"></td>
<td edit="true" edit-type="input">v2</td>
<td>p2</td>
<td edit="true" edit-type="select" set-val="1" global-key="STATUS_DICT">在线</td>
</tr>
<tr>
<td><input type="checkbox"></td>
<td edit="true" edit-type="input">v3</td>
<td>p3</td>
<td edit="true" edit-type="select" set-val="1" global-key="STATUS_DICT">在线</td>
</tr>
</tbody>
</table>
<script src="jquery-3.1.0.min.js"></script>
<script >
STATUS_DICT = [
{'id': 1, 'value': "在线"},
{'id': 2, 'value': "下线"}
];
$(function(){
BindSingleCheck('#edit_mode','#tb1');
});
function BindSingleCheck(mode,tb){
$(tb).find(':checkbox').bind('click',function(){
var tr = $(this).parent().parent();
if($(mode).hasClass('editing')){
if($(this).prop('checked')){
RowIntoEdit(tr)
}else{
RowOutEdit(tr)
}
}
});
}
/*
监听是否已经按下control键
*/
window.globalCtrlKeyPress = false;
window.onkeydown = function (event) {
console.log(event.keyCode);
if(event && event.keyCode == 17){
window.globalCtrlKeyPress = true;
console.log('ctrl + ok')
}
};
window.onkeyup = function (event) {
if(event && event.keyCode == 17 ){
window.globalCtrlKeyPress = false;
console.log('ctrl -----ok')
}
};
// 写一个创建select标签的函数
function CreateSelect(attrs,csses,option_dict,item_key,item_value,current_val){
var doc = document;
var sel = doc.createElement('select');
$.each(attrs,function(k,v){
$(sel).attr(k,v);
});
$.each(csses,function(k,v){
$(sel).attr(k,v)
});
$.each(option_dict,function(k,v){
var opt1 = doc.createElement('option');
var sel_text = v[item_value];
var sel_value = v[item_key];
if(sel_value == current_val) {
$(opt1).text(sel_text).attr('value', sel_value).attr('text', sel_text).attr('selected',true).appendTo($(sel));
}else{
$(opt1).text(sel_text).attr('value', sel_value).attr('text', sel_text).appendTo($(sel));
}
});
return sel;
}
// 写一个当select变化时,批量修改下面的值和当前值一致
function MultiSelect(ths){
console.log('33333');
if(window.globalCtrlKeyPress){
console.log('171717171');
var index = $(ths).parent().index();
var value = $(ths).val();
$(ths).parent().parent().nextAll().find(':checkbox').each(function(){
$(this).parent().parent().children().eq(index).children().val(value)
})
}
}
//写一个将行变成编辑模式的函数
function RowIntoEdit(tr){
// console.log('1111')
tr.children().each(function(){
//$(this), 当前循环元素,当前td
if($(this).attr('edit') == 'true'){
if($(this).attr('edit-type') == 'select' ){
//在线,对应的value值
var select_val = $(this).attr('set-val');
// global_key全局变量的变量名
var global_key = $(this).attr('global-key');
//创建一个select标签,并且让select标签默认选中当前的值
/*
<select onchange="MultiSelect(this);">
<option value="1" selected="selected">在线</option>
<option value="2">下线</option>
</select>
*/
var select_tag = CreateSelect({"onchange":"MultiSelect(this)"},{},window[global_key],'id','value',select_val);
/*这里有一个特殊的window[global_key],这个是干什么的呢? 举个例子,现在有一个全局变量 A = [11,22]
直接调用console.log(A)打印记过 [11,22]
使用console.log(window['A']) 打印结果也是[11,22],所以 window['A'] 就是变量A的用字符的表达方式,
这里global_key = 'global-key',所以 window['global-key'] 就相当于直接调用变量global-key
*/
//把创建后的select标签,换到当前td中
$(this).html(select_tag)
}else{
var orgin_value = $(this).text();
var temp = "<input value='" +orgin_value +"'>";
$(this).html(temp);
}
}
})
}
//写一个行退出编辑模式的函数
function RowOutEdit(tr){
tr.children().each(function(){
if($(this).attr('edit') == 'true'){
if($(this).attr('edit-type') == 'select'){
var new_val = $(this).children(':first').val();
var new_text = $(this).children(':first').find("option[value = '" +new_val+"']").text();
$(this).attr('set-val',new_val);
$(this).text(new_text);
}else{
var orgin_value = $(this).children().first().val();
$(this).text(orgin_value);
}
}
});
}
//全选按钮调用的函数,函数检查是否在编辑模式下进行的全选,如果是则将所有的行选中并且循环每行调用RowIntoEdit(tr),如果不是则直接全选中
function CheckAll(mode,tb){
// mode = '#edit-mode'
// tb = '#tb'
//判断是否在编辑模式,如果是
if($(mode).hasClass('editing')){
//选中所有的checkbox,并将所有行进行处理成进入编辑模式.
$(tb).children().each(function(){
//找到tr
var tr = $(this);
//找到tr下的checkbox
var check_box=tr.children().first().find(':checkbox');
if(check_box.prop('checked')){
}else{
//选中
check_box.prop('checked',true);
RowIntoEdit(tr);
}
})
//如果不在编辑模式下,直接全选
} else {
$(tb).find(':checkbox').prop('checked',true);
}
}
//反选按钮调用的函数,函数检查是否在编辑模式下进行的全选,如果是反选的同时调用RowIntoEdit和RowOutEdit,如果不是直接反选
function CheckReverse(mode,tb){
//判断是否进入编辑模式
if($(mode).hasClass('editing')){
$(tb).children().each(function(){
var tr = $(this);
var check_box = tr.children().first().find(':checkbox');
if(check_box.prop('checked')){
check_box.prop('checked',false);
RowOutEdit(tr)
}else{
check_box.prop('checked',true);
RowIntoEdit(tr)
}
});
//如果不在编辑模式,则直接反选
}else{
$(tb).find(':checkbox').each(function(){
var check_box = $(this);
if(check_box.prop('checked')){
check_box.prop('checked',false);
}else{
check_box.prop('checked',true);
}
})
}
}
//取消按钮调用的函数,首先判断是否在编辑模式下,如果是则取消并调用RowOutEdit函数,如果不是则直接取消
function CheckCancel(mode,tb){
if($(mode).hasClass('editing')){
$(tb).children().each(function(){
var tr = $(this);
var check_box = tr.children().first().find(':checkbox');
if (check_box.prop('checked')){
check_box.prop('checked',false);
//当前行退出编辑模式
RowOutEdit(tr);
}
});
}else{
$(tb).find(':checkbox').prop('checked',false);
}
}
//点击"进入编辑模式"时调用的函数.
function EditMode(ths,tb){
var mode = $(ths);
//如果点击前是编辑模式,点击后为非编辑模式,我们就要找到正在编辑的行tr,并使用RowOutEdit(tr)
if(mode.hasClass('editing')){
mode.removeClass('editing');
$(tb).children().each(function(){
var tr = $(this);
var check_box = tr.children().first().find(':checkbox');
if(check_box.prop('checked')){
RowOutEdit(tr)
}
});
//如果之前不是编辑模式,则进入,并循环循环行,检测是否checked,选中的进入编辑模式
} else{
mode.addClass('editing');
$(tb).children().each(function(){
var tr = $(this);
var check_box = tr.children().first().find(':checkbox');
if(check_box.prop('checked')){
RowIntoEdit(tr);
}
})
}
}
</script>
</body>
</html>
二.jQuery插件介绍
jQuery插件之验证图片轮番和图标
jQuery插件之jQueryUI和EasyUI
jQuery插件之BootStrap介绍
-- parsleyjs
http://parsleyjs.org/
-- jQuery Validate
http://jqueryvalidation.org/
-- bxslider
http://bxslider.com/
-- Bootstrap
http://www.bootcss.com/
-- Font Awesome
http://fontawesome.io/
-- jQuery EasyUI
http://www.jeasyui.com/download/index.php
-- jQuery UI
http://jqueryui.com/
BootStrap介绍
现在很多网站都在搞BootStrap,它即适用后台,也适用于前台.
1.bootStrap中提供了各种好看的格式工具.不是用了bootstrap就能做出美观的网站,还是要看你自己的本事.
2.BootStrap是响应式的
什么是响应式,有些网站当窗体大时,选项列出来,如果窗体小时选项就放倒一个图标,点击时才显示.
这个功能我们可以使用js写一个事件,用if else也能实现.但是如果应用css中一个样式也可以实现:
@media (min-768px){
.lead{
font-size:21px;
}
}
这里@media关键字后面定义了min-768px(最小宽度),意思只有当窗体宽度大于768px时里面的lead样式才生效.
@media后面可以跟mediatype选项,意思是对于哪些项目才生效响应式的配置如
@media (min-768px) 括号里面设置条件,这里指窗体宽度最小值为768.
@media all 用于所有设备,相当于上面的括号的条件
@media aural 已废弃.用于语音和声音合成器.相当于上面的括号的条件
@media braille 已废弃. 用于盲文触摸式反馈设备. 相当于上面的括号里的条件
@media embossed 已废弃. 用于打印的盲人印象设备.相当于上面的括号里的条件
@media handheld 已废弃. 用于掌上设备或者更小的设备.相当于上面的括号里的条件
@media print 用于打印机和打印预览.相当于上面的括号里的条件
@media projection 已废弃.用于投影设备.相当于上面的括号里的条件
@media screen 用于电脑屏幕,平板电脑,智能手机等.相当于上面的括号里的条件. 最常用的就是用屏幕大小进行判断.
@media speech 应用于屏幕阅读器等发声设备.相当于上面的括号里的条件
@media tty 已废弃. 用于固定的字符网格,如电视终端设备和字符有限制的便携设备.相当于上面的括号里的条件
@media tv 已废弃. 用于电视和网络电视.相当于上面的括号里的条件
逻辑判断
and,且
not,非
only,只有
媒体功能
aspect-ratio 定义输出设备中的页面可见区域宽度与高度的比率
color 定义输出设备每一组彩色原件的个数.如果不是彩色设备,则值等于0
color-index 定义在输出设备的彩色查询表中的条目数.如果没有使用彩色查询表,则值等于0
device-aspect-ratio 定义输出设备的屏幕可见宽度与高度的比率.
device-height 定义输出设备的屏幕可见高度.
device-width 定义输出设备的屏幕可见宽度.
grid 用来查询输出设备是否使用栅格或点阵.
height 定义输出设备中的页面可以区域高度.
max-aspect-ratio 定义输出设备的屏幕可见宽度与高度的最大比率.
max-color 定义输出设备每一组彩色原件的最大个数.
max-color-index 定义在输出设备的彩色产讯表中的最大条目数.
max-device-aspect-ratio 定义输出设备的屏幕可见宽度与高度的最大比率.
max-device-height 定义输出设备的屏幕可见的最大高度.
max-device-width 定义输出设备的屏幕最大可见宽度.
max-height 定义输出设备中的页面最大可见区域高度.
max-monochrome 定义在一个单色框架缓冲区中每像素包含的最大单色原件个数
max-resolution 定义设备的最大分辨率.
max-width 定义输出设备中的页面最大可见区域宽度.
min-aspect-ratio 定义输出设备中的页面可见区域宽度与高度的最小比率.
min-color 定义输出设备每一组彩色原件的最小个数.
min-color-index 定义在输出设备的彩色查询表中饿最小条目数.
min-device-aspect-ratio 定义输出设备的屏幕可见宽度与高度的最小比率.
min-device-width 定义输出设备的屏幕最小可见宽度.
min-device-heigth 定义输出设备的屏幕的最小可见高度.
min-height 定义输出设备中的页面最小可见区域高度.
min-monochrome 定义在一个单色框架缓冲区中每像素包含的最小单色原件个数
min-resolution 定义设备的最小分辨率
min-width 定义输出设备中的页面最小可见区域宽度.
monochrome 定义在一个单色框架缓冲区中每像素包含的单色原件个数.如果不是单色设备,则值为0
orientation 定义输出设备中的页面可见区域高度是否大于或等于宽度.
resolution 定义设备的分辨率. 如:96dpi,300dpi,118dpcm
scan 定义电视类设备的扫描工序
width 定义输出设备中的页面可见区域宽度.
能设置最小宽度,能不能设置最大宽度?能,代码如下:
@media screen and (max-1000px) and (min- 800px){
body{
background-color:red;
}
}
@media screen and (max- 800px){
body{
background-color: green;
}
}
上面是页面响应式的原理.我们说bootstrap是响应式的,是说bootstrap利用这些原理,通过调用bootstrap提供的方法可以实现响应式样式.
具体怎么用,课堂上没说,可以浏览bootstrap网页关于响应式设置的内容.你要知道的是bootstrap支持响应式的网页
三.CSS特殊内容补充
CSS内容补充之伪类(又称伪元素)
什么是伪类用语言不好描述,只能描述伪类的功能.
假如有一个css样式如下:
.add{}
那么当一个div标签如果定义了class='add',div标签就会应用.add里定义的样式.这是普通情况下的执行过程.
接下来我们使用CSS的伪类,将.add{}代码更改成如下:
<style>
.add:after{
background-color: #ffd00f;
content:'sb';
}
</style>
<body>
<div class='add'>alex</div>
</body>
上面我们看到.add:after ,after就是伪类,也称为伪元素.
我们在页面上看到的结果就是:
alexsb
但是要注意的时 只有 sb 两个字符是有background-color设置的背景色.而alex这四个字符是没有背景色的.并且在页面中是无法选中.(所以当你一个标签应用了含有伪类的css样式,那么这个标签本身不会应用样式里设置的属性,只是伪元素本身应用样式里设置的属性.)
默认情况是这样了,BootStrap在伪元素里做了一个加工,把'sb'换成字体文件里的unicode编码,比如'u2709',这样,在alex后面出现的就不是sb了,而是一个图标.
比如在BootStrap中的css文件源码中有这么一段样式代码:
.glyphicon-envelope:before{
content:"2709" /* 2709就是字体文件的unicode编码,当执行这段代码时就会浏览器就会去字体文件中取unicode编码为2709的图标*/
}
当标签调用这个样式时,就会在标签text的前面加上图标,这个就是BootStrap插件实现调用css样式实现在调用标签的前面或后面显示图标的原理.
学了伪类和BootStrap总结亮点:
1.xxx:after,before (联合content使用)这种定义的css是伪类,内容前后插入数据
2.bootstrap 是通过伪类 和 字体对应关系 来实现的
那么问题来了,利用伪类能做什么?有一个常用的,之前我们在学css样式的float时,知道在使用float的时候,要在最后加一个clear=both,如下:
<style>
.c2{
background-color: #ffd00f;
}
</style>
<body>
<div class="c2">
<div style="float: left;">11</div>
<div style="float: left;">22</div>
<div style="clear: both;"></div>
</div>
</body>
原因是一个标签里没有内容是不显示的.所以想显示背景色,就要把float的拉下来.
那我们现在学了伪类就可以使用伪类来实现了:(把c2改成伪类c2:after,当div应用了c2样式,让after自动加一个内容,并且clear=both)
代码如下:
<head>
<meta charset="UTF-8">
<title>Title</title>
<style>
.c2{
}
.c2:after{
content:'.'; /*在应用c2样式的标签后加一个'.',因为div标签或者其他标签只有当有值的时候才会撑起来*/
clear: both; /*定义伪元素的clear为both*/
visibility: hidden; /*让这个伪类隐藏*/
}
</style>
</head>
<body>
<div class="c2">
<div style="float: left;">11</div>
<div style="float: left;">22</div>
</div>
</body>
上面的代码实现了使用伪类把div撑起来的作用,我们想一个html中可能有很多使用了float的div标签.所以能不能定义一个大家都能使用的伪类.
所以上面的代码就可以改成:
<head>
<meta charset="UTF-8">
<title>Title</title>
<style>
.c2{
}
.clearfix:after{
content:'.'; /*在应用c2样式的标签后加一个'.',因为div标签或者其他标签只有当有值的时候才会撑起来*/
clear: both; /*定义伪元素的clear为both*/
visibility: hidden; /*让这个伪类隐藏*/
}
</style>
</head>
<body>
<div class="c2 clearfix"> //这里应用了两个class,
<div style="float: left;">11</div>
<div style="float: left;">22</div>
</div>
</body>
这样写实现了两个好处:
1.不用每次在div里多加一条<div clear='both'><div> 标签了,让css样式自动加了
2.不用为每一个div加一个伪类了.只需要在div应用一个指定的clear样式即可.
为类实例:返回顶部终极版(返回顶部已经到极致了,没有比这在牛逼的了)
有些网页右下方有返回顶部,当你鼠标没有移动上去的时候,是一个图标,鼠标移上后就是"返回顶部"字符串.这个如何实现
我们可以通过js,绑定事件,这样当然可以实现,但是这样实现就是每一个有返回顶部功能的页面都要引用这个js.
这时候就可以使用伪类来实现,定义当鼠标放上去的时候,就调用after,加上样式和伪类.具体代码如下:
.gotop:hover:after{
top:0;
left:0;
100%;
height:100%;
content: "";
position: absolute;
}
我们知道.gotop:hover{} 的意思是当鼠标移动到上面应用该样式.而.gotop:hover:after{}的意识是当鼠标移动到上面,在class="gotop"标签内容后面应用该样式
CSS内容补充之无法被覆盖
四.讲述上面知识点(二)的目的是介绍bootstrap的实现响应式的原理,讲解知识点(三)的目的是介绍bootstrap引用图标的实现原理.这两点都是针对css的.
bootstrap也有js部分.js 的部分我们在使用的时候讲解,ps:bootstrap的所有的js都是基于jQuery实现的.所以在导入bootstrap的js前要先导入jQuery.
接下来讲解bootstrap的使用
1. 下载导入
2. css head
3. js body底部
a.先倒入jQuery,版本要是2.1.4以上
b.再导入bootstrap
那到底bootstrap能对整个html做哪些定义呢?
4. 可以定义头部信息
<meta charset="utf-8">
编码
<meta http-equiv="X-UA-Compatible" content="IE=edge">
Bootstrap 不支持 IE 古老的兼容模式。为了让 IE 浏览器运行最高级别的可用模式渲染显示内容
<meta name="viewport" content="width=device-width, initial-scale=1">
为了确保适当的绘制和触屏缩放,需要在 <head> 之中添加 viewport 元数据标签。
在移动设备浏览器上,通过为视口(viewport)设置 meta 属性为 user-scalable=no 可以禁用其缩放(zooming)功能。这样禁用缩放功能后,用户只能滚动屏幕,就能让你的网站看上去更像原生应用的感觉。注意,这种方式我们并不推荐所有网站使用,还是要看你自己的情况而定!
(最直观的是在手机上是不是允许用两个手指进行放大缩小)
<meta name="renderer" content="webkit">
国内浏览器厂商一般都支持兼容模式(即 IE 内核)和高速模式(即 webkit 内核),不幸的是,所有国产浏览器都是默认使用兼容模式,这就造成由于低版本 IE (IE8 及以下)内核让基于 Bootstrap 构建的网站展现效果很糟糕的情况。幸运的是,国内浏览器厂商逐渐意识到了这一点,某些厂商已经开始有所作为了
目前只有360浏览器支持此 <meta> 标签。希望更多国内浏览器尽快采取行动、尽快进入高速时代!
(其实就是说好多浏览器的内核是IE浏览器内核,但是IE浏览器内核低版本又不支持响应式布局,bootstrap有因为这个来出解决方案,即使解决了效率也变底了.而使用<meta>属性就可以设置浏览器使用chrom浏览器内核.但是又不是所有的浏览器有这个属性.)
(对于现在的开放,你就不用考虑IE浏览器了,因为IE67早该被淘汰了)
ps:其实对于bootstrap这个插件来说,你要想把它的所有功能都应用上,其实很费劲的,为什么呢?就是因为IE浏览器的存在.IE很烂.
bootstrap2文件结构如下,其中bootstrap-responsive.css和bootstrap-responsive.min.css这两个文件的存在的意义很大程度上就是为了解决IE浏览器响应式布局的,有的IE版本67是不支持响应式布局的.解决掉了,但是效率也变得底了.
bootstrap2
├── css
│ ├── bootstrap-responsive.css
│ ├── bootstrap-responsive.min.css
│ ├── bootstrap.css
│ └── bootstrap.min.css
├── img
│ ├── glyphicons-halflings-white.png
│ └── glyphicons-halflings.png
└── js
├── bootstrap.js
└── bootstrap.min.js
body中请查看bootstrap官网文档.
PS:以后在写页面的时候,在最外层加一个最小宽度,这样当缩小网页时,两个float的div才不会堆叠,而是出现左右的滚动条.
五.更改框架
假如我们现在使用了框架,写了一个html.框架的背景色是红色,我现在想把背景色改成灰色,该如何修改.
理论上我们直接写一个css样式,然后在body处用上class等于我们自定义的css样式文件即可.但是实际上即使你使用了,也不起任何作用.为什么?
原因是框架里设置的css样式都用到了优先级,声明了框架里的样式优先级高.如:
<style>
.c1{
background-color: #2aabd2 !important;
}
.c2{
background-color: #2b669a;
}
</style>
在属性后面加了!important,标示我最牛逼,你们谁也代替不了我.
那么我们应该如何做,才能改呢.当然是在c2里定义的属性也加上!important,这样两个都是最牛逼的在一个级别上,又遵循从上到下的顺序了.
!important这个就是知识点三中提到的css样式补充之无法覆盖的内容.
六.Web框架
七.Django
Django 框架基础
1.安装
pip install django=1.9.5
2.创建Django程序
我们想,我们创建自己的Django程序之前,是不是要拿到Django框架的基础文件.所以我们在脑中想象,有两步骤1.拿到django框架的基础文件.2.创建自己的APP程序
知道创建Django程序有两步后,我们又要知道创建的方式有两种().
a.命令方式 django-admin startproject project名字
例子:
django-admin startproject mysite 当执行此命令时,我们就创建了一个名为mysite 的django工程
cd mysite 进入我们创建的工程目录,这时候查看下目录结构如下
ls -l
-rwxr-xr-x 1 tedzhou staff 249 8 1 13:17 manage.py 用于在此工程目录下创建程序项目的脚本程序,比如我们在此工程目录中创建cmdb程序项目,创建monitor程序项目,创建openstacks程序项目等
drwxr-xr-x 6 tedzhou staff 204 8 1 13:17 mysite 此目录下有一个同名工程目录的目录,此目录下放了一些配置文件.
python manage.py startapp app01 创建app01
python manage.py startapp app02 创建app02
再次查看目录结构如下:
$ ls -l
drwxr-xr-x 9 tedzhou staff 306 8 1 13:21 app01 创建出来的程序项目app01
drwxr-xr-x 9 tedzhou staff 306 8 1 13:21 app02 创建出来的程序项目app02
-rwxr-xr-x 1 tedzhou staff 249 8 1 13:17 manage.py
drwxr-xr-x 7 tedzhou staff 238 8 1 13:21 mysite
b.使用phcharm工具进行创建工程项目project,创建app时还是需要在终端进行创建
创建Django程序
终端,python manage.py startapp app0 -windows
如果在mac中,在创建过Django程序的前提下按 option+R组合键,弹出一个配置界面.
使用django框架配置自己的程序.
我们创建好Django工程并且创建了自己的app后.有以下几个文件需要关注
mysite/manage.py 这个是相当于自己框架中的main程序.同时又是创建app的脚本文件和执行django程序的脚本文件
mysite/mysite/urls.py 相当于自己框架文件中的urls.py文件,用于访问路由相当于映射,你访问/admin/路径映射到一个函数,访问/home/映射到一个函数
mysite/mysite/settings 全局配置文件,可配置BASE_DIR目录,templates目录的地址,等等其他的...具体用的时候自然会知道
mysite/app01/models.py 相当于自己框架中的models.py,配置连接后台数据库的方法.这个只能是文件,不能搞成目录,因为Django框架就是这么定的
mysite/app01/views.py 相当于自己框架中的views目录,这里是文件,我们可以创建一个目录叫做views目录,然后再在目录中在根据不同的业务把函数按文件分为:账户登陆相关views/account.py ,用户信息相关views/userInfo.py等等
mysite/templates/ 这是一个目录,目录中放了一些html的模版文件,文件里使用jinja2的语法.
模拟一个用户请求Django框架的流程
用户请求 -> mysite/mysite/urls.py(判断请求的地址做路由) -> mysite/app01/views.py(取相应的函数) -> models.py(拿到数据库数据) --> 将结果返回给请求者 (over)
-> templates/* (取html模版文件)
3.执行django程序
进入project项目
python manage.py runserver 127.0.0.1:8000
点pycharm >
4.Django依赖数据库
配置:settings
生成数据库表:
python manage.py makemirations # 生成配置文件
python manage.py migrate # 根据配置文件创建数据库相关
5.admin
Django是一个特别全,特别牛逼的框架.为什么说它牛逼,它比其他框架提供的功能要多.比如ORM关系对象映射,我们学过sqlachemy,而Django自己就有一套ROM.另外Django还有一个网站后台.而admin就是用来配置后台的.
我们在浏览器输入http://127.0.0.1:8000/admin我们会看到后台的登陆界面,这里默认是没有用户名和密码的.后面会说admin用户名和密码是如何创建的.
admin就是配置后台的,比如说我们之前连接数据库,以及进行基本的增删改查,是需要通过数据库终端进行操作.Django中的admin就是让你快速的数据库的.比如我们通过Django的ORM来操作数据库.首先创建类,然后通过类操作表(创建表,对数据进行增删改查).一个类就是一个表.
这时候我们就可以把我们创建的Django的ORM类注册到admin,就可以通过admin后台对注册的类进行快速的增删该查.老男孩教育Alex做的那个点名的表就是使用admin进行配置后把功能实现的.具体怎么配置我们后面会学到,现在先知道Django有admin这个后台管理即可.
并且Django的厉害之处还有单元测试.这个现在还涉及不到.
另外我们看下app01程序目录下有这么一个文件夹:migrations,这个文件夹是做什么用的呢?
我们之前学习sqlachemy的时候,知道sqlachemy不能对表结构进行修改.而Django中的ORM类就可以实现对表结构的修改.它是怎么实现的呢?它的实现就和migrations文件夹有关系了.
sqlachemy中创建表,只需要一个命令就在数据库里就生成一个表,而Django框架中的ORM类就不是直接通过命令创建了,它需要两步:第一步,使用一个命令生成一个配置文件,第二步再使用一个命令根据配置文件做数据库的操作.
相当于如果在Django中做数据库变更,或者要删除表中的一个字段或者增加一个字段再或者改变一个字段的类型.你都需要先使用一个命令生成中间的配置文件(相当于代码),在根据配置文件操作数据库,这个配置文件放在哪呢?就是放在migrations文件夹内.
上面的内容都是admin的基本的介绍.
我们在说一个知识点,对于Django的admin是需要用户名和密码的.那这个用户名密码前面我们说默认是没有的.那我们要进行创建.创建在哪里保存呢?哈哈,Django之能够提供admin后台那么强大的功能的前提是,它是需要一些数据源进行支撑的.
所以当我们使用django-admin startproject mysite创建一个Django工程的时候会默认创建一个db.sqlte3 库,但是默认库里是没有表的,需要执行初始化命令进行创建的.
我们可以在mysite/mysite/settings.py文件里找到关于初始化数据库的位置如:
# Database
# https://docs.djangoproject.com/en/1.9/ref/settings/#databases
DATABASES = {
'default': {
'ENGINE': 'django.db.backends.sqlite3',
'NAME': os.path.join(BASE_DIR, 'db.sqlite3'),
}
}
在settings.py文件中我们找到了初始化db文件的路径为os.path.join(BASE_DIR, 'db.sqlite3')
我们知道初始化时候会有一个sqlite数据库.那么我们就可以在这个库里创建admin后台的管理账户了.那么怎样创建呢?前面说了在Django中创建一个表需要两个步骤.1.生成配置文件.2.根据配置文件操作数据库.当然操作初始库也不能例外.
生成数据库表:
python3.5 manage.py makemigrations # 生成配置文件
python manage.py migrate # 根据配置文件创建数据库相关
执行完初始化数据库命令后,就可以使用命令创建后台管理账户了,同时我们可以使用navicat for sqlite查看都创建了哪些表.
python manage.py createsuperuser
...
用户 ted
密码 Zh....
http://localhost:8000/admin
至此,我们关于Django框架自带的后台管理功能admin暂时了解到这,后面会有专门章节进行讲解.
6.路由系统
我们学Django就是为了用它.用它来做我们的项目.
对于Django请求到来之后现到达路由系统,那么对于路由系统的配置,需要配置什么.
我们知道在django框架中,project_name/project_name/urls.py文件是路由系统的文件,也就是说所有路由系统的配置都和这个文件有关.为什么说有关,而不是就配置这个呢?
因为存在二级路由的方式.
初始化时urls.py文件内容:
from django.conf.urls import url
from django.contrib import admin
urlpatterns = [
url(r'^admin/', admin.site.urls),
]
1.静态路由
就是简单的url(r'^home/', admin.site.urls),写死的URL
from django.conf.urls import url
from django.contrib import admin
from app01 import views
urlpatterns = [
url(r'^admin/', admin.site.urls),
url(r'^home/', views.home),
]
同时在app01中定义home:
from django.shortcuts import render
from django.shortcuts import HttpResponse
# Create your views here.
# 在Django里面所有处理请求的函数必须有一个request参数,原因是所有的函数都是Django框架内部调用的,调用的时候把用户请求的数据封装到request中,Django框架在根据路由选择,调用views里函数时再把请求的信息封装成request穿入函数.
#
def home(request):
# 我们自己写的框架可以直接返回字符串.而在Django中,函数中return时不能直接返回字符串,需要用框架中的HttpResponse模块封装.
# request 'aaaaa'
return HttpResponse('222222 OK')
2.动态路由
我们经常看到,如果页面上的内容很多时,会有分页.当我们访问分页时,往往只是URL后面的数字不一样如:
http://www.cnblogs.com/#p2 第二页
http://www.cnblogs.com/#p3 第三页
http://www.cnblogs.com/#p4 第四页
那我们肯定不能在路由中写上这每一页的URL,这尼玛不科学.那么怎么写呢?
urlpatterns = [
url(r'^admin/', admin.site.urls),
url(r'^home/', views.home),
url(r'^news/(d+)', views.news), #这里我们设置news/(d+),d+为正则表达式,
]
然后在app01下的views在定义一个news
def news(request,nid): #request,必须有,没得选择就像类里的self一样,必须有
return HttpResponse(nid)
这样我们就可以访问:
http://127.0.0.1:8000/news/66
http://127.0.0.1:8000/news/11
http://127.0.0.1:8000/news/22
我们想既然可以匹配一个参数,能不能匹配两个呢?当然可以,如下
urlpatterns = [
url(r'^admin/', admin.site.urls),
url(r'^home/', views.home),
url(r'^news/(d+)', views.news),
url(r'^shop/(d+)/(d+)', views.shop),
]
这时我们在app01/views.py要定义一个shop函数
def shop(request,nid1,nid2): #这里我们传入了两个参数.
nid = nid1+nid2
return HttpResponse(nid)
这里切记,参数严格按照顺序进行赋值.
那么有没有办法,让路由里的参数和views里的函数对应上呢,这样传入函数时,就不需要留意顺序了,有,如下:
urlpatterns = [
url(r'^admin/', admin.site.urls),
url(r'^home/', views.home),
url(r'^news/(d+)', views.news),
url(r'^shop/(d+)/(d+)', views.shop),
url(r'^page/(?P<n1>d+)/(?P<n2>d+)', views.page), # 这里(?P<n1>/d+)就指定了我第一个匹配的数字传给n1形式参数,第二个匹配的值传给n2这个形式参数.
]
这时,我们在app01/views.py里定义个page函数
def page(request,n2,n1): #此时n1,n2的位置就不必在意了,但是变量名一定要和urls配置的一样
nid = n1+n2
return HttpResponse(nid)
总结:
按照顺序,按n个匹配的数据,交给函数的第N个参数,严格按照顺序
模版的方法,将匹配的参数,传给指定的形式参数
3.二级路由
我们假设mysite 是Django工程,在里面有两个app项目app01和app02,两个项目又都有home目录和page目录,
我们此时要是在mysite/mysite/urls.py里进行路由,那尼玛就麻烦了.因为app01和app02都要通过views里的函数进行处理,两个名字一样了,当然你可以通过别名,但是那样low,咱们有高大上的做法.
我们假设每一个程序目录自己有一个urls.py文件,让访问app01目录下的所有请求去app01/urls.py去路由,访问app02目录下的请求,去找app02/urls.py去路由.就完美了.怎样实现呢?
如下:
在mysite/mysite/urls.py文件配置如下
from django.conf.urls import url,include # 导入include,用于加载app01和app02目录下的urls文件
from django.contrib import admin
from app01 import views
urlpatterns = [
url(r'^admin/', admin.site.urls),
url(r'^home/', views.home),
url(r'^news/(d+)', views.news),
url(r'^shop/(d+)/(d+)', views.shop),
url(r'^page/(?P<n1>d+)/(?P<n2>d+)', views.page),
url(r'^app01/', include('app01.urls')), #设置当访问app01/下的请求时,使用app01.urls进行路由
url(r'^app02/', include('app02.urls')), #设置当访问app02/下的请求时,使用app02.urls进行路由
]
在mysite/app01/urls.py 这个文件默认不存在,可以从mysite目录下拷贝,配置如下:
from django.conf.urls import url
from django.contrib import admin
from app01 import views
urlpatterns = [
url(r'^home/', views.home),
]
在mysite/app01/views.py定义home函数
def home(request):
# 我们自己写的框架可以直接返回字符串.而在Django中,函数中return时不能直接返回字符串,需要用框架中的HttpResponse模块封装.
# request 'aaaaa'
return HttpResponse('APP01.home')
在mysite/app02/urls.py 这个文件默认不存在,可以从mysite目录下拷贝,配置如下:
from django.conf.urls import url
from django.contrib import admin
from app02 import views
urlpatterns = [
url(r'^home/', views.home),
]
在mysite/app02/views.py定义home函数
from django.shortcuts import render
from django.shortcuts import HttpResponse
# Create your views here.
def home(request):
return HttpResponse("app02.home") #这里一定要用HttpResponse进行封装,我都忘记很多次了.
这时我们在浏览器访问我测试:
访问http://127.0.0.1:8000/app02/home/ 显示app02.home
http://127.0.0.1:8000/app01/home/ 显示APP01.home
总结:
app01
url.py
project_name
url: app01 -> include("app01.url")
7.基本的数据库操作
我们学过sqlalchemy,是ORM框架关系对象
ORM框架
code first 代码优先
自己写类 -> 然后根据类创建数据库表
db first
自己命令创建数据库表和字段 -->根据表创建类 (不懂)
上面两个最终还是使用类进行数据操作.code first更省事,而对于db first先修改数据库,在根据库生成类.具体咋整的先不用明白.
Django框架内部有自己的orm框架,它的ORM框架类型也是code first ,即先写类在创建数据库表
具体的实例:
我们前面在mysite工程项目里创建了两个APP:app01和app02
这两个app项目里都有models.py文件.
这里我们就可以利用app01/models.py文件生成app01程序项目里需要的表.
利用app02/models.py文件生成app02程序项目里需要的表.这样公用一个数据库分别生成自己程序需要的表,这样的华两个程序之间没有表的关联降低了两个程序之间的耦合,如果它们之间需要通讯,就需要通过API进行交互.以后会学现在先知道下需要通过API
我们知道创建表,先创建类,类生成表.类就需要放到models里面.对于models里面要想创建一个类,就需要使用Django框架里的ORM的模块以及语法.
所以我们要掌握的是如何使用Django框架里的ORM框架进行创建类.以及实例化类后的对象的增删改查的语法.
1.在Django框架中创建ORM类,然后创建一个表的步骤.
在app01/models.py文件中创建ORM类
from django.db import models
class UserInfo(models.Model): # 创建一个UserInfo类,一个类就是一个表
username = models.CharField(max_length=32) # username字段,类型为CharField,类型为CharField的时候,后面必须跟(max_length=32)
password = models.CharField(max_length=32) # password字段
age = models.IntegerField() # age字段
# 至此我们就创建了一个类,接下来就可以命令先创建配置文件,然后在执行配置文件了.
python manage.py makemirations # 生成配置文件
python manage.py migrate # 根据配置文件创建数据库相关
但是 我们执行上面两个命令现在还不能成功,为什么呢?你想啊,之前我们初始化Django数据库时能成功,那是配置文件里有admin的信息.我们现在要给自己创建的app项目创建表,并且使用的models文件路径在app01/models.py
试问,执行python manage.py makemirations 这个命令时,它如何才能找到app01/models.py文件呢?所以当然要在全局配置文件mysite/mysite/settings.py进行如下配置:
1.首先把自己的项目名称,在INSTALLED_APPS序列里
# Application definition
INSTALLED_APPS = [
'django.contrib.admin',
'django.contrib.auth',
'django.contrib.contenttypes',
'django.contrib.sessions',
'django.contrib.messages',
'django.contrib.staticfiles',
'app01', #在最后加上你自己的app项目的名字.切记后面要有逗号.这样Django程序就会主动找app01/models.py文件
]
在settings.py文件里进行设置完成后,在执行那两个命令.
python manage.py makemirations # 生成配置文件,此时在app01/migrations/目录下多出一个配置文件0001——initial.py
python manage.py migrate # 根据配置文件创建数据库相关,其实就是执行上面那个配置文件
执行完成后,我们使用navicat for sqlite工具查看表,会多出一个表,表名为:
app01_userinfo
这里Django默认把项目app01的表加了前缀为app01_userinfo .是不是超级智能.
OK,到此我们就掌握了如何为APP项目配置自己的models.py文件,并在models.py文件里创建类的方法.
PS: 我们创建数据表时只给了username,password,age三个字段,在执行python manage.py makemirations命令后,配置文件中自动添加一个id字段为自增且主键的属性.高级把,但是我们如果在类中设置了主键,就不会自动添加了.可查看0001——initial.py文件.
2.接下来,要学会如何利用已经在models.py文件里创建的类,进行增删该查操作了.
进行增删改查的操作,就是根据具体请求,通过路由模块,到达指定的函数,然后由函数进行数据的增删改查然后在结合模版html进行返回.
所以我们定义对数据库的增删改查需要在app01/views.py中定义.
我们在app01/urls.py中先定义一个handle_db,如下:
urlpatterns = [
url(r'^home/', views.home),
url(r'^handle_db/', views.handle_db),
]
然后在app01/views.py中定义handle_db函数,如下:
先小测试下:
def handle_db(request):
return HttpResponse('OK .....app01')
访问http://127.0.0.1:8000/handle_db
查看返回结果是否为OK .....app01
如果报错,就需要检查mysite/mysite/urls.py 文件和和app01/urls.py文件 以及settings.py文件
测试结果无问题,下面就可以在函数中定义对数据库的增删改查了.我们对数据库操作的时候需要用到在models.py文件里定义的类,所以要在views.py文件里引入models文件
函数代码如下:
from app01 import models
def handle_db(request):
# 增 方式一
# models.UserInfo.objects.create(username='alex',password='123',age='22') # UserInfo是我们在models中创建的类,objects是Django框架中定义必须要加的.create就是创建数据的方法
# 增 方式二
dic = {'username':'ted',"password":"123123",'age':18}
models.UserInfo.objects.create(**dic)
return HttpResponse('OK')
上面两种方式方式二要注意的是传入字典时前面加两个** ,格式为**dic
我们想,实际开发中,我门要创建的数据是从哪里来的.是不是应该是用户在浏览器中输入,通过post,或者get方式提交过来的.
get方式提交的时候就是在url后面加拼接.这里先不多说了,在模版章节中会详细说明.我们这里先要知道的是,我们在views.py中定义函数时加入的request,如def handle_db(request):
request封装了用户的具体请求数据.
通过request.post可以获得用户通过post方式提交的数据
通过request.get 可以获得用户通过get方式提交的数据
对于Django里基本的也是最常用的增删改查代码试例如下:
def handle_db(request):
# request 用户请求的所有内容
# request.POST 获取用户以POST提交
# request.GET 获取用户以GET提交的数据
# 增 方式一
# models.UserInfo.objects.create(username='alex',password='123',age='22') # UserInfo是我们在models中创建的类,objects是Django框架中定义必须要加的.create就是创建数据的方法
# 增 方式二
# dic = {'username':'ted',"password":"123123",'age':18}
# models.UserInfo.objects.create(**dic)
# 删除
# models.UserInfo.objects.filter(username='alex').delete() # filter后面加过滤条件.不可能将所有数据都删除
# 修改
# models.UserInfo.objects.all().update(age=10)
# 查找
# models.UserInfo.objects.all()
# models.UserInfo.objects.filter(age=10)
# models.UserInfo.objects.filter(age=10).first()
return HttpResponse('OK')
3.基本的增删改查操作我们会了,接下来我们学习如何将数据和html结合起来.
首先我们通过查询语句获得用户列表:
def handle_db(request):
user_list_obj = models.UserInfo.objects.all() #首先我们知道这里获得了多条数据记录,每条数据记录就是UserInfo类的实例
return HttpResponse('OK')
当用户请求访问了http://127.0.0.1:8000/app01/handle_db/,函数里的user_list_obj是多个UserInfo 实例的列表在Django中它是一个特殊列表(queryset列表).
如果我们想把这些数据展示到浏览器上.就需要:
1.把这个列表传给html的模版
2.模版在接到数据后要对这个列表进行循环.
首先:在函数中我们使用return HttpResponse()来返回字符串,那我们如何返回html模版呢.这就需要用到views.py文件默认引入的render方法了.
render方法的使用:
def handle_db(request):
user_list_obj = models.UserInfo.objects.all() #获得对象列表
return render(request,'t1.html',{'li':user_list_obj})
# render() 三个参数,
# 第一个参数,request就是封装了请求那个request.
# 第二个参数,'t1.html' 模版文件的名称.
# 第三个参数,是一个字典 ,字典的key 就是模版文件中使用的变量.而user_list_obj 才是要循环的数据列表.
结下来我们来写t1.html模版文件.
<body>
<table border="1">
<thead>
<tr>
<th>用户名</th>
<th>密码</th>
<th>年龄</th>
</tr>
</thead>
<tbody>
{% for item in li %} // li就是render()里的第三个参数 字典的key,拿到这个key后,它内部实现对这个字典key对应的value进行循环
<tr>
<td>{{ item.username }}</td> // jinja2里循环里面循环具体数据的时候用的是{{}}
<td>{{ item.password }}</td>
<td>{{ item.age }}</td>
</tr>
{% endfor %} //jinja2里for循环结尾标记
</tbody>
</table>
</body>
PS:Django模版默认使用python的jinja2模块.
jinja2里规定for循环或者if else时用的标记是 {% xxxx %}
循环里面循环具体数据时用{{}}
模版文件我们写好了,views.py里函数也用rander()指定了文件和循环的数据.应该可以在浏览器动态返回数据了把.
no,还差一步,我们在函数里指定模版文件的文件名,请问函数怎么去找到这个文件,去哪个目录下去找.它是如何知道呢.当然是通过settings.py配置了.
于是我们还要一步配置settings.py
TEMPLATES = [
{
'BACKEND': 'django.template.backends.django.DjangoTemplates',
'DIRS': [os.path.join(BASE_DIR,'templates')], # 这里是添加的.默认列表为空即:[]
'APP_DIRS': True,
'OPTIONS': {
'context_processors': [
'django.template.context_processors.debug',
'django.template.context_processors.request',
'django.contrib.auth.context_processors.auth',
'django.contrib.messages.context_processors.messages',
],
},
},
]
配置完成后,就可以访问http://127.0.0.1:8000/app01/handle_db/,我们在浏览器上就可以看到动态表格了.
这个例子执行成功了就说明我们理解了Django中配置app项目如何进行后台数据抓取后配合html模版进行前端页面展示.
此例中我们创建的t1.html文件相当简单,以至于很low,我们知道想让界面美观需要配合上css,想要界面可以互动,就需要使用js.
那么我们应该把这些css和js放到哪里呢?它们都属于静态文件.所以我们应该在mysite目录下创建一个static目录(和templates目录同级),专门存放静态文件.
并且还要在settings.py文件里配置下静态文件:
找到:
STATIC_URL = '/static/' (这个不起作用)
在它下面加上:
STATICFILES_DIRS = (
os.path.join(BASE_DIR,'static'),
)
settings.py配置完成后,就可以在模版文件中引用了.(你会想我们之前写html的时候,也不需要配置啊,只是在html中直接写引入的css路径和js文件路径,这里为什么还要配置.)
原因是,我们是通过views.py里的函数return给请求者的.函数里return的是一个长字符串.所以函数在return时,函数要把html模版中引用的js文件和css文件找到并解析后返回给浏览器.
所以说配置settings.py其实就是告诉函数要去哪里找这些js和css静态文件.
然后我们在html中就可以引入了
<script src="/static/jquery-3.1.0.min.js"></script>
//这里看我们引入要写/static/xxxx.js 这样函数才会去settings.py里设置的static下去找xxxx.js
f.通过页面,提交数据到后台.
我们知道前端通过form表单将数据提交到后台.form标签中有一个action,一般action指定url地址.而咱们这里有一个url:^~/app01/handle_db/,
form如果提交给本地的URL,前面的IP或者域名就不需要写了,另外如果form表单的提交方式为post,这就牵涉到Django框架中的跨站请求.默认是拒绝提交的,那么我们就需要在配置中开启.
settings.py配置如下:
MIDDLEWARE_CLASSES = [
'django.middleware.security.SecurityMiddleware',
'django.contrib.sessions.middleware.SessionMiddleware',
'django.middleware.common.CommonMiddleware',
# 'django.middleware.csrf.CsrfViewMiddleware', #把这一行注释掉就行了.
'django.contrib.auth.middleware.AuthenticationMiddleware',
'django.contrib.auth.middleware.SessionAuthenticationMiddleware',
'django.contrib.messages.middleware.MessageMiddleware',
'django.middleware.clickjacking.XFrameOptionsMiddleware',
]
settings.py配置好后,我们就可以完成一个简单的form表单代码提交到后台数据库了:
首先我们在html模版页中加入下面的代码
<form action="/app01/handle_db/" method="post">
<p><input type="text" name="username"/></p>
<p><input type="text" name="password"/></p>
<p><input type="text" name="age"/></p>
<p><input type="submit" value="提交" /></p>
</form>
我们知道直接访问url就是get方式.而点击上面定义的form表单中的submit按钮时才是post提交.所以我们在点击提交之前是不是要通过浏览器请求这个页面.
在页面中输入内容点击submit的提交按钮后,在进行post提交.
也就是说我们访问http://127.0.0.1:8000/app01/handle_db/时 要调用app01/views.py里定义的handle_db函数.
当我们在打开的页面输入数据后,还要在调用handle_db函数.
所以我们要在handle_db函数中做if语句进行判断.一般请求都是get,这时候就返回页面.只有当请求是post时才提交数据.于是handle_db函数的代码如下:
def handle_db(request):
if request.method == 'POST':
# print(request.POST) 这里是看看我们获得的POST数据是什么类型
# <QueryDict: {'username': [''], 'password': [''], 'age': ['']}>
# 我们看到是input里定义的name属性,和输入的值.值放在列表里.
models.UserInfo.objects.create(username=request.POST['username'],
password=request.POST['password'],
age=request.POST['age']
)
user_list_obj = models.UserInfo.objects.all()
return render(request,'t1.html',{'li':user_list_obj})
完成这个函数后,我们就可以在页面进行提交数据添加用户名,密码和age保存到数据库了.
本节相关知识点完结.我们学会了
如何创建Django工程.
如何创建APP项目
如何启动工程.
django自带的admin后台管理功能简述.
Django提供的ORM使用介绍
路由系统的使用方法.
项目中使用ORM,完成对数据库增删改查的基本操作.
使用html模版,把前端和后台数据进行关联.
前端通过post提交数据写入数据库的基本使用演示.