跟b站武沛齐老师的视频学习
对学习过程进行一些记录以便复习 同时自我督促 :D冲鸭视频链接: 15天django入门到放弃-哔哩哔哩.
文章目录
回顾
对数据库的操作:pymysql、SQLAlchemy、navicat(终端)
-
安装
-
创建用户 + 授权
-
连接
- 数据库 终端创建数据库(字符编码) - 数据表 终端、ORM、pymysql pymysql:create ...(数据类型/是否可以为空/主键/自增/外键/索引)engine=innodb支持事务性操作 - 数据行 增、删、改、查 查:limit、group by、order by ...
关闭
内容概要
Web框架
- socket
- http协议
连接方式对比
HTTP:无状态、短连接
TCP:不断开 - HTML知识
- 数据库(pymysql、SQLAlchemy)
Web应用
-
浏览器(socket客户端)
② www.cnblogs.com(42.121.252.58,80)sk.socket() sk.connect((42.121.252.58,80) sk.send('我想要xx')
⑤ 接受
⑥ 连接断开 -
博客园(socket服务端)
① 监听ip和端口(42.121.252.58,80)
while True:
– 用户 = 等待用户连接
– ③ 收到‘我想要xx’
– ④ 相应:“好”
用户断开
铺垫练习
s1 简单的byte传输
简单的数据传输。
# -*- coding: utf-8 -*-
import socket
server_socket = socket.socket(socket.AF_INET, socket.SOCK_STREAM, 0)
server_socket.bind(('127.0.0.1', 8000))
server_socket.listen(5)
while True:
client_socket, client_addr = server_socket.accept() # 阻塞
data = client_socket.recv(8096) # 最大接收字节
response = "HTTP/1.1 200 OK\r\n\r\n" # 200 表示找到这个资源
client_socket.send(response.encode('utf8'))
client_socket.send(b'123123')
client_socket.send(bytes("hello", encoding='utf-8'))
client_socket.close()
s2 URL
对接收数据进行分割,提取出URL内容,实现不同URL页面显示不同文字。
# -*- coding: utf-8 -*-
import socket
server_socket = socket.socket(socket.AF_INET, socket.SOCK_STREAM, 0)
server_socket.bind(('127.0.0.1', 8000))
server_socket.listen(5)
while True:
client_socket, client_addr = server_socket.accept() # 阻塞
data = client_socket.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(' ')
response = "HTTP/1.1 200 OK\r\n\r\n" # 200 表示找到这个资源
client_socket.send(response.encode('utf8'))
if url == "/xxxx":
client_socket.send(b'123123')
else:
client_socket.send(b'404 not found')
client_socket.close()
s3 静态网站
在s2基础上,将文件作为data返回,实现更复杂的数据传输。
s3.python
:
# -*- coding: utf-8 -*-
import socket
def f1():
"""
处理用户请求,并返回相应的内容
:param request: 用户请求的所有信息
:return:
"""
f = open('index.html', 'rb')
data = f.read()
f.close()
return data
def f2():
f = open('article.html', 'rb')
data = f.read()
f.close()
return data
def f3():
return b'word'
routers = [
('/index', f1),
('/article', f2),
('/word', f3)
]
def run():
server_socket = socket.socket(socket.AF_INET, socket.SOCK_STREAM, 0)
server_socket.bind(('127.0.0.1', 8000))
server_socket.listen(5)
while True:
client_socket, client_addr = server_socket.accept() # 阻塞
data = client_socket.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(' ')
response_header = "HTTP/1.1 200 OK\r\n\r\n" # 200 表示找到这个资源
func_name = None
for item in routers:
if item[0] == url:
func_name = item[1]
break
if func_name:
response_body = func_name()
else:
response_body = b'404'
client_socket.send(response_header.encode('utf8'))
client_socket.send(response_body)
client_socket.close()
if __name__ == '__main__':
run()
article.html
:
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>article</title>
</head>
<body>
<table>
<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>
</html>
index.html
:
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>index</title>
</head>
<body>
<h1>用户登录</h1>
<form>
<p><input type="text" placeholder="用户名" /></p>
<p><input type="password" placeholder="密码" /></p>
</form>
</body>
</html>
s4 动态网站
修改了 s3.py
中函数 f2()
和 f3()
,实现时间的显示和数据库内容的读取。
def f2():
f = open('time.html', 'r', encoding='utf-8')
data = f.read()
f.close()
import time
ctime = time.asctime(time.localtime(time.time()))
data = data.replace('@@time@@', str(ctime))
return bytes(data, encoding='utf-8')
def f3():
import pymysql
conn = pymysql.connect(host='127.0.0.1', port=3306, user='root', passwd='123456', db='db_test')
cursor = conn.cursor(cursor=pymysql.cursors.DictCursor) # 游标设置为字典类型
cursor.execute("select id, username, password from userinfo")
user_list = cursor.fetchall()
conn.commit()
cursor.close()
conn.close()
print(user_list)
"""
要替换html文件中的@@content@
将数据库中读取的数据改写为格式如:
<tr>
<th>id</th>
<th>username</th>
<th>password</th>
</tr>
"""
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 += tp
content = "".join(content_list)
f = open('userlist.html', 'r', encoding='utf-8')
template = f.read()
f.close()
data = template.replace('@@content@@', content)
return bytes(data, encoding='utf-8')
time.html
:
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>article</title>
</head>
<body>
<h1>@@time@@</h1>
</body>
</html>
userlist.html
:
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>article</title>
</head>
<body>
<table>
<thead>
<tr>
<th>ID</th>
<th>用户名</th>
<th>密码</th>
</tr>
</thead>
<tbody>
@@content@@
</tbody>
</table>
</body>
</html>
过渡性总结
自己写网站
- a. socket服务端
- b. 根据URL不同返回不同的内容
路由系统:URL -> 函数 - c. 字符串返回给用户
模板引擎渲染:HTML充当模板(特殊字符)、自己创造任意数据
Web框架
框架种类:
- abc
Tornado - [第三方a]+bc
Django (应用python中wsgiref模块) - [第三方ac]+b
flask(wsgiref、jinja2)
分类:
- Django
- 其他(轻量级框架)
Django入门
创建项目
基本指令:
安装:pip3 install django
django-admin startproject mysite
若没有添加环境变量,要在路径下执行。
其他常用命令:
python manage.py runserver ip:port (启动服务器,默认ip和端口为http://127.0.0.1:8000/)
python manage.py startapp appname (新建 app)
python manage.py syncdb (同步数据库命令,Django 1.7及以上版本需要用以下的命令)
python manage.py makemigrations (显示并记录所有数据的改动)
python manage.py migrate (将改动更新到数据库)
python manage.py createsuperuser (创建超级管理员)
python manage.py dbshell (数据库命令行)
python manage.py (查看命令列表)
项目建立完成得到如下文件夹:
mysite
├─── manage.py # 运行
└─── mysite
┈ ┈ ┈ ┈├─── __init__.py
┈ ┈ ┈ ┈├─── settings.py # 基本配置
┈ ┈ ┈ ┈├─── urls.py # 路由系统 url -> 函数
┈ ┈ ┈ ┈└─── wsgi.py # Web服务器网关接口 调用wsgiref模块 实现socket
基础配置
在 urls.py
文件中,urlpatterns
列表中增加路径;
引入 HttpResponse
模块,login
函数的返回值以此形式传递;
引入 HttpResponse
模块,可以读取其他文件,如 login.html
作为返回值。
from django.contrib import admin
from django.urls import path
from django.shortcuts import HttpResponse, render
def login(request):
"""
处理用户请求,并返回内容
:param request: 用户请求相关的所有信息(对象)
:return:
"""
# return HttpResponse('login')
# 自动找到模板路径下的login.html文件,读取内容并返回给用户
return render(request, 'login.html')
urlpatterns = [
path('admin/', admin.site.urls),
path('login/', login),
]
寻找文件的路径依据,在 settings.py
中 TEMPLATES
里进行了定义:
TEMPLATES = [
{
'BACKEND': 'django.template.backends.django.DjangoTemplates',
'DIRS': [os.path.join(BASE_DIR, 'templates')]
...
templates
文件夹中的 login.html
:
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>login</title>
</head>
<body>
<h1 style="color: cadetblue">用户登录</h1>
<form>
<p><input type="text" placeholder="用户名" /></p>
<p><input type="password" placeholder="密码" /></p>
<p><input type="submit" value="登录"></p>
</form>
</body>
</html>
运行可看到页面:
也可以将样式写入CSS文件中,修改 login.html
,加入样式链接:
<link rel="stylesheet" href="/sta/commons.css">
在 sta
文件夹下新建 commons.css
文件:
h1{
color: cadetblue;
}
运行发现样式未应用,检查报错,发现CSS文件未能被找到;
原因是静态文件路径需要配置,在 setting.py
中最后修改静态路径:
STATIC_URL = '/static/' # 使用时前缀 /static
STATICFILES_DIRS = (
os.path.join(BASE_DIR, 'sta'),
)
修改 login.html
中的链接:
<link rel="stylesheet" href="/static/commons.css">
再运行时,样式应用成功。
*将 sta
文件夹直接命名未 static
更加和谐统一,日后将所有静态文件放在 static
目录下。
(静态文件:图片、JS、CSS)
完善登录功能
login.html
:
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>login</title>
<link rel="stylesheet" href="/static/commons.css">
</head>
<body>
<h1>用户登录</h1>
<form method="post" action="/login/">
<p><input type="text" placeholder="用户名" name="username"/></p>
<p><input type="password" placeholder="密码" name="password"/></p>
<p><input type="submit" value="登录"></p>
{{ msg }}
</form>
</body>
</html>
这里用户端返回的消息形式:<QueryDict: {'username': ['root'], 'password': ['123123']}>
urls.py
:
def login(request):
"""
处理用户请求,并返回内容
:param request: 用户请求相关的所有信息(对象)
:return:
"""
# return HttpResponse('login')
# 自动找到模板路径下的login.html文件,读取内容并返回给用户
if request.method == "GET":
return render(request, 'login.html', {'msg': '请输入用户名和密码'})
else:
# 用户POST提交的数据(请求体)
"""
usr = request.POST['username']
psw = request.POST['password']
这样的方式并不好,取决于html文件中的name,如果不匹配会报错
"""
usr = request.POST.get('username')
psw = request.POST.get('password')
if usr == 'bernie' and psw == '123456':
# 登录成功
return redirect('http://www.baidu.com')
else:
# 登录失败
return render(request, 'login.html', {'msg': '用户名或密码错误'})
首次访问,请求形式为 GET
,将登录界面返回给用户;用户输入用户名和密码后,请求形式为 POST
,则进行判断,登录成功则借助 redirect
重定位跳转到指定网址;否则重新将登录界面返回给用户,并给出错误提示信息。
总结
1. 创建project
2. 配置
模板路径 - templates目录
静态文件路径 - static目录
3. 额外配置
将 MIDDLEWARE
中 'django.middleware.csrf.CsrfViewMiddleware',
注释掉
4. url对应关系
/login/ login
def login(request):
– request.method
– request.POST ->请求体
– request.GET ->请求头中的url中
(GET请求:只有request.GET有值;POST请求:request.GET和request.POST都可能有值)
– return HttpResponse(…)
– return render(request, ‘login.html’, {…})
– return redirect(‘要跳转的网址’或’/后缀/’)
5. 模板引擎的特殊标记
login.html
{{name}}
def login(request):
return render(request, ‘login.html’, {‘name’: ‘alex’})
html文件中的引用:
<p>{{ name }}</p>
<p>{{ users.0 }}</p>
<p>{{ users.1 }}</p>
<p>{{ user_dict.k1 }}</p>
<p>{{ user_dict.k2 }}</p>
<h3>循环</h3>
<ul>
{% for item in users %}
<li> {{ item }} </li>
{% endfor %}
</ul>