Django基础
互联网架构
C/S客户端模式 (主要处理一些复杂任务)
B/S浏览器模式 (主要处理一些比较简单的任务) --> Web开发
Web开发的本质
所有的Web应用本质上就是一个socket服务端,而用户的浏览器就是一个socket客户端
互联网上2台机器之间通信
- IP
- 端口
- 协议
面试题
描述下当我们在浏览器输入一个url网址,比如说www,jd.com, 然后显示整个页面的过程
域名-DNS解析-IP地址和端口-服务端-返回消息-浏览器-服务端-浏览器
1. 当在浏览器地址栏输入http://www.jd.com的时候,先把请求发送到本地DNS服务器里找www.jd.com对应的Ip地址和端口,如果有,返回ip地址和端口
2. 如果本地DNS服务器里面没有字符串 www.jd.com对应的ip地址和端口,则会去DNS根域服务器去找(root-server.net),根域会把字符串交给顶级域服务器.com DNS服务器,然后会 将字符串交个权威DNS服务器找到jd.com,然后再去二级域名服务器找到www.jd.com
3. 当浏览器获得了ip地址和端口,浏览器发送请求到服务器,服务器把写好的HTML页面返回给浏览器, 浏览器用HTML格式渲染
字符串和字节的转换
\>>> s="hello"
\>>> s
'hello'
\>>> bytes(s,encoding="utf-8")
b'hello'
\>>> b=bytes(s,encoding="utf-8")
\>>> b
b'hello'
\>>> str(b,encoding='utf-8')
'hello'
半成品自定义web框架
'''使用socket 搭建一个简单的web服务端'''
from socket import *
sk = socket(AF_INET, SOCK_STREAM)
sk.setsockopt(SOL_SOCKET, SO_REUSEADDR, 1)
sk.bind(('127.0.0.1', 8080))
sk.listen()
while 1:
conn, addr = sk.accept()
data = conn.recv(9000)
print(data)
conn.send(b'ok')
conn.close()
输出
b'GET / HTTP/1.1\r\nHost: 127.0.0.1:8080\r\nConnection: keep-alive\r\nCache-Control: max-age=0\r\nUpgrade-Insecure-Requests: 1\r\nUser-Agent: Mozilla/5.0 (Macintosh; Intel Mac OS X 10_14_5) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/77.0.3865.90 Safari/537.36\r\nSec-Fetch-Mode: navigate\r\nSec-Fetch-User: ?1\r\nAccept: text/html,application/xhtml+xml,application/xml;q=0.9,image/webp,image/apng,*/*;q=0.8,application/signed-exchange;v=b3\r\nSec-Fetch-Site: none\r\nAccept-Encoding: gzip, deflate, br\r\nAccept-Language: zh-CN,zh;q=0.9\r\nCookie: csrftoken=zKkESNPByFIypehiB8KlgGvB7Xs1QTtLZRDrJbMmgAncnhG5fmUEFAqkTse6GPet\r\n\r\n'
响应信息
响应相关信息可以在浏览器调试窗口的network标签页中看到
点击view source可以查看响应信息未被浏览器解析处理前的状态:
收发的消息需要按照一定的格式来, 这个格式是要遵循HTTP协议
HTTP协议
主要由请求协议和响应协议构成
请求协议:浏览器给服务器发的格式的限定
响应协议:服务器给浏览器发的格式的限定
每个HTTP请求和响应都遵循相同的格式,一个HTTP包含Header和Body两部分,其中Body是可选的。 HTTP响应的Header中有一个 Content-Type
表明响应的内容格式。如 text/html
表示HTML网页
请求和响应
-
HTTP协议中
- 浏览器给服务端发消息的过程叫请求(request)
- 服务端给浏览器回复消息的过程叫响应(response)
-
请求和响应的消息都必须遵循一个固定的格式
HTTP GET请求的格式:
HTTP响应的格式:
get 和 post请求
1. GET请求提交的数据会放在url之后,以?分割URL和传输的数据,参数之间用&相连,比如http://127.0.0.1:8800/?name=changwei&pwd=GET
2. GET 提交的数据大小有限制(因为浏览器对URL长度有限制),而POST方法提交的数据没有限制
3. GET与POST都是提交数据的方式,当需要对服务器所在的数据库的数据进行查询的时候提交GET请求,添加或删除提交POST请求
常见的响应状态码
1xx Informational(信息性状态码) 接受的请求正在处理
2xx Success(成功状态码) 请求正常处理完毕
3xx Redirection(重定向状态码) 需要进行附加操作以完成请求 (域名地址变了了,跳转到新的域名地址,这种情况浏览器发2次请求)
4xx Client Error (客户端错误状态码) 服务器无法处理请求 404 (not found) 403(forbidden 禁止访问)
5xx Server Error(服务器错误状态码) 服务器处理请求出错 500(服务端代码错误) 502(网关错误 bad gateway)
web框架改良
第一版:处女版自定义web框架
'''使用socket 搭建一个简单的web服务端'''
from socket import *
sk = socket(AF_INET, SOCK_STREAM)
sk.setsockopt(SOL_SOCKET, SO_REUSEADDR, 1)
sk.bind(('127.0.0.1', 8080))
sk.listen()
while 1:
conn, addr = sk.accept()
data = conn.recv(9000)
conn.send(b'HTTP/1.1 200 OK\r\n\r\no98hhhggk')
'''
client.send(b'o98hhhggk') # 浏览器端会显示 127.0.0.1 发送的响应无效
浏览器要访问数据必须加上HTTP/1.1 状态码
响应数据由状态行+响应头部+响应正文, 状态行至少加上HTTP/1.1, 响应头部可以不写
响应头部的最后一行和响应正文之间一共有2对\r\n
'''
print(data) # 将浏览器发来的消息打印出来
conn.close()
第二版:根据不同的路径返回不同的内容--函数版
'''使用socket 搭建一个简单的web服务端'''
import time
from socket import *
sk = socket(AF_INET, SOCK_STREAM)
sk.setsockopt(SOL_SOCKET, SO_REUSEADDR, 1)
sk.bind(('127.0.0.1', 8080))
sk.listen()
def yingying(url):
data = 'you visit the website is : {}'.format(url)
return bytes(data, encoding='utf-8')
def hpg(url):
data = 'a huang de computer is stolen'
return bytes(data, encoding='utf-8')
def login(url):
with open('login.html', mode='r', encoding='utf-8') as f:
data = f.read()
now = str(time.time())
data = data.replace('xxx', now) # 通过替换html中的字符串来实现动态效果
return bytes(data, encoding='utf-8')
url_list = [
('/yingying/',yingying),
('/hpg/', hpg),
('/login/', login),
]
while 1:
conn, addr = sk.accept()
data = conn.recv(9000)
# 把收到的字节类型的数据转换为字符串
data_str = data.decode('utf-8')
# 得到请求行
first_line = data_str.split('\r\n\r\n')[0]
# 对请求行进行切割
url = first_line.split(' ')[1]
conn.send(b'HTTP/1.1 200\r\n\r\n')
func = None
for i in url_list:
if i[0] == url:
func = i[1]
break
else:
func = None
if func:
msg = func(url)
else:
msg = b'404 not found'
conn.send(msg)
conn.close()
第三版:根据不同的路径返回不同的内容--面向对象版
from socket import *
class WebSocket(object):
def __init__(self):
self.sk = socket(AF_INET, SOCK_STREAM)
self.sk.setsockopt(SOL_SOCKET, SO_REUSEADDR, 1)
self.sk.bind(('127.0.0.1', 8080))
self.sk.listen()
@property
def ren(self):
data = '''
<html>
<body>
<ul>
<li>列表1</li>
<li>列表2</li>
<li>列表3</li>
</ul>
</body>
</html>
'''
return f'HTTP/1.1\r\n\r\n{data}'.encode('gbk')
@property
def ren2(self):
data = '''
<html>
<body>
<ul>
<li>列表4</li>
<li>列表5</li>
<li>列表6</li>
</ul>
</body>
</html>
'''
return f'HTTP/1.1\r\n\r\n{data}'.encode('gbk')
def main(self):
while True:
conn, addr = self.sk.accept()
data = conn.recv(9000)
data_str = data.decode('utf-8')
if 'google' not in data_str:
first_line = data_str.split('\r\n\r\n')[0]
path_list = first_line.split()
if path_list:
global path
path = first_line.split()[1]
data = self.url_dict.get(path)
if data:
conn.send(data)
conn.close()
if __name__ == '__main__':
obj = WebSocket()
obj.main()
Python中web框架的分类
web框架的功能
A. 收发socket消息, 按照HTTP协议解析消息 (web服务程序 wsgiref, gunicorn, uwsgi)
B. 字符串替换
C. 业务逻辑处理 (web应用程序)
分类
根据功能来分类
-
自己实现A, B, C
tornado(并发支持)
-
自己实现B, C, 使用别人的A
Django
-
自己实现C, 实现别人的A和B
Flask(语法简单)
根据框架种类来分类
- Django
- 其他
服务器程序和应用程序
web服务程序 <--- WSGI协议 ---> web应用程序
对于真实开发中的python web程序来说,一般会分为两部分:服务器程序和应用程序。
服务器程序负责对socket服务器进行封装,并在请求到来时,对请求的各种数据进行整理。
应用程序则负责具体的逻辑处理。为了方便应用程序的开发,就出现了众多的Web框架,例如:Django、Flask、web.py 等。不同的框架有不同的开发方式,但是无论如何,开发出的应用程序都要和服务器程序配合,才能为用户提供服务。
这样,服务器程序就需要为不同的框架提供不同的支持。这样混乱的局面无论对于服务器还是框架,都是不好的。对服务器来说,需要支持各种不同框架,对框架来说,只有支持它的服务器才能被开发出的应用使用。
这时候,标准化就变得尤为重要。我们可以设立一个标准,只要服务器程序支持这个标准,框架也支持这个标准,那么他们就可以配合使用。一旦标准确定,双方各自实现。这样,服务器可以支持更多支持标准的框架,框架也可以使用更多支持标准的服务器。
WSGI(Web Server Gateway Interface)就是一种规范,它定义了使用Python编写的web应用程序与web服务器程序之间的接口格式,实现web应用程序与web服务器程序间的解耦。
常用的WSGI服务器有uwsgi、Gunicorn。而Python标准库提供的独立WSGI服务器叫wsgiref,Django开发环境用的就是这个模块来做服务器。
Django
安装
安装(安装最新LTS版)
$ pip3 install django==1.11.11
$ pip3 list |grep -i django
Django 1.11.11
djangorestframework 3.10.2
创建一个django项目
创建了一个名为"mysite"的Django 项目
- 命令行创建
$ django-admin startproject mysite
$ ls -ld mysite/
drwxr-xr-x 4 cjw staff 128 9 27 20:34 mysite/
$ tree mysite/
mysite/
├── manage.py
└── mysite
├── __init__.py
├── settings.py
├── urls.py
└── wsgi.py
1 directory, 5 files
-
pycharm创建
File -> new Project -> 左侧选第二项,右侧第一项是路径,第二项是选python版本
在新的PyCharm窗口打开新建的Django项目
创建django项目报错
一般mac环境下会报错
$ django-admin startproject mysite
-bash: django-admin: command not found
>>> 找到django-admin命令的绝对路径
$ sudo find / -name 'django-admin' -type f
Password:
find: /.Spotlight-V100: Operation not permitted
find: /Library/Application Support/com.apple.TCC: Operation not permitted
/Library/Frameworks/Python.framework/Versions/3.6/bin/django-admin
>>>> 在 环境变量定义的路径 /usr/local/bin下创建软链接
$ sudo ln -s /Library/Frameworks/Python.framework/Versions/3.6/bin/django-admin /usr/local/bin/
目录介绍
mysite/
├── manage.py >>> 项目的命令行入口文件
└── mysite >>> 项目目录
├── __init__.py
├── settings.py >>> 项目配置文件
├── urls.py >>> 路由 --> URL和函数的对应关系
└── wsgi.py >>> runserver命令就使用wsgiref模块做简单的web server
启动Django项目
- 命令行启动
$ cd mysite/
$ python3 manage.py runserver 127.0.0.1:8000
-
pycharm启动
点绿色三角
注意左侧框中的名字一定要是项目名称!
模板文件配置
TEMPLATES = [
{
'BACKEND': 'django.template.backends.django.DjangoTemplates',
'DIRS': [],
'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',
],
},
},
]
静态文件配置
STATIC_URL = '/static/' --> HTML中使用的静态文件夹前缀, 通过这个前缀可以在HTML中找静态文件 (css/js/img)
STATICFILES_DIRS = [
os.path.join(BASE_DIR, "static"), ---> 静态文件存放位置
]
Django基础必备三件套
from django.shortcuts import HttpResponse, render, redirect
HttpResponse
内部传入一个字符串参数,返回给浏览器。
def index(request):
# 业务逻辑代码
return HttpResponse("OK")
render
除request参数外还接受一个待渲染的模板文件和一个保存具体数据的字典参数。
将数据填充进模板文件,最后把结果返回给浏览器
def index(request):
# 业务逻辑代码
return render(request, "index.html", {"name": "alex", "hobby": ["烫头", "泡吧"]})
redirect
接受一个URL参数,表示跳转到指定的URL
def index(request):
# 业务逻辑代码
return redirect("/home/")
重定向是怎么回事?
启动Django报错
Django 启动时报错 “UnicodeEncodeError ...”
报这个错误通常是因为计算机名为中文,改成英文的计算机名重启下电脑就可以了。
Django 启动报错“SyntaxError: Generator expression must be parenthesized”
报这个错很大可能是因为使用了Python3.7.0,而目前(2018-06-12)Python3.7.0和Django还有点兼容性问题,换回Python3.6的环境即可
form表单提交数据的三个要素
- form标签必须要有action和method属性
- 所有获取用户输入的标签必须放在form表单中,必须要有name属性
- 必须要有submit按钮
request相关的属性
-
request.method
返回的是请求的方法(全大写):GET/POST
-
request.GET
取得是URL里面的参数,类似于字典的数据结构
-
request.POST
post提交的数据,类似于字典的数据结构
django的模板语言
{{ 变量名 }}
程序连mysql
使用pymysql模块
- 导入pymysql模块
- 创建连接
- 获取执行命令的游标
- 用游标去执行SQL语句
- 获取SQL语句的执行结果
- 关闭游标
- 关闭连接
ORM
一种工具 帮你翻译SQL语句 --> ORM(Object Relationship Model)
优点
- 开发效率高
- 开发不用直接写SQL语句
缺点
- 执行效率低
ORM与数据的对应关系
ORM DB
类 <--> 数据表
属性 <--> 字段
对象 <--> 数据行
Django中ORM的使用
用处
-
操作数据表
-
操作数据行
-
使用
- 手动创建一个数据库
-> create database mysite;
- 告诉Django连哪个数据库
DATABASES = { 'default': { 'ENGINE': 'django.db.backends.mysql', # 连接数据库的类型 'NAME': 'mysite', # 数据库名 'HOST': '127.0.0.1', # 数据库主机地址 'PORT': 3306, # 数据库的端口 'USER': 'root', 'PASSWORD': '', } }
- 用什么连数据库?
利用第三方的包,比如第三方包:pymysql和MySQLdb
告诉Django用pymysql模块代替默认的MySQLdb去连接MySQL数据库
和settings.py同级的__init__.py文件,写上:
import pymysql pymysql.install_as_MySQLdb()
-
在app/models.py的文件中创建类类必须继承models.Model
-
命令行操作
manager.py
python manage.py makemigrations --> 找个小本本把models.py的变更记录一下 python manage.py migrate --> 把上面的变更记录翻译成SQL语句,去数据库执行
- ORM查询
User.objects.filter(email='', pwd='')
- 手动创建一个数据库
PIP命令
pip freeze > requirements.txt
pip install -r requirements.txt
登录案例(Django1.X+bootstrap3.x)
数据库的配置
/mysite/settings.py
INSTALLED_APPS = [
'django.contrib.admin',
'django.contrib.auth',
'django.contrib.contenttypes',
'django.contrib.sessions',
'django.contrib.messages',
'django.contrib.staticfiles',
'app01.apps.App01Config',
]
MIDDLEWARE = [
'django.middleware.security.SecurityMiddleware',
'django.contrib.sessions.middleware.SessionMiddleware',
'django.middleware.common.CommonMiddleware',
# 'django.middleware.csrf.CsrfViewMiddleware',
'django.contrib.auth.middleware.AuthenticationMiddleware',
'django.contrib.messages.middleware.MessageMiddleware',
'django.middleware.clickjacking.XFrameOptionsMiddleware',
]
DATABASES = {
'default': {
'ENGINE': 'django.db.backends.mysql', # 连接数据库的类型
'NAME': 'mysite', # 数据库名
'HOST': '127.0.0.1', # 数据库主机地址
'PORT': 3306, # 数据库的端口
'USER': 'root',
'PASSWORD': '123',
}
}
# 静态文件相关
STATIC_URL = '/static/'
# 静态文件的实际存放目录
STATICFILES_DIRS = [
os.path.join(BASE_DIR, 'static'),
]
/app01/models.py
from django.db import models
# Create your models here.
class User(models.Model):
id = models.AutoField(primary_key=True) # -> 创建一个自增的ID列作为主键
email = models.CharField(max_length=24) # -> varchar(32)
pwd = models.CharField(max_length=16) # -> varchar(32)
路由配置
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'^login/$', views.login),
url(r'^index/$', views.index),
]
业务逻辑处理
/app01/views.py
from django.shortcuts import render, HttpResponse, redirect
from app01.models import User
# Create your views here.
def login(request):
error_msg = ''
if request.method == 'POST':
email, pwd = request.POST.get('email'), request.POST.get('pwd')
print(email, pwd)
user_queryset = User.objects.filter(email=email, pwd=pwd)
if user_queryset:
return redirect('/index/')
else:
error_msg = '邮箱或密码错误'
return render(request, 'login.html', {'error_msg': error_msg})
def index(request):
return render(request, 'index.html')
前端页面
/templates/login.html
<!DOCTYPE html>
<html lang="zh-CN">
<head>
<meta http-equiv="content-Type" charset="UTF-8">
<meta http-equiv="x-ua-compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1">
<title>欢迎登录</title>
<link rel="stylesheet" href="/static/bootstrap-3.3.7/css/bootstrap.css">
<link rel="stylesheet" href="/static/font-awesome-4.7.0/css/font-awesome.css">
<style>
body {
background-color: #eeeeee;
}
.login-box {
margin-top: 50px;
}
</style>
</head>
<body>
<div class="container">
<div class="row">
<div class="col-md-4 col-md-offset-4 login-box">
<form class="form-horizontal" action="http://127.0.0.1:8000/login/" method="post">
<div class="col-sm-9">
<h2 class="text-center">请登录</h2>
</div>
<div class="form-group">
<div class="col-sm-9">
<div class="input-group margin-bottom-sm">
<label for="email" class="hidden">邮箱</label>
<span class="input-group-addon"><i class="fa fa-envelope-o fa-fw"></i></span>
<input class="form-control" name="email" type="text" id="email" placeholder="您的邮箱地址">
</div>
<span class="help-block"></span>
</div>
</div>
<div class="form-group">
<div class="col-sm-9">
<div class="input-group">
<label for="password" class="hidden">密码</label>
<span class="input-group-addon"><i class="fa fa-key fa-fw"></i></span>
<input class="form-control" name="pwd" type="password" id="password" placeholder="请输入密码">
</div>
<span class="help-block"></span>
</div>
</div>
<div class="form-group">
<div class="col-sm-9">
<div class="checkbox">
<label>
<input type="checkbox"> 记住我
</label>
</div>
</div>
</div>
<div class="form-group">
<div class="col-sm-9">
<button type="submit" id="b1" class="btn btn-block btn-primary">登录</button>
<p style="color: red;text-align: center">{{ error_msg }}</p>
</div>
</div>
</form>
</div>
</div>
</div>
<script src="/static/jquery-3.3.1.js"></script>
</body>
</html>
/templates/index.html
<!DOCTYPE html>
<html lang="zh-CN">
<head>
<meta http-equiv="content-Type" charset="UTF-8">
<meta http-equiv="x-ua-compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1">
<title>Title</title>
<link rel="stylesheet" href="/static/bootstrap-3.3.7/css/bootstrap.css">
</head>
<body>
<div class="container">
<div class="row">
<div class="col-md-12">
<div class="page-header">
<h1>信息收集卡
<small>共三步</small>
</h1>
</div>
<div class="progress">
<div class="progress-bar progress-bar-success" role="progressbar" aria-valuenow="60" aria-valuemin="0"
aria-valuemax="100" style="width: 33%;">
1/3
</div>
</div>
<!--面板-->
<div class="panel panel-primary">
<div class="panel-heading">基本信息
<span class="glyphicon glyphicon-pushpin pull-right"></span>
</div>
<div class="panel-body">
<!--表单-->
<form class="form-horizontal">
<div class="form-group">
<label for="inputEmail3" class="col-sm-2 control-label">姓名</label>
<div class="col-sm-4">
<input type="text" class="form-control" id="inputEmail3" placeholder="Email">
</div>
</div>
<div class="form-group">
<label for="inputPassword3" class="col-sm-2 control-label">手机号</label>
<div class="col-sm-4">
<input type="text" class="form-control" id="inputPassword3" placeholder="Password">
</div>
</div>
<div class="form-group">
<label for="inputPassword3" class="col-sm-2 control-label">邮箱</label>
<div class="col-sm-4">
<input type="email" class="form-control" id="inputPassword" placeholder="Password">
</div>
</div>
<div class="form-group">
<label for="inputPassword3" class="col-sm-2 control-label">密码</label>
<div class="col-sm-4">
<input type="password" class="form-control" id="inputPassword4" placeholder="Password">
</div>
</div>
<div class="form-group">
<label for="inputPassword3" class="col-sm-2 control-label">头像</label>
<div class="col-sm-4">
<input type="file" id="inputPassword5" placeholder="Password">
<span class="help-block">只支持jpg,png,gif格式</span>
</div>
</div>
<hr>
<div class="form-group">
<label for="inputPassword3" class="col-sm-2 control-label">属性</label>
<div class="col-sm-10">
<div class="radio">
<label>
<input type="radio" name="optionsRadios" id="optionsRadios1" value="option1"
checked>
Option one is this and that—be sure to include why it's great
</label>
</div>
<div class="radio">
<label>
<input type="radio" name="optionsRadios" id="optionsRadios2" value="option2">
Option two can be something else and selecting it will deselect option one
</label>
</div>
<div class="radio disabled">
<label>
<input type="radio" name="optionsRadios" id="optionsRadios3" value="option3"
disabled>
Option three is disabled
</label>
</div>
</div>
</div>
</form>
</div>
</div>
<!--下一步按钮-->
<button class="btn btn-success pull-right">下一步</button>
</div>
</div>
</div>
</body>
</html>
登录过程分析
第一步
第二步
第三步
第四步
第五步
form表单上传文件案例(Django1.X)
<!DOCTYPE html>
<html lang="zh-CN">
<head>
<meta http-equiv="content-Type" charset="UTF-8">
<meta http-equiv="x-ua-compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1">
<title>Title</title>
</head>
<body>
<!--上传文件的form-->
<form action="" method="post" enctype="multipart/form-data">
<input type="file" name="file">
<input type="submit">
</form>
</body>
</html>