對於全部的Web應用,本質上其實就是一個socket服務端,用戶的瀏覽器其實就是一個socket客戶端。javascript
import socket def f1(request): """ 處理用戶請求,並返回相應的內容 :param request: 用戶請求的全部信息 :return: """ f = open('index.fsw','rb') data = f.read() f.close() return data """ <body> <h1>用戶登陸</h1> <form> <p><input type="text" placeholder="用戶名" /></p> <p><input type="password" placeholder="密碼" /></p> </form> </body> """ def f2(request): f = open('aricle.tpl','rb') data = f.read() f.close() print(data) return data """ <body> <table border="1"> <thead> <tr> <th>ID</th> <th>用戶名</th> <th>郵箱</th> </tr> </thead> <tbody> <tr> <th>1</th> <th>root</th> <th>root@qq.com</th> </tr> </tbody> </table> </body> """ routers = [ ('/xxx', f1), ('/ooo', f2), ]#手動寫的網址 def run(): sock = socket.socket() sock.bind(('127.0.0.1',8080)) sock.listen(5) while True: conn,addr = sock.accept() # hang住 #print(conn)#得到的兩個套接字,我去charm本身會發送請求一個/favicon.ico頁面的報文 # print(addr) # 有人來鏈接了 # 獲取用戶發送的數據 data = conn.recv(8096) data = str(data,encoding='utf-8') #print(data)#get報文 headers,bodys = data.split('\r\n\r\n') #print("head:",headers) #print("body:",bodys) body是空的 temp_list = headers.split('\r\n') # print(temp_list) method,url,protocal = temp_list[0].split(' ') # print(method) GET # print(url) /ooo /favicon.ico # print(protocal) /HTTP/1.1 conn.send(b"HTTP/1.1 200 OK\r\n\r\n") func_name = None for item in routers: if item[0] == url: func_name = item[1] break if func_name: response = func_name(data) print(data) else: response = b"404" conn.send(response) conn.close() if __name__ == '__main__': run()
這種靜態頁面不能與數據庫鏈接交互,因此也是很是的low。css
import socket def f1(request): """ 處理用戶請求,並返回相應的內容 :param request: 用戶請求的全部信息 :return: """ f = open('index.fsw','rb') data = f.read() f.close() return data """ <body> <h1>用戶登陸</h1> <form> <p><input type="text" placeholder="用戶名" /></p> <p><input type="password" placeholder="密碼" /></p> </form> </body> """ def f2(request): f = open('aricle.tpl','r',encoding='utf-8') data = f.read() f.close() import time ctime = time.time() data = data.replace('@@sw@@',str(ctime)) return bytes(data,encoding='utf-8') """ <body> <table border="1"> <thead> <tr> <th>ID</th> <th>用戶名</th> <th>郵箱</th> </tr> </thead> <tbody> <tr> <th>1</th> <th>@@sw@@</th> <th>root@qq.com</th> </tr> </tbody> </table> </body> """ def f3(request): import pymysql # 建立鏈接 conn = pymysql.connect(host='127.0.0.1', port=3306, user='root', passwd='jeff@123', db='db1') cursor = conn.cursor(cursor=pymysql.cursors.DictCursor) cursor.execute("select id,username,password from userinfo") user_list = cursor.fetchall() #print(user_list) cursor.close() conn.close() content_list = [] for row in user_list: tp = "<tr><td>%s</td><td>%s</td><td>%s</td></tr>" %(row['id'],row['username'],row['password']) content_list.append(tp) content = "".join(content_list) f = open('userlist.html','r',encoding='utf-8') template = f.read() f.close() # 模板渲染(模板+數據) data = template.replace('@@sdfsdffd@@',content) return bytes(data,encoding='utf-8') """ mysql> select * from userinfo; +----+----------+----------+ | id | username | password | +----+----------+----------+ | 1 | alex | 123 | +----+----------+----------+ <body> <table border="1"> <thead> <tr> <th>ID</th> <th>用戶名</th> <th>郵箱</th> </tr> </thead> <tbody> @@sdfsdffd@@ </tbody> </table> </body> """ def f4(request): import pymysql # 建立鏈接 conn = pymysql.connect(host='127.0.0.1', port=3306, user='root', passwd='jeff@123', db='db1') cursor = conn.cursor(cursor=pymysql.cursors.DictCursor) cursor.execute("select id,username,password from userinfo") user_list = cursor.fetchall() cursor.close() conn.close() f = open('hostlist.html','r',encoding='utf-8') data = f.read() f.close() # 基於第三方工具實現的模板渲染 from jinja2 import Template template = Template(data) data = template.render(xxxxx=user_list,user='sdfsdfsdf') return data.encode('utf-8') """ {% for row in xxxxx %} <tr> <td>{{row.id}}</td> <td>{{row.username}}</td> <td>{{row.password}}</td> </tr> {% endfor %} </tbody> </table> {{user}} """ routers = [ ('/xxx', f1), ('/ooo', f2), ('/userlist.html', f3), ('/host.html', f4), ] def run(): sock = socket.socket() sock.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1) sock.bind(('127.0.0.1',8080)) sock.listen(5) while True: conn,addr = sock.accept() # hang住 # 有人來鏈接了 # 獲取用戶發送的數據 data = conn.recv(8096) data = str(data,encoding='utf-8') headers,bodys = data.split('\r\n\r\n') temp_list = headers.split('\r\n') method,url,protocal = temp_list[0].split(' ') conn.send(b"HTTP/1.1 200 OK\r\n\r\n") func_name = None for item in routers: if item[0] == url: func_name = item[1] break if func_name: response = func_name(data) else: response = b"404" conn.send(response) conn.close() if __name__ == '__main__': run()
這裏要說兩點,首先這裏使用了jinjia2模塊,因此要簡單的介紹一下這個模塊。html
渲染模板(使用render_template方法)前端
@app.route('/about/') def about(): # return render_template('about.html',user='username') return render_template('about.html',**{'user':'username'})
渲染模版時有兩種傳遞參數的方式:用 var='value' 傳遞一個參數;使用字典組織多個參數,而且加兩個*
號轉換成關鍵字參數傳入。java
在jinja2模板中:python
{{ ... }}
:裝載一個變量,模板渲染的時候,會使用傳進來的同名參數這個變量表明的值替換掉。mysql
{% ... %}
:裝載一個控制語句。jquery
{# ... #}
:裝載一個註釋,模板渲染的時候會忽視這中間的值。git
變量:web
設置全局變量:{% set name='xx' %},以後就可使用此變量了。
設置局部變量:
{% with foo = 42 %}
{{ foo }}
{% endwith %}
這裏的foo變量只能在with標籤中使用。
{% if kenny.sick %} Kenny is sick. {% elif kenny.dead %} You killed Kenny! You bastard!!! {% else %} Kenny looks okay --- so far {% endif %}
#通常循環 <ul> {% for user in users %} <li>{{ user.username|e }}</li> {% endfor %} </ul> #遍歷字典 {% for key, value in my_dict.iteritems() %} <dt>{{ key|e }}</dt> <dd>{{ value|e }}</dd> {% endfor %}
jinja2模塊最重要的部分是宏,宏至關於一個搭建好的頁面一部分,能夠被引入,能夠往宏傳遞參數。能夠將一些常常用到的代碼片斷放到宏中,而後把一些不固定的值抽取出來當成一個變量,在使用宏時傳遞參數,從而將宏渲染成爲頁面的一部分。
更多關於此模塊的操做,能夠查看博客https://www.cnblogs.com/ygj0930/p/7170621.html。
要說的第二點就是這種方法仍是太low了。
import socket def handle_request(client): buf = client.recv(1024) client.send("HTTP/1.1 200 OK\r\n\r\n".encode("utf8")) client.send("<h1 style='color:red'>Hello, yuan</h1>".encode("utf8")) def main(): sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM) sock.bind(('localhost',8001)) sock.listen(5) while True: connection, address = sock.accept() handle_request(connection) connection.close() if __name__ == '__main__': main()
最簡單的Web應用就是先把HTML用文件保存好,用一個現成的HTTP服務器軟件,接收用戶請求,從文件中讀取HTML,返回。
若是要動態生成HTML,就須要把上述步驟本身來實現。不過,接受HTTP請求、解析HTTP請求、發送HTTP響應都是苦力活,若是咱們本身來寫這些底層代碼,還沒開始寫動態HTML呢,就得花個把月去讀HTTP規範。
正確的作法是底層代碼由專門的服務器軟件實現,咱們用Python專一於生成HTML文檔。由於咱們不但願接觸到TCP鏈接、HTTP原始請求和響應格式,因此,須要一個統一的接口,讓咱們專心用Python編寫Web業務。這個接口就是WSGI:Web Server Gateway Interface。
# from wsgiref.simple_server import make_server # # # def application(environ, start_response): # start_response('200 OK', [('Content-Type', 'text/html')]) # return [b'<h1>Hello, web!</h1><h2>Hello, py!</h2>'] # # # httpd = make_server('127.0.0.2', 8080, application)#(ip,pork,func) # # print('Serving HTTP on port 8080...') # # 開始監聽HTTP請求: # httpd.serve_forever()
django是一個基於python的高級web開發框架,由於他的高度集成,將會在從此的web開發裏給予咱們很大的幫助。
首先建立一個django工程(加不加.py均可以):
django-admin.py startproject project_name #django-admin.py startproject myblog
工程下面有幾個核心測文件:
manage.py Django項目裏面的管理工具,經過它能夠調用django shell和數據庫等。
settings.py 包含了項目的默認設置,包括數據庫信息,調試標誌以及其餘一些工做的變量。
urls.py 負責把URL模式映射到應用程序,路由(就是url與函數的對應關係)。
wsgi.py 調用python內置的wsgiref模塊,web服務網關接口。他定義django用什麼socket實現,默認就是wsgiref模塊。
注:除了命令的方式pycharm也能夠進行django工程的搭建。
HttpResponse模塊
from django.conf.urls import url from django.shortcuts import HttpResponse def index(request):#request用戶請求相關的全部信息 return HttpResponse('whatever') urlpatterns = [ url(r'^index/', index), ]
啓動django自帶的服務器,
python manage.py runserver 8080
在瀏覽器訪問127.0.0.1:8080/index便可查看到django渲染後的網頁。
render模塊
from django.conf.urls import url from django.shortcuts import render def index(request):#request用戶請求相關的全部信息 return render(request,"a.html")#默認要加request參數 urlpatterns = [ url(r'^index/', index), ]
接下來在瀏覽器訪問127.0.0.1:8080/index便可查看到django渲染後的網頁(服務器在改變了代碼的狀況下會自動重啓)。還有,訪問的前提是在templates目錄下有一個a.html的文件。那麼django是如何找到這個路徑的呢,由於在settings.py下有一個TEMPLATES列表,其中'DIRS': [os.path.join(BASE_DIR, 'templates')]指明瞭render須要從這個目錄下拿到。
靜態文件的配置
在工程文件夾下建立一個static文件夾裏面存放靜態文件,並將路徑寫入settings.py下。
STATIC_URL = '/static/' STATICFILES_DIRS=(os.path.join(BASE_DIR,'static'),)
而後在導入文件時一概使用/static引入。
request相關
request.method得到當前請求的方法。request.GET與request.POST能夠取到用戶提交的數據。
from django.conf.urls import url from django.shortcuts import render,redirect def index(request):#request用戶請求相關的全部信息 if request.method =='GET':#瀏覽器默認傳get,區分返回來的信息 return render(request,"a.html") else: u=request.POST.get('user')#取出post方式傳回來的字典的值 p=request.POST.get('pwd')#get取不到會轉化爲none if u=='jeff' and p=='123': return redirect('http://www.baidu.com')#固然也能夠重定向到本身的目錄 else: return render(request, "a.html") urlpatterns = [ url(r'^index/', index), ]
a.html中的修改:
<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <link rel="stylesheet" href="/static/as.css"> <title>Title</title> </head> <body> <h1>用戶登陸</h1> <form method="post" action="/index/">{# 發送以post方式發到index下 #} <input type="text" name="user"/> <input type="password" name="pwd"/> <input type="submit" value="login"/> </form> </body> </html>
這樣用戶訪問127.0.0.1:8080/index會使用get方法返回a.html頁面,輸入用戶名和密碼提交會用post方法返回給index頁面經判斷是重定向仍是從新輸入。
django的渲染模板
django基本的html的模板與jinja2很類似,咱們能夠在form表單里加入一個{{ msg }}的模板,而後在render裏添加一個msg:value用於自動傳入。
django的模板取序列的值也是簡單粗暴,好比取列表就是{{ list.index }}例如{{ s.0 }}{{ s.1 }},字典就是{{ dict.key }}例如{{row.id}}{{ row.name }}。
from django.conf.urls import url
from django.contrib import admin
from django.shortcuts import HttpResponse,render,redirect
def index(request):#request用戶請求相關的全部信息
if request.method =='GET':
return render(request,"a.html")
else:
u=request.POST.get('user')
p=request.POST.get('pwd')
print(u)
print(p)
if u=='jeff' and p=='123':
return render(request, "b.html",{'user':[{'id':1,'name':'jeff','age':0},
{'id': 2, 'name': 'frank', 'age': 1},
{'id': 3, 'name': 'xixi', 'age': 2}]})
else:
return render(request, "a.html")
urlpatterns = [
url(r'^admin/', admin.site.urls),
url(r'^index/', index),
]
<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <link rel="stylesheet" href="/static/as.css"> <title>Title</title> </head> <body> <h1>用戶登陸</h1> <form method="post" action="/index/">{# 發送以post方式發到index下 #} <input type="text" name="user"/> <input type="password" name="pwd"/> <input type="submit" value="login"/> </form> </body> </html>
<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <title>Title</title> </head> <body> <table border="1"> {% for item in user %} <tr> <td>{{ item.id }}</td> <td>{{ item.name }}</td> <td>{{ item.age }}</td> <td><a href="/del/?nid=={{ item.id }}"></a></td>{# 跳轉到專門的del頁面 #} </tr> {% endfor %}{#循環結束 #} </table> </body> </html>
這裏render傳入的字典可使用pymsql導入,這樣就與數據庫緊密的鏈接到一塊兒了。
模板傳入字典:
def func(request): v = {'name':'jeff', 'age':25} return render(request, 'test.html', {'v': v})
{% for item in v %} <h6>{{ item }}</h6> {% endfor %}
結果只顯示key。
{% for item in v.keys %} <h6>{{ item }}</h6> {% endfor %}
與上顯示一致。
{% for item in v.values %} <h6>{{ item }}</h6> {% endfor %}
只顯示values。
{% for k,v in v.items %} <h6>{{ item }}</h6> {% endfor %}
顯示鍵值。
C:\USERS\JEFFD\PYCHARMPROJECTS\DAY65
│ db.sqlite3
│ manage.py
├─app_name
│ └─views.py
├─day65
│ │ settings.py
│ │ urls.py
│ │ wsgi.py
│ └─__init__.py
├─static
└─templates
add_clas.html
class.html
增長與查看單表數據
建立過程:在urls.py建立路由
from app_name import views
url(r'^class/', views.clas),
訪問127.0.0.1:8000/class/會直接跳轉到views下的clas函數。
def clas(request): conn = pymysql.connect(host='127.0.0.1', port=3306, user='root', passwd='jeff@123', db='db3', charset='utf8') cursor = conn.cursor(cursor=pymysql.cursors.DictCursor)#字典方式取數據 cursor.execute("select cid,caption from class") class_list = cursor.fetchall() cursor.close() conn.close() return render(request,'class.html',{'class_list':class_list})#將mysql的數據傳給模板語言
<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <title>Title</title> </head> <body> <h1>班級列表</h1> <table> <thead> <tr> <th>ID</th> <th>班級名稱</th> <th>操做</th> </tr> </thead> <tbody> {% for row in class_list %} <tr> <td>{{ row.cid }}</td> <td>{{ row.caption }}</td> <td> <a href="/add_clas/">添加</a> <a href="/edit_class/?nid={{ row.id }}">編輯</a> <a href="/del_class/?nid={{ row.id }}">刪除</a> </td> </tr> {% endfor %} </tbody> </table> </body> </html>
點擊class.html的a標籤,跳轉到"/add_clas/",此時咱們要在urls.py中增長一條路由。
url(r'^add_clas/', views.add_clas),
def add_clas(request): if request.method=='GET': return render(request,'add_clas.html') else: cap=request.POST.get('title')#傳值的name參數 print(cap) conn = pymysql.connect(host='127.0.0.1', port=3306, user='root', passwd='jeff@123', db='db3', charset='utf8') cursor = conn.cursor(cursor=pymysql.cursors.DictCursor) cursor.execute("insert into class(caption) values(%s)",[cap,]) conn.commit() cursor.close() conn.close() return redirect('/class/')
<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <title>Title</title> </head> <body> <h1>添加班級</h1> <form method="POST" action="/add_clas/"> <p>班級名稱:<input type="text" name="title" /></p>{# title給request.post取值 #} <input type="submit" value="提交" /> </form> </body> </html>
重定向會第二次再請求‘/class/’頁面,從而獲取新的數據庫中的值。
刪除單表數據
在class.html上加上跳轉連接:
<a href="/del_clas/?nid={{ row.cid }}">刪除</a>
添加路由:
url(r'^del_clas/', views.del_clas),
增長del函數:
def del_clas(request): nid=request.GET.get('nid') conn = pymysql.connect(host='127.0.0.1', port=3306, user='root', passwd='jeff@123', db='db3', charset='utf8') cursor = conn.cursor(cursor=pymysql.cursors.DictCursor) cursor.execute("delete from class where cid=%s", [nid, ]) conn.commit() cursor.close() conn.close() return redirect('/class/')
post能夠在連接上傳數據,nid由此返回到del_clas函數,取出id並在數據庫刪除,重定向到初始頁面,數據就被刪掉了。
修改單表數據
同理class.html須要連接到新的頁面,urls.py要增長路由。
def edit_clas(request): if request.method == "GET":#第一次get請求,獲取id的參數 nid = request.GET.get('nid') conn = pymysql.connect(host='127.0.0.1', port=3306, user='root', passwd='jeff@123', db='db3', charset='utf8') cursor = conn.cursor(cursor=pymysql.cursors.DictCursor) cursor.execute("select cid,caption from class where cid = %s", [nid, ]) result = cursor.fetchone() cursor.close() conn.close() return render(request, 'edit_clas.html', {'result': result}) else:#第二次post請求,將修改值更新到數據庫 nid = request.GET.get('nid')#post在url能夠傳參,可是要用get取url的參數 # nid = request.POST.get('nid') print(nid) title = request.POST.get('title') conn = pymysql.connect(host='127.0.0.1', port=3306, user='root', passwd='jeff@123', db='db3', charset='utf8') cursor = conn.cursor(cursor=pymysql.cursors.DictCursor) cursor.execute("update class set caption=%s where cid = %s", [title,nid, ]) conn.commit() cursor.close() conn.close() return redirect('/class/')
<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <title>Title</title> </head> <body> <h1>編輯班級</h1> <form method="POST" action="/edit_clas/?nid={{ result.cid }}"> <p>班級名稱:<input type="text" name="title" value="{{ result.caption }}" /></p> <input type="submit" value="提交" /> </form> </body> </html>
注:get請求信息在請求頭裏,就是url上,post請求信息在請求體中,若是要使用post在url中傳遞參數,那麼要用request.GET來獲取參數。
這就是單表的數據完成的增刪改查。
單表操做之模態對話框
此次以學生表爲例,先建立查看學生表。
先建立工程,添加路由,建立app_name文件夾和views.py文件以及students函數。
import pymysql from django.shortcuts import render, redirect,HttpResponse def students(request): """ 學生列表 :param request: 封裝請求相關的全部信息 :return: """ # conn = pymysql.connect(host='127.0.0.1', port=3306, user='root', passwd='jeff@123', db='db3', charset='utf8') cursor = conn.cursor(cursor=pymysql.cursors.DictCursor) cursor.execute( "select student.sid,student.sname,class.caption from student left JOIN class on student.class_id = class.cid") student_list = cursor.fetchall() cursor.close() conn.close() return render(request, 'students.html', {'student_list': student_list})
<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <title>Title</title> </head> <body> <h1>學生列表</h1> <div> <a href="/add_student/">添加</a> </div> <table> <thead> <tr> <th>ID</th> <th>學生姓名</th> <th>所屬班級</th> <th>操做</th> </tr> </thead> <tbody> {% for row in student_list %} <tr> <td>{{ row.sid }}</td> <td>{{ row.sname }}</td> <td>{{ row.caption }}</td> <td> <a href="/edit_student/?nid={{ row.sid }}">編輯</a> | <a>刪除</a> </td> </tr> {% endfor %} </tbody> </table> </body> </html>
學生表之add
先url再函數,
def add_student(request):
if request.method == "GET":
conn = pymysql.connect(host='127.0.0.1', port=3306, user='root', passwd='jeff@123', db='db3', charset='utf8')
cursor = conn.cursor(cursor=pymysql.cursors.DictCursor)
cursor.execute("select cid,caption from class")
class_list = cursor.fetchall()
cursor.close()
conn.close()
return render(request, 'add_student.html', {'class_list': class_list})
else:
name = request.POST.get('name')
class_id = request.POST.get('class_id')
conn = pymysql.connect(host='127.0.0.1', port=3306, user='root', passwd='jeff@123', db='db3', charset='utf8')
cursor = conn.cursor(cursor=pymysql.cursors.DictCursor)
cursor.execute("insert into student(sname,class_id) values(%s,%s)", [name, class_id, ])
conn.commit()
cursor.close()
conn.close()
return redirect('/students/')
<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <title>Title</title> </head> <body> <h1>添加學生</h1> <form method="POST" action="/add_student/"> <p>學生姓名<input type="text" name="name" /></p> <p> 所屬班級 <select name="class_id"> {% for row in class_list %} <option value="{{ row.cid }}">{{ row.caption }}</option> {% endfor %} </select> </p> <input type="submit" value="提交" /> </form> </body> </html>
在以前的的增刪改查中有不少pymsql的操做,大量的重複代碼,咱們能夠將數據庫查詢單獨拿出來作成一個工具包。咱們能夠在項目中增長一個utils的包做爲sql操做包。
import pymysql
conn = pymysql.connect(host='127.0.0.1', port=3306, user='root', passwd='jeff@123', db='db3', charset='utf8')
cursor = conn.cursor(cursor=pymysql.cursors.DictCursor)
def get_list(sql,args):#查找數據庫中全部數據
cursor.execute(sql,args)
result = cursor.fetchall()
cursor.close()
conn.close()
return result
def get_one(sql,args):#查找一條數據
cursor.execute(sql,args)
result = cursor.fetchone()
cursor.close()
conn.close()
return result
def modify(sql,args):#更新數據
cursor.execute(sql,args)
conn.commit()
cursor.close()
conn.close()
注:conn和cursor拿出來可能會致使在同一個函數中操做時可能會致使conn被關閉第二次取不到值。
編輯學生表:
from utils import sqlhelper def edit_student(request): if request.method == "GET": nid = request.GET.get('nid') class_list = sqlhelper.get_list("select cid,caption from class", []) current_student_info = sqlhelper.get_one('select sid,sname,class_id from student where sid=%s', [nid, ]) print(current_student_info) return render(request, 'edit_student.html', {'class_list': class_list, 'current_student_info': current_student_info}) else: nid = request.GET.get('nid') name = request.POST.get('name') class_id = request.POST.get('class_id') sqlhelper.modify('update student set sname=%s,class_id=%s where sid=%s', [name, class_id, nid, ]) return redirect('/students/')
<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <title>Title</title> </head> <body> <h1>編輯學生</h1> <form method="POST" action="/edit_student/?nid={{ current_student_info.sid }}"> <p>學生姓名<input type="text" name="name" value="{{ current_student_info.sname }}" /></p> <p> 所屬班級 <select name="class_id"> <!-- 循環全部的班級 --> {% for row in class_list %} <!-- 若是是當前學生所在班級,則默認選中 --> {% if row.cid == current_student_info.class_id %} <option selected value="{{ row.cid }}">{{ row.caption }}</option> {% else %} <option value="{{ row.cid }}">{{ row.caption }}</option> {% endif %} {% endfor %} </select> </p> <input type="submit" value="提交" /> </form> </body> </html>
爲了確保編輯時現實的select就是此id的班級,因此這裏使用的if判斷來完成。只有row.cid == current_student_info.class_id時,默認被selected便可。
到這裏開始和以前的班級表並無什麼差別,可是,每次編輯和添加都使用新的連接並非一件很可靠的事,在不少狀況下咱們都須要在當前頁進行增刪改查,這個時候咱們就須要使用到模態對話框了。而submit的提交必定會致使頁面的刷新(後臺指定),因此有須要使用的前端的ajax技術。
一句話介紹ajax就是頁面不刷新並悄悄地日後臺發數據,而後在後臺進行簡單的操做。
<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <title>Title</title> <style> .hide{ display: none; } .shadow{ position: fixed; left: 0; top: 0; right: 0; bottom: 0; background-color: black; opacity: 0.4; z-index: 999; } .modal{ z-index: 1000; position: fixed; left: 50%; top: 50%; height: 300px; width: 400px; background-color: white; margin-left: -200px; margin-top: -150px; } </style> </head> <body> <h1>學生列表</h1> <div> <a href="/add_student/">添加</a> <a onclick="showModal();">對話框添加</a> </div> <table> <thead> <tr> <th>ID</th> <th>學生姓名</th> <th>所屬班級</th> <th>操做</th> </tr> </thead> <tbody> {% for row in student_list %} <tr> <td>{{ row.sid }}</td> <td>{{ row.sname }}</td> <td>{{ row.caption }}</td> <td> <a href="/edit_student/?nid={{ row.sid }}">編輯</a> | <a>刪除</a> </td> </tr> {% endfor %} </tbody> </table> <div id="shadow" class="shadow hide"></div> <div id="modal" class="modal hide"> <h2>添加學生</h2> <p> 學生姓名<input id="sname" type="text" name="sname" /> </p> <p> 所屬班級 <select name="class_id" id="add_classid">{# 至關於提交了一個class_id=value回後臺 #} {% for row in class_list %} <option value="{{ row.cid }}">{{ row.caption }}</option> {% endfor %} </select> </p> <input type="button" value="提交" onclick="AjaxSend();" /><span id="errormsg"></span> <input type="button" value="取消" onclick="cancleModal();" /> </div> <script src="/static/jquery-3.2.1.js"></script> <script> function showModal(){ document.getElementById('shadow').classList.remove('hide'); document.getElementById('modal').classList.remove('hide'); } function cancleModal(){ document.getElementById('shadow').classList.add('hide'); document.getElementById('modal').classList.add('hide'); } function AjaxSend(){ $.ajax({ url: '/modal_add_student/', type: 'POST', data: {'sname': $('#sname').val(),'class_id':$('#add_classid').val()},//返回的是節點下value的值 success: function(data){ // 當服務端處理完成後,返回數據時,該函數自動調用 // data=服務端返回的值 console.log(data); if(data == "ok"){ location.href= "/students/"; }else{ $('#errormsg').text(data); } } }) } </script> </body> </html>
def modal_add_student(request): sname = request.POST.get('sname') class_id=request.POST.get('class_id') if len(sname) > 0: sqlhelper.modify('insert into student(sname,class_id) values(%s,%s)',[sname,class_id,]) return HttpResponse('ok') else: return HttpResponse('班級標題不能爲空')
因此,總的來講Ajax的步驟就三部:
1.url,發送的後端路徑,
2.type,發送到後端的方式(GET/POST),
3.data,發送到後端的數據。
後端處理完之後會觸發Ajax的success裏的函數,location.href指向要跳轉的地址。
模態對話框與新url的方式各自的優缺點:
模態對話框:處理少許輸入框而且輸入的部分比較少時使用,好比登錄界面等;
新url方式:處理操做較多,數據量偏大時使用,好比博客的編輯頁面等。
因此咱們要根據實際的操做狀況來選擇合適的方式。
學生表之對話框編輯
<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <title>Title</title> <style> .hide{ display: none; } .shadow{ position: fixed; left: 0; top: 0; right: 0; bottom: 0; background-color: black; opacity: 0.4; z-index: 999; } .modal{ z-index: 1000; position: fixed; left: 50%; top: 50%; height: 300px; width: 400px; background-color: white; margin-left: -200px; margin-top: -150px; } .editModal{ z-index: 1000; position: fixed; left: 50%; top: 50%; height: 300px; width: 400px; background-color: white; margin-left: -200px; margin-top: -150px; } </style> </head> <body> <h1>學生列表</h1> <div> <a href="/add_student/">添加</a> <a onclick="showModal();">對話框添加</a> </div> <table> <thead> <tr> <th>ID</th> <th>學生姓名</th> <th>所屬班級</th> <th>操做</th> </tr> </thead> <tbody> {% for row in student_list %} <tr> <td>{{ row.sid }}</td> <td>{{ row.sname }}</td> <td clsId="{{ row.cid }}">{{ row.caption }}</td> <td> <a href="/edit_student/?nid={{ row.sid }}">編輯</a> | <a onclick="modelEdit(this);">對話框編輯</a> | <a>刪除</a> </td> </tr> {% endfor %} </tbody> </table> <div id="shadow" class="shadow hide"></div> <div id="modal" class="modal hide"> <h2>添加學生</h2> <p> 學生姓名<input id="sname" type="text" name="sname" /> </p> <p> 所屬班級 <select name="class_id" id="add_classid">{# 至關於提交了一個class_id=value回後臺 #} {% for row in class_list %} <option value="{{ row.cid }}">{{ row.caption }}</option> {% endfor %} </select> </p> <input type="button" value="提交" onclick="AjaxSend();" /> <input type="button" value="取消" onclick="cancleModal();" /><span id="errormsg"></span> </div> <div id="editModal" class="editModal hide"> <h2>編輯學生</h2> <p> <input type="text" style="display: none" id="stuId"> 學生姓名<input id="stuname" type="text" name="sname" /> </p> <p> 所屬班級 <select name="classId" id="edit_classid">{# 至關於提交了一個class_id=value回後臺 #} {% for row in class_list %} <option value="{{ row.cid }}">{{ row.caption }}</option> {% endfor %} </select> </p> <input type="button" value="提交" id="btnEdit" /> <input type="button" value="取消" onclick="cancleModal();" /><span id="errorm"></span> </div> <script src="/static/jquery-3.2.1.js"></script> <script> function showModal(){ document.getElementById('shadow').classList.remove('hide'); document.getElementById('modal').classList.remove('hide'); } function cancleModal(){ document.getElementById('shadow').classList.add('hide'); document.getElementById('modal').classList.add('hide'); $('.editModal').hide(); } function AjaxSend(){ $.ajax({ url: '/modal_add_student/', type: 'POST', data: {'sname': $('#sname').val(),'class_id':$('#add_classid').val()}, success: function(data){ // 當服務端處理完成後,返回數據時,該函數自動調用 // data=服務端返回的值 //console.log(data); if(data == "ok"){ location.href= "/students/";{# js實現頁面跳轉#} }else{ $('#errormsg').text(data); } } }) } function modelEdit(ths){ document.getElementById('shadow').classList.remove('hide'); document.getElementById('modal').classList.add('hide'); $('.editModal').show(); /* 1. 獲取當前點擊標籤 2. 當前標籤父標籤,再找其上方標籤 3. 獲取當前行班級名稱,賦值到編輯對話框中 */ var row = $(ths).parent().prevAll(); var clas = $(row[0]).attr('clsId'); $('#edit_classid').val(clas); //要在對話框中取到classid那麼在獲取數據的時候就須要從數據庫中查詢到, // 以attr或隱藏方式傳入原始表單,才能取到。 var stuname = $(row[1]).text(); $('#stuname').val(stuname); var stu_id = $(row[2]).text();//修改的學生的id也應該傳給後臺用於sql更新 $('#stuId').val(stu_id); } $('#btnEdit').click(function(){ $.ajax({ url:'/modal_edit_student/', type: 'POST', data: {'nid': $('#stuId').val(), 'name':$('#stuname').val(),'class_id': $('#edit_classid').val()}, dataType: 'JSON', //JSON.parse(arg) success:function(arg){ if(arg.status){ location.reload(); }else{ $('#errorm').text(arg.message); } } }) }) </script> </body> </html>
咱們在模態對話框中顯示的編輯對象,須要顯示學生姓名以及學生班級由於學生班級要用select表單方式展現因此必需要拿到學生對應的classid號,可是頁面只須要顯示課程的名稱,因此能夠採起兩種方案,都要如今查詢聯表時查詢到classid傳到模板,第一種方式是添加隱藏的列來取到id,另外一種就是這裏使用的增長自定義屬性clsId="{{ row.cid }}"來取到編輯用戶的id。
取classid的時候使用的是row = $(ths).parent().prevAll();取出點擊的標籤以前全部的兄弟標籤元素(td),$(row[index]).attr('clsId')來取出classid。
location.reload()//頁面從新加載
注:JSON.parse(arg)只能操做可序列化元素,咱們這裏使用dataType: 'JSON'至關於在回調函數中進行了JSON.parse(arg)操做。
import json def modal_edit_student(request): ret = {'status': True,'message': None} try: nid = request.POST.get('nid') name = request.POST.get('name') class_id = request.POST.get('class_id') sqlhelper.modify('update student set sname=%s,class_id=%s where sid=%s',[name,class_id,nid,]) except Exception as e: ret['status'] = False ret['message'] = str(e) return HttpResponse(json.dumps(ret))
注:Ajax向後臺發數據時更新學生信息因此須要獲取學生id。
教師表之多對多操做
查詢操做和一對一,一對多基本相同,惟一注意的就是,查詢出來的teacher_list是列表中套字典的格式:
[{'tid': 1, 'tname': '波多', 'student_id': 2, 'class_id': 3, 'caption': '三年一班'},]
在頁面中顯示的時候咱們但願看到的友好的界面是一名老師對應多門課程,多門課程在同一個單元格中顯示。
result = {} for row in teacher_list: tid =row['tid'] if tid in result: result[tid]['captions'].append(row['caption']) else: result[tid] = {'tid': row['tid'],'name':row['tname'],'captions': [row['caption'],]}
sql輔助模塊
前面使用了sql的工具包存在着一些一些問題,每次鏈接都須要拿連接和cursor,而且不停的打開關閉數據庫會浪費不少時間,因此有什麼方法能夠只拿到一次conn和cursor就往裏面寫,寫完再斷開呢?這很容易就聯想到python裏的類屬性。
class SqlHelper(object): def __init__(self): # 讀取配置文件 self.connect() def connect(self): self.conn = pymysql.connect(host='127.0.0.1', port=3306, user='root', passwd='', db='s4db65', charset='utf8') self.cursor = self.conn.cursor(cursor=pymysql.cursors.DictCursor) def get_list(self,sql,args): self.cursor.execute(sql,args) result = self.cursor.fetchall() return result def get_one(self,sql,args): self.cursor.execute(sql,args) result = self.cursor.fetchone() return result def modify(self,sql,args): self.cursor.execute(sql,args) self.conn.commit() def multiple_modify(self,sql,args): # self.cursor.executemany('insert into bd(id,name)values(%s,%s)',[(1,'alex'),(2,'eric')]) self.cursor.executemany(sql,args) self.conn.commit() def create(self,sql,args): self.cursor.execute(sql,args) self.conn.commit() return self.cursor.lastrowid def close(self): self.cursor.close() self.conn.close()
添加老師的課程,由於班級是能夠多選的,首先前端日後端發送的課程是一個列表,後端使用request.POST.getlist(''),獲取提交的列表,同時咱們須要得到老師的id號用於insert,cursor.lastrowid是最後一條記錄插入的記錄(非併發)。
相對於優化過的數據庫一次連接屢次提交,咱們使用一次連接一次提交更爲高效。
# self.cursor.executemany('insert into bd(id,name)values(%s,%s)',[(1,'alex'),(2,'eric')])
data_list = [] for cls_id in class_ids: temp = (teacher_id,cls_id,) data_list.append(temp) obj = sqlheper.SqlHelper() obj.multiple_modify('insert into teacher2class(teacher_id,class_id) values(%s,%s)',data_list) obj.close()
若是數據不少,查詢數據庫須要等待,等待的界面就是shadow和一個屏幕正中間的div(填充了背景爲gif的圖片)show,表格div爲hide,在Ajax收到後臺返回的數據後在success裏將gif的div改成hide,表格div爲show就是這樣的效果了。
在這裏使用對話框進行添加和編輯老師時,對於獲取全部的課程咱們一直是在頁面加載的時候就進行數據庫查詢,而後隱藏起來,須要操做的時候show,但實際上某些時候表基本上不會改動,那麼等於每次都進行了額外的數據庫查詢,這裏咱們還提供使用Ajax的方式動態展示。
$.ajax({ url:'/get_all_class/', type:'GET', dataType: 'JSON', success:function(arg){ /* arg = [ {id:1,title:xx} {id:1,title:xx} {id:1,title:xx} ] */ //console.log(arg); // 將全部的數據添加到select,option $.each(arg,function(i,row){ var tag = document.createElement('option'); tag.innerHTML = row.title; tag.setAttribute('value',row.id); $('#classIds').append(tag); }); $('#loading').hide(); $('#addModal').show(); } })
Ajax在發送data時,data數據中須要傳遞列表類型的數據則要使用traditional: true,後臺才能夠接收到數據。
function bindAddSubmit(){ $('#addSubmit').click(function(){ var name = $('#addName').val(); var class_id_list = $('#classIds').val(); console.log(name,class_id_list); $.ajax({ url:'/modal_add_teacher/', type: 'POST', data: {'name':name, 'class_id_list': class_id_list}, dataType:'JSON', traditional: true,// 若是提交的數據的值有列表,則須要添加此屬性 success: function (arg) { if(arg.status){ location.reload(); }else{ alert(arg.message); } } }) }); } 後臺: class_id_list = request.POST.getlist('class_id_list') #若是沒有 traditional: true,那麼後臺接收到的就是一個空列表。
表單提交中的input、button、submit的區別
http://blog.csdn.net/ldc5306590/article/details/54376417
簡而言之就是:
<input type="button" /> 這就是一個按鈕。若是你不寫javascript 的話,按下去什麼也不會發生。
<input type="submit" /> 這樣的按鈕用戶點擊以後會自動提交 form,除非你寫了javascript 阻止它或者綁定onsubmit="return false;"。
<button> 這個按鈕放在 form 中也會點擊自動提交,比前兩個的優勢是按鈕的內容不光能夠有文字,還能夠有圖片等多媒體內容。
附:源碼的git地址:https://gitee.com/gouliguojiashengsiyi/jeffsys1.0/tree/master/