Django基础一

Django基础一

Web框架本质

框架,即framework,特指为解决一个开放性问题而设计的具备必定约束性的支撑结构,使用框架能够帮你快速开发特定的系统,简单地说,就是你用别人搭建好的舞台来作表演。html

对于全部的Web应用,本质上其实就是一个socket服务端,用户的浏览器其实就是一个socket客户端前端

复制代码
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服务本质上都是在这十几行代码基础上扩展出来的。这段代码就是它们的祖宗。python

用户的浏览器一输入网址,会给服务端发送数据,那浏览器会发送什么数据?怎么发?这个谁来定? 你这个网站是这个规定,他那个网站按照他那个规定,这互联网还能玩么?mysql

因此,必须有一个统一的规则,让你们发送消息、接收消息的时候有个格式依据,不能随便写。jquery

这个规则就是HTTP协议,之后浏览器发送请求信息也好,服务器回复响应信息也罢,都要按照这个规则来。web

HTTP协议主要规定了客户端和服务器之间的通讯格式,那HTTP协议是怎么规定消息格式的呢?sql

让咱们首先看下咱们在服务端接收到的消息是什么。shell

而后再看下咱们浏览器收到的响应信息是什么。数据库

响应头在浏览器的network窗口能够看到,咱们看到的HTML页面内容就是响应体。本质上仍是字符串,由于浏览器认识HTML,因此才会渲染出页面。django

每一个HTTP请求和响应都遵循相同的格式,一个HTTP包含Header和Body两部分,其中Body是可选的。 HTTP响应的Header中有一个 Content-Type代表响应的内容格式。如 text/html表示HTML网页

HTTP GET请求的格式:

GET /path HTTP/1.1
header1:v1\r\n
header2:v2\r\n

使用 \r\n分隔多个header

HTTP POST请求格式:

POST /path HTTP/1.1
header1:v1\r\n
header2:v2\r\n
\r\n\r\n
请求体...

当遇到连续两个 \r\n\r\n时,表示Header部分结束了,后面的数据是Body。

HTTP响应的格式:

200 OK
Header1:v1\r\n
Header2:v2\r\n
\r\n\r\n
响应体...

上述经过socket来实现了其本质。

对于真实开发中的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开发环境用的就是这个模块来作服务器

最简单的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 run_server(environ, start_response):
    start_response('200 OK', [('Content-Type', 'text/html;charset=utf8')])  # 设置HTTP响应的状态码和头信息
    return [bytes("<h1>Hello world!</h1>", encoding="utf8"),]


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

这样就结束了吗? 如何让咱们的Web服务根据用户请求的URL不一样而返回不一样的内容呢?

小事一桩,咱们能够从请求相关数据里面拿到请求的URL,而后作一个判断

复制代码
from wsgiref.simple_server import make_server


def run_server(environ, start_response):
    start_response('200 OK', [('Content-Type', 'text/html;charset=utf8'), ])  # 设置HTTP响应的状态码和头信息
    url = environ['PATH_INFO']  # 取到用户输入的url
    if url == "/index/":
        return [bytes("<h1>这是index页面</h1>", encoding="utf8"), ]
    elif url == "/home/":
        return [bytes("<h1>这是home页面</h1>", encoding="utf8"), ]
    else:
        return [bytes("404没有该页面", encoding="utf8"), ]


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

解决了不一样URL返回不一样内容的需求。 可是问题又来了,若是有不少不少页面怎么办?难道要挨个判断? 固然不用,咱们有更聪明的办法

复制代码
from wsgiref.simple_server import make_server


def index():
    return [bytes("<h1>这是index页面</h1>", encoding="utf8"), ]


def home():
    return [bytes("<h1>这是home页面</h1>", encoding="utf8"), ]


# 定义一个url和函数的对应关系
URL_LIST = [
    ("/index/", index),
    ("/home/", home),
]


def run_server(environ, start_response):
    start_response('200 OK', [('Content-Type', 'text/html;charset=utf8'), ])  # 设置HTTP响应的状态码和头信息
    url = environ['PATH_INFO']  # 取到用户输入的url
    func = None  # 将要执行的函数
    for i in URL_LIST:
        if i[0] == url:
            func = i[1]  # 去以前定义好的url列表里找url应该执行的函数
            break
    if func:  # 若是能找到要执行的函数
        return func()  # 返回函数的执行结果
    else:
        return [bytes("404没有该页面", encoding="utf8"), ]


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

完美解决了不一样URL返回不一样内容的问题。 可是我不想仅仅返回几个字符串,我想给浏览器返回完整的HTML内容,这又该怎么办呢?

没问题,不论是什么内容,最后都是转换成字节数据发送出去的。 我能够打开HTML文件,读取出它内部的二进制数据,而后发送给浏览器

复制代码
from wsgiref.simple_server import make_server


def index():
    with open("index.html", "rb") as f:
        data = f.read()
    return [data, ]


def home():
    with open("home.html", "rb") as f:
        data = f.read()
    return [data, ]


# 定义一个url和函数的对应关系
URL_LIST = [
    ("/index/", index),
    ("/home/", home),
]


def run_server(environ, start_response):
    start_response('200 OK', [('Content-Type', 'text/html;charset=utf8'), ])  # 设置HTTP响应的状态码和头信息
    url = environ['PATH_INFO']  # 取到用户输入的url
    func = None  # 将要执行的函数
    for i in URL_LIST:
        if i[0] == url:
            func = i[1]  # 去以前定义好的url列表里找url应该执行的函数
            break
    if func:  # 若是能找到要执行的函数
        return func()  # 返回函数的执行结果
    else:
        return [bytes("404没有该页面", encoding="utf8"), ]


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

这网页可以显示出来了,可是都是静态的啊。页面的内容都不会变化的,我想要的是动态网站。

没问题,我也有办法解决。我选择使用字符串替换来实现这个需求

复制代码
from wsgiref.simple_server import make_server


def index():
    with open("index.html", "rb") as f:
        data = f.read()
    import time
    time_str = str(time.time())
    data_str = str(data, encoding="utf8")
    data_str = data_str.replace("@@a@@", time_str)
    return [bytes(data_str, encoding="utf8"), ]


def home():
    with open("home.html", "rb") as f:
        data = f.read()
    return [data, ]


# 定义一个url和函数的对应关系
URL_LIST = [
    ("/index/", index),
    ("/home/", home),
]


def run_server(environ, start_response):
    start_response('200 OK', [('Content-Type', 'text/html;charset=utf8'), ])  # 设置HTTP响应的状态码和头信息
    url = environ['PATH_INFO']  # 取到用户输入的url
    func = None  # 将要执行的函数
    for i in URL_LIST:
        if i[0] == url:
            func = i[1]  # 去以前定义好的url列表里找url应该执行的函数
            break
    if func:  # 若是能找到要执行的函数
        return func()  # 返回函数的执行结果
    else:
        return [bytes("404没有该页面", encoding="utf8"), ]


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

这是一个简单的动态,我彻底能够从数据库中查询数据,而后去替换我html中的对应内容,而后再发送给浏览器完成渲染。 这个过程就至关于HTML模板渲染数据。 本质上就是HTML内容中利用一些特殊的符号来替换要展现的数据。 我这里用的特殊符号是我定义的,其实模板渲染有个现成的工具: jinja2

下载jinja2:

pip install jinja2
复制代码
<!DOCTYPE html>
<html lang="zh-CN"> <head> <meta 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> <h1>姓名:{{name}}</h1> <h1>爱好:</h1> <ul> {% for hobby in hobby_list %} <li>{{hobby}}</li> {% endfor %} </ul> </body> </html>
复制代码

使用jinja2渲染index2.html文件:

复制代码
from wsgiref.simple_server import make_server
from jinja2 import Template


def index():
    with open("index2.html", "r") as f:
        data = f.read()
    template = Template(data)  # 生成模板文件
    ret = template.render({"name": "Alex", "hobby_list": ["烫头", "泡吧"]})  # 把数据填充到模板里面
    return [bytes(ret, encoding="utf8"), ]


def home():
    with open("home.html", "rb") as f:
        data = f.read()
    return [data, ]


# 定义一个url和函数的对应关系
URL_LIST = [
    ("/index/", index),
    ("/home/", home),
]


def run_server(environ, start_response):
    start_response('200 OK', [('Content-Type', 'text/html;charset=utf8'), ])  # 设置HTTP响应的状态码和头信息
    url = environ['PATH_INFO']  # 取到用户输入的url
    func = None  # 将要执行的函数
    for i in URL_LIST:
        if i[0] == url:
            func = i[1]  # 去以前定义好的url列表里找url应该执行的函数
            break
    if func:  # 若是能找到要执行的函数
        return func()  # 返回函数的执行结果
    else:
        return [bytes("404没有该页面", encoding="utf8"), ]


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

如今的数据是咱们本身手写的,那可不能够从数据库中查询数据,来填充页面呢?

使用pymysql链接数据库:

conn = pymysql.connect(host="127.0.0.1", port=3306, user="root", passwd="xxx", db="xxx", charset="utf8")
cursor = conn.cursor(cursor=pymysql.cursors.DictCursor)
cursor.execute("select name, age, department_id from userinfo")
user_list = cursor.fetchall()
cursor.close()
conn.close()

模板的原理就是字符串替换,咱们只要在HTML页面中遵循jinja2的语法规则写上,其内部就会按照指定的语法进行相应的替换,从而达到动态的返回内容。

框架总结

本身定义的web框架
a. 本身写socket处理请求相关的数据(web服务端) wsgiref uWSGI gunicorn
WSGI协议:
Python Web服务端和Python 应用程序之间通信的一个标准

b. URL -> 函数 --> 函数里面写业务逻辑(取到数据)

c. 把数据填充到HTML页面(字符串替换)

分类:
1. a、b、c都是用本身的 --> Tornado
2. a用别人的,b和c用本身的 --> Django
3. a和c都用别人的,b用本身的 --> Flask

另一个维度的分类:
1. Django (大而全)
2. 其余 (小而精)

Django

安装(安装最新LTS版)

pip3 install django==1.11.9

MVC和MTV模式

Django的MTV模式本质是各组件之间为了保持松耦合关系,Django的MTV分别表明:

       Model(模型):负责业务对象与数据库的对象(ORM)

       Template(模版):负责如何把页面展现给用户

       View(视图):负责业务逻辑,并在适当的时候调用Model和Template

       此外,Django还有一个url分发器,它的做用是将一个个URL的页面请求分发给不一样的view处理,view再调用相应的Model和Template

Django基本命令

建立一个django project

django-admin.py startproject mysite

当前目录下会生成mysite的工程,目录结构以下:

manage.py ----- Django项目里面的工具,经过它能够调用django shell和数据库等。

settings.py ---- 包含了项目的默认设置,包括数据库信息,调试标志以及其余一些工做的变量。

urls.py ----- 负责把URL模式映射到应用程序。

PyCharm方式:
File -> new project -> 选Django -> 起名字 -> 点右下角create

在mysite目录下建立应用,好比blog

python manage.py startapp blog

启动django项目

python manage.py runserver 8080

这样咱们的django就启动起来了!当咱们访问:http://127.0.0.1:8080/时就能够看到:

 

setting文件配置

模板文件配置

复制代码
TEMPLATES = [
    {
        'BACKEND': 'django.template.backends.django.DjangoTemplates',
        'DIRS': [os.path.join(BASE_DIR, "template")],  # template文件夹位置,里面放网页文件
        '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',
            ],
        },
    },
]
复制代码

静态文件配置

# 在HTML页面上引用静态文件时写的名字
STATIC_URL = '/static/'  # HTML中使用的静态文件夹前缀
# 静态文件(CSS文件和JS文件)存放的实际位置
STATICFILES_DIRS = [
    os.path.join(BASE_DIR, "static"),  # 静态文件存放位置
]

复制代码
#三、STATIC文件还能够配置STATICFILES_DIRS,指定额外的静态文件存储位置。
    #  STATIC_URL的含义与MEDIA_URL相似。

    # ----------------------------------------------------------------------------
    #注意1:
        #为了后端的更改不会影响前端的引入,避免形成前端大量修改

        STATIC_URL = '/static/'               #引用名
        STATICFILES_DIRS = (
            os.path.join(BASE_DIR,"statics")  #实际名 ,即实际文件夹的名字
        )

        #django对引用名和实际名进行映射,引用时,只能按照引用名来,不能按实际名去找
        #<script src="/statics/jquery-3.1.1.js"></script>
        #------error-----不能直接用,必须用STATIC_URL = '/static/':
        #<script src="/static/jquery-3.1.1.js"></script>

    #注意2(statics文件夹写在不一样的app下,静态文件的调用):

        STATIC_URL = '/static/'

        STATICFILES_DIRS=(
            ('hello',os.path.join(BASE_DIR,"app01","statics")) ,
        )

        #<script src="/static/hello/jquery-1.8.2.min.js"></script>

    #注意3:
        STATIC_URL = '/static/'
        {% load staticfiles %}
       # <script src={% static "jquery-1.8.2.min.js" %}></script>
复制代码

刚开始学习时可在配置文件中暂时禁用csrf中间件,方便表单提交测试

复制代码
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',
]
复制代码

Django基础必备三件套

from django.shortcuts import HttpResponse, render, redirect

HttpResponse

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

例如:

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

render

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

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

例如:

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

结合一个给定的模板和一个给定的上下文字典,并返回一个渲染后的 HttpResponse 对象。

参数:
     request: 用于生成响应的请求对象。

     template_name:要使用的模板的完整名称,可选的参数

     context:添加到模板上下文的一个字典。默认是一个空字典。若是字典中的某个值是可调用的,视图将在渲染模板以前调用它。

     content_type:生成的文档要使用的MIME类型。默认为DEFAULT_CONTENT_TYPE 设置的值。

     status:响应的状态码。默认为200。
复制代码

redirect

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

例如:

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

 

复制代码
-----------------------------------url.py

 url(r"login",   views.login),
 url(r"yuan_back",   views.yuan_back),

-----------------------------------views.py
def login(req):
    if req.method=="POST":
        if 1:
            # return redirect("/yuan_back/")
            name="yuanhao"

            return render(req,"my backend.html",locals())

    return render(req,"login.html",locals())


def yuan_back(req):

    name="苑昊"

    return render(req,"my backend.html",locals())

-----------------------------------login.html

<form action="/login/" method="post">
    <p>姓名<input type="text" name="username"></p>
    <p>性别<input type="text" name="sex"></p>
    <p>邮箱<input type="text" name="email"></p>
    <p><input type="submit" value="submit"></p>
</form>
-----------------------------------my backend.html
<h1>用户{{ name }}你好</h1>

#总结: render和redirect的区别:
#   1 if render的页面须要模板语言渲染,须要的将数据库的数据加载到html,那么全部的这一部分
#     除了写在yuan_back的视图函数中,必须还要写在login中,代码重复,没有解耦.

#   2 the most important: url没有跳转到/yuan_back/,而是还在/login/,因此当刷新后
#     又得从新登陆.
相关文章
相关标签/搜索