Django老男孩笔记

Django基础篇

  • 1. 浏览器和服务器的通信过程

注意:
web通信的时候必须遵循HTTP协议,而HTTP协议的请求和响应是有固定的格式的.比如,我们现在写一个简单的web应用,打印出浏览器的请求体,如果只是单纯的发送二进制,在浏览器那边访问的时候会出现如下的页面

import socket

# 1. 生成socket实例对象
sk = socket.socket()
# 2. 绑定IP地址和端口
sk.bind(('127.0.0.1', 8000))
# 3. 设置监听
sk.listen()

# 循环等待客户端的连接
while 1:
    conn, addr = sk.accept()
    # 接收客户端发来的消息
    data = conn.recv(4096)
    print(data)  # 打印一下浏览器发送的消息是什么
    '''
    b'GET / HTTP/1.1\r\n  //请求行
    Host: 127.0.0.1:8000\r\nConnection: keep-alive\r\nUpgrade-Insecure-Requests: 1\r\n
    User-Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/72.0.3626.109 Safari/537.36\r\n
    Accept: text/html,application/xhtml+xml,application/xml;q=0.9,image/webp,image/apng,*/*;q=0.8\r\n
    Accept-Encoding: gzip, deflate, br\r\nAccept-Language: zh-CN,zh;q=0.9\r\n
    # 中间全部是请求头
    \r\n'   这里是空行
    
    //请求体没有,没有请求体.
    '''
    # 给客户端回消息
    conn.send(b'qnmd')

    # 关闭连接
    conn.close()
    sk.close()

HTTP之请求消息Request

第一部分:

<请求行> 用来说明请求类型,要访问的资源路径,以及使用的协议版本
GET / HTTP/1.1\r\n 请求方法(GET) 请求的URL(/这里省略了域名) HTTP/1.1(请求协议) 回车符换行符

第二部分:

<请求头> 紧接着第一行之后的部分,用来说明服务器要使用的附加信息
Host: 127.0.0.1:8000\r\nConnection: keep-alive\r\n
Upgrade-Insecure-Requests: 1\r\n
User-Agent: Mozilla/5.0 (Windows NT 10.0;)/r/n
Accept: text/html,application/xhtml+xml,application/xml;q=0.9,image/webp,image/apng,/;q=0.8\r\n
Accept-Encoding: gzip, deflate, br\r\nAccept-Language: zh-CN,zh;q=0.9\r\n

第三部分:

<空行> 请求头后面的空行是必须的
即使后面没有请求体,空行也是必须的 \r\n

第四部分:

<请求主体> 可以添加任意的其他数据
name=Professional%20Ajax&publisher=Wiley


HTTP之响应消息Response

一般情况下,服务器接收并处理客户端发送过来的请求后会返回一个HTTP的响应消息
HTTP响应也由四部分构成:状态行,响应头部,空行和响应正文

第一部分:

状态行 HTTP协议版本 状态码 状态描述 \r\n

第二部分:

响应头部 客户端可以使用的一些信息

第三部分

空行 响应头和响应体之间必须有一个空行

第四部分

响应体 比如HTML文件,或者是其他的一些信息.

上面的例子由于只是发了二进制文件,并没有按照规定的格式发送,所以浏览器会报网站有错误.如何解决呢?
解决的根本途径就是按照它们的格式发送对应的响应体

按照HTTP协议的响应格式,发送响应,并且规定写清楚响应体的具体的类型.(text/html)和使用的编码(charset=utf-8)

import socket

# 1. 生成socket实例对象
sk = socket.socket()
# 2. 绑定IP地址和端口
sk.bind(('127.0.0.1', 8000))
# 3. 设置监听
sk.listen()

# 循环等待客户端的连接
while 1:
    conn, addr = sk.accept()
    # 接收客户端发来的消息
    data = conn.recv(4096)
    print(data)  # 打印一下浏览器发送的消息是什么
    '''
    b'GET / HTTP/1.1\r\n  //请求行
    Host: 127.0.0.1:8000\r\nConnection: keep-alive\r\nUpgrade-Insecure-Requests: 1\r\n
    User-Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/72.0.3626.109 Safari/537.36\r\n
    Accept: text/html,application/xhtml+xml,application/xml;q=0.9,image/webp,image/apng,*/*;q=0.8\r\n
    Accept-Encoding: gzip, deflate, br\r\nAccept-Language: zh-CN,zh;q=0.9\r\n
    # 中间全部是请求头
    \r\n'   这里是空行
    
    //请求体没有,没有请求体.
    '''
    # 给客户端回消息
    # 先按照固定的格式发送响应  状态行  响应头部  空行  响应体(响应正文)
    # 下面指定响应体的格式类型是html格式的.
    conn.send(b'HTTP/1.1 200 OK\r\ncontent-type:text/html; charset=utf-8\r\n\r\n')
    # 这里就是浏览器上想要显示的正文
    conn.send(b"

I am fine and fuck you!

") # 关闭连接+ conn.close() sk.close()

  • 2. 根据不同的路径url返回不同的内容
    思路: 就是首先获取请求数据,然后按照格式获取到请求的路径,然后根据路径的不同做出不同的响应
import socket

# 1. 生成socket实例对象
sk = socket.socket()
# 2. 绑定IP地址和端口
sk.bind(('127.0.0.1', 8000))
# 3. 设置监听
sk.listen()

# 循环等待客户端的连接
while 1:
    conn, addr = sk.accept()
    # 接收客户端发来的消息
    data = conn.recv(4096)
    print(data)  # 打印一下浏览器发送的消息是什么
    '''
    b'GET / HTTP/1.1\r\n  //请求行
    Host: 127.0.0.1:8000\r\nConnection: keep-alive\r\nUpgrade-Insecure-Requests: 1\r\n
    User-Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/72.0.3626.109 Safari/537.36\r\n
    Accept: text/html,application/xhtml+xml,application/xml;q=0.9,image/webp,image/apng,*/*;q=0.8\r\n
    Accept-Encoding: gzip, deflate, br\r\nAccept-Language: zh-CN,zh;q=0.9\r\n
    # 中间全部是请求头
    \r\n'   这里是空行
    
    //请求体没有,没有请求体.
    '''
    # 首先是获取请求的url是什么
    # 先给数据转换为字符串
    str_data = str(data, encoding='utf-8')
    # 然后进行分割,分成两部,先取到请求行,然后再将请求行按照空格进行分割,找到请求路径
    url = str_data.split('\r\n')[0].split(' ')[1]

    print(url)

    # 给客户端回消息
    # 先按照固定的格式发送响应  状态行  响应头部  空行  响应体(响应正文)
    # 下面指定响应体的格式类型是html格式的.
    conn.send(b'HTTP/1.1 200 OK\r\ncontent-type:text/html; charset=utf-8\r\n\r\n')
    # 这里就是浏览器上想要显示的正文
    if url == '/yimi/':
        response = '

Hello yimi

' elif url == '/xiaohei/': response = '

Hello xiaohei

' else: response = '

404 not found!

' conn.send(bytes(response, encoding='utf-8')) # 关闭连接+ conn.close()
  • 根据不同的路径,返回不同的内容,函数版本.
    可以将不同的路径下做出的响应封装为一个函数,这样的话做的事情就更多了,但是最终也是返回一个二进制的字符串.
import socket

# 1. 生成socket实例对象
sk = socket.socket()
# 2. 绑定IP地址和端口
sk.bind(('127.0.0.1', 8000))
# 3. 设置监听
sk.listen()

def yimi(url):
    return 'hello {}'.format(url)

def xiaohei(url):
    return 'hello {}'.format(url)

def notFound(url):
    return '404 not found'


# 循环等待客户端的连接
while 1:
    conn, addr = sk.accept()
    # 接收客户端发来的消息
    data = conn.recv(4096)
    print(data)  # 打印一下浏览器发送的消息是什么
    '''
    b'GET / HTTP/1.1\r\n  //请求行
    Host: 127.0.0.1:8000\r\nConnection: keep-alive\r\nUpgrade-Insecure-Requests: 1\r\n
    User-Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/72.0.3626.109 Safari/537.36\r\n
    Accept: text/html,application/xhtml+xml,application/xml;q=0.9,image/webp,image/apng,*/*;q=0.8\r\n
    Accept-Encoding: gzip, deflate, br\r\nAccept-Language: zh-CN,zh;q=0.9\r\n
    # 中间全部是请求头
    \r\n'   这里是空行
    
    //请求体没有,没有请求体.
    '''
    # 首先是获取请求的url是什么
    # 先给数据转换为字符串
    str_data = str(data, encoding='utf-8')
    # 然后进行分割,分成两部,先取到请求行,然后再将请求行按照空格进行分割,找到请求路径
    url = str_data.split('\r\n')[0].split(' ')[1]

    print(url)

    # 给客户端回消息
    # 先按照固定的格式发送响应  状态行  响应头部  空行  响应体(响应正文)
    # 下面指定响应体的格式类型是html格式的.
    conn.send(b'HTTP/1.1 200 OK\r\ncontent-type:text/html; charset=utf-8\r\n\r\n')
    # 这里就是浏览器上想要显示的正文
    if url == '/yimi/':
        response = yimi(url)
    elif url == '/xiaohei/':
        response = xiaohei(url)
    else:
        response = notFound(url)
    conn.send(bytes(response, encoding='utf-8'))
    # 关闭连接+
    conn.close()

这样还不是很好,要写很多的if else语句,可以用另外一种实现,将访问的路由url和对应的处理函数用一个元组对应起来,
然后将很多个这样的对应关系放到一个列表中.然后遍历列表,根据路由找到对应的处理函数即可

路由和函数对应关系版web应用

1>建立和客户端的通信(sockt编程)
2>客户端连接,服务端收到客户端的请求.
3>按照格式解析客户端的请求,获取到请求的路径
4>按照路径和函数的对应关系,获取对应的要调用的处理函数
5>处理函数进行对应的处理,然后返回要响应的内容,服务器再发送给客户端,客户端想要请求的内容

import socket

# 1. 生成socket实例对象
sk = socket.socket()
# 2. 绑定IP地址和端口
sk.bind(('127.0.0.1', 8000))
# 3. 设置监听
sk.listen()


def yimi(url):
    return 'hello {}'.format(url)


def xiaohei(url):
    return 'hello {}'.format(url)


def notFound(url):
    return '404 not found'


# 循环等待客户端的连接
while 1:
    conn, addr = sk.accept()
    # 接收客户端发来的消息
    data = conn.recv(4096)
    print(data)  # 打印一下浏览器发送的消息是什么
    '''
    b'GET / HTTP/1.1\r\n  //请求行
    Host: 127.0.0.1:8000\r\nConnection: keep-alive\r\nUpgrade-Insecure-Requests: 1\r\n
    User-Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/72.0.3626.109 Safari/537.36\r\n
    Accept: text/html,application/xhtml+xml,application/xml;q=0.9,image/webp,image/apng,*/*;q=0.8\r\n
    Accept-Encoding: gzip, deflate, br\r\nAccept-Language: zh-CN,zh;q=0.9\r\n
    # 中间全部是请求头
    \r\n'   这里是空行
    
    //请求体没有,没有请求体.
    '''
    # 首先是获取请求的url是什么
    # 先给数据转换为字符串
    str_data = str(data, encoding='utf-8')
    # 然后进行分割,分成两部,先取到请求行,然后再将请求行按照空格进行分割,找到请求路径
    url = str_data.split('\r\n')[0].split(' ')[1]

    print(url)

    # 先将路由和处理函数的对应关系写到一个列表中
    url_funcs = [
        ('/yimi/', yimi),
        ('/xiaohei/', xiaohei),
    ]

    # 遍历路由和函数的列表,根据不同的路由,获取不同的出去函数
    for i in url_funcs:
        if url == i[0]:
            func = i[1]
            break
    else:
        func = notFound

    response = func(url)

    # 给客户端回消息
    # 先按照固定的格式发送响应  状态行  响应头部  空行  响应体(响应正文)
    # 下面指定响应体的格式类型是html格式的.
    conn.send(b'HTTP/1.1 200 OK\r\ncontent-type:text/html; charset=utf-8\r\n\r\n')
    # 这里就是浏览器上想要显示的正文

    conn.send(bytes(response, encoding='utf-8'))
    # 关闭连接+
    conn.close()

返回具体的html文件的web服务端示例
将返回的字符串换成具体的html文件

import socket

# 1. 生成socket实例对象
sk = socket.socket()
# 2. 绑定IP地址和端口
sk.bind(('127.0.0.1', 8000))
# 3. 设置监听
sk.listen()


def yimi(url):
    with open('yimi.html','rb') as f:
        ret = f.read()
    return ret


def xiaohei(url):
    with open('xiaohei.html','rb') as f:
        ret = f.read()
    return ret


def notFound(url):
    return b'404 not found'


# 循环等待客户端的连接
while 1:
    conn, addr = sk.accept()
    # 接收客户端发来的消息
    data = conn.recv(4096)
    print(data)  # 打印一下浏览器发送的消息是什么
    '''
    b'GET / HTTP/1.1\r\n  //请求行
    Host: 127.0.0.1:8000\r\nConnection: keep-alive\r\nUpgrade-Insecure-Requests: 1\r\n
    User-Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/72.0.3626.109 Safari/537.36\r\n
    Accept: text/html,application/xhtml+xml,application/xml;q=0.9,image/webp,image/apng,*/*;q=0.8\r\n
    Accept-Encoding: gzip, deflate, br\r\nAccept-Language: zh-CN,zh;q=0.9\r\n
    # 中间全部是请求头
    \r\n'   这里是空行
    
    //请求体没有,没有请求体.
    '''
    # 首先是获取请求的url是什么
    # 先给数据转换为字符串
    str_data = str(data, encoding='utf-8')
    # 然后进行分割,分成两部,先取到请求行,然后再将请求行按照空格进行分割,找到请求路径
    url = str_data.split('\r\n')[0].split(' ')[1]

    print(url)

    # 先将路由和处理函数的对应关系写到一个列表中
    url_funcs = [
        ('/yimi/', yimi),
        ('/xiaohei/', xiaohei),
    ]

    # 遍历路由和函数的列表,根据不同的路由,获取不同的出去函数
    for i in url_funcs:
        if url == i[0]:
            func = i[1]
            break
    else:
        func = notFound

    response = func(url)

    # 给客户端回消息
    # 先按照固定的格式发送响应  状态行  响应头部  空行  响应体(响应正文)
    # 下面指定响应体的格式类型是html格式的.
    conn.send(b'HTTP/1.1 200 OK\r\ncontent-type:text/html; charset=utf-8\r\n\r\n')
    # 这里就是浏览器上想要显示的正文

    conn.send(response)
    # 关闭连接+
    conn.close()

上面我们返回的都是静态网页,至于动态网页是怎么实现的呢?

动态网页的实现,本质上是字符串的替换.事先在html文件中,将需要动态展示的地方,有一个字符串进行标记.
然后服务器端读取这个html文件,当需要动态展示的时候,用replace方法将原来占位标记的字符串替换为需要展示
的字符串,然后返回给客户端.

import socket

# 1. 生成socket实例对象
sk = socket.socket()
# 2. 绑定IP地址和端口
sk.bind(('127.0.0.1', 8000))
# 3. 设置监听
sk.listen()

import time


def yimi(url):
    with open('yimi.html', 'r', encoding='utf-8') as f:
        ret = f.read()
        ret = ret.replace('@@xx@@', time.ctime())
    return bytes(ret, encoding='utf-8')


def xiaohei(url):
    with open('xiaohei.html', 'rb') as f:
        ret = f.read()
    return ret


def notFound(url):
    return b'404 not found'


# 循环等待客户端的连接
while 1:
    conn, addr = sk.accept()
    # 接收客户端发来的消息
    data = conn.recv(4096)
    print(data)  # 打印一下浏览器发送的消息是什么
    '''
    b'GET / HTTP/1.1\r\n  //请求行
    Host: 127.0.0.1:8000\r\nConnection: keep-alive\r\nUpgrade-Insecure-Requests: 1\r\n
    User-Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/72.0.3626.109 Safari/537.36\r\n
    Accept: text/html,application/xhtml+xml,application/xml;q=0.9,image/webp,image/apng,*/*;q=0.8\r\n
    Accept-Encoding: gzip, deflate, br\r\nAccept-Language: zh-CN,zh;q=0.9\r\n
    # 中间全部是请求头
    \r\n'   这里是空行
    
    //请求体没有,没有请求体.
    '''
    # 首先是获取请求的url是什么
    # 先给数据转换为字符串
    str_data = str(data, encoding='utf-8')
    # 然后进行分割,分成两部,先取到请求行,然后再将请求行按照空格进行分割,找到请求路径
    url = str_data.split('\r\n')[0].split(' ')[1]

    print(url)

    # 先将路由和处理函数的对应关系写到一个列表中
    url_funcs = [
        ('/yimi/', yimi),
        ('/xiaohei/', xiaohei),
    ]

    # 遍历路由和函数的列表,根据不同的路由,获取不同的出去函数
    for i in url_funcs:
        if url == i[0]:
            func = i[1]
            break
    else:
        func = notFound

    response = func(url)

    # 给客户端回消息
    # 先按照固定的格式发送响应  状态行  响应头部  空行  响应体(响应正文)
    # 下面指定响应体的格式类型是html格式的.
    conn.send(b'HTTP/1.1 200 OK\r\ncontent-type:text/html; charset=utf-8\r\n\r\n')
    # 这里就是浏览器上想要显示的正文

    conn.send(response)
    # 关闭连接+
    conn.close()

socket服务端功能划分

  • 1. 负责socket通信,收发消息
  • 2. 根据用户访问的路径执行对应的视图函数
  • 3. 从HTML文件中读取内容,并且完成字符串的替换

常见的框架分类,以及包含哪些部分.

  • Django框架
    Django框架是一个大而全的框架,基本上我们进行web开发用到的东西,Django框架都有.
    它是自带了路由视图函数模块和模板处理模块,用了第三方的socket通信模块.

  • Tornado框架
    Tornado框架以上所有的功能都是自带的,没有使用第三方模块

  • Flask框架
    Flask框架自带路由视图函数对应关系,而是用的第三方socket通信模块以及模板语言模块.

WSGI

简介
WSGI(Web Server Gateway Interface,web 服务器网关接口)是python语言中所定义的web服务器和web应用程序之间或框架之间的通信接口标准.类似一种协议和规定.

为什么需要WSGI
我们在web开发的过程中,我们经常会把常用的功能封装起来,例如Django,Flask框架.但是服务器端和应用程序端需要相互配合才能给用户提供服务,而不同的框架会有不同的实现方式,所以它需要一种标准,让服务器和客户端都遵循这种标准,那么两者就能很好的配合使用了.

WSGI就是一种接口标准,类似于协议.它是服务器和客户端的一种约定,规定了各自使用的接口和功能,以便它们能够很好的配合.

WSGI将Web组件分成了三类

1> Web服务器(WSGI server)
2> Web中间件(WSGI middleware)
3> Web应用程序(WSGI Applacation)

Web server接收HTTP的请求,封装所需要的一系列环境变量.按照WSGI接口标准调用注册的WSGI Application,然后将处理结果返回给客户端

web应用的本质过程

1> 用户输入网址,浏览器发送HTTP请求
2> 服务器收到浏览器的请求,按照格式解析对应的URL,也就是请求的资源位置
3> 服务器根据url,调用对应的处理函数,生成对应的HTML文档
4> 服务器端把HTML文档作为HTTP响应的body发送给浏览器
5> 浏览器收到服务器端的响应,从HTTP body取出HTML文档进行显示

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

我们python的标准库wsgiref模块代替我们写的web框架的socket server部分.
主要分成以下几步

1> 定义不同的url对应的视图处理函数
2> 定义url和要执行的函数的对应关系
3> 定义一个run_server(environ,start_response)函数,在里面社会响应的头部信息,以及根据url获取对应的要执行的函数.
然后返回一个列表[response,]
4> 主函数调用make_server,设置服务器的地址和端口以及一开始要运行的函数接口. 再调用server_forever()启动服务器.

# encoding:utf-8
__author__ = 'Fioman'
__time__ = '2019/2/19 14:26'
import time
from wsgiref.simple_server import make_server


# 将url对应的处理程序封装成函数
def yimi(url):
    with open('yimi.html', 'r', encoding='utf-8') as f:
        s = f.read()
        s = s.replace('@@xx@@', time.ctime())
    return bytes(s, encoding='utf-8')


def xiaohei(url):
    with open('xiaohei.html', 'r', encoding='utf-8') as f:
        s = f.read()
    return bytes(s, encoding='utf-8')


# 定义个url和要执行的函数之间的对应关系
url_funcs = [
    ('/yimi/', yimi),
    ('/xiaohei/', xiaohei),
]


# 定义run_server函数
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_funcs:
        if i[0] == url:
            func = i[1]
            break

    if func:
        response = func(url)
    else:
        response = b'404 not found'

    return [response, ]


if __name__ == '__main__':
    httpd = make_server('127.0.0.1', 8000, run_server)
    print('我在8000端口等你哦')
    httpd.serve_forever()

Django基础知识

初始创建一个Django项目的时候的目录介绍

settings.py 文件中内容介绍
1. 路径相关

2.Django基础必备三件套

  • HttpResponse
    内部传入一个字符串参数,返回给浏览器
    例如:
def index(request):
    # 业务逻辑代码
    return HttpResponse("OK")
  • render
    除了request参数外还接受一个待渲染的模板文件和一个保存具体数据的字典参数.
    将数据填充进模板文件,最后把结果返回给浏览器.(类似于我们上面用到的jinja2)
    例如:
def index(request):
    # 业务逻辑代码
    return render(request, "index.html", {"name": "alex", "hobby": ["烫头", "泡吧"]})
  • redirect
    接收一个URL参数,表示跳转到指定的URL.重定向
def index(request):
    # 业务逻辑代码
    return redirect("/home/")

重定向是怎么回事

标准意义上的“重定向”指的是HTTP重定向,它是HTTP协议规定的一种机制。这种机制是这样工作的:当client向server发送一个请求,要求获取一个资源时,在server接收到这个请求后发现请求的这个资源实际存放在另一个位置,于是server在返回的response中写入那个请求资源的正确的URL,并设置reponse的状态码为301(表示这是一个要求浏览器重定向的response),当client接受到这个response后就会根据新的URL重新发起请求。重定向有一个典型的特症,即:当一个请求被重定向以后,最终浏览器上显示的URL往往不再是开始时请求的那个URL了。这就是重定向的由来.

MVC和MTV模式的介绍

MVC模式指的是Model(模型),View(视图),Controller(控制)
1)最上面一层,是直接面向最终用户的视图层.它提供给用户的操作界面,是程序的外壳
2)最底下一层,是核心的数据层(Model),也就是程序需要操作的数据或信息
3)中间的一层,就是控制层,它负责根据用户从视图层输入的指令,选取数据层中的数据,然后对其进行相关的操作,产生最终的结果

Django框架的设计借鉴了MVC框架的思想,也是分成三部分. 它和MVC的不同之处是它分成了MTV模式
Model(模型): 负责业务对象与数据库对象(ORM)
Template(模板): 负责如何把页面展示给用户
View(视图): 负责业务逻辑,并在适当的时候调用Model和Template

APP
一个django项目可以分成很多个app,用来隔离不同功能模块的代码

命令行创建
python manage.py startapp app01

pycharm创建
打开manage.py任务管理器,然后输入 startapp app01就可以创建

创建以后一定要注册

ORM

ORM概念

对象关系映射(object relationship mapping对象关系模型),通过对象和数据库之间建立一种映射关系,将程序中数据方便的存储到数据库中的一种技术.

ORM的由来
几乎所有的软件开发过程中都会涉及到对象和关系数据库.在用户层面和业务逻辑层面,我们是面向对象的.当对象的信息发生变化的时候,我们就需要将数据的信息保存到数据库中.而按照传统的做法是要写很多重复的SQL语句,这样开发效率就会很低.所以Django开发了ORM,用对象的操作来代替SQL语句的操作

ORM的优点
ORM解决的主要问题是对象和关系的映射.通常一个类对应一张数据表,类的每个实例对应表中的一条记录,而类的属性对相应表中的字段.
不用直接编写SQL语句,让程序员专注于业务逻辑,提高了开发效率.

ORM的缺点
ORM的缺点是一定程序上牺牲了程序的执行效率.并且需要记忆这个特殊的语法,如果ORM使用的多了,SQL语句的操作的能力就会有所退化.

ORM能做哪些事
1.操作数据表 创建表/删除表/修改表
操作models.py里面的类
2.操作数据行 数据的增删改查
不能做的事情是,不能创建数据库,需要手动创建数据库

使用Django的ORM的详细步骤

  • 1.手动创建一个数据库mysite
create database mysite;
  • 2.在settings.py中配置数据库
  • 3.告诉Django,用pymysql代替默认的MySQLdb 在项目目录的init.py中
import pymysql

# 告诉Django用pymysql代替默认的MySQLdb
pymysql.install_as_MySQLdb()
  • 4. 在app下的models.py中创建一个类,这个类必须继承自models.Model
class UserInfo(models.Model):
    id = models.AutoField(primary_key=True)  # 创建一个自增的主键字段
    name = models.CharField(null=False, max_length=32)  # 创建一个varchar(32)类型的不能为空的字段

    # 当打印这个类的值得时候会调用这个方法
    def __str__(self):
        return "<{}-{}>".format(self.id, self.name)
  • 5.打开manage.py的任务管理器,执行两条命令.
    1.makemigrations
    2.migrate

ORM的相关操作
表的查询操作
1. 查询所有的数据,返回一个列表

2. 为一个表添加一条数据

3. 删除一条数据
我们删除数据的时候,要知道删除的是哪一条,需要在url中以参数的形式传递过去.

而这个id的值,在后台是可以通过如下的方式获取的

4. 修改数据
修改的时候一样要知道修改的是哪一条,,需要在url中以参数的形式传递过去.

 编辑出版社的视图函数
def edit_publisher(request):
    if request.method == 'POST':
        # 如果是post请求,用户已经编辑完成提交过来,我们要知道提交的是哪个出版社
        # 1.先获取是哪个出版社
        edit_id = request.POST.get('id', None)
        # 2.根据id获取要编辑的对象
        edit_obj = models.Publisher.objects.get(id=edit_id)
        # 3.修改对象的值,取到对应的属性,直接赋值即可,寄到保存.修改的时候要保存才能在数据库中生效
        # 而创建和删除的时候不用保存
        new_name = request.POST.get('publisher_name', None)
        edit_obj.name = new_name
        edit_obj.save()

        return redirect('/publisher_list/')

    # 当是get请求的时候,只是要显示编辑界面,这个时候我们可以将要编辑的出版社先显示在编辑界面上,通过url的参数,获取id
    edit_id = request.GET.get('id', None)
    # 根据id获取要编辑的出版社对象
    edit_obj = models.Publisher.objects.get(id=edit_id)

    return render(request, 'edit_publisher.html', {'publisher': edit_obj})

修改数据的思路
1> 首先是获取要修改的模型的orm对象
2> 然后通过属性赋值,修改字段的值
3> 修改之后一定要保存,才能在数据库中生效

ORM的多表关联
当是一对多的关系时,一般在多的表中建立外键,Django带的ORM会自动的在字段的后面加_id,所以我们在创建字段的时候
不需要在后面加上_id

下面是一个ORM中多表关联的一个示例

# 书 和出版社的关系是多对一的关系
class Book(models.Model):
    id = models.AutoField(primary_key=True)
    title = models.CharField(null=False,unique=True,max_length=64) # 书名
    publisher = models.ForeignKey(to='Publisher') # 外键,表示的是出版社对象,而在数据库存储的是publisher_id

在前端页面book_obj.publisher获取的是出版社对象,而book_obj.publisher_id获取的才是出版社id,关键的外键id值

如果数据库中已经有了数据,这个时候我们要在models.py中添加新的字段,如果没有设定默认值.会出现如下情况

数据库表的多对多的关系,利用第三张表建立关联

ORM中创建表的时候,可以指定为多对多关系,Django的ORM会自动生成第三张关联的表

多对多的关系在使用的时候,可以从一个对象,获取和它关联的对象.因为是多对多,所以要用all来获取,他的对象列表

当用form表单提交的是单个数据的时候,我们在后端获取这个值一般用get('name属性'),但是如果是提交了多个值,并且在后端要一次性获取这些值得时候,就不能用get,而应该用getlist()

还有一点就是多对多的关系表中,怎么创建一条记录.创建一条记录的时候,如果把跟它关联的那张表中值,保存到数据库中

你可能感兴趣的:(Django老男孩笔记)