lldbyt

一. Django基础介绍

  1.1 Django是一个WEB应用框架

  1.2 WEB应用程序

    什么是WEB应用?  socket套接字?

    客户端(浏览器)<----->服务端

      发送请求<----->响应请求

    WEB应用就是服务器上的程序,它接受客户端(浏览器)的请求,并响应请求发送字符串给客户端.

二. WEB应用程序1

  2.1 socket套节字  网络数据传输必须要用到socket,(其实本地电脑的应用程序和操作系统之间也需要socket)

  2.2 实例: 一个简单的WEB应用程序

# 一个简单的WEB应用程序
import socket

sock = socket.socket()
sock.bind(("127.0.0.1", 8800))
sock.listen(5)

while 1:
    print(\'server waiting......\')
    conn, addr = sock.accept()
    data = conn.recv(1024)
    print(\'data\', data)
    # conn.send(b"hello luffycity")  # 用浏览器访问时,显示该网页无法正常运作127.0.0.1 发送的响应无效。ERR_INVALID_HTTP_RESPONSE.因为浏览器无法识别发过来的数据.
    conn.send(b"HTTP/1.1 200 OK\r\n\r\nhello luffycity")  # 规范的WEB请求格式,浏览器的页面可以正常显示了.
    conn.close()
一个简单的WEB应用程序

 三. WEB应用程序2

  3.1 发送字符串,让浏览器渲染出带html标签<h1>的字符串

带html标签的WEB应用程序

  3.2 WEB应用程序读取html文件,并发送到客户端,客户端浏览器进行渲染

<!DOCTYPE html>

<head>
    <meta charset="UTF-8">
    <title>page</title>
</head>
<body>
    <h1>hello dashijie</h1>
    <img src="https://ss0.bdstatic.com/70cFuHSh_Q1YnxGkpoWK1HF6hhy/it/u=3318256381,3210603049&fm=26&gp=0.jpg">

</body>
</html>
index.html
# -*- coding:utf-8 -*-
import socket

sock = socket.socket()
sock.bind(("127.0.0.1", 8899))
sock.listen(5)

while 1:
    conn, addr = sock.accept()
    data = conn.recv(1024)
    print("data", data)
    with open(\'index.html\', \'r\') as f:
        data2 = f.read()
    conn.send(("HTTP/1.1 200 OK\r\n\r\n %s" % data2).encode(\'utf8\'))
    conn.close()
读取上面的index.html文件

 四. HTTP请求协议1

  浏览器向服务器发送的请求,本质是一堆字符串.

  4.1 包含以下三个部分:

    (每一个小块数据用\r\n区隔开)

    a. 请求首行  包含:  请求方式(get或post)  请求路径(url)  协议(HTTP/1.1)

    b. 请求头(若干个键值对)  常包含:Host  Connection  content-Type等  

    c. 请求体  连续两个\r\n后的就是请求体  只有post请求才有请求体,get请求没有请求体

 

  4..2 请求数据实例

GET / HTTP/1.1\r\nHost: 127.0.0.1:8899\r\nConnection: keep-alive\r\nUpgrade-Insecure-Requests: 1\r\nUser-Agent: Mozilla/5.0 (Windows NT 6.1; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/86.0.4240.75 Safari/537.36\r\nAccept: text/html,application/xhtml+xml,application/xml;q=0.9,image/avif,image/webp,image/apng,*/*;q=0.8,application/signed-exchange;v=b3;q=0.9\r\nSec-Fetch-Site: none\r\nSec-Fetch-Mode: navigate\r\nSec-Fetch-User: ?1\r\nSec-Fetch-Dest: document\r\nAccept-Encoding: gzip, deflate, br\r\nAccept-Language: zh-CN,zh;q=0.9\r\n\r\n\'
View Code

 

  4.3 get与post的使用区别

    get一般应用于查询,post应用于更新数据库数据

    只有POST请求才有请求体

五. HTTP请求协议2

  post请求实例,是一个典型的用户登录框

    

 

 

    服务器的接收到的请求(末尾黄色字是请求体,即浏览器用户输入的用户名和密码):

    POST / HTTP/1.1\r\nHost: 127.0.0.1:8800\r\nConnection: keep-alive\r\nContent-Length: 31\r\nCache-Control: max-age=0\r\nUpgrade-Insecure-Requests: 1\r\nOrigin: http://127.0.0.1:8800\r\nContent-Type: application/x-www-form-urlencoded\r\nUser-Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/87.0.4280.88 Safari/537.36\r\nAccept: text/html,application/xhtml+xml,application/xml;q=0.9,image/avif,image/webp,image/apng,*/*;q=0.8,application/signed-exchange;v=b3;q=0.9\r\nSec-Fetch-Site: same-origin\r\nSec-Fetch-Mode: navigate\r\nSec-Fetch-User: ?1\r\nSec-Fetch-Dest: document\r\nReferer: http://127.0.0.1:8800/\r\nAccept-Encoding: gzip, deflate, br\r\nAccept-Language: zh-CN,zh;q=0.9\r\n\r\nusername=lldbyt&password=123456\'

  一个典型的服务器端post请求源代码

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>用户登录</title>
</head>
<body>
    <form action="http://127.0.0.1:8800/" method="post">  <!--这里提交后地址没变,action可以省略-->
        用户名:<input type="text" name="username">  <!--name就是POST请求体键值对的KEY-->
        密码:<input type="password" name="password">  <!--服务器接收到的请求体nusername=lldbyt&password=123456-->
        <input type="submit">
    </form>
</body>
</html>
login.html
import socket

sock = socket.socket()
sock.bind(("127.0.0.1", 8800))
sock.listen(5)

while 1:
    print(\'server waiting......\')
    conn, addr = sock.accept()
    data = conn.recv(1024)
    print(\'data\', data)
    with open(\'login.html\', \'r\', encoding=\'utf-8\') as f:  # 注意编码
        data_login = f.read()
    conn.send(("HTTP/1.1 200 OK\r\n\r\n%s" % data_login).encode(\'utf-8\'))
    conn.close()
服务器端主程序start.py

 六. HTTP之响应协议

   6.1 请求方式: get与post请求的区别

    - get提交的数据会放在URL之后,以?分割URL和传输数据,参数之间以&相连,如EditBook?name=test1&id=123456. POST方法是把提交的数据放在HTTP包的请求体中.

    - get提交的数据大小有限制(因为浏览器对URL的长度有限制),而POST方法提交的数据没有限制.

    - get与post请求在服务端获取请求数据方式不同.

  6.2 响应格式(浏览器接收到服务器发来的信息)

    

 

  6.3 自定义响应头(黄色字符部分)

    conn.send(b"HTTP/1.1 200 OK\r\nContent-Type:text\r\n\r\n%s" % data)

  6.4 响应状态码

    状态码如200 OK,以3位数字和原因 成。数字中的 一位指定了响应 别,后两位无分 。响应 别有以5种。

    

     重定向的解释: 浏览器向服务器发送请求,服务器接收到后再发送响应信息给浏览器,该响应信息告诉浏览器需要(浏览器再发一次请求)转到另外一个资源. 

 七. wsgiref模块1

  - wsgi是接口协议

  7.1  wsgiref模块功能 

    - 按照HTTP请求协议格式进行解包(解析数据)

    - 按照HTTP响应协议封装数据

    - wgiref模块封装了socket

    - 让WEB应用开发者专注于WEB业务开发

 八. wsgiref模块2

  - wsgiref做的简单的,根据用户不的输入,实现不同网页的效果

# wsgiref做的WEB应用程序
from wsgiref.simple_server import make_server

def application(environ, start_response):
    # 按照http协议解析数据:environ字典格式
    # 按照http协议组装数据:start_response
    # print(environ)
    # print(type(environ))

    # 获取当前请求路径
    path = environ.get("PATH_INFO")
    print(path)

    # 根据用户不同的输入,实现不同的网页,动态网站效果.
    if path == \'/login\':  # 登录页面
        with open(\'login.html\', \'r\', encoding=\'utf-8\') as f:
            data = f.read()
    elif path == \'/index\':  # 主页面
        with open(\'index.html\', \'r\', encoding=\'utf-8\') as f:
            data = f.read()

    # 响应体
    start_response(\'200 OK\', [("Content-Type", "text/html")])
    return [data.encode("utf-8")]  # 返回值一定要加[],这是规定

\'\'\'
    # 下面两句构成了完整的最简单的响应体
    start_response(\'200 OK\', [("Content-Type", "text/html")])  # 响应首行,  响应头
    return [b\'<h1>Hello, WEB!</h1>\']  # 响应主体
\'\'\'

# make_server封装了socket, application是回调函数
httped = make_server("", 8090, application)  # 省略host地址表示127.0.0.1

# 等待用户连接,相当于之前的: conn, addr = sock.accept()
httped.serve_forever()  # 一旦请求进来后,就调用上面的application函数
基于wsgiref模块的简单WEB应用

 

九. diy一个web框架

  - 路由的解释

  9.1 浏览器默认会发送图标请求

    - 此响应时,浏览器只接收图片,如果发送字符串,则字符串不会显示.

from wsgiref.simple_server import make_server

def application(environ, start_response):
    start_response(\'200 OK\', [(\'Content-Type\', \'text/html\')])
    print(\'PATH\', environ.get(\'PATH_INFO\'))
    path = environ.get("PATH_INFO")
    if path == "/favicon.ico":
        # return [b"hello favicon.ico"]  # 此字符串响应不会在浏览器上显示,图标请求只接收图片请求.
        with open(\'baidu.ico\', \'rb\') as f:  # 读取图标文件
            data = f.read()
        return [data]  # 返回图标文件
    return [b"<h1>Hello</h1>"]

httped = make_server(\'\', 8899, application)

httped.serve_forever()
实现浏览器图标的响应

十. diy一个web框架

  10.1 用户输入不同的路径,实现访问不同的页面,不用很多的if..else,用路由分发的方法

    - 其实也是解了耦合

from wsgiref.simple_server import make_server

# 定义每个视图函数
def login(environ):
    with open(\'login.html\', \'rb\') as f:
        data = f.read()
    return data
def index(environ):
    with open(\'index.html\', \'rb\') as f:
        data = f.read()
    return data
def fav(environ):
    with open(\'favicon.ico\', \'rb\') as f:
        data = f.read()
    return data


def application(environ, start_response):
    start_response(\'200 OK\', [(\'Content-Type\', \'text/html\')])
    print(\'PATH\', environ.get(\'PATH_INFO\'))
    path = environ.get("PATH_INFO")
    
    # 路由
    url_patterns = [
        (\'/login\', login),
        (\'/index\', index),
        (\'/favicon.ico\', fav)
    ]

    func = None
    for item in url_patterns:
        if path == item[0]:
            func = item[1]
            break
    if func:
        return [func(environ)]  # 返回值中包含environ的原因是视图中一般都会用到请求的数据
    else:
        return [b\'404!\']


httped = make_server(\'\', 8899, application)

httped.serve_forever()
路由分发,解耦合,实现用户输入不用路径,实现访问不用页面.

十一. diy一个web框架2

  11.1 软件开发规范,再解耦合,修改diy的web框架

    

from wsgiref.simple_server import make_server


def application(environ, start_response):
    start_response(\'200 OK\', [(\'Content-Type\', \'text/html\')])
    print(\'PATH\', environ.get(\'PATH_INFO\'))
    path = environ.get("PATH_INFO")


    from urls import url_patterns

    func = None
    for item in url_patterns:
        if path == item[0]:
            func = item[1]
            break
    if func:
        return [func(environ)]  # 返回值中包含environ的原因是视图中一般都会用到请求的数据
    else:
        return [b\'404!\']


httped = make_server(\'\', 8899, application)

httped.serve_forever()
main.py
from views import *

# 路由
    url_patterns = [
        (\'/login\', login),  # 视图函数
        (\'/index\', index),
        (\'/favicon.ico\', fav)
    ]
urls.py
# 定义每个访问页面视图函数
def login(environ):
    with open(\'login.html\', \'rb\') as f:
        data = f.read()
    return data
def index(environ):
    with open(\'index.html\', \'rb\') as f:
        data = f.read()
    return data
def fav(environ):
    with open(\'favicon.ico\', \'rb\') as f:
        data = f.read()
    return data
views.py

 

十二. 框架的简单使用

   增添功能,只需要修改路由文件,视图文件,模板等就可以了.

十三. 扩展框架关于数据库的操作

  如果没有数据库,就没有什么意义.

十四. Django简介

  大而全,组建特别多

  14.1 mvc和mtv模型(模型的主要目的是解耦合)

十五. Django的下载与基本命令

  15.1 cmd纯命令行, 安装Django模块命令

    pip install Django  #注意: Django首字母大写

    如果提示pip错误,则先python.exe -m pip install --upgrade pip,注意该命令前面不能带路径

  15.2 cmd纯命令行, 在当前文件夹下创建一个mysite项目

    django-admin.py startproject mysite  # 注意首字母d是小写

    

 

    项目的文件夹树, manage.py为启动文件,相当于之前的main.py

    manage.py ----Django项目里面的工具,通过它可以调用django shell和数据库

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

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

  15.3 cmd纯命令行, 在mysite目录下创建应用blog(注意:mysite是项目文件夹,blog是应用程序文件夹, 一个项目中可以包含多个应用程序)

    首先cd mysite文件夹,进入sysite项目根目录

    然后python manage.py startapp blog创建blog应用程序

    下图为创建blog应用后的文件夹

     

 

    下图为blog应用程序的文件结构

     

    15.4 cmd命令下, 启动Django项目

    python manage.py runserver 8000

    下图为命令输入后显示的效果:

    浏览器访问http://127.0.0.1:8000后的效果

    

十六. Django简单示例

   16.1 一个显示当前时间的小程序

    16.1.1 创建一个Django项目

    

 

    16.1.2 生成的一个fisrt_pro项目,新建一个app01程序的目录

    

 

     16.1.3 几个文件的代码

from django.contrib import admin
from django.urls import path
from app1 import views

urlpatterns = [
    path(\'admin/\', admin.site.urls),
    path(\'timer/\', views.timer),  # path帮助调用自定义的views.timer函数
]
urls.py
views.py
<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>Title</title>
</head>
<body>
    <h2>当前时间:{{ date }}</h2>
{#上面的两个大括号包着变量date这个是规定的格式#}
</body>
</html>
timer.thml

 

  16.2 Django服务(请求)执行过程(MPV过程)

    ---->1. urls.py(由它决定用户发送的路径交给哪个视图函数处理)  ---->2. views.py(主要逻辑处理)  ---->3. models.py(涉及到数据库的从这里取)  ---->4. timer.html(最终的返回页面的模板) ---->

十七. 静态文件配置1

   17.1 什么叫静态文件?

    项目中的css, js都应当处理为静态文件. 项目中的任何文件都需要Django去找, 最终都是由客户端的浏览器去执行的,引入jquery-3.1.1.js时就需要注意文件路径问题(不能当成客户端的本地文件去执行),  js文件需要写成服务器的地址.

  17.2 如何配置静态文件?

    在项目的根目录下创建static文件夹,所有静态文件都放入其中.此时通过客户端浏览器无法直接访问该文件,因为(Django采用虚拟文件夹的方式访问)客户端的所有访问路径都经过urls.py路由处理,而urls.py中没有添加关于js的路由.  在settings.py中配置一下就可以通过别名访问了.配置方法在下面.

    相关文件在项目中的位置:

    

 

 

    处理settings.py文件

STATIC_URL = \'/static/\'  # 路径别名(虚拟目录),客户端浏览器输入地址(路径)可直接访问了
STATICFILES_DIRS = [
    os.path.join(BASE_DIR, \'statics\'),  # 实际路径,  这里是方便为了区分才使用statics这个名字, 正式使用时,实际目录和别名都用static
]

    模板文件引入jquery-3.5.1.js,文字点击变为红色效果timer.html

<body>
    <h2>当前时间:{{ date }}</h2>
    <script src="/static/jquery-3.5.1.js"></script>  # 这里使用statics目录的别名static, statics是实际目录, static是虚拟目录(别名)
                                                     # 正式使用时,实际目录和别名都用static
    # 下面的效果是,点击变为红色
    <script type="text/javascript">
        $(\'h2\').click(function () {
            $(this).css(\'color\', \'red\');
        })
    </script>
</body>

 

十八. 静态文件配置2

  18.1 html文件中引入css和js

    css文件和js文件也是静态文件,需要放到项目中的static文件夹下,针对不同的应用可以在static文件夹下再建立应用的子文件夹,比如app01,将app01应用程序的css和js文件放到app01文件夹下.这里需要注意的是,在html中引入css\js文件时需要使用static静态目录.

  18.2 实例(显示当前时间戳),原文字颜色为红色,鼠标点击文字变为蓝色,采用css和jquery结合的方法

    文件目录如下:

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>Title</title>
    <link type="text/css" rel="stylesheet" href="/static/app/timer.css">
    <script src="/static/jquery-3.5.1.js"></script>
</head>
<body>
    <h2>当前时间是:{{ ctime }}</h2>
    <script rel="script" src="/static/app/timer.js"></script>

</body>
</html>
timer.html模板文件
h2{
    color: red;
}
timer.css样式文件
$(\'h2\').click(function () {
    $(this).css(\'color\', \'blue\');
})
timer.js点击变色jquery文件
from app import views

urlpatterns = [
    path(\'admin/\', admin.site.urls),
    path(\'timer/\', views.timer),
]
urls.py路由文件(自定义部分)
STATIC_URL = \'/static/\'
STATICFILES_DIRS = [
    os.path.join(BASE_DIR, \'static\')
]
settings.py配置文件(自定义部分)
from django.shortcuts import render

# Create your views here.


def timer(request):
    import time
    ctime = time.time()
    return render(request, \'timer.html\', {\'ctime\': ctime})
views.py视图文件

 

十九. 路由控制之简单配置

  19.1 re_path模块, Django1版本的路由

    路由配置:   路径-------->视图函数

    采用正则表达式re匹配路径,(路径中加括号的分组)

简单路由实例urls.py
from django.shortcuts import render, HttpResponse  # HttpResponse

# Create your views here.


def timer(request):
    import time
    ctime = time.time()
    return render(request, \'timer.html\', {\'ctime\': ctime})



def special_case_2003(request):
    return HttpResponse("special_case_2003")

def year_archive(request, year):
    return HttpResponse(year)


def month_archive(request, year, month):
    return  HttpResponse(year+"-"+month)


def article_detail(request, year, month, date):
    return HttpResponse(year+"-"+month+"-"+date)
视图View.py

 

  19.2 注意:

    - 若要从URL中获取一值,只需要在它周围放置一对圆括号.

    - 不需要添加一个前导的反斜杠,应为每个URL都有.例如,应该是^articles而不是^/articles.

    - 每个正则表达式前面的"r"是可选的,但建议加上.它告诉python这个字符串是"原始的"----字符串中任何字符都不应该转义.

二十. 路由控制之有名分组

  20.1 为路径中的分组起名字,关键字参数传参

    # re_path(r"^articles/([0-9]{4})/([0-9]{2})/$", views.month_archive), # month_archive(request, yaer, month)  # 路径中包含年月,re分三个组
    re_path(r"^articles/(?P<y>[0-9]{4})/(?P<m>[0-9]{2})/$", views.month_archive),  # 分组前加?P<y>相当于定义了y=[0-9]{4},及定义了关键字参数,并向函数传递  # month_archive(request, y=2009, m=12)
urls.py分组和有名分组比较
def month_archive(request, m, y):  # 关键字参数,颠倒位置不影响传值,名称要与url中的分组名称一致
    return  HttpResponse(y+"-"+m)


def article_detail(request, year, month, date):
    return HttpResponse(year+"-"+month+"-"+date)
View.py关键字参数

二十一. 路由控制之分发

  21.1 就是将全局的urls.py文件进行细分, 每个app建立一个自己的urls.py文件,也就是再解耦.

    全局urls.py和一个app应用程序的urls.py,文件结构

 

   21.2 两个URLS.py代码

浏览器访问地址: http://127.0.0.1:8000/app/articles/2009/02/

全局urls.py中的关键代码

re_path(\'app/\', include("app.urls")),  # 分发到一个app中
from django.contrib import admin
from django.urls import path, re_path, include   # include
from app import views, urls  # app的urls

urlpatterns = [
    path(\'admin/\', admin.site.urls),
    path(\'timer/\', views.timer),
    re_path(\'app/\', include("app.urls")),  # 分发到一个app中
    # path(\'app/\', include(urls))  # 这样写也可以
]
全局urls.py文件
from django.contrib import admin
from django.urls import path, re_path   # re_path
from app import views

urlpatterns = [

    # ^表示以指定字符串开头    $表示以指定字符串结尾
    re_path(r"^articles/2003/$", views.special_case_2003),  # special_case_2003(request)
    re_path(r"^articles/([0-9]{4})/$", views.year_archive), # year_archive(request, 1999)  这里会多传一个参数,及有两个参数.只要是匹配分组的(re正则表达式中一个小括号,就是一个分组),一个分组项目就有一个参数.
    # re_path(r"^articles/([0-9]{4})/([0-9]{2})/$", views.month_archive), # month_archive(request, yaer, month)  # 路径中包含年月,re分三个组
    re_path(r"^articles/(?P<y>[0-9]{4})/(?P<m>[0-9]{2})/$", views.month_archive),  # 分组前加?P<y>相当于定义了y=[0-9]{4},及定义了关键字参数,并向函数传递  # month_archive(request, y=2009, m=12)

    re_path(r"^articles/([0-9]{4})/([0-9]{2})/([0-9]+)/$", views.article_detail), # article_detail(request, year, month, date)


]
其中一个APP的urls.py文件

   21.3 让浏览器访问的地址中不需要输入APP

re_path(r\'^\', include("app.urls")), 

二十二. 路由控制之登录验证实例

  22.1 访问login路径及提交验证用户名密码是否正确,及有两次请求同一路径.

      第一次是在地址栏访问网址:http://127.0.0.1:8000/login/, 这是get请求

      第二次是在login页面点击提交按钮, 这是POST请求

from django.contrib import admin
from django.urls import path, re_path, include
from app import views, urls 

urlpatterns = [
    path(\'admin/\', admin.site.urls),
    path(\'timer/\', views.timer),
    re_path(\'login/\', views.login),  # 登录
]
urls.py
注意Views.py文件中两次请求的逻辑设置
from django.shortcuts import render, HttpResponse  # HttpResponse

def login(request):
    print(request.GET)
    print(request.POST)
    if request.method == \'GET\':  #第一次get请求访问/login/路径
        return render(request, \'login.html\')
    else:  #第二次post请求提交用户名密码
        print(request.GET)
        print(request.POST)
        user = request.POST.get(\'user\')
        pwd = request.POST.get(\'pwd\')
        if user == \'杨阳\' and pwd == \'123\':
            return HttpResponse(\'登录成功!\')
        else:
            return HttpResponse("用户名密码错误!")

\'\'\'
请求结果
    print(request.GET)
    print(request.POST)

输出样式如下:
<QueryDict: {}>
<QueryDict: {\'user\': [\'杨阳\'], \'pwd\': [\'123\']}>

使用字典的get方法可获取字典的值.
user = request.POST.get(\'user\')
\'\'\'
Views.py
<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>用户登录</title>
</head>
<body>
    <form action="http://127.0.0.1:8000/login/" method="post">
        用户名:<input type="text" name="user">
        密码:<input type="password" name="pwd">
        <input type="submit">
    </form>

</body>
</html>
login.html

     post提交时出现错误提示,需要把settings.py文件中的一句代码删除,或注释掉.

      # \'django.middleware.csrf.CsrfViewMiddleware\',

 

二十三. 路由控制之反向解析1

  23.1 用别名指代URL. 在模板中进行反向解析

  urls.py中,对于路径采用别名.应用场景:当产品经理要求更改访问路径的时候,只需要改urls.py中的路径名就可以了,其它文件无需改动. 

     使用urls.py中使用别名,增加参数name=\'别名\',注意在模板文件中把action中的路径改为规定的格式{% url "别名" %}. views.py中的函数名称与别名要保持一致.

from django.contrib import admin
from django.urls import path, re_path, include   # include
from app import views, urls  # app的urls

urlpatterns = [
    path(\'admin/\', admin.site.urls),
    path(\'timer/\', views.timer),
    re_path(\'login.html\', views.login, name=\'login\'),  # 别名为login,地址栏访问时的路径写login.html
]
urls.py
<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>用户登录</title>
</head>
<body>
    <form action="{% url "login" %}" method="post">
        用户名:<input type="text" name="user">
        密码:<input type="password" name="pwd">
        <input type="submit">
    </form>

</body>
</html>
login.html注意别名的位置以及书写格式

 

二十四. 路由控制之反向解析2

  24.1 在视图函数中进行反向解析

    可以在后台获取到用户访问的完整路径

    需要在视图函数中导入反向解析函数reverse, 需要注意的是,如果urls.py路径中包含正则表达式,则需要在视图函数中增加args参数

from django.contrib import admin
from django.urls import path, re_path   # re_path
from app import views

urlpatterns = [

    re_path(r"^articles/2003/$", views.special_case_2003, name="s_c_2003"),  # name就是路径的别名
    re_path(r"^articles/([0-9]{4})/$", views.year_archive, name="year_archive"), # 此处包含正则表达式, 视图函数中如果要获取这个路径,需要增加args参数
 ]
app/urls.py
from django.shortcuts import render, HttpResponse  # HttpResponse
from django.urls import reverse  # reverse反向解析函数

def timer(request):
    url = reverse(\'s_c_2003\')  # 可以在任何函数中获取的需要的路径
    print(url)
    import time
    ctime = time.time()
    return render(request, \'timer.html\', {\'ctime\': ctime})

def special_case_2003(request):
    url = reverse(\'s_c_2003\')  # 括号中的参数填写urls.py中的路径别名
    print(url)
    return HttpResponse("special_case_2003")

def year_archive(request, year):
    url = reverse(\'year_archive\', args=("2005",))  # 此处包含正则表达式的匹配问题, 需要增加一个args参数,注意书写规则, 2005是四位数与匹配的路径位数相等[0-9]{4}
    print(url)
    return HttpResponse(year)
app/views.py

  后台输出如下:

    /app/articles/2005/

二十五. 路由控制之名称空间1

  25.1 名称空间 (namespace),也叫命名空间,是表示标示符的可见范围.

    由于name没有作用域,Django在反解析URL时,会在项目全局顺序搜索,当查找到第一个name指定URL时,立即返回,我们在开发项目时,会经常使用name属性反解除URL,当不小心在不同的app的urls中定义相同的name时,可能会导致URL反解错误,为了避免这种事情发生,引入命名空间.

 

  25.2 实例

    25.2.1 使用pycharm的命令行窗口创建新应用app02

 

    25.2.2 创建新应用后的目录如下:

  

 

     25.2.3 为全局urls.py添加app02的路由分发

from django.contrib import admin
from django.urls import path, re_path, include
from app import views, urls 

urlpatterns = [
    re_path(r\'^app/\', include("app.urls")),  # 分发到app中
    re_path(r\'^app02/\', include("app02.urls")),  # 分发到app02中
]
全局urls.py文件

    25.2.4 为app02增加index视图

from django.shortcuts import render, HttpResponse

# Create your views here.


def index(request):

    return HttpResponse("index2")
app02的views.py

    25.2.5 为app02的urls.py增加index的路由解析

from django.contrib import admin
from django.urls import path, re_path   # re_path
from app02 import views

urlpatterns = [

    re_path("index/", views.index)

]
app02的urls.py

     25.2.6 访问http://127.0.0.1:8000/app02/index/得到了想要的结果,app02应用建立成功了.

     25.2.7 为app应用添加index视图和路由,以及访问的效果.

def index(request):
    return HttpResponse(\'index01\')
app下的views.py
from django.contrib import admin
from django.urls import path, re_path   # re_path
from app import views

urlpatterns = [

    re_path(\'index/\', views.index)

]
app下的urls.py

 

二十六. 路由控制之名称空间2

     25.2.8 分别为app和app01下的两个index加别名

      应用程序中的urls.py文件中路由解析

       re_path(\'index/\', views.index, name=\'index\')

      现在就有两个index,名字就重复了.

    25.2.9 两个index,反向解析,就出问题了.

      先分别为两个app的views.py添加名称index的反向解析    reverse(\'index\')

      分别访问两个index,反向解析路径,得到的路径是相同的,就出问题了.(覆盖了)

from django.shortcuts import render, HttpResponse

# Create your views here.


from django.urls import reverse

def index(request):

    return HttpResponse(reverse(\'index\'))  # reverse(\'index\')反向解析出名称为"index"的实际路径
app和app02下的views.py

 

 

 

     25.2.10 下面解决名称相同,的反向解析相同的问题

     25.2.11 在全局urls.py中给include()路由分发添加名称空间namespace

urlpatterns = [
    path(\'admin/\', admin.site.urls),
    path(\'timer/\', views.timer),
    re_path(\'login.html\', views.login, name=\'login\'),
    
    re_path(r\'^app/\', include(("app.urls", "app"))),  # 名称空间namespace="app"  注意这里增加了一个元组
    re_path(r\'^app02/\', include(("app02.urls", "app02"))),  # 名称空间namespace="app02"
]
全局urls.py

    25.2.12 再给两个app的views.py文件中的反向解析添加上namespace的名称

def index(request):
    return HttpResponse(reverse(\'app:index\'))  # 为反向解析添加了namespace,注意有个冒号
app下的views.py
def index(request):

    return HttpResponse(reverse(\'app02:index\'))   # 注意书写的格式
app02的views.py

    25.2.14 再访问路径,问题解决了.反向解析得到了正确的结果

 

 

二十七. url控制之path方法

  27.1 django2.0的path. re_path是django1.0版本的语法

      re_path传的值是字符串, 如果要使用数字,还需要进行转换. path方法就是解决这类问题而生的.

        参考:https://www.cnblogs.com/yuanchenqi/articles/8931472.html

 

urlpatterns = [
    path(\'admin/\', admin.site.urls),
    path(\'timer/\', views.timer),
    path(\'login/\', views.login),
    path("articles/<int:year>", views.path_year),  # path_year(request, 2001)
]
全局urls.py

 

def path_year(request, year):
    print(year)
    print(type(year))
    return HttpResponse("path_year...")
app下的views.py

 

 

 

   27.2 path的5个转化器

    str,匹配除了路径分隔符/之外的非空字符串,这是默认的形式

    int,匹配正整数,包含0

    slug,匹配字母,数字以及横杆,下划线组成的字符串

    uuid,匹配格式化的uuid,如 075194d3-6885-417e-a8a8-6c931e272f00

    path,匹配任何非空字符串, 包含了路径分隔符

 

 

二十八. path的自定义转换器

  28.1 对于一些复杂或者复用的需要,可以定义自己的转化器.转化器是一个类或接口,它的要求有三点:

  • regex类属性,字符串类型
  • to_python(self, value)方法, value是由类属性regex所匹配到的字符串,返回具体的python变量值,以供Django传递到对应的视图函数中
  • to_url(self, value)方法,和to_python方法相反,value是一个具体的Python变量值,返回其字符串,通常用于url反向引用.

  28.2 自定义path转化器应用实例.(使用register_converter 将其注册到URL配置中)

# app中新建文件urlconvert.pyz,自定义匹配规则

class MonConvert:

    regex = "[0-9]{2}"

    def to_python(self, value):
        return int(value)

    def to_url(self, value):
        return "%04d" % value
app中新建文件urlconvert.py
# 视图函数
def path_month(request, month):
    print(month)
    print(type(month))
    return HttpResponse("path_month...")
app下的views.py
from django.contrib import admin
from django.urls import path, register_converter  # register_converter注册自定义转化器

from app1.urlconvert import MonConvert
register_converter(MonConvert, "mm")  # 注册并自定义为名称为mm

from app1 import views

urlpatterns = [
    path(\'admin/\', admin.site.urls),
    path(\'timer/\', views.timer),
    path(\'login/\', views.login),
    # path("articles/<int:year>", views.path_year),  # path_year(request, 2001)
    path(\'articles/<mm:month>\', views.path_month),

]
全局urls.py

 

 

 

 

二十九. url控制总结

  29.1 简单配置

  29.2 有名分组 按关键字传参

  29.3 分发 include

  29.4 反向解析 利用别名

  29.5 名称空间

  29.6 path

 

三十. 视图层之请求对象

  参考:https://www.cnblogs.com/yuanchenqi/articles/8876856.html

   30.1 在视图函数中获取用户的请求数据

    先关闭settings.py中的一项设置项目才可正常运行

 

 

from django.contrib import admin
from django.urls import path
from app01 import views

urlpatterns = [
    path(\'admin/\', admin.site.urls),
    path(\'index/\', views.index),
]
全局urls.py
from django.shortcuts import render, HttpResponse


# Create your views here.


def index(request):
    print(request.GET)
    print(request.POST)
    print(request.method)
    return render(request, "index.html")
    # return HttpResponse(\'ok\')
app01下的views.py
<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>主页</title>
</head>
<body>
    <form action="" method="post">
        姓名:<input type="text" name="name">
        年龄:<input type="text" name="age">
        <input type="submit">
    </form>

</body>
</html>
模板文件index.html

    后台获取到了用户的输入,表单post请求:

 

 

    获取到的get请求数据,是个字典格式.

 

 

     获取值:

      request.GET.get("name")  request.POST.get("name")

  30.2 urls.py中匹配根目录,及访问地址是http://127.0.0.1:8000/

    re_path("^$", views.index) 或者 path(r"", views.index),

 

  30.3 方法request.get_full_path(),获取完成的路径可保护get数据

    下面来看看两个区别:

    print(request.path)  #只能获取路径

    print(request.get_full_path())  #路径+get请求的数据

    

    后台显示结果:

    

 

三十一. 视图响应对象

  31.1 最终都是返回HttpResponse(), render()也会转化为HttpResponse()

    31.1.1 HttpResponse("<h1>ok</h1>")

    31.1.2 render(request, "index.html", {"变量名": 变量值})  # 把变量传递给模板,注意书写字典格式

        模板文件index.html中使用{{ 变量名 }}  # 模板语法, 注意书写格式,双大括号

三十二. 模板语法之变量

  参考:https://www.cnblogs.com/yuanchenqi/articles/8876892.html

  32.1 字典,列表,对象,字符串,  等更丰富的数据类型怎么嵌入到模板中

    模板语法: 渲染变量用{{}}  用句点符深度查询, 过滤器

        渲染标签用{%%}

    使用句点符达到的效果就是从字典,列表等数据中再获取指定位置的值

def index(request):
    """
    模板语法:
    渲染变量用{{}}
        1. 深度查询 用句点符
        2. 过滤器
    渲染标签用:{%%}
    """
    # 各种类型的数据
    name = "yuan"
    i = 10
    l = [111, 222, 333]
    info = {\'name\': "yuan", "age": 22}
    b = True

    class Person:
        def __init__(self, name, age):
            self.name = name
            self.age = age
    alex = Person(\'alex\', 35)
    egon = Person(\'egon\', 33)
    person_list = [alex, egon]

    # return render(request, "index.html", {"name": name})
    return render(request, "index.html", locals())  # locals()传递所有的局部变量
app下views.py
<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>主页</title>
</head>
<body>

    <p> {{ name }}</p>
    <p> {{ i }}</p>
    <p> {{ info }}</p>
    <p> {{ l }}</p>
    <p> {{ alex }}</p>
    <p> {{ person_list }}</p>
    <p> {{ b }}</p>
    <hr>
    <p>深度查询,用句点符,获取具体值</p>
    <p>{{ info.name }}</p>
    <p>{{ l.2 }}</p>
    <p>{{ alex.age }}</p>
    <p> {{ person_list.1.name }}</p>
</body>
</html>
模板文件index.html

三十三. 过滤器

    参考:https://www.cnblogs.com/yuanchenqi/articles/8876892.html的第二部分过滤器

    把视图中的变量按照某种形式在模板中渲染出来

    33.1 语法:

{{ obj|filter_name:param }}

    33.2 日期过滤器date

      视图文件代码:

        import datetime

        date = datetime.datetime.now()

      模板文件代码:

        <p>{{ date }}</p>  显示效果:Jan. 17, 2021, 1:06 p.m.    (显示的不友好)

        <p>{{ date|date:\'Y-m-d\' }}</p>  显示效果:2021-01-17  这里date|date,两个date后面一个date是过滤器名称

      下图为显示效果对比:

         

 

    33.3 default过滤器,默认在数据为空的情况下的显示内容

      视图文件代码:  name_list = []   列表值为空

      模板文件代码:  <p>{{ name_list }}</P>      显示效果: []

              <p>{{ name_list|default:"姓名数据为空"}} </p>  显示效果:姓名数据为空

     33.4 length过滤器,返回值的长度

        {{ value|length }}  如果value是[\'a\', \'b\', \'c\'],那么输出是3

    33.5 filesizeformat,将值格式化为一个"人类可读"文件尺寸.主要用于显示文件的大小

        {{ value|filesizeformat }}  如果value是123456789,输出将会是117.7MB

    33.6 slice过滤器,将字符串切片

      {{ value|slice:"2:-1" }}  如果value="hello word",将输出llo wor

    33.7 truncatechars,截断过滤器,如果字符串字符多余指定的字符数量,那么会被截断.截断的字符串将以可翻译的省略号序列"..."结尾

      {{ value|truncatechars:9 }}  参数9,就是要显示(截断)的字符数包含...一共是9个字符,(注意一个字母就是一个字符)

      {{ hello_str|truncatewords:1 }}  truncatewords参数3,表示显示的单词数量,不包含...(注意hello表示一个单词)

      下图为显示效果:

      

 

    33.8 safe过滤器,强调不转义,用在变量是html标签时

      {{ value }}  如果value = \'<a href="#">click</a>\' 在浏览器中当成是字符串显示 <a href="#">click</a>

      {{ value|safe }}  如果value = \'<a href="#">click</a>\' 则可在浏览器中当成是html标签渲染出<a>标签的效果

        下图为显示效果对比:

         

 

    33.9 add加法过滤器

      {{ l.0|add:100}}  如果l.0 = 100,那么输出200

    33.10 upper转大写过滤器

      {{ name|upper }}  如果name = "hao",那么输出HAO

    import datetime
    date = datetime.datetime.now()

    name_lsit = []
    hello_str = \'hello word\'

    link_click = "<a href=\'#\'>click</a>"
视图函数
    <p>过滤器</p>
    <p>{{ date }}</p>
    <p>{{ date|date:\'Y-m-d\' }}</p>
    <p>{{ name_list|default:\'姓名数据为空\' }}</p>
    <p>{{ hello_str|slice:"2:-1" }}</p>
    <p>{{ hello_str|truncatechars:3 }}</p>
    <p>{{ hello_str|truncatewords:1 }}</p>
    <p>{{ link_click }}</p>
    <p>{{ link_click|safe }}</p>
过滤器

 

三十四. 模板语法之标签

  34.1 标签看起来像这样的:{% tag %}.标签比变量更加复杂:一些在输出中创建文本,一些通过循环或逻辑来控制流程,一些加载其后的变量将使用到的额外信息到模板中.一些标签需要开始和结束标签(例如{% tag %}...标签内容...{% endtag %})

  34.2 最少需要掌握for标签和if标签

  34.3 for循环标签

遍历每一个元素:

{% for person in person_list %}
    <p>{{ person.name }}</p>
{% endfor %}

可以利用{% for obj in list reversed %}反向完成循环。

遍历一个字典:

{% for key,val in dic.items %}
    <p>{{ key }}:{{ val }}</p>
{% endfor %}

如果{% for key in dic %}则只遍历key

注:循环序号可以通过{{forloop}}显示  

forloop.counter            The current iteration of the loop (1-indexed)从1开始,显示元素的下标
forloop.counter0           The current iteration of the loop (0-indexed)从0开始
forloop.revcounter         The number of iterations from the end of the loop (1-indexed)
forloop.revcounter0        The number of iterations from the end of the loop (0-indexed)
forloop.first              True if this is the first time through the loop
forloop.last               True if this is the last time through the loop

    

 

 

   

 

 

   34.4 for...empty数据为空时的处置.为空的时候不会渲染.当使用for...empty时就可以渲染指定的内容.

for 标签带有一个可选的{% empty %} 从句,以便在给出的组是空的或者没有被找到时,可以有所操作。

 

{% for person in person_list %}
    <p>{{ person.name }}</p>

{% empty %}
    <p>sorry,no person here</p>
{% endfor %}

  34.5 if标签

    {% if %}会对一个变量求值,如果它的值是“True”(存在、不为空、且不是boolean类型的false值),对应的内容块会输出。

复制代码
{% if num > 100 or num < 0 %}
    <p>无效</p>
{% elif num > 80 and num < 100 %}
    <p>优秀</p>
{% else %}
    <p>凑活吧</p>
{% endif %}
复制代码
模拟网站用户登录状态视图文件views.py
name = None
# name = "chendong"

 

 

 

 

    <h4>模板文件.当用户登录时显示问候和注销,当用户没有登录显示注册和登录</h4>
    {% if name %}
        <p>
            <a href="">hi {{ name }}</a>
            <a href="">注销</a>
        </p>
    {% else %}
        <p>
            <a href="">注册</a>
            <a href="">登录</a>
        </p>
    {% endif %}

 

 

 

 

 

 

   34.6 with标签,把长名字缩减成短名字

      {% with person_list.1.name as n %}

        {{ n }}     使用n就相当于用person_list.1.name,当深度查询非常长的时候用

      {% endwith%}

使用一个简单地名字缓存一个复杂的变量,当你需要使用一个“昂贵的”方法(比如访问数据库)很多次的时候是非常有用的

例如:

{% with total=business.employees.count %}
    {{ total }} employee{{ total|pluralize }}
{% endwith %}

  34.7 csrf_token这个标签用于跨站请求伪造保护

    此功能与全局settings.py中的\'django.middleware.csrf.CsrfViewMiddleware\'有关系

 

 

   在表单的post提交中会出现下面的提示Forbidden (403):

 

 

 

   如果在模板中添加csrf_token就不会出现Forbidden (403)错误了.

    <form action="" method="post">
        {% csrf_token %}
        <input type="text">
        <input type="submit">
    </form>

查看网页源代码,多出了一个input标签,多出了的input就是{% csrf_token %}的功劳.有了它就可以正确提交post请求.(识别是不是第二次访问,拦截判断用)

 

三十五. 模板语法之自定义标签与过滤器

   34.1 自定义标签与过滤器,流程固定,背下来

    

1、在settings中的INSTALLED_APPS配置当前app,不然django无法找到自定义的simple_tag.

2、在app中创建templatetags模块(模块名只能是templatetags)

3、创建任意 .py 文件,如:my_tags.py

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
from django import template
from django.utils.safestring import mark_safe
 
register = template.Library()   #register的名字是固定的,不可改变
 
 
@register.filter
def filter_multi(v1,v2):   # 自定义乘法过滤器
    return  v1 * v2
<br>
@register.simple_tag
def simple_tag_multi(v1,v2):  # 自定义乘法标签
    return  v1 * v2
<br>
@register.simple_tag
def my_input(id,arg):
    result = "<input type=\'text\' id=\'%s\' class=\'%s\' />" %(id,arg,)
    return mark_safe(result)

4、在使用自定义simple_tag和filter的html文件中导入之前创建的 my_tags.py

1
{% load my_tags %} 

5、使用simple_tag和filter(如何调用)

1
2
3
4
5
6
7
8
9
10
-------------------------------.html
{% load xxx %}  
      
# num=12
{{ num|filter_multi:2 }} #24
 
{{ num|filter_multi:"[22,333,4444]" }}
 
{% simple_tag_multi 2 5 %}  参数不限,但不能放在if for语句中
{% simple_tag_multi num 5 %}

注意:filter可以用在if等语句后,simple_tag不可以

1
2
3
{% if num|filter_multi:30 100 %}
    {{ num|filter_multi:30 }}
{% endif %}

 

   34.2 实例自定义乘法过滤器

    34.2.1 在settings.py中配置app

 

    34.2.2 在app中创建templatetags模块(模块名只能是templatetags)

    34.2.3 创建my_tag_filter .py 文件,目录如下:

from django import template

register = template.Library()

@register.filter
def multi_filter(x,y):
    # 自定义乘法过滤器
    return x*y

    34.2.4 在使用自定义multi_filter的html文件中导入之前创建的my_tag_filter .py

{% load my_tag_filter %}

    34.2.5 使用自定义multi_filter(如何调用)

    <p>{{ i|multi_filter:5 }}</p> 

 

 

 

 

 

   34.3 自定义标签相关代码

@register.simple_tag
def multi_tag(x, y, z):
    # 自定义乘法标签
    return x*y*z
    <h4>自定义标签调用</h4>
    <p>{% multi_tag i 5 2 %}</p>

三十六. 模板语法之继承

   36.1 {% include \'advertise.html\' %}在html模板文件中嵌入其它html模板文件

两个父子模板文件在目录中的位置

 

在index.html文件中的左侧嵌入advertise.html

 

 

 advertise.html文件全部代码

 

 最终效果,左侧三个面板为 {% include \'advertise.html\' %}嵌入的内容:

 

 

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>首页</title>
    <!-- 最新版本的 Bootstrap 核心 CSS 文件 -->
    <link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/bootstrap@3.3.7/dist/css/bootstrap.min.css"
          integrity="sha384-BVYiiSIFeK1dGmJRAkycuHAHRg32OmUcww7on3RYdg4Va+PmSTsz/K68vbdEjh4u" crossorigin="anonymous">
    <style type="text/css">
        * {
            margin: 0;
            padding: 0;
        }

        .header {
            width: 100%;
            height: 50px;
            background-color: #369;
        }
    </style>
</head>
<body>
<div class="header"></div>
<div class="container">
    <div class="row">
        <div class="col-md-3">
            {% include \'advertise.html\' %}
        </div>
        <div class="col-md-9">
            <h1>首页</h1>
            <h1>首页</h1>
            <h1>首页</h1>
            <h1>首页</h1>
            <h1>首页</h1>
            <h1>首页</h1>
            <h1>首页</h1>
            <h1>首页</h1>
            <h1>首页</h1>
            <h1>首页</h1>
        </div>
    </div>
</div>
index.html
<div class="action">
    <div class="panel panel-danger">
        <div class="panel-heading">Panel heading without title</div>
        <div class="panel-body">
            Panel content
        </div>
    </div>
    <div class="panel panel-warning">
        <div class="panel-heading">Panel heading without title</div>
        <div class="panel-body">
            Panel content
        </div>
    </div>
    <div class="panel panel-success">
        <div class="panel-heading">Panel heading without title</div>
        <div class="panel-body">
            Panel content
        </div>
    </div>
</div>
advertise.html

  36.2 继承.{% extends \'base.html\' %}

    建立一个新页面order.html该页面想和前面的index.html拥有相同的头部和左侧面板,这就用到了继承.

    新建base.html基础模板页面,作为父页面,复制index.html中的内容到该业中,该页面中,包含css,头部和左侧面板,内容部分被删除,代替以block盒子{% block con%}在这个中间加入该页面独特内容{% endblock %},代码如下:

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>首页</title>
    <!-- 最新版本的 Bootstrap 核心 CSS 文件 -->
    <link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/bootstrap@3.3.7/dist/css/bootstrap.min.css"
          integrity="sha384-BVYiiSIFeK1dGmJRAkycuHAHRg32OmUcww7on3RYdg4Va+PmSTsz/K68vbdEjh4u" crossorigin="anonymous">
    <style type="text/css">
        * {
            margin: 0;
            padding: 0;
        }

        .header {
            width: 100%;
            height: 50px;
            background-color: #369;
        }
    </style>
</head>
<body>
<div class="header"></div>
<div class="container">
    <div class="row">
        <div class="col-md-3">
            {% include \'advertise.html\' %}
        </div>
        <div class="col-md-9">
            {% block con %}
{#                此处为可扩展的盒子, 等待继承者在自己的页面中加入自己独特的内容#}
            {% endblock %}
        </div>
    </div>
</div>


</body>
</html>
base.html

    清空index.html页面,在页面中输入{% extends \'base.html\' %},意思是index.html页面继承了base.html.页面中block盒子{% block con%}此处添加index.html页面独特的内容{% endblock%}.

{#继承自base.html,包含头部,左侧等内容#}
{% extends \'base.html\' %}

{% block con %}
{#    index.html页面独特的内容#}
    <h1>首页内容区域</h1>
    <h1>首页内容区域</h1>
    <h1>首页内容区域</h1>
    <h1>首页内容区域</h1>
    <h1>首页内容区域</h1>
    <h1>首页内容区域</h1>
    <h1>首页内容区域</h1>
    <h1>首页内容区域</h1>
{% endblock %}
index.html

    新建订单页面orders.html,继承base.html页面,

{% extends \'base.html\' %}


{% block con %}
    <h1>订单页面</h1>
    <h1>订单页面</h1>
    <h1>订单页面</h1>
    <h1>订单页面</h1>
    <h1>订单页面</h1>
    <h1>订单页面</h1>
    <h1>订单页面</h1>
{% endblock %}
orders.html

    可以在base.html页面中留多个盒子,甚至<title></title>标签都可以设计为一个盒子.下面的代码是增加了一个控制标题的盒子{% block title %}.模板页面中可以添加多个盒子,以解决代码复用的问题.

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    {% block title %}
{#这里是标题的盒子,每个页面就可以有自己的标题#}
    {% endblock %}
    <!-- 最新版本的 Bootstrap 核心 CSS 文件 -->
    <link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/bootstrap@3.3.7/dist/css/bootstrap.min.css"
          integrity="sha384-BVYiiSIFeK1dGmJRAkycuHAHRg32OmUcww7on3RYdg4Va+PmSTsz/K68vbdEjh4u" crossorigin="anonymous">
    <style type="text/css">
        * {
            margin: 0;
            padding: 0;
        }

        .header {
            width: 100%;
            height: 50px;
            background-color: #369;
        }
    </style>
</head>
<body>
<div class="header"></div>
<div class="container">
    <div class="row">
        <div class="col-md-3">
            {% include \'advertise.html\' %}
        </div>
        <div class="col-md-9">
            {% block con %}
{#                此处为可扩展的盒子, 等待继承者在自己的页面中加入自己独特的内容#}
            {% endblock %}
        </div>
    </div>
</div>


</body>
</html>
base.html
{#继承自base.html,包含头部,左侧等内容#}
{% extends \'base.html\' %}

{#标题盒子#}
{% block title %}
    <title>网站首页</title>
{% endblock %}

{% block con %}
{#    index.html页面独特的内容#}
    <h1>首页内容区域</h1>
    <h1>首页内容区域</h1>
    <h1>首页内容区域</h1>
    <h1>首页内容区域</h1>
    <h1>首页内容区域</h1>
    <h1>首页内容区域</h1>
    <h1>首页内容区域</h1>
    <h1>首页内容区域</h1>
{% endblock %}
index.html
{% extends \'base.html\' %}


{% block title %}
    <title>订单页面</title>
{% endblock %}

{% block con %}
    <h1>订单页面</h1>
    <h1>订单页面</h1>
    <h1>订单页面</h1>
    <h1>订单页面</h1>
    <h1>订单页面</h1>
    <h1>订单页面</h1>
    <h1>订单页面</h1>
{% endblock %}
orders.html

  36.3 如何显示父模板的盒子{% block %}中的内容(默认子模板会覆盖父模板盒子总的内容),在子模板中的盒子内加入{{ block.super }}

        <div class="col-md-9">
            {% block con %}
{#                此处为可扩展的盒子, 等待继承者在自己的页面中加入自己独特的内容#}
                <h1>这是base.html中盒子里显示的内容</h1>
            {% endblock %}
        </div>
base.html
{% block con %}
{#    index.html页面独特的内容#}
    {{ block.super }}
    <h1>首页内容区域</h1>
    <h1>首页内容区域</h1>
    <h1>首页内容区域</h1>
    <h1>首页内容区域</h1>
    <h1>首页内容区域</h1>
    <h1>首页内容区域</h1>
    <h1>首页内容区域</h1>
    <h1>首页内容区域</h1>
{% endblock %}
index.html

 

   36.4 注意事项

  • 如果你在模版中使用 {% extends %} 标签,它必须是模版中的第一个标签。其他的任何情况下,模版继承都将无法工作。

  • 在base模版中设置越多的 {% block %} 标签越好。请记住,子模版不必定义全部父模版中的blocks,所以,你可以在大多数blocks中填充合理的默认内容,然后,只定义你需要的那一个。多一点钩子总比少一点好。

  • 如果你发现你自己在大量的模版中复制内容,那可能意味着你应该把内容移动到父模版中的一个 {% block %} 中。

  • If you need to get the content of the block from the parent template, the {{ block.super }} variable will do the trick. This is useful if you want to add to the contents of a parent block instead of completely overriding it. Data inserted using {{ block.super }} will not be automatically escaped (see the next section), since it was already escaped, if necessary, in the parent template.

  • 为了更好的可读性,你也可以给你的 {% endblock %} 标签一个 名字 。例如:

    1
    2
    3
    {% block content %}
    ...
    {% endblock content %}  

    在大型模版中,这个方法帮你清楚的看到哪一个  {% block %} 标签被关闭了。

  • 不能在一个模版中定义多个相同名字的 block 标签。

 

三十七. ORM概念简介

参考:https://www.cnblogs.com/yuanchenqi/articles/8933283.html

  37.1 Django的几大部分

    url控制器,视图部分,模板部分,模型层

  37.2 模型层用于和数据库打交道.ORM就是用来控制数据库的.

    ORM直接把python中的类转换为(翻译)mysql语句,以后我们直接编写Python类就可以控制数据库了.

 

 

 

 

 注意ORM只能对表做操作,不能对库做操作.

#sql中的表                                                      

 #创建表:
     CREATE TABLE employee(                                     
                id INT PRIMARY KEY auto_increment ,                    
                name VARCHAR (20),                                      
                gender BIT default 1,                                  
                birthday DATA ,                                         
                department VARCHAR (20),                                
                salary DECIMAL (8,2) unsigned,                          
              );


  #sql中的表纪录                                                  

  #添加一条表纪录:                                                          
      INSERT employee (name,gender,birthday,salary,department)            
             VALUES   ("alex",1,"1985-12-12",8000,"保洁部");               

  #查询一条表纪录:                                                           
      SELECT * FROM employee WHERE age=24;                               

  #更新一条表纪录:                                                           
      UPDATE employee SET birthday="1989-10-24" WHERE id=1;              

  #删除一条表纪录:                                                          
      DELETE FROM employee WHERE name="alex"                             





#python的类
class Employee(models.Model):
     id=models.AutoField(primary_key=True)
     name=models.CharField(max_length=32)
     gender=models.BooleanField()
     birthday=models.DateField()
     department=models.CharField(max_length=32)
     salary=models.DecimalField(max_digits=8,decimal_places=2)


 #python的类对象
      #添加一条表纪录:
          emp=Employee(name="alex",gender=True,birthday="1985-12-12",epartment="保洁部")
          emp.save()
      #查询一条表纪录:
          Employee.objects.filter(age=24)
      #更新一条表纪录:
          Employee.objects.filter(id=1).update(birthday="1989-10-24")
      #删除一条表纪录:
          Employee.objects.filter(name="alex").delete()
View Code

 

 

三十八. 单表操作之生成表模型

 参考:https://www.cnblogs.com/yuanchenqi/articles/8933283.html

   38.1 使用pycharm创建app01和orm数据库的Book表

    38.1.1 使用pycharm新建orm项目app01应用程序,使用cmd命令行创建orm数据库

      如果下载Django速度很慢,参考:https://www.cnblogs.com/lldbyt/p/14286952.html

      cmd下连接mysql,进入mysql命令提示符,输入创建orm库的命令,完成数据库创建.

      create database orm;

    38.1.2 在orm文件夹下的settings.py中修改DATABASES数据库设置如下:

DATABASES = {
    \'default\': {
        \'ENGINE\': \'django.db.backends.mysql\',
        \'NAME\': \'orm\',  # 要连接的数据库, 连接前需要创建好
        \'USER\': "root",  # 连接数据库的用户名
        "PASSWORD": "",  # 连接数据库的密码
        "HOST": "127.0.0.1",  # 连接主机, 默认本机
        "PORT": 3306  # 端口, 默认3306
    }
}
全局settings.py中修改数据库设置

    38.1.3 在orm文件夹下的__init__.py中添加pymysql模块的相关代码

import pymysql
pymysql.version_info = (1, 4, 13, "final", 0)  # 防止Django版本引起错误
pymysql.install_as_MySQLdb()
orm下__init__.py文件中添加pymysql模块

    38.1.4 在app01应用程序目录下的models.py文件中建立表的结构

class Book(models.Model):
    id = models.AutoField(primary_key=True)  # 自增, 主键
    title = models.CharField(max_length=32)  # 字符串,最大32
    pub_date = models.DateField()  # 日期
    price = models.DecimalField(max_digits=8, decimal_places=2)  # 123456.78 整数部分6位,小数部分2位,共计8位
    publish = models.CharField(max_length=32)
数据库orm中与应用程序app01相关的book表的创建类

    38.1.5 在pycharm的Terminal命令提示窗口使用下面的两条命令,系统会依照models.py文件中建立表的结构创建出表,及其字段结构.

      python manage.py makemigrations  #依照models.py中的类创建出表, 创建迁移表

      python manage.py migrate  # 迁移

      

 

       

 

 

    38.1.6 创建出的表,查看效果如下:

      

    38.1.7 如果想在orm转换过程中把对应的sql语句输出在后台,需要在settings中进行如下配置:

LOGGING = {
    \'version\': 1,
    \'disable_existing_loggers\': False,
    \'handlers\': {
        \'console\':{
            \'level\':\'DEBUG\',
            \'class\':\'logging.StreamHandler\',
        },
    },
    \'loggers\': {
        \'django.db.backends\': {
            \'handlers\': [\'console\'],
            \'propagate\': True,
            \'level\':\'DEBUG\',
        },
    }
}
View Code

 

三十九. 单表操作之添加记录

  39.1 ORM主要用于增删改查数据记录

  39.2 添加一条表记录的方法,相关代码写在视图层文件中.

    两种方法,一种是实例化类,一种的类的.objects.create方法

from django.shortcuts import render, HttpResponse
from app01.models import Book  # 引入创建的表的类

# Create your views here.


def index(request):

    # 添加表记录方法一
    # book_obj = Book(id=1, title=\'python红宝书\', price=100, pub_date=\'2012-12-12\', publish=\'人民出版社\')  # 实例化Book对象
    # book_obj.save()  # 实例化对象的方法时,只有save一下数据才会保存到数据库中

    # 添加表记录方法二
    book_obj = Book.objects.create(title=\'linux从入门到精通\', price=80, pub_date=\'2013-01-25\', publish=\'人民邮电出版社\')  # 此种方法无需再用save
    print(book_obj.title)  # .objects方法创建的,带有返回值,可以将值打印出来.
    print(book_obj.price)
    print(book_obj.pub_date)
    print(book_obj.publish)

    return HttpResponse("ok")
app01下的views.py

 

四十. 单表操作之查询API

参考:https://www.cnblogs.com/yuanchenqi/articles/8933283.html查询表记录

  40.1 单表查询接口,需要了解清楚的

    QuerySet数据类型,Django特有的数据类型,该类型有点像列表,可以想列表一样来处理QuerySet,支持索引,支持for循环

    弄清楚查询表的接口的,1每个方法的返回值  2以及每个方法是谁来调用的

    在app01下的models.py中的Book类中添加def __str__(self)方法用于print打印指定的数据

# app01下的models.py
from django.db import models

# Create your models here.


class Book(models.Model):
    id = models.AutoField(primary_key=True)
    title = models.CharField(max_length=32)
    pub_date = models.DateField()
    price = models.DecimalField(max_digits=8, decimal_places=2)
    publish = models.CharField(max_length=32)

    def __str__(self):  # 此处功能是,在执行print命令时显示对象的title
        return self.title
app01下的models.py

 

  40.2 all方法: 返回值是一个queryset对象, 该方法由Book.objects管理器直接调用

    all方法返回全部数据,all方法源代码:

from django.shortcuts import render, HttpResponse
from app01.models import Book

# Create your views here.


def index(request):

    # 查询表记录, all方法
    book_qs = Book.objects.all()
    print(book_qs)
    print(\'-\')
    # 遍历
    for obj in book_qs:
        print(obj.title, obj.price)  # 输出书名,和价格
    print(\'-\')
    # 索引访问
    print(book_qs[1].title)  # 输出第二本书书名

    return HttpResponse(\'OK\')
app01下的views.py

    后台输出效果:

  40.3 firsh, last方法,调用者是queryset对象, 返回值是一个model对象

    # 查询记录,first, last方法, 调用者是queryset对象, 返回值是一个model对象
    book_obj = Book.objects.all().first()
    book_obj = Book.objects.all()[0] # 和上面的等价
    book_obj = Book.objects.last()
View Code

  40.4 filter()筛选(过滤), 根据条件筛选,  返回值:queryset对象. 该方法与all()方法类似. filter()方法也可以调用first()方法

    # 查询记录, filter()  筛选, 返回:queryset对象
    # filter()与all()的差别就是多个筛选条件,别的都一样.
    book_list = Book.objects.filter(price=80)  # 筛选出价格是80的所有记录
    print(book_list)  # [obj1, obj2, ...]
    book_obj = Book.objects.filter(price=80).first()
View Code

  40.5 get() 有且只有一个查询结果时才有意义, 有多个结果或没有查到时会报错. 返回值:models对象

    # 查询记录, get() 有且只有一个查询结果时才有意义, 返回值:model对象
    book_obj = Book.objects.get(price=100)
    # book_obj = Book.objects.get(price=80)  # 因为有两本书价格是80,所以浏览器上会显示报错信息
    print(book_obj.title)
View Code

  40.6 本节app01下的views.py完整代码,包含上面4个常用的查询方法

from django.shortcuts import render, HttpResponse
from app01.models import Book

# Create your views here.


def index(request):

    # 插入记录,方法一
    # book_obj = Book(title=\'python从入门到精通\', price=100, pub_date=\'2018-02-21\', publish=\'人民出版社\')
    # book_obj.save()

    # 插入记录,方法二
    # book_obj = Book.objects.create(title=\'python自动化\', price=80, pub_date=\'2019-05-02\', publish=\'清华大学出版社\')
    # print(book_obj.title)
    # print(book_obj.publish)

    # 查询表记录, all方法
    # book_qs = Book.objects.all()
    # print(book_qs)
    # print(\'-\')
    # # 遍历
    # for obj in book_qs:
    #     print(obj.title, obj.price)  # 输出书名,和价格
    # print(\'-\')
    # # 索引访问
    # print(book_qs[1].title)  # 输出第二本书书名

    # 查询记录,first, last方法, 调用者是queryset对象, 返回值是一个model对象
    # book_obj = Book.objects.all().first()
    # book_obj = Book.objects.all()[0] # 和上面的等价
    # book_obj = Book.objects.last()

    # 查询记录, filter()  筛选, 返回:queryset对象
    # filter()与all()的差别就是多个筛选条件,别的都一样.
    # book_list = Book.objects.filter(price=80)  # 筛选出价格是80的所有记录
    # print(book_list)  # [obj1, obj2, ...]
    # book_obj = Book.objects.filter(price=80).first()

    # 查询记录, get() 有且只有一个查询结果时才有意义, 返回值:model对象
    book_obj = Book.objects.get(price=100)
    # book_obj = Book.objects.get(price=80)  # 因为有两本书价格是80,所以浏览器上会显示报错信息
    print(book_obj.title)

    return HttpResponse(\'OK\')
View Code

 

四十一. 单表操作之查询API-2

  41.1 exclude()排除,方法,相当于!=不等于, 所有符合条件的,然后取反.与filter()方法相反.注意:可以设置多个条件(多个条件是与的关系)

    返回值是一个queryset对象, 该方法由Book.objects管理器直接调用

    # exclude()排除 ,返回值是:queryset对象. 调用者:Book.objects管理器
    book_list = Book.objects.exclude(title=\'php\', price=80)  # 可以设置多个排除条件,多个条件是与关系,也就是同时满足.filter()方法也是可以设多个条件的.
    print(book_list)
View Code

  41.2 order_by()排序, 调用者:queryset对象, 返回值:queryset对象

    # order_by()排序
    ret = Book.objects.all().order_by(\'id\')  # 按ID升序排列
    ret = Book.objects.all().order_by(\'-id\')  # 条件前面加负号,按ID降序排列
    ret = Book.objects.all().order_by(\'price\', \'id\')  # 先按照price排序, 如何price一样,则按照id排序
    print(ret)
View Code

  41.3 count()计数,调用者:queryset对象,返回值:int类型

    # count()
    ret = Book.objects.all().count()
    print(ret)
View Code

  41.4 exist()判断表是否为空,调用者:queryset对象,返回值:布尔值True,False

    # exist()判断是否为空
    # ret = Book.objects.all()  # 这种判断是否为空的方法太消耗资源(执行查询全部数据)
    ret = Book.objects.all().exists()  # exists()方法只查询表中的一条记录,所以节约资源
    if ret:
        print(\'ok\')
View Code

  41.5 queryset对象支持链式,多个方法串起来

四十二. 单表操作之查询API-3

  42.1 values()方法, 获取对象中每个元素的值,输出列表套字典,返回值:queryset类型, 调用者:queryset类型

    ret = Book.objects.all().values("price")  # values方法获取每个元素的值, 默认可以省略all(),写成Book.objects.values("price")
    print(ret)
    print(ret[0].get(\'price\'))
    # 输出:<QuerySet [{\'price\': Decimal(\'100.00\')}, {\'price\': Decimal(\'80.00\')}, {\'price\': Decimal(\'80.00\')}]>
    """
    valuers()方法的工作原理
    temp = []
    for obj in Book.objects.all():
        temp.append({
        \'price\': obj.price
        })
    :return temp
    """
View Code

  42.2 values_list()方法,类似values()方法,输出的是列表套元组,返回值:queryset类型, 调用者:queryset类型

    # values_list()方法
    ret = Book.objects.all().values_list(\'price\')  # 这里只演示了一个price字段, 可以有多个字段, values()方法也可以有多个字段
    print(ret)
    # 输出:<QuerySet [(Decimal(\'100.00\'),), (Decimal(\'80.00\'),), (Decimal(\'80.00\'),)]>
View Code

  42.3 distinct()去重, 配合values(),values_list()方法使用.返回值:queryset类型, 调用者:queryset类型

    # distinct()去重, 返回值:queryset类型, 调用者:queryset类型
    ret = Book.objects.all().values(\'price\').distinct()  # 获得图书的价格,去除相同价格的.
    print(ret)
    # 输出:<QuerySet [{\'price\': Decimal(\'100.00\')}, {\'price\': Decimal(\'80.00\')}]>
View Code

  42.4 reverse():对查询结果反向排序.返回值:queryset类型, 调用者:queryset类型

    注意该方法需要配合order_by()方法使用,该方法是个鸡肋.这个方法只有在已经定义顺序的queryset上调用,才能对结果反向排序

    # reverse()反向排序, 鸡肋方法,没什么鸟用.
    ret = Book.objects.all().filter(price=80).order_by(\'id\')
    ret2 = Book.objects.all().filter(price=80).order_by(\'id\').reverse()
    print(ret)
    print(ret2)
View Code

 

  42.4 queryset对象支持链式,多个方法串起来

      Book.objects.all().filter().order_by().filter().reverse().first()

    

四十三. 单表操作之模糊查询

  大于小于的时候就需要用模糊查询, 用双下划线. 注意得到的仍然是queryset类型

  43.1 参数price__gt=80表示价格大于80

    参数price__lt=200表示价格小于200

    ret = Book.objects.filter(price__gt=80, price__lt=200)  # 查询价格大于80,且小于200的图书
    print(ret)
View Code

  43.2 参数title__startswith=\'py\'表示标题以py开头的

    参数title__contains=\'h\'表示标题中包含字母h的

    参数title__icontains=\'h\'表示标题中包含字母h的,不区分大写

    ret = Book.objects.filter(title__startswith=\'py\')  # 查询书名以py开头的所有书
    ret = Book.objects.filter(title__contains=\'h\')  # 查询书名中包含h的所有书
    ret = Book.objects.filter(title__icontains=\'h\')  # 查询书名中包含h的所有书,不区分大小写
    print(ret)
View Code

  43.4 参数price__in=[80, 100, 200]表示只要是价格等于80或100或200

    参数price__range=[80, 100]查询价格在80到100之间的,包含80和100

    ret = Book.objects.filter(price__in=[80, 100, 200]) #查询只要是价格等于80或100或200的
    ret = Book.objects.filter(price__range=[80, 100]) #查询价格在80到100之间的,包含80和100
    print(ret)
View Code

  43.5 参数pub_date__year=2018查询日期是2018年的,月份和日随意, 只有日期类型有此参数

    参数pub_date__month=5查询月份是5月的.

    ret = Book.objects.filter(pub_date__year=2021, pub_date__month=1)  # 查询年份是2018且月份是1月的
    print(ret)
View Code

  43.6 不止以上的这些个参数哦

 

四十四. 单表操作之删除与修改操作

  44.1 delete()方法,删除记录. 返回值是删除的数据条数

    delete()方法的调用对象是queryset对象, model对象也可以调用它.

    # 删除记录
    # ret = Book.objects.filter(price=100).delete()  # queryset对象调用delete()
    # print(ret)
    # 返回值:(1, {\'app01.Book\': 1})

    Book.objects.filter(price=80).first().delete()  # model对象调用delete()
View Code

  44.2 update()方法,修改记录. update()的调用者只能是queryset对象.

    # 修改记录
    Book.objects.filter(title=\'PHP\').update(title=\'php零基础入门\')  # 注意,筛选出多少条记录,就会修改多少条.返回值是条数
View Code

 

四十七. 实例:创建简单图书管理系统

  系统功能简介:可以新建图书,查看图书数据,删除图书,修改图书信息.

    

 

 

 

   

 

 

  1. 建立数据库的表结构,即在models.py文件中设计Book表的结构,只要创建一个python Book类就可以了.

  2. 迁移数据库,即通过pycharm底部Terminal的,输入两条迁移命令.这里数据库使用了Django的默认sqlite3数据库.迁移完成后能看到在根目录中创建了db.sqlite3的数据库文件,pycharm窗口的的右侧可以打开查看db.效果如下,book表结构清晰可见:

    (如果数据库未能显示出创建的book表,则有可能是pycharm连接sqlite3的驱动未安装(右侧db下有暗红色线),点击右侧的db,点击数据库连接测试按钮,就会自动安装驱动.解决办法参考:https://www.cnblogs.com/lldbyt/p/14360650.html)

    

 

  3. 由于本系统需要用到bootstrap等,所以需要对文件目录进行修改

    3.1 新增静态目录(包)static,用于存放bootstrap, 在settings.py中增加static的设置

    3.2 复制bootstrap到static目录中

  4. 由于数据库中暂时没有数据,所有,下面,进行\'新增图书\'的设计.

    4.1 建立\'新增图书的\'路由设置

    4.2 建立\'新增图书\'的视图创建

    4.3 创建\'新增图书\'的模板,注意在表单部分的最前面增加{% csrf_token %},防止跨域403错误

      模板注意使用class="container"制作外框,class="row"   class="col-md-6 col-lg-push-3(右移3)"建立模型

      <input type="submit" class="btn btn-success(按钮造型) pull-right(移到右边)">

      <a class="btn btn-info(按钮造型)" href="/addbook/">新增图书</a>(新增图书按钮,把a标签变成按钮)

  5. 所有\'图书\'信息查看的设计,基本步骤和4一致.注意图书出版日期的显示{{ book.pub_date|date:\'Y-m-d\' }}

  6. \'删除\'功能.注意.注意路由设计re_path(r\'books/(\d+)/delete/\', views.delbook),

    注意删除按钮的html代码:<a href="books/{{ book.id }}/delete/" class="btn btn-danger">删除</a>

  7. 修改功能,注意点和6一样


  8.完整代码:

from django.db import models

# Create your models here.


class Book(models.Model):
    id = models.AutoField(primary_key=True)
    title = models.CharField(max_length=32)
    pub_date = models.DateField()
    price = models.DecimalField(max_digits=8, decimal_places=2)
    publish = models.CharField(max_length=32)
models.py
from django.contrib import admin
from django.urls import path, re_path
from app01 import views

urlpatterns = [
    path(\'admin/\', admin.site.urls),
    path(\'addbook/\', views.addbook),
    path(\'books/\', views.books),
    re_path(r\'books/(\d+)/delete/\', views.delbook),
    re_path(r\'books/(\d+)/edit/\', views.editbook),
]
urls.py
from django.shortcuts import render, HttpResponse, redirect  # redirect重定向
from app01.models import Book

# Create your views here.

def addbook(request):

    if request.method == "POST":
        title = request.POST.get(\'title\')
        pub_date = request.POST.get(\'pub_date\')
        price = request.POST.get(\'price\')
        publish = request.POST.get(\'publish\')

        Book.objects.create(title=title, pub_date=pub_date, price=price, publish=publish)
        return redirect(\'/books/\')  # 重定向前的斜杠代表根目录

    return render(request, \'app01/addbook.html\')

def books(request):
    boos = Book.objects.all()
    return render(request, \'app01/books.html\', locals())

def delbook(request, id):
    Book.objects.filter(id=id).delete()
    return redirect(\'/books/\')  # 重定向前的斜杠代表根目录

def editbook(request, id):
    book_obj = Book.objects.filter(id=id).first()

    if request.method == "POST":
        title = request.POST.get(\'title\')
        pub_date = request.POST.get(\'pub_date\')
        price = request.POST.get(\'price\')
        publish = request.POST.get(\'publish\')

        Book.objects.filter(id=id).update(title=title, pub_date=pub_date, price=price, publish=publish)
        return redirect(\'/books/\')  # 重定向前的斜杠代表根目录

    return render(request, \'app01/editbook.html\', {\'book_obj\': book_obj})
views.py
<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>新增图书</title>
    <link rel="stylesheet" href="/static/bootstrap/css/bootstrap.css">
    <style type="text/css">
        .container{
            margin-top: 100px;
        }
        .btn{
            margin-top: 10px;
        }
    </style>
</head>
<body>

    <h2>新增图书</h2>
    <div class="container">
        <div class="row">
            <div class="col-md-6 col-lg-push-3">
                <form action="" method="post">
                    {% csrf_token %}
                    <div>
                        <label>图书名称</label>
                        <input type="text" class="form-control" name="title">
                    </div>
                    <div>
                        <label>出版日期</label>
                        <input type="date" class="form-control" name="pub_date">
                    </div>
                    <div>
                        <label>图书价格</label>
                        <input type="text" class="form-control" name="price">
                    </div>
                    <div>
                        <label>出版社</label>
                        <input type="text" class="form-control" name="publish">
                    </div>
                    <div>
                        <input type="submit" class="btn btn-success pull-right">
                    </div>
                </form>


            </div>
        </div>

    </div>

</body>
</html>
addbook.html
<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>所有图书</title>
    <link rel="stylesheet" href="/static/bootstrap/css/bootstrap.css">
    <style type="text/css">
        .container{
            margin-top: 100px;
        }
        .btn{
            margin-top: 10px;
        }
    </style>
</head>
<body>

    <h2>所有图书</h2>
    <div class="container">
        <div class="row">
            <div class="col-md-6 col-lg-push-3">
                <div>
                    <a class="btn btn-info" href="/addbook/">新增图书</a>
                </div>
                <table class="table">
                    <thead>
                        <tr>
                            <td>序号</td>
                            <td>图书名称</td>
                            <td>出版日期</td>
                            <td>价格</td>
                            <td>出版社</td>
                            <td></td>
                            <td></td>
                        </tr>
                    </thead>
                    <tbody>
                        {% for book in boos %}
                            <tr>
                                <td>{{ book.id }}</td>
                                <td>{{ book.title }}</td>
                                <td>{{ book.pub_date|date:\'Y-m-d\' }}</td>
                                <td>{{ book.price }}</td>
                                <td>{{ book.publish}}</td>
                                <td><a href="books/{{ book.id }}/delete/" class="btn btn-danger">删除</a></td>
                                <td><a href="books/{{ book.id }}/edit/" class="btn btn-success">修改</a></td>
                            </tr>
                        {% endfor %}

                    </tbody>
                </table>


            </div>
        </div>

    </div>

</body>
</html>
books.html
<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>修改图书信息</title>
    <link rel="stylesheet" href="/static/bootstrap/css/bootstrap.css">
    <style type="text/css">
        .container{
            margin-top: 100px;
        }
        .btn{
            margin-top: 10px;
        }
    </style>
</head>
<body>

    <h2>修改图书信息</h2>
    <div class="container">
        <div class="row">
            <div class="col-md-6 col-lg-push-3">
                <form action="" method="post">
                    {% csrf_token %}
                    <div>
                        <label>图书名称</label>
                        <input type="text" class="form-control" name="title" value="{{ book_obj.title }}">
                    </div>
                    <div>
                        <label>出版日期</label>
                        <input type="date" class="form-control" name="pub_date" value="{{ book_obj.pub_date|date:\'Y-m-d\' }}">
                    </div>
                    <div>
                        <label>图书价格</label>
                        <input type="text" class="form-control" name="price" value="{{ book_obj.price }}">
                    </div>
                    <div>
                        <label>出版社</label>
                        <input type="text" class="form-control" name="publish" value="{{ book_obj.publish }}">
                    </div>
                    <div>
                        <input type="submit" class="btn btn-success pull-right">
                    </div>
                </form>


            </div>
        </div>

    </div>

</body>
</html>
editbook.html

  完整目录结构:

    

 

 

五十一. 数据库表关系之一对多

参考:https://www.cnblogs.com/yuanchenqi/articles/8963244.html  Django模型层(2)

  51.1 多表,为啥要用多个表?

    避免存储大量重复数据.把大表拆成多个表.

  51.2 一对多表,一个出版社对应多本书.

    

 

  51.3 总结:一旦确定表关系是一对多:创建关联字段,上图的publish_id是关联字段,创建关联字段要在多的表中

     

 

 

五十二. 数据库表关系之多对多

参考:https://www.cnblogs.com/yuanchenqi/articles/8963244.html  Django模型层(2)

 

    52.1 多对多表,一本书对应多个作者,一个作者对应多本书.

        

 

   52.2 总结:一旦确定表关系是多对多:要创建第三张关系表,如上图Book2Author

    

 

 

五十三. 数据库表关系之一对一

    

 

  53.1 一对一表,本质是把一张表中的几个字段抽离出来组建一个新表.

    一对多与一对一的差别.一对一的关联字段加上unique约束.关联字段放在哪张表都可以.

  53.2 总结: 一旦确定是一对一的关系: 在两张表中的任意一张表中建立关联字段+unique

    

 

五十四. 数据库表关系之关联字段与外键约束

     建关联关系是为了查询,建约束关系是为了维护数据的完整.

    约束关系存在的目的, 删除被关联表的一条数据,如果两表只有关联字段,无约束关系,则会直接删除.如果有约束关系,则无法直接删除,会有提示,无法删除.

     

 

     如上表,如果删除publish表中的第1条数据,则再查下时将无法查出php书籍的出版社信息.所以增加了约束关系,使得操作者无法随意删除.

    若只有关联关系而无约束关系,就可以直接删除.

 

五十五. 数据库表关系之sql创建关联表

    

     上图的publsish_id字段是两表的关联字段,上图的foreign key就是建立约束关系

     

 

 

五十六. ORM生成表模型

 参考:https://www.cnblogs.com/yuanchenqi/articles/8963244.html 多表操模型

下转本课笔记第二部分:    第六模块: WEB框架开发之Django框架开发学习笔记2

 

 

 

 

 

 

  

  

    

 

      

      

 

 

 

 

 

 

 

 

 

 

 

 

 

for...empty

创建任意 .py 文件,如:my_tags.py

objects.create

分类:

技术点:

相关文章: