web框架 以及 初步使用Django

浏览器服务器的通信

Web应用的本质就是:

  1. 浏览器发送一个HTTP请求;

  2. 服务器收到请求,生成一个HTML文档;

  3. 服务器把HTML文档作为HTTP响应的Body发送给浏览器;

  4. 浏览器收到HTTP响应,从HTTP Body取出HTML文档并显示。

from socket import *
sc = socket()
sc.bind(("127.0.0.1",8080))
sc.listen(5)
while True:
    conn,addr = sc.accept()
    aa = conn.recv(1024)
    # aa就是get请求,请求分为头部和内容,头部与内容用\r\n\r\n分割,头部之间的内容使用\r\n分割
    # GET / HTTP/1.1 是头部第一个\r\n的内容,get是get请求, / 这个位置是url,HTTP/1.1是HTTP版本
    print(aa)
    # conn.send(b"HTTP/1.1 200 OK\r\n\r\n") # 在给客户端回复响应的时候按照HTTP协议的规则加上响应头
    conn.send(b"HTTP/1.1 200 OK\r\nContent-Type:text/html;charset=utf8\r\n\r\n")  # 这是Header
    conn.send(bytes("你好,世界",encoding="utf8")) # 这是Body
    conn.close()

浏览器和你的服务端通信需要遵循一个规则,这个规则就是HTTP协议

简单理解为HTTP协议规定了传输内容的格式,客户端和服务端按照HTTP规则来发送和解析消息

每个HTTP请求和响应都遵循相同的格式,一个HTTP包含Header(头部)和Body(内容)两部分,其中Body是可选的。

HTTP是个无状态的协议,所以,需要在请求头和响应头中包括自身的一些信息和想要执行的动作,这样,对方在收到信息后,就可以知道你是谁,你想干什么。

头部和内容间使用了两个\r\n来分割,头部之间存在着一些键值对使用一个\r\n来分割

b'GET / HTTP/1.1\r\nHost: 127.0.0.1:8080\r\nConnection: keep-alive\r\nUser-Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/64.0.3278.0 Safari/537.36\r\nUpgrade-Insecure-Requests: 1\r\nAccept:text/html,application/xhtml+xml,application/xml;q=0.9,image/webp,image/apng,*/*;q=0.8\r\nAccept-Encoding: gzip, deflate, br\r\nAccept-Language: zh-CN,zh;q=0.9\r\n\r\n'

分割之后
b'GET / HTTP/1.1
Host: 127.0.0.1:8080
Connection: keep-alive
User-Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/64.0.3278.0 Safari/537.36
Upgrade-Insecure-Requests:1
Accept:text/html,application/xhtml+xml,application/xml;q=0.9,image/webp,image/apng,*/*;q=0.8
Accept-Encoding: gzip, deflate, br
Accept-Language: zh-CN,zh;q=0.9

'
一个GET请求
  • 浏览器发送 ---> 请求 (request)
    • 请求头描述了客户端向服务器发送请求时使用的http协议类型,所使用的编码,以及发送内容的长度,refer,等等。refer干嘛的,一般用来做简单的防跨站攻击
    • 请求分为多种类型,包括GET,POST等,其中GET请求指头Header部分,没有Body
  • 服务端回复 ---> 响应 (response)
    • 相应的响应头用来描述服务器回给你对所返回的content的一些一些描述,我是什么服务器,我返回的是啥编码,我返回的内容有多长等等。

wsgiref模块的初级服务器

from wsgiref.simple_server import make_server

def index():
    return "这是home界面"

def home():
    return "这是home界面"
# 存储url与其对应的函数
URL_FUNC = [
    ("/index/",index),
    ("/home/",home)
]

def run_server(environ,start_response): # 处理函数
    # environ:一个包含所有HTTP请求信息的dict对象;
    # start_response:一个发送HTTP响应的函数。
    start_response("200 OK",[('Content-Type', 'text/html;charset=utf8')])  # 发送HTTP响应的状态码和头信息,每一对键值都用元组表示
    url = environ["PATH_INFO"]  # 取到用户输入的url,然后根据url返回对应的内容
    func_name = None
    for i in URL_FUNC:
        if url == i[0]:
            func_name = i[1]  # 如果找到对应关系,就把函数名拿到
            break

    if func_name:  # 拿到可执行的函数,获取执行结果
        body = func_name()
    else:
        body = "404找不到这个页面"

    return [bytes("<h1>{}</h1>".format(body),encoding="utf8"),]  # 发送给客户端的信息,注意数据类型


if __name__ == "__main__":
    httpd = make_server('',8000,run_server) # 启动服务端,指定IP,端口,处理函数
    print("Serving HTTP on port 8000...")  # 开始监听HTTP请求

使用jinja2完成动态界面(渲染)

# jinja2 主要用来替换
from wsgiref.simple_server import make_server
import jinja2
def userlist():
    with open("userlist.html", encoding="utf8") as f:
        data = f.read()
    # 将读取到的内容生成了一个jinja2的模板对象
    template = jinja2.Template(data)
    # 替换模板对象中的内容,需要传入一个字典,字典的键与与模板中要使用的对应
    new_data = template.render({
        "user_list": [
            {"name": "jianchao", "pwd": "1234"},
            {"name": "liyan", "pwd": "5678"}
        ]
    })
    return new_data

URL_FUNC = [
    ("/userlist/", userlist),
]

def run_server(environ, start_response):
    start_response('200 OK', [('Content-Type', 'text/html;charset=utf8')])  # 设置HTTP响应的状态码和头信息
    url = environ['PATH_INFO']  # 取到用户输入的url
    func_name = None

    if url == "/userlist/":  # 如果能找到对应关系,就把函数名拿到
        func_name = userlist
    if func_name:
        body = func_name()
    else:
        body = "404找不到这个页面"

    return [bytes("<h1>{}</h1>".format(body), encoding="utf8"),]

if __name__ == '__main__':
    httpd = make_server('', 9000, run_server)
    print("Serving HTTP on port 8000...")
    httpd.serve_forever()

 

DJango

Python标准库提供的独立WSGI服务器叫wsgiref,Django开发环境用的就是这个模块来做服务器。

Django 是一个由 Python 写成的开放源代码的 Web 应用框架。它最初是被开发来用于管理劳伦斯出版集团旗下的一些以新闻内容为主的网站的,即是 CMS(内容管理系统)软件。并于2005年7月在 BSD 许可证下发布。这套框架是以比利时的吉普赛爵士吉他手 Django Reinhardt 来命名的。由于 Django 的是在一个快节奏的新闻编辑室环境下开发的,它的目的是使常见的 Web 开发任务,快速和容易。

Django项目的目录介绍

mysite/
├── manage.py  # 管理文件
└── mysite  # 项目目录
    ├── __init__.py
    ├── settings.py  # 配置
    ├── urls.py  # 路由 --> URL和函数的对应关系
    └── wsgi.py  # runserver命令就使用wsgiref模块做简单的web server
└── static # 动态文件目录(名字是自己起的,要与配置文件对应)
└── template # HTML文件目录(名字是自己起的,要与配置文件对应)

配置型中个个配置的意义

#项目目录
BASE_DIR = os.path.dirname(os.path.dirname(os.path.abspath(__file__))) 

中间件
MIDDLEWARE = [
    'django.middleware.security.SecurityMiddleware',
    'django.contrib.sessions.middleware.SessionMiddleware',
    'django.middleware.common.CommonMiddleware',
    # 'django.middleware.csrf.CsrfViewMiddleware',  # 刚开始学习时可在配置文件中暂时禁用csrf中间件,方便表单提交测试。
    'django.contrib.auth.middleware.AuthenticationMiddleware',
    'django.contrib.messages.middleware.MessageMiddleware',
    'django.middleware.clickjacking.XFrameOptionsMiddleware',
]

TEMPLATES = [
    {
        'BACKEND': 'django.template.backends.django.DjangoTemplates',
        'DIRS': [os.path.join(BASE_DIR,"templates")],  # 这个就是HTML文件目录
        '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/'  # /static/代表了动态文件目录,在Django项目中引用文件/static/就代表了下面的实际目录
STATICFILES_DIRS = [
    os.path.join(BASE_DIR,"static"),  # 实际的动态文件目录
]
配置文件中的配置意义

url对应的函数的返回值便是服务器经过处理发送给浏览器的内容,不过不能直接返回字符串,需要借助其他的方法,来进行封装(封装头部),

浏览器本身是发送的字节码,django按规则对他进行了解析,将他封装成了一个对象(request),

下面介绍基础必备三件套

HttpResponse

内部传入一个字符串参数,返回给浏览器,

def index(request):
    # 业务逻辑代码
    return HttpResponse("OK")

又或者
def index(request):
    # 业务逻辑代码
    return HttpResponse("<h1>OK</h1>") # 标题1的OK

render

除request参数外还接受一个待渲染的模板文件和一个保存具体数据的字典参数。

将数据填充进模板文件,最后把结果返回给浏览器。(类似于我们上面用到的jinja2)

注意,是在后端完成的替换,要注意模板中的内容,模板本身就是一大串字符串,字符串的替换你的字符串替换进去是没有引号的,所以如果你的字符串替换进js中,很可能会变成一个变量

def index(request):
    # 业务逻辑代码
    return render(request, "index.html", {"name": "alex", "hobby": ["烫头", "泡吧"]})

redirect

接受一个URL参数,表示跳转到指定的URL。

def index(request):
    # 业务逻辑代码
    return redirect("/home/")

请求的数据传输

POST请求

post请求一般用在表单提交,表单的name和value就存放在request对象里

request.POST.get("表单中的键")就能取到对应的值

GET请求

打开一个页面就是一次get请求,请求的数据会附加在URL之后,以?分割URL和传输数据,多个参数用&连接。

比如

https://www.baidu.com/s?wd=get%E8%AF%B7%E6%B1%82%E7%9A%84%E6%95%B0%E6%8D%AE&rsv_spt=1&rsv_iqid=0xf06c9e650007f307&issp=1&f=8&rsv_bp=0&rsv_idx=2&ie=utf-8&tn=baiduhome_pg&rsv_enter=1&rsv_sug3=17&rsv_sug1=16&rsv_sug7=101
百度get请求的数据的url
request.GET.get("url中的键")就能取到对应的值

经常在url对应函数中拥戴的参数

request -- 函数需要传递一个请求的对象,其中包括了请求相关的数据

request.body -- 请求的内容部分

request.method -- 请求的种类 GET 或者 POST等

request.POST -- POST请求的对象

request.GET -- GET请求的对象

request.GET.get("键") 或者 request.POST.get("键")  获取请求的键对应的值

 

例:

import pymysql
from django.contrib import admin
from django.shortcuts import HttpResponse, render, redirect
def binaji(request):
    if request.method == "POST":
        cname = request.POST.get("cname")
        id = request.POST.get("id")
        print(cname)
        #   打开数据库,更新数据
        conn = pymysql.connect(host="localhost", database="test", user="root", password="123456", charset="utf8")
        cursor = conn.cursor(cursor=pymysql.cursors.DictCursor)
        sql = "update class set cname=%s where id=%s"
        cursor.execute(sql, (cname,id))
        conn.commit()  # 要提交操作
        cursor.close()
        conn.close()
        return redirect("/classlist/")

    # 打开数据库,提取数据
    classId = request.GET.get("class_id")
    conn = pymysql.connect(host="localhost", database="test", user="root", password="123456", charset="utf8")
    cursor = conn.cursor(cursor=pymysql.cursors.DictCursor)
    sql = "select id,cname from class where id=%s"
    cursor.execute(sql, classId)
    ret = cursor.fetchone()
    cursor.close()
    conn.close()
    return render(request, "bianji.html",{"key":ret})
urlpatterns = [
    url(r"bianji/",binaji)
]

模板语言

html文件就是模板文件,render()可以来渲染模板文件,模板文件中有着对应的语法

使用{{}}来替换,比如你传入了以下内容,

使用{{k1}}就能替换成v1,{{k3.kk1}}就能替换成vv1,而且即使是列表也同样使用.的方法,k2.0就是取列表中索引为0的元素

new_data = template.render({
        k1:v1,
       k2:[],
       k3:{kk1:vv1,kk2:vv2}  
    })

for循环

{% for i in k3%}

  <a>{{i}}</a>

{%endfor%}

for循环的计数

  {{forloop.counter}},默认由1开始,

  {{forloop.counter0}},可以指定起始的位置

生成两个a标签<a>vv1</a><a>vv2</a>

if语句

{% if 条件 %}
内容及要替换的内容
{% else %}
内容及要替换的内容
{% endif %}
  条件支持:if语句支持 and 、or、==、>、<、!=、<=、>=、in、not in、is、is not判断。
实例
{% for i in class %}
       {% if i.id == student.cid %}
            <option selected value="{{i.id }}">{{ i.cname }}</option>
       {% else %}
            <option value="{{i.id }}">{{ i.cname }}</option>
       {% endif %}
{% endfor %}

 安装Django

  • pip安装===>pip3 install django==1.11.9 #==后是版本

创建Django项目

  • 切换到存放Django项目的文件下
  • django-admin startproject 项目名

启动Django项目

  • 切换到项目的根目录下
  • 指定端口打开,默认为8000端口 python manage.py runserver 127.0.0.1:8888 or ip一般为本机简写为-python manage.py runserver 8888

Django 启动时报错 UnicodeEncodeError ...

报这个错误通常是因为计算机名为中文,改成英文的计算机名重启下电脑就可以了。

创建APP

  • 切换到Django目录下
  • python manage.py startapp 项目名
posted @ 2018-01-14 16:06  瓜田月夜  阅读(169)  评论(0)    收藏  举报