Django

新建

支持Python的版本–3.5,3.6,3.7,3.8·在线安装
虚拟机执行sudo pip3 install django==3.2.12 检查是否成功sudo pip3 freezelgrep -i ‘Django’

1
2
3
tarena@tedu :~$ sudo pip3 freeze|grep -i 'Django '
Django==2.2.12
django-admin startproject HelloWorld

有该输出代表成功

·离线安装–官网下载离线安装包
·将安装包复制到虚拟机
·解压缩tar -xvf Django-2.2.12.tar.gz·进入目录cd Django-2.2.12
·执行安装sudo python3 setup.py install
·检查是否成功sudo pip3 freeze|grep -i ‘Django’

启动服务

启动[测试开发阶段]

  • 1.终端cd进入到项目文件夹,例如cd mysite1
  • 2.进入到项目文件夹后,执行python3 manage.py runserver 端口启动django服务【注:该启动方式下,Django在前台启动服务,默认监听8000端口】
  • 3.浏览器访问http://127.0.0.1:8000可看到django的启动页面注:【如果想更换端口,则可以用python.3 manage.py runserver端口号】

·关闭服务
方式1:在runserver启动终端下执行Ctrl +c可关闭Django服务·

方式2:在其他终端下执行sudo lsof -i:8000查询出Django的进程id执行kill -9对应Django进程id

使用python manage.py查看可以启动的服务

settings.py包含了Django项目启动的所有配置项·配置项分为公有配置和自定义配置
·配置项格式例:(BASE_DIR’xxx‘)变量名一定要大写

公有配置- jango官方提供的基础配置

文件配置

settings文件

1
2
3
4
BASE_DIR#项目的路径
DEBUG = True #表示处于开发模式False上线模式
ALLOWED_HOSTS = []#请求头,使用网站域名
TIME_ZONE = "Asia/Shanghai"

BASE_DIR
用于绑定当前项目的绝对路径(动态计算出来的)这个路径是…/项目文件名,所有文件
夹都可以依懒此路径
DEBUG
用于配置Django项目的启动模式,取值:
True表示开发环境中使用开发调试模式(用于开发中). False表示当前项目运行在生产环境中

  • INSTALLED_APPS -指定当前项目中安装的应用列表
  • MIDDLEWARE -用于注册中间件
  • TEMPLATES -用于指定模板的配置信息
  • DATABASES -用于指定数据库的配置信息
  • LANGUAGE_CODE-用于指定语言配置

ALLOWED HOSTS
示例:如果要在局域网其它主机也能访问此主机的Django服务,启动方式如下:
python3 manage.py runserver
0.0.0.0:5000
指定网络设备如果内网环境下其他主机想正常访问该站点,
需加ALLOWED_HOSTS =['内网ip]

ROOT_URLCONF -用于配置主url配置’mysite1.urls’.

设置中文(在settings文件中)

1
2
3
4
5
from django.utils.translation import gettext_lazy as _
LANGUAGES = [
('zh-Hans', _('Chinese')),
]
LANGUAGE_CODE = "zh-Hans"

LANGUAGE_CODE = “zh-Hans”

URL文件(Uniform Resource Locator统一资源定位符)

1
2
3
4
5
6
7
8
9
from django.contrib import admin
from django.urls import path
from . import views#导入当前目录下的views文件

urlpatterns = [
path('admin/', admin.site.urls),
path('',views.main),#网站首页
path('page/',views.函数名)#在views文件中定义的函数名不能带括号
]

url分配地址

path()函数

  • 导入- from django.urls.import path
  • 语法- path(route, views, name=None)

参数:

  • route:字符串类型,匹配的请求路径
  • views:指定路径所对应的视图处理函数的名称
  • name:为地址起别名,在模板地址反向解析时使用

在同名的文件夹下面新建一个视图文件views.py

1
2
3
4
5
from django.http import HttpResponse

def page_one(request):
html="<h1>this is a page<h2>"
return HttpResponse(html)

path转换器
语法:<转换器类型:自定义名>
作用:若转换器类型匹配到对应类型的数据,则将数据按照关键字传参的方式传递给视图函数
例子:path(‘page/<int:page>’, views.xxx)

转化器类型 作用 案例 可匹配
str 匹配除了’/'之外的非空字符串 page/<str:user> page/Bloss
int 匹配0或任何整数,返回int型 page/<int:num> page/10
slug 匹配任意由ASCII字母或数字以及连字符和下划线组成的短标签 page/<slug:sl> page/im-am
path 匹配任意由ASCII字母或数字以及连字符和下划线组成的短标签 page/<path:ph> page/1/v/

在url文件中:

1
path('page/<int:num1>/<str:way>/<int:num2>', views.page_num),

在views文件中

1
2
3
4
def page_num(request, num1, way, num2):
if way == "add":
html = f"<h1> {num1} {way}{num2}={num1 + num2}<h1>"
return HttpResponse(html)

三个参数传入view文件中的page_num函数

re_path()函数
在url的匹配过程中可以使用正则表达式进行精确匹配

语法:
re_path(reg, view, name=xxx)

正则表达式为命名分组模式(?Ppattern);匹配提取参数后用关键字传参方式传递给视图函数

1
2
3
from django.urls import path, re_path

re_path(r'',view)

请求及相应

序号 方法 描述
1 GET 请求指定的页面信息,并返回实体主体。
2 HEAD 类似于get请求,只不过返回的响应中没有具体的内容,用于获取报头
2 POST 向指定资源提交数据进行处理请求(例如提交表单或者上传文件)。数据被包含在请求体中。POST请求可能会导致新的资源的建立和/或已有资源的修改。
4 PUT 从客户端向服务器传送的数据取代指定的文档的内
5 DELETE 请求服务器删除指定的页面。
6 CONNECT HTTP/1.1协议中预留给能够将连接改为管道方式的代理服务器。
7 OPTIONS 允许客户端查看服务器的性能。
8 TRACE 回显服务器收到的请求,主要用于测试或诊断。

Django中的请求

path_info: URL字符串
method:字符串,表示HTTP请求方法,常用值:‘GET’、“POST”
GET: QueryDict查询字典的对象,包含get请求方式的所有数据
POST: QueryDict查询字典的对象,包含post请求方式的所有数据
FILES:类似于字典的对象,包含所有的上传文件信息

在views文件中

1
2
3
4
5
6
def methond(request):
print(request.method)
print(request.path_info)
print(request.GET)
print(request.get_full_path)
return HttpResponse("this is test")

例如GET方法,当浏览器中传入参数时www.***.com/methond?a=8

1
2
3
4
5
>>>
GET
/methond
<QueryDict: {'a': ['8']}>
<bound method HttpRequest.get_full_path of <WSGIRequest: GET '/methond?a=8'>>

COOKIES: Python字典,包含所有的cookie,键和值都为字符串

session:似于字典的对象,表示当前的会话

body:字符串,请求体的内容(POST或PUT)

scheme:请求协议(‘http’/‘https’)

request.get_full_path():请求的完整路径

request.META:请求中的元数据(消息头)

  • request.META[‘REMOTE ADDR’]:客户端IP地址

  • 200请求成功

  • 301永久重定向-资源(网页等)被永久转移到其它URL-

  • 302 临时重定向

  • 404-请求的资源(网页等)不存在-

  • 500内部服务器错误

构造函数格式:
HttpResponse(content=响应体, content_type=响应体数据类型, status=状态码)
作用:
向客户端浏览器返回响应,同时携带响应体内容

  • 'text/html’(默认的,html文件)

  • 'text/plain’(纯文本)

  • ‘text/css’ (css文件)

  • ‘text/javascript’ (js文件)

  • 'multipart/form-data’(文件提交)

  • ‘application/json’ (json传输)- ‘application/xml’ (xml文件)数据类型, status=状态码)

  • 类型 作用 状态码
    HttpResponseRedirect 重定向 302
    HttpResponseNotModified 未修改 304
    HttpResponseBadRequest 错误请求 400
    HttpResponseNotFound 无对应资源 404
    HttpResponseForbidden 请求被禁止 403
    HttpResponseServerError 服务器错误 500
1
2
3
from django.http import HttpResponse,HttpResponseRedirect#使用的上面的表格内容
def anchor(request):
return HttpResponseRedirect('/admin/')

GET&POST

通常在views中先进行判断使用的哪个方法

1
2
3
4
5
6
7
8
def re_get(request):
if request.method == "GET":
print(request.GET["a"])
elif request.method == "POST":
pass
else:
pass
return HttpResponse("this is get test")

在浏览器中输入www.***.com/methond?a=8,则在终端中会打印出1,如果没有参数传入则会报错

使用request.GET.get方法可以解决无参数传入不报错的情况,输出no

1
2
3
4
5
6
7
8
def re_get(request):
if request.method == "GET":
print(request.GET.get("a","no"))#注意括号
elif request.method == "POST":
pass
else:
pass
return HttpResponse("this is get test")

在又多个相同参数名传入不同参数时,使用GET.get.getlist()。应用场景时同一个键值传入多个数据

1
2
3
4
5
6
7
8
def re_get(request):
if request.method == "GET":
print(request.GET.getlist("a"))#注意括号
elif request.method == "POST":
pass
else:
pass
return HttpResponse("this is get test")

在浏览器中输入www.***.com/methond?a=8&a=50s

1
>>['1', '50']

使用post接收传入的数据

1
2
3
4
5
6
7
8
9
10
POST_FORM = """
<form method="POST" action="/test_get_post">
用户名:<input type="text" name="username"
<input type="submit" value="提交">
</form>
"""
def re_get(request):
if request.method == "POST":
print(request.POST["username"])
else:

CSRF请求终端,防止CSRF网络攻击。在setting中注释掉csrf

1
2
-禁止掉 settings.py
中MIDDLEWARE中的'django.middleware.csrf.CsrfViewMiddleware'的中间件

传统MVC代表 Model-View-Controller(模型-视图-控制器)模式。
√M模型层(Model),主要用于对数据库层的封装
√V视图层(View),用于向用户展示结果(WHAT+ HOW )
√C控制(Controller ,用于处理请求、获取数据、返回结果(重要)

作用:降低模块耦合度

django的MTV代表 Model-Template-View(模型-模板-视图)模式。
√ M模型层(Model)负责与数据库交互
√T模板层(Template)负责呈现内容到浏览器(HOW)
√V视图层(View)是核心,负责接收请求、获取数据、返回结果(WHAT)作用:降低模块间的耦合度(解耦)

模板

创建模板文件夹<项目名>/templates在settings.py 中TEMPLATES配置项
1.BACKEND:指定模板的引擎

  1. DIRS:模板的搜索目录(可以是一个或多个)
  2. APP_DIRS∶是否要在应用中的templates 文件夹中搜索模板文件
  3. OPTIONS:有关模板的选项
    ·配置项中需要修改部分
    设置DIRS - 'DIRS": [os.path.join(BASE_DIR, ‘templates’)],
1
2
'DIRS": [os.path.join(BASE_DIR, 'templates')],
'DIRS': [BASE_DIR / 'templates'], #两种方法

templates创建在根目录下

方案1-通过 loader 获取模板,通过HttpResponse进行响应在视图函数中

1
2
3
4
5
from django.template import loader
def get_template(request):
t = loader.get_template("index.html")#引入模板
html = t.render()#加载模板
return HttpResponse(html)

方案2-使用render()直接加载并响应模板

在视图函数中:

1
2
3
from django.shortcuts import render
def get_template(request):
return render(request, "index.html",字典)

视图函数中可以将Python变量封装到字典中传递到模板

模板中,我们可以用的语法调用视图传进来的变量

1
2
3
4
5
def get_template(request):
dic = {
"uname":"I am Blosslom"
}
return render(request, "index.html",dic)

在html中

1
<div>{{uname}}</div>

能传入的种类

  • str -字符串
  • int - 整型
  • list -数组
  • tuple -元组
  • dict - 字典
  • func -方法
  • obj -类实例化的对象

在模板中使用变量语法

1
2
3
4
5
6
7
8
9
{{变量名}}

{{变量名index }}

{{变量名.key}}

{{对象.方法}}

{{函数名}}

在views中

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17

def test_value(request):
dic = {}
dic['int'] = 88 #int型
dic['str'] = "xiaoming" #字符串
dic['list'] = ['1', '3'] #列表
dic['dic'] = {"wang": '213', "ming": "123", } #字典
dic['func'] = a_fun #方法,不要加括号否则使用返回值
dic['class'] = cls() #类
return render(request, "index.html", dic)

def a_fun():
return "这是一个方法"

class cls():
def __init__(self):
pass

在对应html中

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
 <!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Title</title>
</head>
<body>
<b>this is {{ int }}</b><--这里的int不是指数据-->
<b>this is {{ str }}</b> <--这里的str不是指字符串-->
<b>this is {{ list }}</b> <--调用列表-->
<b>this is {{ list.1 }}</b> <--调用列表第二个索引-->
<b>this is {{ dic.ming }}</b><--调用字典对应的键值-->
<b>this is {{ func }}</b><--调用函数-->
<b>this is {{ class }}</b> <--调用类-->
</body>
</html>

面向对象

1
2
3
4
5
6
7
8
class FangJan:
Wupin = 'zhuozi' #对象中的变量
difang = 'beijing'

wod = FangJan() #实例化一个对象
wod.Wupin #对象中的地址
Nid = FangJan() #实例化一个对象
Nid.daxiao = 'buda' #给实例化对象增加属性不影响wod这个实例化

实例数据初始化

1
2
3
4
5
class FangJan:
flag = "wd" #类的变量属性,可以在外面使用FangJan.flag=""修改
def __init__(self):#为实例进行数据初始化不可以在外面使用FangJan.flag=""修改
self.Wupin = ""
self.difang = ""

传入参数

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
class human:
where = ""
def __init__(self,name)
self.w = "上"
self.s = "下"
self.a = "左"
self.d = "右"
self.name = "name"

def away(self,name)#这个name是上门的name
print(self.name)
human.where = "地球"

p1 = human("ni")
p2 = human("wo")
p1.away('')#在使用的时候内容不能为空

模板标签

if标签

1
2
3
4
5
6
7
{%if 条件表达式1 %}
...
{%elif 条件表达式1 %}
...
{%else%}
...
{%endif%}<--没有结束标签肯定报错-->

if 标签注意:

  1. if 条件表达式里可以用的运算符==,!=,<, >,<=,>=,in, not
    in, is,is not, not、and、or
  2. 在if标记中使用实际括号是无效的语法。如果您需要它们指示
    优先级,则应使用嵌套的if标记。

for标签

1
2
3
4
5
{% for 变量 in 可迭代对象 %}
循环语句
{% empty %}
可迭代对象无数据时候填充
{% endfor %}

https://docs.djangoproject.com/en/2.2/ref/templates/builtins/#for

变量 描述
forloop.counter 循环的当前迭代(从1开始索引)
forloop.counter0 循环的当前迭代(从0开始索引)
forloop.revcounter counter值的倒序
forloop.revcounter0 revcounter值的倒序
forloop.first 如果这是第一次通过循环,则为真
forloop.last 如果这是最后一次循环,则为真
forloop.parentloop 当嵌套循环,parentloop表示外层循环
1
2
3
4
def for_test(request):
list_one = ["tom", "Bill", "Doum"] #第一个列表
list_two = ["aaa","bbb","ccc"] #第二个列表
return render(request, "test1.html", locals())

在html中

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Title</title>
</head>
<body>
{% for i in list_one %} <--遍历第一个-->
<ul>
<li>
{{ forloop.counter }}<br>
{{ i }}
</li>
</ul>
{% empty %}
<b>no data</b>
{% endfor %}
{% for i in list_two %}<--遍历第二个-->
{% if forloop.first %} <b>first for</b> {% endif %} <--首次循环执行的操作-->
{% if forloop.last %}<b>last for</b> {% endif %} <--最后循环执行的操作-->
<ul>
<li>
{{ forloop.counter }}<br>
{{ i }}
</li>
</ul>
{% empty %}
<b>no data</b>
{% endfor %}
</body>
</html>

在使用for循环,对字典中的数据进行操作时候,需要dic.键名

定义:在变量输出时对变量的值进行处理
作用:可以通过使用过滤器来改变变量的输出显示

1
语法:{{变量|过滤器1:参数值1'|过滤器2:参数值2'…..}}.
过滤器 说明
lower 将字符串转换为全部小写
upper 将字符串转换为大写形式
safe 默认不对变量内的字符串进行html转义
add:“n” 将value的值增加n
truncatechars:“n” 如果字符串字符多于指定的字符数量,那么会被截断。截断的字符串将以可翻译的省略号序列("…")结尾。

在html代码中

1
2
3
4
5
6
7
8
9
10
11
12
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Title</title>
</head>
<body>
<b>this is {{ int|add:"50" }}</b>
<b>this is {{ str|upper }}</b>
<b>{{ script|safe }}</b>
</body>
</html>

在python中传入和前端有关的代码会自动过滤掉敏感字符例如:

1
dic['script'] = "<script>alert('warning')</script>"

这个正常传入就不会被html执行,如果加上过滤器|safe表示这段代码是安全的,可以被执行。

模板的继承

模板继承可以使父模板的内容重用,子模板直接继承父模板的全部内容并可以覆盖父模板中相应的块

语法–父模板中:

  • 定义父模板中的块block标签

  • 标识出哪些在子模块中是允许被修改的

  • block标签:在父模板中定义,可以在子模板中覆盖

  • 语法–子模板中:
    继承模板extends标签(写在模板文件的第一行)

    1
    例如{% extends 'base.html' %}
  • 子模板重写父模板中的内容块

1
{% block block_name %}

子模板块用来覆盖父模板中 block_name块的内容

1
{% endblock block_name %}

在视图文件中

1
2
3
4
5
6
7
8
9
10
def main_page(request):
return render(request, "main.html")


def son1(request):
return render(request, "son1.html")


def son2(request):
return render(request, "son2.html")

上面定义了三个文件的视图返回模板

在html文件中

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
{% block title %}
<title>Title</title> <--被block标记的是能够在子模板被改变的内容-->
{% endblock %}
</head>
<body>
<a href="/son1">进入son1</a> <--链接到子页面,注意这里链接的是路由里面的路径-->
<a href="/son2">进入son2</a>

{% block tail %}
<b>这是主页面的东西</b>
{% endblock %}
</body>
</html>

son1

1
2
3
4
5
6
7
{% extends "main.html" %} <--开头继承主视图文件-->
{% block title %}
<title>son1</title> <--使用定义的block更改需要改的内容-->
{% endblock %}
{% block tail %}
<b>这是son1</b>
{% endblock %}

son2

1
2
3
4
5
6
7
{% extends "main.html" %} <--开头继承主视图文件-->
{% block title %}
<title>son2</title> <--使用定义的block更改需要改的内容-->
{% endblock %}
{% block tail %}
<b>这是son1</b>
{% endblock %}

重写的覆盖规则

  • 不重写,将按照父模板的效果显示
  • 重写,则按照重写效果显示

注意

  • 模板继承时,服务器端的动态内容无法继承

静态资源标签

模板中访问静态文件– img标签为例方案2

1
通过{% static %}标签访问静态文件
1
2
3
1.加载static - {% load static %}

2.使用静态资源– {% static '静态资源路径' %}

3.样例

1
2
{% load static %}
<img src="% static 'images/lena.jpg'%}"

URL反向解析

1,绝对地址

整个路径(协议+IP:port+路径)

2,相对地址

/path/1,带/开头的为(协议+IP:port+/path/1)

path/1,没有/开头的(当前最后一个/前的URL+path/1)

url反向解析是指在视图或模板中,用path定义的名称来动态查找或计算出相应的路由
path函数的语法

  • path(route, views,name=“别名”)
  • path(‘page’, views.page_view, name=“page_url”)

根据path中的name=关键字传参给url确定了个唯一确定的名字,在模板或视图中,可以通过这个名字反向推断出此url信息

1
在模板中使用{% url "反向的name" "path转换器的名字"%}

在url文件中

1
2
path('son2', views.son2,name='url2'),
path('url/reverse/<int:num>',views.url_reverse,name="url1") #使用了path转换器参数名为num

定义的这两个name表示在全部文件中都可以使用

在html文件中使用

1
2
3
4
5
6
7
8
9
10
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Title</title>
</head>
<body>
<a href="{% url "url1" "100"%}">一个url地址</a> <--传入的url1的路径,后面的100是传入num的参数-->
</body>
</html>

在视图文件中使用

1
2
from django.urls import reverse
reverse('name名',args=[],kwargs={}) #获得反向解析的url,args是参数kwargs是参数组

静态文件配置

用于存放css,js文件和font文件,image图像

静态文件配置- settings.py中
1,配置静态文件的访问路径【该配置默认存在】

  • 通过哪个url地址找静态文件
  • STATIC_URL = ‘/static/’
  • 说明:
  • 指定访问静态文件时是需要通过/static/xxx或http://127.0.0.1:8000/static/xxx
    [xxx表示具体的静态资源位置]

再setting文件中

1
2
3
4
5
STATIC_URL = '/static/' #静态资源请求的url
STATICFILES_DIRS = [
os.path.join(BASE_DIR, 'static') #当前路径下的static文件夹
os.path.join(BASE_DIR, 'static').replace('\\', '/'), #解决windows开发环境路径问题
]

还可以添加其他路径

应用

应用的概念再django中,是为了分开为多个MTV进行管理每个板块应用中有单独的路由,视图,模板,模型。应用之间的代码相互隔离。

创建应用

1
python3 manage.py startapp 应用名

注册应用在setting中

1
2
3
INSTALLED_APPS = [
"应用名"
]

删除应用,先删除应用中的models.py再删除整个目录树,取消setting中的注册

分布式路由

Django中,由路由配置文件(url.py)不进行处理用户具体路由的请求,路由配置文件的可以做请求的分发(分布式请求处理)。具体的请求可以由各自的应用的url.py来进行处理

步骤1–主路由中调用include函数

语法:include(‘app名字.url模块名’)
作用:用于将当前路由转到各个应用的路由配置文件的urlpatterns进行分布式处理

步骤2–应用下配置urls.py应用下手动创建urls.py文件内容结构向全路由完全一样

在主路由url中

1
2
3
4
5
from django.urls import path, include
urlpatterns = [
# myapp
path('myapp/', include("myapp.urls")), #匹配到myapp路径交给app的路由处理/放到主路由下
]

在app中需要创建urls.py

1
2
3
4
5
6
7
from django.urls import path
from . import views #引入的是当前app的views

urlpatterns=[ #名字不能拼错
path('index', views.index) #接着主路由传来的路径匹配index

]

应用内部可以配置模板目录
1.应用下手动创建templates文件夹

2.settings.py中开启应用模板功能
TEMPLATE配置项中的’APP_DIRS’值为True即可

应用下templates和外层templates都存在时,django得查找模板规则

  • 1.优先查找外层templates目录下的模板
  • 2.按INSTALLED_APPS配置下的应用顺序逐层查找

命名模板最好app_***.html或者在app目录的templates下面再添加一个和app同名的文件夹然后把html移入。

在views中

1
2
def ***(request):
return render(request,"app/***.html")

ORM

windows系统数据库部署

https://www.cnblogs.com/zhangkanghui/p/9613844.html

在windows系统下安装数据库http://mirrors.sohu.com/mysql/MySQL-8.0/

下载好解压后放入python目录

配置环境变量

进入变量配置界面在path中加入路径

1
E:\Python\mysql-8.0.26-winx64\bin

使用管理员打开cmd

1
mysqld --initialize-insecure --user=mysql

在E:\python\mysql\mysql-8.0.12-winx64目录下生成data目录

安装mysql

1
mysqld -install

启动mysql

1
net start MySQL

登录mysql

登录MySQL

登录mysql:(因为之前没设置密码,所以密码为空,不用输入密码,直接回车即可)

1
mysql -u root -p

查询密码

1
2
3
4
5
select host,user,authentication_string from mysql.user;
use mysql
ALTER USER 'root'@'localhost' IDENTIFIED WITH mysql_native_password BY '123456';
flush privileges;
quit

Linux系统

安装mysql

1
apt install mysql-server

具体mysql查询命令查看https://www.runoob.com/mysql/mysql-create-database.html

配置mysql

1
2
pip install mysqlclient
pip install Pillow

在ubuntu中需要libmysqlclient-dev和python3-dev

1
2
apt list --installed|grep -E 'libmysqlclient-dev|python3-dev' #判断是否有依赖包
apt install python3-dev default-libmysqlclient-dev #没有则安装

创建数据库

ORM:

  • 类=数据表
  • 对象=数据行
  • 属性=字段

进入mysql数据库执行

1
create database 数据库名 default charset utf8

通常数据库名跟项目名保持一致
settings.py里进行数据库的配置
修改DATABASES配置项的内容,由sqlite3变为mysql

1
2
3
4
5
6
7
8
9
10
DATABASES = {
'default': {
'ENGINE': 'django.db.backends.mysql',
'NAME': 'mysite',
'USER': 'root',
'PASSWORD': '123456',
'HOST': '127.0.0.1',
'PORT': '3306'
}
}

定义: ORM (Object Relational Mapping)即对象关系映射,是一种程序技术,它允许你使用类和对象对数据库进行操作,人避免通过SQL语句操作数据库
作用:

  • 1.建立模型类和表之间的对应关系,允许我们通过面向对象的方式来操作数据库。
  • 2.根据设计的模型类生成数据库中的表格。
  • 3.通过简单的配置就可以进行数据库的切换。

优点:

  • 只需要面向对象编程,不需要面向数据库编写代码。

  • 对数据库的操作都转化成对类属性和方法的操作。

  • 不用编写各种数据库的sql语句

  • 实现了数据模型与数据库的解耦,屏蔽了不同数据库操作上的差异。

  • 不在关注用的是mysql、oracle…等数据库的内部细节。

  • 通过简单的配置就可以轻松更换数据库,而不需要修改代码

编写model

数据库迁移

迁移是Diango同步您对模型所做更改(添加字段,删除模型等)到您的数据库模式的方式

生成迁移文件–执行python3 manage.py makemigrations将应用下的models.py文件生成一个中间文件,并保存在migrations文件夹中

执行迁移脚本程序–执行python3 manage.py migrate执行迁移程序实现迁移。将每个应用下的migrations目录中的中间文件同步回数据库

1
2
python manage.py makemigrations
python manage.py migrate

在迁移数据库的时候,如果出现问题删除,migrations中除了__init__.py的内容全部删除,再删除models文件中对应的拆创建的表,再删除数据库中django_session对应的记录数据

1
2
3
4
5
6
7
8
9
drop databases *
drop table *
delete from 表名 where 列=数据名
alter table 表名 drop column 列名
update tablename set colname='xx' where id=1; //更改某个字段中个别数据tablename :表名,colname:字段名
mysqldump -uroot -p abc > abc.sql //备份数据库
create database abc default charset utf8; # 创建数据库
use abc; # 使用已创建的数据库
source /home/abc/abc.sql # 导入备份数据库

在app文件的models文件中

1
2
3
4
from django.db import models
class book(models.Model):
title = models.CharField(verbose_name='书名', max_length=100, default='') #verbose_name可以不用写,字符,书名会出现在数据库中
price = models.DecimalField('价格', max_digits=8, decimal_places=2, default='') #适用于金钱,digits表示位数,decimal_palces表示小数点位数000000.00

defualt表示默认值,字段存入的时候不知道则走默认值

字段类型 数据库对应名字 注意
models.BooleanField() tinyint(1) 编程中使用的是True或False数据库中对应的01
models.CharField() varchar 必须指定max_length参数
models.DateField() date auto_now:每次保存对象时,自动设置该字段为当前时间(取值:True/False)。auto_now_add:当对象第一次被创建时自动设置当前时间(取值:True/False)。default:设置当前时间(取值:字符串格式时间如: ‘2019-6-1’)。以上三个参数只能多选一
models.DateTimeField() datetime(6) 表示日期和时间,参数同DateField
models.FloatField() double 都是用小数表示
models.DecimalField() decimal(x,y) max_digits,decimal_places
models.EmailField() varcahr 都是字符串
models.intergerField() int 都是使用整数
models.imageField() varchar(100) 都使用字符串
models.TextField() longtext 标识不定长的字符数据

字段选项

字段选项,指定创建的列的额外的信息
允许出现多个字段选项,多个选项之间使用,隔开

  • primary_key
    如果设置为True,表示该列为主键,如果指定一个字段为主键,则此数库表不会创建id字段
  • blank
    设置为True时,字段可以为空。设置为False时,字段是必须填写
  • null
    如果设置为True,表示该列值允许为空。
    默认为False,如果此选项为False建议加入default选项来设置默认值
  • default
    设置所在列的默认值,如果字段选项null=False建议添加此项
  • unique=Ture唯一

null和default二选一

  • db_index
    如果设置为True,表示为该列增加索引unique
    如果设置为True,表示该字段在数据库中的值必须是唯一(不能重复出现的)
  • db_column
    指定列的名称,如果不指定的话则采用属性名作为列名
  • verbose_name
    设置此字段在admin界面上的显示名
1
title = models.CharField('书名', max_length=100, default='',unique=True,null=True)#default默认值意思是如果不输入默认为多少,unique表示唯一不能重复,null可以允许为空。

整形的字段限制

validators=[最小,最大]

1
2
3
4
5
6
7
from django.db import models
from django.core.validators import MinValueValidator, MaxValueValidator

size = models.IntegerField(u'尺寸大小', validators=[
MinValueValidator(1),
MaxValueValidator(42)
])

Meta类

使用内部 Meta类来给模型赋予属性,Meta类下有很多内建的类属性,可对模型类做一些控制

1
2
3
4
5
6
class book(models.Model):
title = models.CharField('书名', max_length=100, default='')
class Meta:
db_table = "book"
verbose_name = "书"#用于管理后台显示默认带s
verbose_name_plural = "书"#用于管理后台显示不带s

文件记录

文件记录在了mysql中的django_migrations字段

管理器对象

每个继承自models.Model的模型类,都会有一个objects对象被同样继承下来。这个对象叫管理器对象

创建数据方法

方法一:

1
b1 = book.objects.create(title='python',price=50)

方法二:

1
2
b1 = Book(title="python",price=50)
b1.save()

Django Shell

在Django提供了一个交互式的操作项目叫
Django Shell它能够在
交互模式用项目工程的代码执行相应的操作
利用Django Shell可以代替编写view的代码来进行直接操作注意:项目代码发生变化时,重新进入Django shell

在交互模式中传入数据需要导入

1
from app.models improt book

ORM查询

数据库的查询需要使用管理器对象进行
通过MyModel.objects管理器方法调用查询方法

all()方法

  • 用法: MyModel.objects.all()
  • 作用:查询MyModel实体中所有的数据等同于select * from tabel
  • 返回值: QuerySet容器对象,内部存放MyModel 实例

values(“列1”,“列2”)

  • 用法:MyModel.objects.values(…)
  • 查询部分列返回元组数据,等同于select 列1,列2 from ***

values_list(“列1”,“列2”)

  • 用法:MyModel.objects.values_list(…)
  • 返回元组形式的查询结果,等同于select 列1,列2 from ***

order_by()

  • 用法:MyModel.objects.order_by("-列",“列”)
  • 作用:与all()方法不同,它会用SQL语句的ORDER BY子句对查询结果进行根据某个字段选择性的进行排序,-表示降序
1
2
3
queryset对象的取出方法
for i in queryset:
i.键名

使用django shell

1
2
3
4
from book.models import book #导入book表单
b1 = Book.objects.all() #select * from book展示表单
for i in b1: #遍历表单
print(book.title) #打印表单的title列

如果不想循环遍历打印,在models文件中

1
2
3
4
from django.db import models
class book(models.Model):
def __str__(self): #定义取数据字段
return f"{self.title}//{self.price}"

组合查询方法:

1
b1 = Book.bojects.all().order_by()

对应sql语句查询

1
print(b1.query)

在视图中接入数据库通过导入,在视图文件中

1
2
3
4
from .models import 类名
def all_book(request):
all_book = Book.objects.all()
return render(request,'.html',locals())

在模板中

1
2
3
4
5
6
7
8
9
10
11
12
13
14
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Title</title>
</head>
<body>
{% for i in all_book %}
<b>i.title</b>
<b>i.price</b>
{% emptyfor %}
{% endfor %}
</body>
</html>

条件查询

filter()
  • 语法: MyModel.objects.filter(属性1=值1,属性2=值2)
  • 作用:返回包含此条件的全部的数据集
  • 返回值:QuerySet容器对象,内部存放MyModel实例
  • 说明:当多个属性在一起时为"与"关系

在视图文件下

1
2
3
from .models import book
def book_info(request):
book_name = book.objects.filter(title="清华大学出版社").exists() #title是类里面的一个属性,加上exists()返回True/False
exclude()
  • 语法:MyModel.objects.exclude(属性1=值1,属性2=值2)
  • 作用:返回不包含此条件的全部数据
get()
  • 语法:MyModel.objects.get(属性1=值1,属性2=值2)

  • 作用:返回满足条件的唯——条数据

  • 说明:该方法只能返回一条数据

  • 查询结果多余一条数据则抛出出,Model.MultipleObjectsReturned异常

  • 查询结果如果没有数据则抛出Model.DoesNotExist异常

注意get和filter获取的数据类型不一样

count()

数据的数量

查询谓语词

  • 定义:做更灵活的条件查询时需要使用查询谓词
  • 说明:每一个查询谓词是一个独立的查询功能
__exact

等值匹配

用法:字段__exact,常用于查询为NULL的数据

1
book.create.filter(id__exact=1)
__contains

包含指定值

用法:字段__contains

1
book.create.filter(id__contains="1")#查询字段包含1的数据select * from book name like "%w%"
__startswith

以什么为开头

__endswith

以什么为结束

__gt

大于多少的

1
book.objects.filter(book__gt=50)
__gte

大于等于

__lt

小于

__lte

小于等于

__in

查找数据是否在指定范围内

1
book.objects.filter(book__in=['python','C'])
__range

查找数据是否在指定区间范围内

1
book.objects.filter(book__page=(300,400))

增删改操作

获取数据,修改保存

1
2
3
4
b = book.objects.get(id = 1)
b.name = name
b.save() #保存数据针对单个数据如果是一个数据组使用b.update(name = name)
book.object.get(id=1).update(name=name)

获取数据,删除数据

1
2
b = book.objects.get(id = 1)
b.delete()

伪删除

通常不会轻易在业务里把数据真正删掉,取而代之的是做伪删除,即在表中添加一个布尔型字段(is_active),默认是True;执行删除时,将欲删除数据的is_active字段置为False
注意:用伪删除时,确保显示数据的地方,均加了is_active=True的过滤查询。

F和Q对象

F对象

用于同时并发处理相同的一个数值,解决资源竞争

在视图文件中

1
2
3
4
5
6
7
from django.db.models import F
book = book.objects.get(id=id)
#F对象
book.mun = F('num')-1 #表示标记数量减1
book.save()
#等同于sql语句 update book set num = num - 1 where id=id
#sql语句有互斥锁,只能由一个人操作

具体用法

1
book.objects.filter(sale_price__gt=F('get_price'))#表示过滤当前售卖价格大于自身获取价格的书

Q对象

当在获取查询结果集使用复杂的逻辑或|逻辑非~等操作时可以借助于Q对象进行操作

在视图文件中

1
2
3
4
5
from django.db.modles import Q
Q(条件1)|Q(条件2) #条件1成立或条件2成立
Q(条件1)&Q(条件2) #条件1成立和条件2同时成立
Q(条件1)&~Q(条件2) #条件1成立且条件2不成立
book.objects.filter(Q(price__gt=50)|Q(title="python"))

聚合查询

聚合查询是指对一个数据表中的一个字段的数据进行部分或全部进行统计查询,查bookstore_book数据表中的全部书的平均价格,查询所有书的总个数等,都要使用聚合查询 。

在视图文件中

聚合函数:Sum求和,Avg求平均值,Count统计数量,Max最大值,Min最小值

整表聚合

1
2
3
from django.db.models import *
MyModel.objects.aggregate(num=聚合函数("列"))#再SQL中select count(列) as num from music将统计后的值以num命名,以某个列名来进行聚合
>>>{结果变量名:值} #返回字典

分组聚合

分组聚合是指通过计算查询结果中每一个对象所关联的对象集合,从而得出总计值(也可以是平均值或总和),即为查询集的每一项生成聚合。
语法:

  • QuerySet.annotate(结果变量名=聚合函数(列’))返回值:
  • QuerySet

先分组,再聚合

1
2
b = Book.objects.values("price").annotate(num=Count('id'))#以price为分组,对id进行统计,意思是price一样的为一个字典
>>>返回一个包含很多字典的QuerySet

原生数据库操作

Django也可以支持直接用sql语句的方式通信数据库

查询:使用MyModel.objects.raw()进行数据库查询操作查询

语法: MyModel.objects.raw(sql语句,拼接参数)

返回值:RawQuerySet集合对象

1
b = book.objects.raw("select * from book where id=%s,['1 or 1=1']")

使用拼接,可以解决注入问题

cursor

完全跨过模型类操作数据库–查询/更新/删除
1.导入cursor所在的包
from django.db import connection

⒉.用创建cursor类的构造函数创建cursor对象,再使用cursor对象,为保证在出现异常时能释放cursor资源,通常使用with语句进行创建操作

1
2
3
from django.db import connection
with connection.cursor() as cur:
cur.execute('SQL语句','拼接参数')

内容修改

音乐名管理系统

路由,使用的app子url

在主路由中配置如下

1
path('music/', include('music.urls')),

在子路由urls文件配置如下:

1
2
3
4
urlpatterns = [
path('index', views.index), #展示页面
path('replace/<int:id>', views.replace), #进入修改页面,id是通过数据库获取,然后经过视图文件传入到模板在定向到这里的。
]

在模型文件中:

1
2
3
4
5
6
7
8
9
10
11
from django.db import models
class Music(models.Model):
name = models.CharField(max_length=50, default="")
time = models.CharField(max_length=5, default="00:00")
id = models.IntegerField(max_length=5,primary_key=True)
auto_name = models.CharField(max_length=50,default="NO")
is_active = models.BooleanField("是否活跃", default=True)
class Meta:
db_table = "music"
def __str__(self):
return f"{self.name}//{self.time}"

定义四个数据类型,id设置为了主键

在视图文件中

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
def index(request,): #展示页面
music_info = Music.objects.filter(is_active=True)#过滤掉删除的数据
return render(request, "music/index.html", locals()) #需要传入数据以供获取id

def replace(request, id):
# 查
try: #检测是否有id传入
m = Music.objects.get(id=id,is_active=True)
except Exception as e: #没有id传入则进行弹出报错页面防止用户乱输入url
return HttpResponse('you are input no result')
# 改
if request.method == 'get': #如过方法为get,则通过传入数据正常进入更新页面
return render(request, 'music/replace.html',locals())
elif request.method == 'post': #如果为post方法进入修改
time = request.POST['time'] #获取post页面提交的数据
auto_name = request.POST['auto_name']
# 改
m.time = time #对应的数据
m.auto_name = auto_name
# 保存
m.save()
return HttpResponseRedirect("/music/index") #重定向到展示页面
def delete(request):
music_id = request.GET.get('music_id') #获取需要删除的id
if not music_id: #判断id是否为空
return HttpResponse("your input error")
try:
m = Music.objects.get(id=music_id, is_active=True) #检测是否有id传入属于数据库内的
except Exception as e:
return HttpResponse("your input error")
m.is_active = False #进行伪删除
m.save() #保存
return HttpResponseRedirect('/music/index') #重定向到展示页面

在模板文件中:

展示页面

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Title</title>
</head>
<body>

<table border="1" cellpadding="" >
<tr style="width: 500px">
<th>
id
</th>
<th>
时间
</th>
<th>
名字
</th>
<th>
作者
</th>
<th>
操作
</th>
</tr>
{% for i in music_info %} <--循环查看-->
<tr>
<td>
{{ i.id }}
</td>
<td>
{{ i.time }}
</td>
<td>
{{ i.name }}
</td>
<td>
{{ i.auto_name }}
</td>
<td>
<a href="/music/replace/{{ i.id }}">更新</a> <--获得重定向带id的位置-->
<a href="href="/music/delete?music_id={{ i.id }}"">删除</a>
</td>
</tr>
{% empty %}
{% endfor %}
</table>
</body>
</html>

admin

django提供了比较完善的后台管理数据库的接口,可供开发过程中调用和测试使用
django会搜集所有已注册的模型类,为这些模型类提拱数据管理界面,供开发者使用

创建最高权限的账号

1
python manage.py createsuperuser

自定义模型类

若要自己定义的模型类也能在/admin后台管理界中显示和管理,需要将自己的类注册到后台管理界面

注册步骤:
1,在应用app中的admin.py中导入注册要管理的模型models类,如:from .models import Book
2,调用admin.site.register方法进行注册,如:admin.site.register(自定义模型类)

模型管理器类

作用:
为后台管理界面添加便于操作的新功能,显示上面的栏目。

说明:
后台管理器类须继承自django.contrib.admin里的ModelAdmin类

再对应的app/admin.py

1
2
3
4
5
from django.contrib import admin
from .models import Music
class BookManager(admin.ModelAdmin):
list_display = ["id", "title", "name", "defaulname"]#这个里面的字段对应的是模型类,里面有定义的字段选项verbose_name="名字"
admin.site.register(Music, BookManager)#用于绑定义的列表

控制后台修改链接

1
list_display_links = ['title']#默认这个字段为可以修改的选项

过滤器

1
list_filter = ['defaulname'] #以defaulname为过滤条件

搜索框

1
search_fields = ['title']#以title为查询索引

添加在列表页编辑字段

1
list_editable = ['name']

https://docs.djangoproject.com/en/2.2/ref/contrib/admin/

在admin文件中

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
from django.contrib import admin
from .models import Music, Author
class MusicManager(admin.ModelAdmin):
list_display = ["name", "time", "id", "auto_name", "is_active"]
list_display_links = ["name", "time", "auto_name", ]
list_filter = ["name", "time", "id", "auto_name", "is_active"]
search_fields = ["name", 'time', "id"]
list_editable = ["is_active", "id"]

class AuthorManager(admin.ModelAdmin):
list_display = ['id', 'name', 'age']
search_fields = ['id', 'name', 'age']
list_filter = ['age']
list_editable = ['name', 'age']

admin.site.register(Music, MusicManager)
admin.site.register(Author, AuthorManager)

在模型文件中

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
from django.db import models

# Create your models here.
class Music(models.Model):
name = models.CharField("名字", max_length=50, default="")
time = models.CharField("时间", max_length=5, default="00:00")
id = models.IntegerField("id", max_length=5, primary_key=True)
auto_name = models.CharField("作者名称", max_length=50, default="NO")
is_active = models.BooleanField("是否活跃", default=True)

class Meta:
db_table = "music"
verbose_name_plural = "音乐"

def __str__(self):
#return f"{self.name}//{self.time}//{self.id}//{self.auto_name}"
return name.self #返回自己的名字,在后台中使用外键显示可以显示自己的名字

class Author(models.Model):
id = models.IntegerField('id', max_length=10, primary_key=True)
name = models.CharField("作者姓名", max_length=50, default='无名')
age = models.IntegerField("年龄", max_length=3, default=1)

class Meta:
db_table = "作者信息"
verbose_name_plural = '作者信息'

simple ui

1
pip install django-simpleui

在setting文件中

1
2
3
4
INSTALLED_APPS = [
'simpleui',
...
]

克隆静态文件

1
python3 manage.py collectstatic

如果克隆出错,在setting文件加入以下代码

1
STATIC_ROOT = os.path.join(BASE_DIR, "static")

修改基础信息,在任意一个app下面的admin.py

1
2
admin.site.site_title = "同剂商城管理系统" #页面上的标题
admin.site.site_header = "同剂商城管理系统" #header上的名字

在setting中定义其他内容

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
SIMPLEUI_CONFIG = {
# 是否使用系统默认菜单,自定义菜单时建议关闭。
'system_keep': False,

# 用于菜单排序和过滤, 不填此字段为默认排序和全部显示。空列表[] 为全部不显示.
'menu_display': ['1任务管理', '2权限认证'],

# 设置是否开启动态菜单, 默认为False. 如果开启, 则会在每次用户登陆时刷新展示菜单内容。
# 一般建议关闭。
'dynamic': False,
'menus': [
{
'app': 'auth',
'name': '2权限认证',
'icon': 'fas fa-user-shield',
'models': [
{
'name': '用户列表',
'icon': 'fa fa-user',
'url': 'auth/user/'
},
{
'name': '用户组',
'icon': 'fa fa-th-list',
'url': 'auth/group/'
}
]
},

{
'name': '1任务管理',
'icon': 'fa fa-th-list',
'models': [
{
'name': '任务列表',
# 注意url按'/admin/应用名小写/模型类名小写/'命名。
'url': '/admin/tasks/task/',
'icon': 'fa fa-tasks'
},
]
},
]
}

内部约束

1
2
3
4
5
gender = (
(1,'男'),
(2,'女'),
)
gender_choice = models.SamllIntegerField(verbose_name='性别',choices=gender)

在外部获取内部约束使用get_字段名_display()

1
models.表名.get_字段名_display()

Form

用来将model中的文件直接转换成为页面的input框。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
from django import forms
class UsermodelsForm(forms.ModelForm):
#校验
字段 = froms.CharField(min_length=3,label='用户名')
字段 = froms.CharField(label='用户名',validators=[RegexValidator(r'正则','提示内容')])
字段 = froms.CharField(disabled=True,label='用户名')#禁止编辑
class Meta:
model = models.UserInfo #引入model文件中的一个类
fields = ['字段''字段'] #对应的是model中的字段名
#fields = '__all__' 选取所有字段
#exclude = ['level'] 除开某个字段
def __init__(self,*args,**kwargs):
super().__init__(*args,**kwargs):
#循环将所有类型全部加上一个样式
for name,field in self.fields.items():
field.widget.attrs = {'class':'样式名','placehoder':field.label}
#校验
def clean_字段(self):
#print(self.instance.pk)都是由创建对象时传入的,表示pk一般表示id
text_字段 = self.cleaned_data['字段']
if ...:
raise ValidationError('格式错误')
#验证通过,返回用户输入的值
return text_字段

def userview(reqeust):
if request.method == 'GET':
form = UsermodelsForm()
return render(request,'template.html',{form:form})
#用户post提交数据
form = UsermodelsForm(data=request.POST)
if form.is_valid():
print(from.cleaned_data) #输出经过校验后的数据
#from.instance.字段名 = 值 可以额外保存对应的内容
from.save() #保存校验后的数据
return redirect()
else:#校验失败返回错误信息,返回的信息有{{form.error.0}}表示取第一个错误
return render(request,'template.html',{form:form})

在html文件中使用

1
2
3
{% for i in form %}
{{i.label}}:{{i}}
{% endfor %}

编辑时候,原数据填入框内

1
2
3
4
5
6
7
8
9
10
11
12
13
14
def userview_edit(reqeust,id):
#get参数获取内容
if reuqest.method == 'GET':
row_data = models.表名.objects.filter(id=id).first()
from = UsermodelsForm(instance=row_data)
return render(request,'template.html',{'form':form})
#post提交后更新到数据库
row_data = models.表名.objects.filter(id=id).first()
from = UsermodelsForm(data=request.POST,instance=row_data)
if form.is_valid():
from.save() #保存校验后的数据
return redirect()
else:#校验失败返回错误信息,返回的信息有{{form.error.0}}表示取第一个错误
return render(request,'template.html',{form:form})

删除

1
2
3
def userview_edit(reqeust,id):
row_data = models.表名.objects.filter(id=id).delete()
return redirect()

关系映射

在关系型数据库中,通常不会把所有数
据都放在同一张表中,不易
于扩展,常见关系映射有:
1.—对一映射
如:一个身份证对应一个人

2.一对多映射
如:一个班级可以有多个学生

3.多对多映射
如:一个学生可以报多个课程,—个课程可以有多个学生学习

一对一映射

—对一是表示现实事物间存在的一对一的对应关系。

语法:OneToOneField(类名,on_delete=)

在模型文件需要绑定的类中

1
2
3
class book(models.Model)
name = models.CharField("名字",max_length=11)
author = models.OneToOneField(类名,on_delete=models.CASCADE) #绑定需要关联的表
  • 特殊字段选项【必须】
  • on_delete - 级联删除(1,2用的多)
  1. models.CASCADE级联删除。Django模拟SQL约束ON DELETE CASCADE的行为,并删除包含ForeignKey的对象。
  2. SET_NULL关联变为空设置ForeignKey null;需要指定null=True,black=True
  3. models.PROTECT 抛出ProtectedError 以阻止被引用对象的删除;[等同于mysql默认的RESTRICT]
  4. SET_DEFAULT 将ForeignKey设置为其默认值;必须设置ForeignKey的默认值。
  • 无外键的模型类[Author]:
    author1 = Author.objects.create(naem=“Blosslom”)
  • 有外键的模型类[book]
    book = book.objects.create(name=“python”, author=author1)#关联Blosslom的类
  • book = Wife.objects.create(name=‘python’,id=1)#关联Blosslom对应主键值

创建

先创建无外键的

1
2
3
author1 = Author.objects.create(naem="Blosslom"#在模型层里的属于class author类
book = book.objects.create(name="python", author=author1)#通过导入的author类所创建的对象绑定
book = book.objects.create(name='python',author_id=1)#通过author类的id字段进行绑定

查询

1,正向查询:直接通关外键属性查询,则称为正向查询,有外键的字段

1
2
3
from .models import book #
book1 = book.objects.create(name='python')
print(f"{book1}的作者是{book1.author.name}")#通过外键属性查询

2,反向查询-没有外键属性的一方,可以调用反向属性查询到关联的另一方

反向关联实例化对象名.关联类名小写.属性

在模型文件中

1
2
3
4
5
class Author(models.Model)
name = models.CharField("名字",max_length=11)
class Book(models.Model)
name = models.CharField("名字",max_length=11)
author = models.OneToOneField(类名,on_delete=models.CASCADE)

在视图文件中

1
2
3
class Author(models.Model)
author1 = Author.objects.get(name = '')
book1 = author1.book.name

一对多映射

一对多是表示现实事物间存在的一对多的对应关系。

如:—个学校有多个班级,—个班级有多个学生,一本图书只能属于一个出版社,一个出版社允许出版多本图书
—对多需要明确出具体角色,在多表上设置外键

在需要绑定的多键值中

1
2
3
4
5
6
7
8
9
10
from django.db import models

# Create your models here.
class User(models.Model):
name = models.CharField('用户名', max_length=50)

class Address(models.Model):
address = models.CharField('用户地址', max_length=200)
//related_name是反向查询关联字符串,使用obj.U.all()即刻反向查询,使用to_field=表示关联对应的字段
user = models.ForeignKey(User,related_name='U',on_delete=models.CASCADE)

创建

先创建一,再创建多

1
2
3
4
user1 = models.objects.create(name='')
address1 = models.objects.create(name='',user=user1)
address2 = models.objects.create(name='',user=user1)
address2 = models.objects.create(name='',user_id=1)

查询

1,正向查询[通过address查询user

1
2
3
from .models import *
address1 = Address.objects.get(name='')
user1_name = address1.user.name #正向查询user是放置在模型文件中的键值绑定

反向查询[通过Publisher查询对应的所有的Book]

1
2
3
user1 = User.objects.get(name='')#获取一个对象
address1 = user1.address_set.all()#所关联的类名小写_set
address1 = User.objects.get(name='').select_related('U')#用关联键值

多对多映射

多对多表达对象之间多对多复杂关系,如:每个人都有不同的学校(小学,初中,高中…),每个学校都有不同的学生…

mysql中创建多对多需要依赖第三张表来实现
Django中无需手动创建第三张表, Django自动完成

语法:在关联的两个类中的任意一个类中,增加:属性= models.ManyToManyField(MyModel)

1
2
3
4
5
6
7
8
9
from django.db import models

# Create your models here.
class Manufactor(models.Model):
maker = models.CharField('生产者', max_length=50)

class Goods(models.Model):
name = models.CharField('装配体', max_length=50)
manufactor = models.ManyToManyField(Manufactor) #没有多余的要求

创建

多对多先创建谁都无所谓

1
2
3
4
5
from .models import *
maker1 = Manufactor.objects.create(maker='')
maker2 = Manufactor.objects.create(maker='')
goods1 = maker1.goods_set.create(name='') #第一种绑定方法
maker2.goods_set.add(goods1)#第二种绑定方法,已经有了一个对象

所有都是没有外键操作的类都使用反向的类名_set

1
2
3
4
from .models import * #先创建有外键的
good = good.object.create(name='')
maker = good.manufactor.create(maker='')#第一种绑定方法
good.manufactor.add(maker)#第二种绑定方法

正向查询

1
2
3
good = goods.objects.get(name='')
good.manufactor.all()
good.manufactor.filter()

反向

1
2
3
maker = manufactor.objects.get(maker='')
maker.goods_set.all()
maker.goods_set.filter()

cookies&session

cookies是存储在客户端浏览器上的

Chrome浏览器可能通过开发者工具的Application >>Storage >> Cookies查看和操作浏览器端所有的Cookies值

  • cookies在浏览器上是以键-值对的形式进行存储的,键和值都是以ASCII字符串的形存储(不能是中文字符串)
  • 存储的数据带有生命周期
  • cookies中的数据是按域存储隔离的,不同的域之间无法访问
  • cookies的内部的数据会在每次访问此网址时都会携带到服务器端,如果cookies过大会降低响应速度

HttpResponse.set_cookie(key, value=’’, max_age=None,expires=None)

  • key:cookie的名字
  • value:cookie的值
  • max_age:cookie存活时间,秒为单位
  • expires:具体过期时间
  • 当不指定max_age和expires时,关闭浏览器时此数据失效
1
2
3
4
def cookies(request):
req = HttpResponse('request cookie') #定义返回值
req.set_cookie('uname', 'Blosslom', 5000) #在返回值中设置cookie
return req #返回带cookie的信息

在视图函数中写cookies

  • 删除Cookies
  • HttpResponse.delete_cookie(key)-删除指定的key 的Cookie。
  • 如果key不存在则什么也不发生
    获取Cookies
  • 通过request.COOKIES 绑定的字典(dict)获取客户端的COOKIES数据
  • value = request.COOKIES.get(‘cookies名’,‘默认值’)
1
2
3
def get_cookie(request):
cok = request.COOKIES.get('uname') #获取cookie
return HttpResponse(f"cookie is {cok}")

session是在服务器上开辟一段空间用于保留浏览器和服务器交互时的重要数据
实现方式
使用session需要在浏览器客户端启动cookie,且在cookie中存储sessionid
每个客户端都可以在服务器端有一个独立的Session-注意:不同的请求者之间不会共享这个
数据,与请求者一一对应

在setting中

1
2
3
4
5
6
INSTALLED_APPS = [
'django.contrib.sessions',
]
MIDDLEWARE = [
'django.contrib.sessions.middleware.SessionMiddleware',
]

session对于象是一个类似于字典的SessionStore类型的对象,可以用类拟于字典的方式进行操作

session 能够存储如字符串,整型,字典,
列表等。

  • 1,保存session的值到服务器
  • request.session[‘KEY’] = VALUE
  • 2,获取session的值
  • value = request.session[‘KEY’]
    value = request.session.get(‘KEY’,默认值)
  • 3,删除session
  • del request.session[‘KEY’]
1
2
3
4
5
6
7
def set_session(request):
request.session['uname'] = 'this is session'
return HttpResponse('get a session')

def get_session(request):
ses = request.session.get('uname')
return HttpResponse(f"you are session is {ses}")

settings.py中相关配置项

  1. SESSION_COOKIE_AGE
    作用:指定sessionid在cookies中的保存时长(默认是2周),如下:例如:SESSION_COOKIE_AGE= 60*60*24*7*2
  2. SESSION_EXPIRE_AT_BROWSER_CLOSE= True设置只要浏览器关闭时,session就失效(默认为False)
  3. 注意:Django中的session数据存储在数据库中,所以使用session前需要确保已经执行过migrate

1, django_session表是单表设计;且该表数据量持续增持【浏览器故意删掉sessionid&过期数据未删除】
2、可以每晚执行python3 manage.py clearsessions【该命令可删除已过期的session数据】

哈希算法

md5:32位16进制

1
2
3
4
5
6
7
import hashlib
m = hashlib.md5() #创建一个对象
m.update(b'13246') #更新对象的值
m.hexdigest() #显示十六禁止
'51cece00aed19468f18042ad0606a3d3'
m.digest() #进行二进制不可视字符
m.update(b'123')#累加了12346

缓存

在setting文件中

1
2
3
4
5
6
7
8
9
10
11
CACHES = {
'default': {
'BACKEND': 'django.core.cache.backends.db.DatabaseCache',
'LOCATION': 'my_cache_table',
'TIMEOUT': 300, # 缓存保存时间单位秒,默认值为300
'OPTIONS': {
'MAX_ENTRIES': 300, # 缓存最大数据条数
'CULL_FREQUENCY': 2, # 缓存条数达到最大值时删除1/x的缓存数据
}
}
}

在python中创建一个表

1
python manage.py createcachetable

全局缓存

在路由里面写缓存

1
2
3
4
5
from django.views.decorators.cache import cache_page

urlpatterns = [
path('admin/', cache_page(60)(admin.site.urls)),
]

在视图函数中写缓存

1
2
3
4
5
from django.views.decorators.cache import cache_page

@cache_page(30) #30表示存30秒
def my_view(request):
pass

局部缓存

1
2
3
from django.core.cache import cache
cache.set('key','mydata',50)#用key名字,存mydata存储50秒
cache.get('key')

1, cache.set(key, value, timeout)-存储缓存

key:缓存的key,字符串类型
value:Python对象
timeout:缓存存储时间(s),默认为CACHES中的TIMEOUT值
返回值: None

2, cache.get(key)- 获取缓存

key:缓存的key
返回值:为key的具体值,如果没有数据
则返回None

3, cache.add(key, value)-存储缓存,只在key不存在时生效返回值: True[存储成功]or False[存储失败]

4, cache.get_or_set(key, value, timeout)-如果未获取到数据则执行set操作返回值: value

5, cache.set_many(dict,timeout)-批量存储缓存dict: key和value的字典
timeout:存储时间(s)
返回值:插入不成功的key的数组

6, cache.get_many(key_list)-批量获取缓存数据

key_list:包含key的数组
返回值:取到的key和value的字典

7, cache.delete(key)-删除key的缓存数据返回值:None

8, cache.delete_many(key_list)- 批量删除返回值: None

浏览器缓存

不会向服务器发送请求,直接从缓存中读取资源1,响应头–Expires
定义:缓存过期时间,用来指定资源到期的时间,是服务器端的具体的时间点
样例: Expires:Thu, 02 Apr 2030 05:14:08 GMT

2,响应头-Cache-Control
在HTTP/1.1中,Cache-Control主要用于控制网页缓存。比如当`Cache-Control:max-age=120代表请求创建时间后的120秒,缓存失效
说明:目前服务器都会带着这两个头同时响应给浏览器,浏览器优先使用Cache-Control

3,Last-Modified响应头和If-Modified-Since请求头说明:
Last-Modified为文件的最近修改时间,浏览器第一次请求静态文件时,服务器如果返回Last-Modified响应头,则代表该资源为需协商的缓存
当缓存到期后,浏览器将获取到的Last-Modified值做为请求
头lf-Modified-Since的值,与服务器发请求协商,服务端返回304响应码[响应体为空].代表缓存继续使用,200响应码代表缓存不可用[响应体为最新资源]

4,Etag是服务器响应请求时,返回当前资源文件的一个唯一标识(由服务器生成),只要资源有变化, Etag就会重新生成。

中间件

中间件是 Django 请求/响应处理的钩子框架。它是一个轻量级的、低级的“插件"系统,用于全局改变 Django的输入或输出。

中间件以类的形式体现

每个中间件组件负责做一些特定的功能。例如, Django 包含一个中间件组件AuthenticationMiddleware,它使用会话将用户与请求关联起来。

中间件类须继承自django.utils.deprecation.MiddlewareMixin类

中间件类须实现下列五个方法中的一个或多个:

process_request(self, request)
执行路由之前被调用,在每个请求上调用,返回None可以进入下一个环节或HttpResponse对象

process_view(self, request, callback, callback_args,callback_kwargs)
调用视图之前被调用,在每个请求上调用,返回None或HttpResponse对象

process_response(self, request, response)
所有响应返回浏览器被调用,在每个请求上调用,response是视图函数return的内容,返回HttpResponse对象

process_exception(self, request, excepption)
当处理过程中抛出异常时调用,返回一个HttpResponse对象

process_template response(self, request, response)
在视图函数执行完毕且试图返回的对象中包含render方法时被调用;该方法需要返回实现了render方法的响应对象
注:中间件中的大多数方法在返回None时表示忽略当前操作进入下一项事件,当返回HttpResponese对象时表示此请求结束,直接返回给客户端

再settings.py中需要注册一下自定义的中间件

1
2
3
4
5
6
7
8
9
10
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',
'middleware.first_midware.Request', #注册的中间件
]

再项目文件夹下创建middleware的软件包,再创建py文件

注意:配置为数组,以视图为分界,中间件被调用时以’先上到下’再‘由下到上的顺序调用

1
2
3
4
5
6
7
8
9
10
class Request(MiddlewareMixin):
def process_request(self, request):
print('before url')

def process_view(self, reqeust, callback, callback_args, callback_kwargs):
print('before view')

def process_response(self, reqeust, response):
print('after view')
return response

request.META['REMOTE_ADDR"]可以得到远程客户端的IP地址

reqeust.path_info获取用户当前访问的路径 返回形式不带ip和端口是 /路径/

request.path_ info 可以得到客户端访问的请求路由信息

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
class LimitVisit(MiddlewareMixin):
visit_dic = {}

def process_request(self, reqeust):
# 获取IP地址
ip_address = reqeust.META['REMOTE_ADDR']
# 获取路径访问路径为相对路径/test
path = reqeust.path_info
# 使用正则匹配路径
if not re.match('^/test', path):
# 通过返回None
return
# 使用字典的get获取这个IP的访问次数get方法如果没有这个ip则返回一个默认值
times = self.visit_dic.get(ip_address, 0)
# 使得这个ip次数+1,没有这个ip则会自动添加
self.visit_dic[ip_address] = times + 1
# 判断ip访问次数是否大于5
if self.visit_dic[ip_address] > 5:
# 大于5禁止访问
return HttpResponse('访问'+str(self.visit_dic[ip_address])+'禁止访问')

CSRF跨站伪造请求攻击

某些恶意网站上包含链接、表单按钮或者JavaScript,它们会利用登录过的用户在浏览器中的认证信息试图在
你的网站上完成某些操作,这就是跨站请求伪造(CSRF,即Cross-Site Request Forgey)。

1
2
django.middleware.csrf.CsrfViewMiddleware
对应标签加在form中任意位置{% csrf_token %}

特殊说明:
如果某个视图不需要django进行csrf保护,可以用装饰器关闭对此视图的检查
样例:

1
2
3
4
from django.views.decorators.csrf import csrf_exempt
@csrf_exempt
def my_view(request):
return HttpResponse('Hello wor1d')

分页

方式一

设置页码

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
from django.utils.safestring import mark_safe

#获取数据总条数
query_num = models.表.objects.filter().count()
total_page,last = divmod(query_num,page_size)#divmod获得(整数,余数),page_size表示每页有多少数据
if last: #如果有余数页数加1
total_page = total_page +1
#计算显示页面的前后
display = 5
if total_page <= 2*display+1:#如果只小于11页那就全部显示
start_page = 1
end_page = total_page
elif:
if page<=5:#如果当前页面小于5页那就开始页面是1
start_page = 1
end_page = page+5
elif page>5:#如果当前页面大于5页那就正常显示前后
start_page = page-5
end_page = page+5
elif page<total_page-5:#如果当前页面快结束那就尾部页面显示全部
start_page = page-5
end_page = total_page
#存放所有页码数据
page_list = []
for i in range(start_page,end_page+1):
if i == page:
div = '<li><li>'
elif:
div = '<li><li>'
page = int(request.GET.get(page,1))
start = (page-1)*10 #表示每页显示十个数据
end = page*10
queryset = models.表.objects.filter()[start:end]

方式二

分页是指在web页面有大量数据需要显示,为了阅读方便在每页页中只显示部分数据。
优点:
1.方便阅读
2.减少数据提取量,减轻服务器压力。

  • Django提供了Paginator类可以方便的实现分页功能

  • Paginator类位于django.core.paginator模块中。

  • 负责分页数据整体的管理

  • 对象的构造方法
    paginator = Paginator(object list, per_page)
    -参数

    • object_list 需要分货数据的对象列表- per_page每页数据个数
      -返回值:

      Paginator的对象

      • count:需要分页数据的对象总数
      • num_pages:分页后的页面总数
      • page_range:从1开始的range对象,用于记录当前面码数
      • per_page 每页数据的个数

      paginator对象

      .page(number)
      -参数number为页码信息(从1开始)

      返回当前number页对应的页信息
      如果提供的页码不存在,抛出InvalidPage异常

      lnvalidPage:总的异常基类,包含以下两个异常子类

      • PageNotAnInteger:当向page()作传入一个不是整数的值时抛出
      • EmptyPage:当尚page()提供一个有效值,但是那个页面上没有任何对象时抛出

      负责具体某一页的数据的管理创建对象
      Paginator对象的page()方法返回Page对象
      page = paginator.page(页码)
      Page对象属性

      • object_list:当前页上所有数据对象的列表-
      • number:当前页的序号,从1开始
      • gaginator:当前page对象相关的Paginator对象
      • has_next():如果有下一页返回True
      • has_previous():如果有上一页返回True
      • has_other_pages():如果有上一页或下页返回True
      • next_page_number():返回下一页的页码,如果下一页不存在,抛出InvalidPage异常
      • previous_page_number():返回上一页的页码,如果上一页不存在,抛出InvalidPage异常

在视图文件中

1
2
3
4
5
6
7
8
from django.core.paginator import Paginator
def test_page(request):
page_num = request.GET.get('page') #GET请求获取需要的页数
all_data = ['a', 'b', 'a', 'b', 'a', 'b', 'a', 'b', 'a', 'b', 'a', 'b', 'a', 'b', 'a', 'b', 'b', 'a', 'b', 'a', 'b',
'a', 'b', 'a', 'b']
paginator = Paginator(all_data, 3) #创建一个对象,每页放三个数据
page_manager = paginator.page(int(page_num)) #使用page管理每个页面的内容int(page)是需要管理的页面
return render(request, 'page.html', locals()) #返回字典

在html文件中

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Title</title>
</head>
<body>
{#循环page里的数据#}
{% for i in page %}
<p>
{{ i }}
</p>
{% empty %}
{% endfor %}

{#下方页面栏,判断是否为最前面一页,是的话就返回True#}
{% if page.has_previous %}
<a href="/test_page?page={{ page.previous_page_number }}">上一页</a>
{% else %}
上一页
{% endif %}

{#循环页面范围#}
{% for i in paginator.page_range %}
{# 如果page在当前页面就显示为正常字符串#}
{% if i == page.number %}
{{ i }}
{# 否则就是超链接#}
{% else %}
<a href="/test_page?page={{ i }}">{{ i }}</a>
{% endif %}
{% empty %}
{% endfor %}

{#判断是否在最后一页#}
{% if page.has_next %}
<a href="/test_page?page={{ page.next_page_number }}">下一页</a>
{% else %}
下一页
{% endif %}
</body>
</html>

CSV文件

逗号分隔值(Comma-Separated Values,CSV,有时也称为字符分隔值,因为分隔字符也可以不是逗号),其文件以纯文本形式存储表格数据(数字和文本)
说明:可被常见制表工具,如excel等直接进行读取Python提供了内建库csv;可直接通过该库操作csv文件案例如下:
import csv
with open(‘eggs.csv’, ‘w’, newline=") as csvfile:
writer = csv.writer(csvfile)
writer.writerow([‘a’ , ‘b’, ‘c’])

在网站中,实现下载CSV,注意如下:
响应Content-Tyoe类型需修改为text/csv。这告诉浏览器该文档是CSV文件,而不是HTML文件
响应会获得一个额外的Content-Disposition标头,其中包含CSV文件的名称。它将被浏览器用于开启“另存为…"对话框

1
2
3
4
5
6
7
8
9
10
import csv
def test_csv(request):
response = HttpResponse(content_type='text/csv')
response['Content-Disposition'] = 'attachment;filename="test.csv"'
all_data = [['a', 'b', 'c', 'd', 'b', 'c', 'd'], ['a', 'b', 'c', 'd', 'b', 'c', 'd'],
['a', 'b', 'c', 'd', 'b', 'c', 'd']]
for i in all_data:
write = csv.writer(response)
write.writerow(i)
return response

上传

在前端

文件上传必须为POST提交方式
表单<form>中文件上传时必须有带有enctype= "multipart/form-data"时才会包含文件内容数据。
表单中用
标签上传文件

1
2
3
<form action='' methon='' enctype= "multipart/form-data">
<input type="file" name="xxx">
</form>

在后端

视图函数中,用request.FILES取文件框的内容
file=request.FILES[‘xxx’]
说明: 1,FILES的key对应页面中file框的name值
2, file 绑定文件流对象,
3, file.name文件名
4, file.file文件的字节流数据

1
2
3
up_file = request.FILES['file'] #file是form中的name
up_file.name
data = up_file.file.read() #获得文件数据

在setting中

配置文件的访问路径和存储路径
在setting.py中设置MEDIA相关配置;Django把用户上传的文件,统称为media资源
Django把用户上传的文件,统称为media资源

1
2
MEDIA_URL = '/media/'
MEDIA_ROOT = os.path.join(BASE_DIR,' media' ) #资源上传后的路径

MEDIA_URL和MEDIA_ROOT需要手动绑定

步骤:主路由中添加路由

1
2
3
from django. conf import settings
from django.conf.urls.static import static
urlpatterns += static(settings.MEDIA_URL,document_root=settings.MEDIA_ROOT)

说明:等价于做了MEDIA_URL开头的路由,Django接到该特征请求后去MEDIA_ROOT路径查找资源

借助ORM

FileField(upload_to=‘子目录名’)

1
2
up_file = request.FILES['file']
类名.objects.create(file = up_file)

邮件发送

SMTP的全称是"Simple Mail Transfer Protocol",即简单邮件传输协议(25号端口)。
它是一组用于从源地址到目的地址传输邮件的规范,通过它来控制邮件的中转
属于“推送”协议

IMAP全称是lnternet Mail Access Protocol,
即交互式邮件访问协议,是一个应用层协议(端口是143)。
用来从本地邮件客户端(Outlook Express.Foxmail、Mozilla Thunderbird等)访问远程服务器上的邮件属于“拉取”协议

两者均为“拉取"型协议,负责从邮件服务器中下载邮件

  • IMAP具备摘要浏览功能,可预览部分摘要,再下载整个邮件
    IMAP为双向协议,客户端操作可反馈给服务器
  • POP3必须下载全部邮件,无摘要功能
    POP3为单向协议,客户端操作无法同步服务器

Django中配置邮件功能,主要为SMTP协议,负责发邮件

原理:
给Django授权一个邮箱
Django用该邮箱给对应收件人发送邮件
django.core.mail封装了电子邮件的自动发送SMTP协议

在setting文件中

1
2
3
4
5
6
EMAIL_BACKEND = 'django.core.mail.backends.smtp.EmailBackend'
EMAIL_HOST = 'smtp.qq.com'#腾讯QQ邮箱SMTP服务器地址
EMAIL_PORT = 25 # SMTP服务的端口号,使用云服务器端口需要设置为587或465
EMAIL_HOST_USER =‘xxxx@qq.com’#发送邮件的QQ邮箱
EMAIL_HOST_PASSWORD ='' #在QQ邮箱->设置->帐户->“POP3/IMAP.....服务”里得到的在第三方登录QQ邮箱授权码
EMAIL_USE_TLS = False #与SMTP服务器通信时,是否启动TLS链接(安全链接)默认False

在视图文件中

1
2
3
4
5
6
from django.core import mail
mail.send_mail(subject='',#题目
message='',#消息内容
from_email='',# 发送者[当前配邮箱]
recipient_list=['xxx@qq.com'],#接收者邮件列表
)

发送html邮件

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
from django.core.mail import EmailMessage
from django.template import loader

from settings import EMAIL_HOST_USER #项目配置邮件地址,请参考发送普通邮件部分

def send_html_mail(subject, html_content, recipient_list):
msg = EmailMessage(subject, html_content, EMAIL_HOST_USER, recipient_list)
msg.content_subtype = "html" # Main content is now text/html
msg.send()

subject = u'邮件主题'
html_content = loader.render_to_string(
template_path, #需要渲染的html模板
{
‘paramters’:paramters #参数
}
)

send_html_mail(subject, html_content, [收件人列表]):

前后端分离

数据获取/发送模式

1
2
3
request.GET.get('')#获取数据
return HttpResponse(res)#发送数据
return JsonResponse(res)#发送json数据

json

1
2
import json
data = json.dumps(get,ensure_ascii=False)

环境部署

项目部署是指在软件开发完毕后,将开发机器上运行的软件实际安装到服务器上进行长期运行
1.在安装机器上安装和配置同版本的环境[py,数据库等

2.django 项目迁移(没用)

1
2
3
sudo scp /home/tarena/django/mysite1
root@88.77.66.55; / home /root/xxx
请输入root密码:

3.用uwSGI替代python3 manage.py runserver 方法启动服务器
4.配置nginx反向代理服务器
5.用nginx配置静态文件路径,解决静态路径问题

wSGl (Web Server Gateway Interface)Web服务器网关接口,是
Python应用程序或框架和Web服务器之间的一种接口,被广泛使用
使用python manage.py runserver通常只在开发和测试环境中使用
当开发结束后,完善的项目代码需要在一个高效稳定的环境中运行,这时可以使用WSGl

uwSGI是wSGl的一种,它实现了http协议wSGI协议以及uwsgi协议
uwSGI功能完善,支持协议众多,在python web圈热度极高
uwSGI主要以学习配置为主

Ubuntu执行sudo pip3 install uwsgi==2.0.18 -i
https://pypi.tuna.tsinghua.edu.cn/simple/

检查是否安装成功
sudo pip3 freezelgrep -i ‘uwsgi’
如果成功安装,则会输出uwSGI==2.0.1

添加配置文件项目同名文件夹/uwsgi.ini
如: mysite1/mysite1/uwsgi.ini
文件以[uwsgi]开头,有如下配置项:套接字方式的IP地址:端口号【此模式需要有nginx】
socket=127.0.0.1:8000
Http通信方式的IP地址:端口号
http=127.0.0.1:8000

项目当前工作目录(绝对路径)
chdir=/home/tarenal…/my_project

项目中wsgi.py文件的目录,相对于当前工作目录
wsgi-file=my_project/wsgi.py

进程个数
process=4

每个进程的线程个数
threads=2

服务的pid记录文件
pidfile=uwsgi.pid

服务的目志文件位置(后台启动加日志)
daemonize=uwsgi.log

开启主进程管理模式
master=true

1
2
3
4
5
6
7
8
9
[uwsgi]
http=127.0.0.1:8000
chdir=/../../project
wsgi-file=project/wsgi.py
process=4 #cpu核数
threads=2
pidfile=uwsgi.pid
daemonize=uwsgi.log
master=True

启动uwsgi
cd 到 uwSGI配置文件所在目录

uwsgi --ini uwsgi.ini

停止uwsgi
cd 到uwSGI配置文件所在目录

uwsgi --stop uwsgi.pid

1,无论是启动还是关闭,都需要执行ps aux|grep 'uwsgi’确认是否符合预期
2,启动成功后,进程在后台执行,所有日志均输出在配置文件所在目录的uwsgi.log中
3,Django中代码有任何修改,需要重新启动uwsgi

redis

Redis是非关系型的kv型数据库
·特点:
1、开源的,使用C编写,基于内存且支持持久化

2、支持数据类型丰富,字符串strings,散列hashes,列表lists,集合sets,有序集合sorted sets等等

3、支持多种编程语言(CC++ Python Java PHP …)

4、单进程单线程

1、持久化
将内存中数据保存到磁盘中,保证数据安全,方便进行数据备份和恢复

2、过期键功能
为键设置一个过期时间,让它在指定时间内自动删除<节省内存空间>
#音乐播放器,日播放排名,过期自动删除

3、事务功能
弱事务型的数据库,只是具备简单的事务功能

4、主从复制
如何让redis保持高可用状态,官方提供主从搭建方案

5、Sentinel哨兵
在搭配了基础的主从结构后,哨兵可做到自动故障转移

1
redis-benchmark -q -n 10000

Ubuntu
安装: sudo apt-get install redis-server
安装后, Ubuntu会将redis列为开机自动启动项

服务端启动/停止/重启:
sudo /etc/init.d/redis-server status | start | stop | restart

客户端连接
redis-cli -h IP地址 -p 6379 -a 密码

1
2
3
redis-cli #进入服务
ping #校验是否进入服务
pong

·配置文件所在路径letc/redis/redis.conf
mysql的配置文件在哪里?
/etc/mysql/mysql.conf.d/mysqld.cnf

1
2
sudo cp /etc/redis/redis.conf /etc/redis/redis_bak.conf  #备份
sudo chown redis:redis /etc/redis/redis_bak.conf #更改权限

配置文件

1
2
3
requirepass 密码
sudo /etc/init.d/redis-server restart启动服务
redis-cli -h IP地址 -p 6379 -a 密码

websocket

安装组件

1
pip install channels

setting

1
2
3
4
#注册
'channels'
#增加语句
ASGI_APPLICATION = '项目名.asgi.application'

asgi文件

1
2
3
4
5
6
7
8
9
10
11
12
13
14
import os

from django.core.wsgi import get_wsgi_application
from django.core.asgi import get_asgi_application
from channels.routing import ProtocolTypeRouter, URLRouter
from . import routing

os.environ.setdefault('DJANGO_SETTINGS_MODULE', 'business_info.settings')

# application = get_wsgi_application()
application = ProtocolTypeRouter({
"http": get_asgi_application(),
'websocket': URLRouter(routing.websocket_urlpatterns)
})

创建routing

1
2
3
4
5
6
from django.urls import re_path
from business import comsumer

websocket_urlpatterns = [
re_path(r'ws/(?P<group>\w+)/$', comsumer.Chatsumer.as_asgi())
]

在app中创建会话文件comsumer

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
from channels.generic.websocket import WebsocketConsumer
from channels.exceptions import StopConsumer


class Chatsumer(WebsocketConsumer):
def websocket_connect(self, message):
# 客户发送websock连接触发,服务端允许和客户端创建连接不允许用raise StopConsumer()
self.accept()

def websocket_receive(self, message):
# 浏览器基于websocket向客户端发送数据,触发
print(message)
服务端断开连接
if value == '':
self.colse()
raise StopConsumer() #如果执行这个 最后一个disconnect就不执行了
return
self.send("none")

def websocket_disconnect(self, message):
# 客户端断开连接触发
print('断开连接')
raise StopConsumer()

在html界面中

1
2
3
4
5
6
7
8
9
10
11
12
13
14
<script>
socket = new WebSocket('ws://ip:port/route')
//几种状态
//建立好连接后,在后端self.accept()之后触发
socket.onopen = function(event){}
//每获取到数据后触发
socket.onmessage = function(event){}
//服务的断开连接触发
socket.onclose = function(event){}
//发送数据,发送到后端revi
socket.send()
//断开连接,发送到后端disconnect
socket.close()
</script>

连接方式

页面由正常路由发送进去(urls+views),和websocket相关的连接在html页面内置了WebSocket发送连接由routing+comsumer文件处理.

客户端推送消息

例如

1
2
3
4
5
6
7
8
9
10
11
12
<body>
<input type="text" placeholder="请输入内容" id="contend">
<input type="submit" value="点击发送" id="send" onclick="sendmssage()">
</body>
<script>
socket = new WebSocket('ws://localhost:8000/ws/123')
function sendmssage(){
contend = document.getElementById('contend');
socket.send(contend.value)
}

</script>

在comsumer中receive函数接收

1
2
3
4
def websocket_receive(self, message):
# 浏览器基于websocket向客户端发送数据,触发
print(message['text'])
self.send("none")

服务端推送消息

comsumer中connect函数发送

1
2
3
4
5
def websocket_connect(self, message):
# 客户发送websock连接触发,服务端允许和客户端创建连接不允许用raise StopConsumer()
print('用户连接')
self.accept()
self.send('welcome')

在html中

1
2
3
4
socket.onmessage = function (event){
//客户端收到的数据自动封装在这个函数event
console.log(event.data);
}

群聊实现

前端未变化,在后端增加一个全局变量,使用列表存储

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
All_list = []
class Chatsumer(WebsocketConsumer):
def websocket_connect(self, message):
self.accept()
All_list.append(All_list)

def websocket_receive(self, message):
mes = message['text']
for i in All_list:
i.send(mes)

def websocket_disconnect(self, message):
# 客户端断开连接触发
print('断开连接')
All_list.remove(self)
raise StopConsumer()

使用内存存储,在setting中配置

1
2
3
4
5
CHANNEL_LAYERS = {
'default': {
'BACKEND': 'channels.layers.InMemoryChannelLayer'
}
}

使用redis存储

1
pip install channels-redis

在setting中配置

1
2
3
4
5
6
7
8
CHANNEL_LAYERS = {
'default': {
'BACKEDN': 'channels_redis.core.RedisChannelLayer',
'CONFIG':{
'host':[('IP',port)]
}
}
}

consumer文件中

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
class Chatsumer(WebsocketConsumer):
def websocket_connect(self, message):
self.accept()
#获取url中的正则分组
group = self.scope['url_route']['kwargs'].get('group')
# 使用同步方法连接对象加入内存组里
async_to_sync(self.channel_layer.group_add)(group, self.channel_name)

def websocket_receive(self, message):
group = self.scope['url_route']['kwargs'].get('group')
# 同步化,对组名分别执行func,处理消息message
async_to_sync(self.channel_layer.group_send)(group, {'type': 'func', 'message': message})

def func(self, event):
mes = event['message']['text']
# 这里会对组里每个人发送信息
self.send(mes)

def websocket_disconnect(self, message):
group = self.scope['url_route']['kwargs'].get('group')
async_to_sync(self.channel_layer.group_discard)(group,self.channel_name)
raise StopConsumer()

项目

笔记项目

  • 产品/运营经理:负责产品功能细节的把控
  • 开发
    前端–负责显示部分内容的开发【多】
    后端–负责服务器部分的功能开发【少】
    运维–管理linux服务器,组件化配置
  • 安全问题
  • 测试–负责找出产品功能的问题(BUG)·美术-负责产品素材方面得绘制

登录界面视图

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
# 接收到get返回页面
print(request.POST)
if request.method == 'GET':
return render(request, 'user/index.html')
# 接收到post处理数据
elif request.method == 'POST':
user_name = request.POST['UserName']
email = request.POST['Email']
password = request.POST['Password']
# 判断密码重复是否正确
if password != request.POST['ConfirmPassword']:
return HttpResponse('非法操作')
# 判断是否已经有用户名
old_user_name = User.objects.filter(user_name=user_name)
if old_user_name:
return HttpResponse('用户已经注册')
# 判断邮箱是否已经存在
old_email = User.objects.filter(email=email)
if old_email:
return HttpResponse('邮箱已经注册')
#密码加密
hash_key = hashlib.md5()
hash_key.update(password.encode())
hash_key = hash_key.hexdigest()
hash_key += "note" #二次加密加字符串
hash_key_again = hashlib.md5()
hash_key_again.update(password.encode())
hash_password = hash_key_again.hexdigest()
# 写入数据库
try:
user = User.objects.create(user_name=user_name, password=hash_password, email=email)
except Exception as e:
return HttpResponse('用户名已注册')
request.session['uname'] = user_name #设置session
request.session['uid'] = user.id
return render(request, 'user/success.html')

​ 装饰器校验账户是否登录

1
2
3
4
5
6
7
8
9
10
11
12
13
# 定义装饰器
def check_login(fn):
def wrap(request, *args, **kwargs):
# if not request.session.get('username') or not request.session.get('uid'):
if 'username' not in request.session or 'uid' not in request.session:
c_name = request.COOKIES.get('uname')
c_id = request.COOKIES.get('uid')
if not c_name or not c_id:
return HttpResponseRedirect('/user/login')
request.session['username'] = c_name
request.session['uid'] = c_id
return fn(request, *args, **kwargs)
return wrap

退出登录

1
2
3
4
5
6
7
8
9
10
11
def login_out(request):
if 'username' in request.session:
del request.session['username']
elif 'uid' in request.session:
del request.session['uid']
resp = HttpResponseRedirect('/user/login')
if 'uname' in request.COOKIES:
resp.delete_cookie('uname')
elif 'uid' in request.COOKIES:
resp.delete_cookie('uid')
return resp