文章目录
- 前言
- Django的安装
- Django数据库的配置
- 补充
- --------------------------------------------------------------------------------------------------------------
- 表的创建
- 表的赋值
- 数据过滤器(查询)
- 模型成员
- 补充(Django数据库时区域问题)
- --------------------------------------------------------------------------------------------------------------
- 模板语句
- 模板继承
- 静态资源
- --------------------------------------------------------------------------------------------------------------
- 路由路径
- 路由反向解析
- GET获取参数
- POST获取参数
- META查看所有信息
- --------------------------------------------------------------------------------------------------------------
- COOKIE 会话
- SEESION 会话
- Token 会话
- --------------------------------------------------------------------------------------------------------------
- 模板迁移
- 模型关系
- 主从表
- --------------------------------------------------------------------------------------------------------------
- 原生缓存
- 使用Redis
- --------------------------------------------------------------------------------------------------------------
- 中间件
- 自定义中间件
- 示例(对首页进行限制访问)
- 总结
前言
在此之前,你需要掌握
- HTML
- CSS
- JS
- JQuery
- Linux
- SQL
- Redis
python部分:
- 多线程,多进程,同步,线程池,队列,了解GIL
- socket 网络编程 TCP,UDP,HTTP
最好掌握部分:
正则表达式
xpath
这个终章其实是对前面的django系列做一个汇总,到时候你直接翻看这一篇博客就好了。目前涉及了django的基本操作,模型,中间件,等等,还有常见的一些错误的解决方案。
Django的安装
虚拟环境的配置
在使用django之前建议先设置虚拟环境,这样做的目的是为了让你的环境更加的干净。
具体如何去做,可以见我先前写的博客:博客链接
这个写的是使用virtualenv和virtualenvwapper去搭建一个虚拟环境。这个效果是你直接使用pycharm是一样的,上面都有介绍,在Linux下和在Windows下。
你要是直接在pycharm的话只需要鼠标点点就好了。当然这里补充一下如何直接使用virtualenv在window下面使用,这个在我上面的博客里面没有说到。
1.创建项目文件
2.使用指令 virtualenv env 创建虚拟环境(此时文件名为env)
3.找到Script目录
4.执行启动脚本(关闭时启动关闭脚本)
之后在你的IDE里面指定虚拟环境就好了。
直接用pycharm的话就比较友好了。
创建Django项目
Django项目的目录结构
他们之间的关系如下图:
创建项目
django-admin startproject projectname
创建应用
django-admin startapp appname
创建完之后进行应用注册,pycharm可能创建django项目的时候不会默认帮你创建一个应用项目所以还要控制台自己创建,此外,pycharm默认帮你创建好了虚拟环境。
项目文件关系图
首先来看看路由文件,这个文件是负责处理请求路径的。
当路由过来后,执行views的函数。注意我这里的时APP下的函数,函数放在views下
模板文件
我们读取的HTML文件就放在我们的模板文件当中。
之后时models文件,这个就是负责我们对数据库的处理部分。
多重路由
当我们的逻辑处理复杂时就需要使用到这个玩意了,我们一般在项目设置多重路由。
我们可以在应用当中创建路由文件。
这个其实和主路由文件很像。
然后我们去主路由文件声明。
那么当你访问 /Two/index这个目录时就会得到响应。
Django数据库的配置
激活默认sqllite
在Django当作默认带有一个数据库
接下来请使用pycharm的数据库工具,vscode请下载相关插件(目前我学习的环境还是windows等我做项目了将迁移到Ubuntu,写md文档是个好习惯,我会不断记录并上传到csdn)
找到箭头指向的位置,我的时2018专业版,如果是其他版本可能不在这个位置,找一下。
那么此时你的右侧就会出现表,但是还没完,你得迁移一下。
输入指令(控制台)
python manage.py migrate
如果一切顺利的话你就可以看到如下图:
之后你就可以点击表进行查看,更改等,非常方便。
切换数据库(sql)
在我们的settings可以搞定修改,但是这里还有几个问题。
'ENGINE':'django.db.backends.mysql',
'NAME':'数据库名',
'USER':'username',
'PASSSWORD':'password',
'HOST':'host',
'PORT':'port',
下载驱动安装,这里我们必然使用pymysql。但是在使用是注意一个问题,找到,初始化文件。
写下这个代码,进行驱动伪装,伪装成MYSQLDB
之后迁移,
python manage.py migrate
数据库简单操作
创建表
这个在我们的models处理。
现在我们创建了表,但是还要,完成映射,也就是把文件代码转一下
python manage.py makemigrations
然后会生成这样的文件
然后在迁移
python manage.py migrate
CURD简单操作
这个就直接演示流程了。
1. 找到表,也就是定义表的那个类,这里是Student类
2. 对实例对象操作(students = Student())
3. 增加 例如: students.name = ‘jack’,students.name = 'jack1’此时就会加入两个数据
4. 读取 students.object,all()读取所有 students.object.get(条件例如:pk=2找到id=2的)
5. 修改,基于查询,找到后直接修改 例如:stu1 = students.object.get(pk=2) stu1.name=‘hello’
6. 删除,基于查询,直接delete() stu1.delete()
7. 最后,所有的操作都必须提交
8. students.save()否则提交不了
补充
示图过程
--------------------------------------------------------------------------------------------------------------
表的创建
表的简单创建
要想创建表,那么就必须先继承Model,这个其实是由于Django自己的一个数据处理方式,在Django当中所有的数据库操作都做了封装。
from django.db import models
class Student(models.Model):
S_name = models.CharField(max_length=32)
S_age = models.ImageField(default=1)
此时我们已经创建好了一个表。
但是我们在处理时还需要进行操作。
python manage.py makemigrations
python manage.py migrate
至于这是什么操作,我们得先来了解一下django对数据模型的处理。
Django的创建流程
在此之前我们先来看看Django对数据库处理这块的大致模式。
所以我们发现django之所以能够对数据库的操作做出封装,其实就是有一个处理机制,这个机制相当于翻译器,能够帮助我们把我们的操作,变成对应数据库的操作语言。
因此要想实现这个方案,那么就存在一个转换过程,和实际到数据库的操作过程。
而这个就是我们刚刚的
python manage.py makemigrations
python manage.py migrate
当执行第一条指令时,它生成了这样一个文件,里面写了这样一些东西。(这个其实昨天的博客有提到)
之后执行第二条指令,那么我们的表就创建好了。
但是这里注意的是,他这个名字并不是Student而是他自己又生成了一个名字,存在数据库里面。
“个性化”表的创建
对表字段的控制
db_colum 字段名
null False表示字段不可为空
blank False表示字段不可为空白
primary_key 设置为主键(默认会给你生成一个主键字段id,就像上面展示的表一样)
unqiue 是否为唯一
示例:
class Person(models.Mode):
p_name = models.CharField(max_length=16,unqiue=True,null=False)
p_age = modeIntegerField(default=18)
p_sex = models.BooleanField(default=False)
这里注意一下,对应sql而言其实,只有几个类型字符串,数字,日期这几种。那个Boolean其实在django中是使用短整型来做的。这一点可以去查看它所生成的DDL。
改表名
这个也简单,看代码
class Person(models.Mode):
p_name = models.CharField(max_length=16,unqiue=True,null=False)
p_age = modeIntegerField(default=18)
p_sex = models.BooleanField(default=False)
class Meta:
db_table = "Person"
但是请你注意一点,那就是无论是对表名进行修改还是对字段进行修改,其实都是修改的数据库里面的内容,当我们用代码进行控制时,还是以我们自己定义的类名,变量名来进行的!!!!!!
表的赋值
属性赋值法
这些方法是我自己定义命名的可能不准确.
这个是直接实例对象然后赋值,之后save()保存,上传到数据库的.
def set_value():
person = Person()
person.p_age=20
person.p_name = "XiaoMing"
person.save()
实例赋值法
这个就是在new(创建一个对象的时候直接传值)
person = Person(p_name="XiaoMing",p_age=20)
缺点就是在编写代码的时候IDE没有提示,而且容易搞错,且有Django版本兼容问题
自定义类方法赋值
我们来创建一个create()方法,在Person中
class Person(models.Mode):
p_name = models.CharField(max_length=16,unqiue=True,null=False)
p_age = modeIntegerField(default=18)
p_sex = models.BooleanField(default=False)#0女1男
class Meta:
db_table = "Person"
@classmethod
def create(cls,p_name,p_age=18,p_sex=True):
return cls(p_name=p_name,p_age=p_age,p_sex=p_sex)
这个cls其实就是我们的Person类
调用创建
Person = Person.create("XiaoMing")
Person.save()
数据过滤器(查询)
这里的话先简单分两大类,一个是返回满足条件的查询结果,一个是返回不满足条件的结果。
此外又可以细分为,返回多个结果的方法和返回一个的方法。此外我们还需要明白一点那就是所以的查询其实是通过objects来控制的,而这个其实是一个控制器,我们是可以自己指定的。
返回多结果的方法
all() 返回所有结果
filter() 返回满足条件的所有结果
exinclude() 返回不满足条件的所有结果
order_by() 排序order_by('p_age') 按照年龄排序
values() 这个可以对返回的结果包装成一个列表,每一个元素是一个字典({’字段名':value})适合转为json
返回单个的方法
get()
first()
last()
count() 统计个数
exisits() 返回布尔值
这些所有的方法都是可以连用的
查询示例
def get_age(request):
person = Person.objects.all().first()
print(person.name)
这个是一个简单的查询,后面还可以接很多的过滤…
例如我要查询年龄在大于18小于80的我们可以这样写
persons = Person.objects.filter(p_age>18).filter(p_age<80)
也可以这样写.
persons = Person.objects.filter(p_age__gt=18).filter(p_age__lt=80)
变量名__条件 = 值
这样的形式来
那么条件大概有
gt >
lt <
gte >=
lte <=
exact =
in 在什么中:Person.objects.filter(p_age__in=[18,20,30])contains : 包含类似于sql中的like like %xx%(icontains忽略大小写)
startswith :以什么开头(istartswith)
endswith :什么结尾(iendswith)
现在我查询名字当中有"X"的
persons = Person.objects.filter(p_name__contains="X")
for person in persons:
print(person.p_name)
那么现在我们通过这个来简单的去实现一下我们的人事验证,我们假设名字是用户名,年龄是密码,看看这个人是不是这个人.
def find_person(person_name,person_age):
persons = Person.objects.filter(p_name=person_name)
if person.exists():#person.count()
person = persons.first()
if person.p_age == person_age:
print("信息无误")
else:
print("年龄错误")
else:
print("查无此人")
查询切片
还是先前的例子.
persons = Person.objects.filter(p_age>18).filter(p_age<80)
这个家伙返回的是一个可迭代对象,那么就意味着可以进行切片.
但是这个切片的原理其实是类似于sql当中的limit offerset操作的.并不是直接把全部结果加载到内存当中,然后进行切片操作的.
persons = Person.objects.filter(p_age>18).filter(p_age<80)[0:3]
左闭右开
[0,3)
此外就是切片当中不能有复数原因就是那玩意是limit操作
跨关系查询
这个其实说白了就是表的关联.
现在假设创建两个表,一个是学生表,一个是班级表,关系为多对一。
class Grade(models.Model):
g_name = models.CharField(max_length=32)
class Student(models.Model):
s_name = models.CharField(max_length=16)
s_grade = models.ForeignKey(Grade) #添加外键约束
这里的话其实有两个方法,假设我们查询有个叫 小明 的学生的班级是什么.
第一个分开来一步步查询.我们先假设名字是唯一的
student = Student.objects.get(s_name="小明")
grade_name = student.s_grade.g_name
一次性查询,一步到位,我们直接查班级
grade_name = Grade.objects.filter(student__s_name="小明").first().g_name
F 对象
这个其实是和查询过滤有关的,在Django.db.models下,这个主要是可以帮助我们对两个字段进行比较.
例如:
class Company(models.Model):
c_name = xxxxx
c_girl = xxxxx 男孩人数
c_boy = xxxxxx 女孩人数
现在我要把男孩比女孩多的公司找出来
companies = Company.objects.filter(c_boy>F('c_girl'))
或者
companies = Company.objects.filter(c_boy__gt=F('c_girl'))
Q对象
这个主要是帮助我们多个条件过滤用的.好处就是不用写那么多过滤方法了.
例如:
persons = Person.objects.filter(p_age>18).filter(p_age<80)
可以改成这样
persons = Person.objects.filter(Q(p_age>18) and Q(p_age<80))
此外还可以这样
persons = Person.objects.filter(Q(p_age>18) & Q(p_age<80))
or ---> |
not ---> ~
聚合函数
举个例子找我们先前定义的Person我们找年纪最大的怎么找
max_person = Person.objects.aggregate(Max("p_age"))
Max也在 django.db.models下
此外还有:
Max
Avg
Min
Sum
Count
返回结果如下格式:
例如:{
'c_age_max':88}
这个其实是做了封装.
模型成员
在前面不知道你注意到了没有,每次我要查询数据都需要调用objects那么这玩意到底为何物嘞.这个其实就是一个模型.
但是这里分为两种,一种是隐性的一种是显性的.我们的objects就是隐性的,最明显的原因就是,我们压根就没有定义这玩意.这个是我们父类Model自带的.那么显性又是怎么样子的呢,显然我们自己定义的就是显性的.那么这样做的好处是什么呢,那就是我们可以自己制定一个规则.
下面举个简单的例子.
显性模型
class Person(models.Mode):
p_name = models.CharField(max_length=16,unqiue=True,null=False)
is_delete = models.BooleanField(default=False)#0没有1已删除
假设我们要拿到数据拿到的数据必然是没有删除的.那么常规下我们可以这样写
persons = Person.objects.filter(is_delete=False)
但是显然我们每次拿数据的时候都需要过滤一下,这个那么如果每次都这样写的话就会显得很麻烦,所以我们可以这样自己指定一下模型,写个过滤规则.
class PersonManager(model.Manager):
def get_queryset(self):#这个是模型返回的数据的方法
return super(PersonManager,self).get_queryset().filter(is_delete=False)
class Person(models.Mode):
p_name = models.CharField(max_length=16,unqiue=True,null=False)
is_delete = models.BooleanField(default=False)#0没有1已删除
objects = PersonManager()#你叫其他名字也可以到时候这样例如:p_m,那么调用就是:Person.p_m.all()等
下图是Model.Manger的部分代码
软删除
都说到这里了,就不得不说一下软删除这玩意,一个数据尤其是重要数据例如用户数据是不可能说删除就删除的,那么这个时候的删除并不会在数据库中真正删除,而是可以通过我们刚刚那个类似于is_delete的字段的值来进行适当的隐藏,过滤.也就是说在删除的时候我们只是设置那个值为真,并且我们自己指定了模型所以为真的就会先被过滤,那么一来数据就实现了一种删除效果.
怎么做呢,也很简单,重写delete方法即可.
class Person(models.Mode):
p_name = models.CharField(max_length=16,unqiue=True,null=False)
is_delete = models.BooleanField(default=False)#0没有1已删除
objects = PersonManager()#你叫其他名字也可以到时候这样例如:p_m,那么调用就是:Person.p_m.all()等
def delete(self):
self.is_delete = True
self.save()
这样一来就好了.
补充(Django数据库时区域问题)
由于在Django当中自己使用了一个时区(我们自己设置的,默认也是世界时间)
这个三个解决方案,一个是设置数据库的时区.
还有一个就是将错就错暂时不去生效时区设置
最后一个是校验时区,把我们当前的时区改为世界时区.
参考博客:django—时区问题(USE_TZ)
--------------------------------------------------------------------------------------------------------------
模板语句
模板是存在于HTML代码当中的,但是这个是不会被浏览器识别的是需要Django或者其他的框架或者渲染器渲染后,才会在浏览器当中显示的。在这里模板可以大致分为两种使用,一种是模板逻辑语句,例如条件判断,循环。另一种是赋值,这个通过强大的‘点’语法轻松搞定。
这里简单说一下django当中的执行流程。我们在使用时是了render直接把模板和数据给了出去。
事实上完整的步骤是先加载,然后渲染,然后使用httpResponse发送。(更新随缘,内容整理起来比较费劲)
temp = loder.get_template("Base.html")
content = temp.render(data)
return HttpResponse(temp)
插值
点 语法
在模板当中的点语法很有意思,它类似于python当中的点语法,点万物。
下面是一个示例:
通过key拿值
def Test(request):
Data = {
"name":"xiaoming",
"age":18,
"Person":{
"height":175},
}
return render(request,"base.html",content=Data)
现在拿到Data的名字
<p> {
{name}} </p>
拿到Person身高
<p> {
{Person.height}} </p>
通过属性&方法
此外点语法还可以调用方法。
举个例子:
class Student(models.Model):
S_name = models.CharField(max_length=32)
S_age = models.ImageField(default=1)
def get_s_name(self):
return self.S_name
def Test(request):
student = Student.objects.get(S_name="XiaoMing")
Data = {
"name":"xiaoming",
"age":18,
"Person":{
"height":175},
"student":studnet,
}
return render(request,"base.html",content=Data)
现在拿到student的名字
<p> {
{studnet.S_name}} </p>
或者
<p> {
{studnet.get_s_name}} </p>
通过索引
现在再改一下
def Test(request):
student = Student.objects.get(S_name="XiaoMing")
Data = {
"Person":[{
"height":175},{
"height":185}],
}
return render(request,"base.html",content=Data)
现在拿185
<p> {
{Person.1.height}} </p>
注入防范
这个原因很简单,我们说{ {value}} 这个是插值,待会会往里面插入数据,进行填充,那么问题来了,如果我插入了一段 HTML 代码呢? 在默认的情况下,如果插入的是HTML或者js代码模板是不会渲染的,它会屏蔽,但是如果要的话也是有方法的.
<p> {
{ code|safe}} </p>
这样一来如果你得code的值是HTML代码那么也会放到里面,并且同样具有相应的代码的功能在浏览器当中
所以你必须保证你得值是安全的,否则很有可能会被植入恶意js代码
此外还有方法
{ % autoescape off %} on是默认的开启
{
{ code}}
{
{code }}
{ % endautoescape %}
逻辑语句
先来说一下格式,这个插值呢是{ { }} 那么我们这个是{% 语句 %} {% end语句 %},这个就和HTML的标签类似,一般情况下有呼应.但也不是全部都有,个别的不用这和 **<input>**标签类似.
循环
def Test(request):
student = Student.objects.all()
Data = {
"students":studnet,
}
#return render(request,"base.html",content=Data)
return render(request,"base.html",locals())
locals() 方法会把局部变量转化为字典,效果和注释掉的代码一样
<ul>
{% for student in students %}
<li>{
{student.S_name}}</li>
{% endif %}
</ul>
此外我们还可以使用forloop.counter查看当前循环次数
<ul>
{% for student in students %}
<li>{
{
{forloop.counter}}-{student.S_name}}</li>
{% endif %}
</ul>
过滤器
这个呢就是对结果做个过滤.例如
def Test(request):
student = Student.objects.all()
count = 5
return render(request,"base.html",locals())
<p>{
{count|add:5}}</p>
|lower 小写
|upper 大写
对 count 加 5
这里没有减,你加个负数来做
乘除用这个
{% withratio count 1 5} count 乘以 5
{% withratio count 5 1} count 除以 5 (这个看一的位置,不怎么用,效率太低,对要加载,再运算再渲染,那直接
运算然后渲染不好吗)
有了这个以后我们就可以做余数运算了.
count | divisibleby:2 对2取余
判断语句
这里说明一下,这里的条件判断没有直接 if = value的使用,如果要这样用 就 equalif value 来代替,其他的IDE里面有提示.这个结合取余运算做个隔行变色的玩意来看看.
<ul>
{ % for studnet in students %}
{% if foorloop.counter|divisibleby:2 %}
<li style="color:red">{
{studnet.S_name}}</li>
{ #{% elif %} #} 这个是注释有elif分支判断
{% else %}
<li style="color:blue">{
{studnet.S_name}}</li>
{% endfor %}
</ul>
上面的代码有误,自行修正
注释
模板注释相比于HTML文本注释的好处就是,HTML的注释会在代码里面显示出来,但是模板的不会(在浏览器里面)
单行注释
{ # 内容 # }
块注释
{ % comment %}
注释内容
{% endcomment %}
模板继承
文件名
Base.html
<!DOCTYPE html>
<html lang="zh-CN">
<head>
<title>Title</title>
</head>
<body>
<div id="sidebar">
{% block sidebar %}
{% endblock %}
</div>
<div id="content">
{% block content %}
<p>我是Base</p>
{% endblock %}
</div>
</body>
</html>
现在就定义好了一个模板,我们使用 block 进行定义区域,也就是划分区域.当继承模板时字模板可以获得父模板的内容,但是在子模版当中的所以HTML代码都只能写在被block划分的区域当中,如果脱离的就会无效.此外子模板会覆盖在父模板当中的内容,如果要保存就需要被继承.
{% extends "base.html" %}
<h2>我在外面,不会显示,但是也不报错</h2>
{% block content %}
{
{block.super}} 显示 我是Base
{% for student in students %}
<p>{
{ student.S_name }}</p>
{% endfor %}
{% endblock %}
之后还有一个 include 这玩意可以把一个html文件直接加载过来当中一个部分,类似于frame
{ % include 'base.html' % }
这个方法对资源的消耗有点大,不建议使用.
静态资源
这个呢是什么呢很简单那就是我们的css js的放置,我们不可能把所有的这种文件都放在html文件当中,所有我们得那个文件专门存放。
之后去注册
或者使用模板动态加载,先加载资源
{% load static %}
然后 href = "{ % static ‘css/hello.css’ %} "
这样做好处就是以后项目变更只需要改配置文件就好了
--------------------------------------------------------------------------------------------------------------
路由路径
这里千万不要被误导了path是不支持正则的只有url才支持
这个其实主要详细说说那个路由,也就是我们的浏览器客户端访问的请求路径的问题。
和我们如何通过它或获取参数,以及如何通过请求(GET、POST)获取客户端的参数在Django当中.
匹配规则
在Django 当中其实这样的匹配是非严格匹配的,这个的话涉及到一些正则。
例如现在有这样的路由路径。
r"^adminnn"
r"^admin"
在你启动服务后,例如127.0.0.1:8000/admin
这个时候访问的很有可能是adminnn页面。匹配不严格,加上就近原则就导致了这个后果。
所以在书写的时候这个路径必须在后面加个 正斜杠 “/"
通过路由传递参数
假设我有个图片网站,我需要做个分页,那么显然我就需要用到路由来帮我们传递参数,例如有个页面
127.0.0.1:8000/image/
当我们在传递1的时候给出第一页的内容,2的时候给第二页的内容。
127.0.0.1:8000/image/2
这个时候我不可能写一堆路由,那么改如何来做
这个时候就要用到正则了
r"^image/(\d+)/
这样一来就解决问题了
那么如何拿到参数呢刻个看view 的处理函数
def images(request,id):
pass
这样 id 就是我们拿到的数据
如果我们有多个的话,例如
r"^image/(\d+)/(\d+)/
那么我们的接受参数也要有多个,并且这是一一对应的按照顺序
def images(request,id,id1):
pass
这样做显然不太方便,有时候我们的参数顺序可能会搞混,那么我们就需要起个名字.
r"^image/(?P<username>\d+)/(?P<password>\d+)/
def images(request,username,password):
pass
子路由
注意默认的path
这个在先前的博客说到很清楚了.(写博客的最主要的目的还是为我自己服务的,我只是将它整理公开了罢了,如果你们觉得乱要么是你没有按顺序看我的博客,这玩意是有顺序的,像这种学习博客.要么就是没有认真看,或者你就是接受不了这种风格.这个都无所谓,还是那句话,这个主要是为我自己服务的,你白嫖还有理了??!!!私信本人大概率不回,没开,除非付费)
Django学习系列(一.初识)
路由反向解析
这个作用就是为了动态的加载请求路径,放置路径更改导致客户端请求失败,这个要配合模板语法.在客户端.
url(r'^admin/', admin.site.urls,name="admin"),
url('',views.hello),
url(r"^app/", include(("APP.urls","APP"),namespace="APP")),#在根路由里面注册子路由
url(r"^app/", include(“APP.urls”,namespace=“APP”))这样写也可以,但是在子路由里面得加个app_name
app_name='APP'
urlpatterns = [
path('',index,name='index'),
]
在子路由里面是这样的
from django.conf.urls import url
from Two import views
#创建子路由
urlpatterns=[
url(r'^index/',views.hello,name="hello"),
]
现在访问admin 和 tow/hello
<a href="{% url 'admin' %}"></a>
<a href="{% url 'two:hello' %}"></a>
那么在代码中的话也简单
url = reverse("admin")
url1 = reverse("two:hello")
搞定
GET获取参数
首先是URL的组成,这个玩这个的应该都很熟悉.
这玩意怎么玩呢,那就是通过这几篇博客都没有提到的request.
这里面包含了所有客户端的所有信息,包括COOKIE IP 你的设备型号,甚至你的设备安装的软件.基本上通过request可以获取你电脑上的大部分信息,所以请不要随便访问不知名的恶意网站,尤其是某小网站等等,你的信息暴露无遗.
先说说request大致有哪些玩意吧
request.method
.path
.GET
.POST
.session
这几个东西一目了然,处理session,这个是会话技术,下篇博客就说
现在构造这样一个url
http://127.0.0.1:8000/login?username="jack"&password="123456789"
获取username,password
def get_parmers(request):
username = request.GET.get("username")
password = request.GET.get("password")
这里还有个细节
那就是get拿到的其实不是一个完整的values
假设有这样一个请求
http://127.0.0.1:8000/login?username="jack"&username="surry"
那么现在再来拿到username
def get_parmers(request):
username = request.GET.get("username")
返回结果是:
surry
所以这个时候要这样
def get_parmers(request):
username = request.GET.getlist("username")
结果是个列表
POST获取参数
这个就简单了,更简单了.看这个代码
<form action="/test/" method="POST">
<input name = "username" type = "text">
<button>提交<button>
</form>
def get_parmers(request):
username = request.POST.getlist("username")
直接通过标签的name拿到
META查看所有信息
拿到IP
IP= request.META.get("REMOTE_ADDR")
这个你看了就知道了
for key in request.META:
print(request.META.get(key))
--------------------------------------------------------------------------------------------------------------
COOKIE 会话
在开始之前记住一句话,那就是cookie一定是服务器发送给客户端,并且保存在客户端的
cookie流程
示例
下面我们举个例子,做个简单的所谓的登录页面,这里为了简化流程,做个了解不会涉及到验证环节(下面的token会加入这个环节)
登录页面
login.html
<form action="/login/" method="POST">
<input name = "username" type = "text">
<button>提交<button>
</form>
minespace.html
<h2>欢迎你:{
{
username}}!</h2>
def login(request):
if request.method == "GET":
render(request,"login.html")
elif request.method =="POST":
username = request.POST.get("username")
response = HttpResponse("登录成功")
response.set_cookie('username',username)
# 这个cookie是可以设置存活时间的,默认是关了浏览器就结束,具体可以查查就是设置一个参数,max-age
#render也是可以设置的,那其实就是个简写,你直接看源码就看得到
return response
def minspace(request):
username = request.COOKIE.get("username")
if username:
return render(request,"minspace.html",context=locals())
else:
return redirect("/login/")
# 失败就返回登录页面
退出登录
def logout(request):
response = render(request)
response.delete_cookie("username")
return response
cookie加密
现在我们这个还是明文的,显然这个并不安全
这样一来一个非常简单的流程就实现了.虽然这里连简单的验证都没有.
那么加密的话也简单.
前面的设置cookie加密,然后读取的时候解密.
response.set_signed_cookied('username',username,salt="密钥")
request.get_signed_cookied("username",salt="密钥")
SEESION 会话
这个的话其实在代码上和我们的cookie类似,其实本质上也是一样的.但是区别的是,在客户端保存的cookie其实是个钥匙,这个是在django里面保存临时用户的信息的.
我们可以来看看这个表
客户端保存的cookie就是session_key字段的东西
流程如下
示例
所有的页面关系和刚刚一样
def login(request):
if request.method == "GET":
render(request,"login.html")
elif request.method =="POST":
username = request.POST.get("username")
response = HttpResponse("登录成功")
request.session["username"]=username
# 这个cookie是可以设置存活时间的,默认是关了浏览器就结束,具体可以查查就是设置一个参数,max-age
#render也是可以设置的,那其实就是个简写,你直接看源码就看得到
return response
def minspace(request):
if request.session.get("username"):
#他会查看客户端返回的cookie然后获取username(被加密)的session_key然后到数据库去看
#如果没有查询到那么就会报错,所以这里最好加个
return render(request,"minspace.html",context=locals())
else:
return redirect("/login/")
# 失败就返回登录页面
退出登录
def logout(request):
response = render(request)
#response.delete_cookie("username") 删除客户端cookie
#del request.session["username"] 删除数据库的信息,但是这里只是删除了值,没有删除那一个行
request.flush() #这个都操作了,并且数据库那一行直接删除 clear() 直接清空数据库
return response
在session当中的话是可以直接输入中文的,这个是由于它做了很多转码,然后他自己做了很多加密.
这个在django当中是存到一个数据库里面的,其他的就不清楚了.同样这个也是有失效的,在数据库里面默认是14天有效.但是这个只是有效,是那个key有效,并不似会自动删除key.那个得你自己去操作.
Token 会话
这个其实是自定义一个cookie会话技术,通过前面的流程图其实已经很清楚了所谓的会话技术是什么,这个说白了就是给个用户暂时的通行证.那么为什么会诞生这玩意呢,其实还是为了满足非BS应用和移动应用.毕竟cookie这玩意只有浏览器支持.所以为了满足其他的需求我们需要不是像浏览器那样基于浏览器会自动提交cookie来验证.那么在我们其他客户端一般是使用json数据,我们返回一个字段token,这个就是我们的暂时令牌.接下来我们继续以浏览器为例自己实现个Token.当我们在例如安卓时,我们直接返回json数据带入token字段即可
例如:
data = {
'msg':"hello,
'satus':800,
'token':token,}
我们服务端要做的就是把token给客户端,在服务器里面能够识别.至于其他的,那得去看客户端的开发者(嘿嘿就是甩锅)
现在我们来建表把完成的流程走一下.
class User(models.Model):
user_name = models.CharField(max_length=64,unqine=ture)
user_password = models.CharField(max_length=64)
token = models.CharField(max_length=256)
这里直接把HTML代码省略了,反正用户就是username 密码就是 password
注册:
def register(request):
if request.method == "GET":
return render(request,"register.html")
elif request.method == "POST":
username = request.POST.get("username")
password = requrst.POST.get("password")
user = User.objects.filter(user_name = username)
if user.exists():
return render(request,"register.html")
else:
user = user.first()
user.user_name=username
user.user_password = password
user.save()
return HttpResponse("注册成功")
登录:
def login(request):
if request.method=="GET":
return render(request,"login.html")
elif request.method == "POST":
username = request.POST.get("username")
password = requrst.POST.get("password")
user = User.objects.filter(Q(user_name=username)and Q(user_password=password))
if user.exists():
user = user.first()
ip = request.META.get("REMOTE_ADDR")
token = JiaMi(ip,username)
user.token = token
user.save()
response = render(request,"minespace.html")
response.set_cookie("token",token)
return response
else:
#用户不对/密码
return render(request,"login.html")
我们在这里定义一个简单的加密程序
def JiaMi(ip,username):
time = time.ctime()
return hashlib.new('md5',(ip+username+time).encode('utf-8')).hexdigest()
个人空间模块
def minespace(request):
token = request.COOKIE.get("token")
try:
user = user.objects.get(token=token)
username = user.user_name
return return render(request,"minspace.html",context=locals())
expect Expection as e:
return redisrect("/login/")
minespace.html
<h2>欢迎你:{
{
username}}!</h2>
流程图
最后那就是删除,这还不简单.而且观察上面的流程图其实这个和我们的session很像,只是我们自己定义了加密手段,并且区别是在未来我们可以通过客户端返回json来维持会话,而不是cookie,当然我们也可以通过cookie如果支持的话.
--------------------------------------------------------------------------------------------------------------
模板迁移
这个是老生常谈的问题,这里的话简单说一下坑点。
模板记录
很多朋友在对原有的表的结构进行修改的时候,肯定会直接把django原来生成的init文件删除然后重写,迁移写入。但是在写入的时候压根没有反应,你的数据库的表压根没动。这个原因其实就是因为,django当中的记录没有删掉。
这个表里面存放了大量的迁移记录,你只有把对应的迁移记录给删掉,那么系统才会重新给你迁移,然后写入数据库。
模型反射
我们前面的操作
python manage.py makemigrations
python manage.py migrate
都是将Model变成sql,那么现在就将sql变成Model,也就是把我们在数据库当中建好的表变到我们的django模型当中,这个也简单。
python maganer.py inspectdb > APP/models.py
这样就把这玩意搞到我们的模型文件中了
模型关系
这个其实就是我们以前学数据库的表之间的对应关系。
例如:一对一,一对多,多对多
一对多
这个玩烂了其实,也是最基础的,其他的关系其实都是通过它来建立的。在sql语句当中我们直接设置一个外键就好了,同样在django中也是这样的。
看代码:
class Grade(models.Model):
g_name = models.CharField(max_length=32)
class Student(models.Model):
s_name = models.CharField(max_length=16)
s_grade = models.ForeignKey(Grade) #添加外键约束
这个就是一个非常典型的一对多,谁多谁就添加外键,在djnago当中,这个外键参照的其实就是id,在django当中的模型会默认生成一个id,所有的关系默认都是通过id来进行绑定的。这个其实没什么好说的非常好理解就是班级和学生的关系,在学生表里面加一个字段用来保存班级的id,这个字段参照(reference Grade(id) )并且可以重复出现班级的id(多个学生在同一个班级),但是在一对多当中,假设(班级与学生一对一)那么在学生表中存储班级id的字段就不允许重复!
一对多
这个看我们上面的那个图其实已经很明显了。
那么在django当中这样表示就好了
class Person(models.Model):
p_name = models.CharField(max_length=32)
class Person_id(models.Model):
p_id = models.CharField(max_length=32)
p_person = models.OneToOne(Person)
现在用身份证和人的关系来做比喻
多对多
这个就要注意一点了,因为操作时不太一样的。
那么是怎么是实现的呢,我们还是先举个例子,购物车和人的关系,这个就是典型的多对多。
class Customer(model.Model):
c_name = models.CharField(max_length=16)
class Goods(models.Model):
g_name = models.CharField(max_length=16)
g_customer = models.MangToMangField(Customer)
现在我们把模型建好了,那么我们先对模型进行操作,后面我们再来说说这玩意是怎么做的。
获取查询部分还是和我们以前一样,但是这里多了一个添加关系,例如我们的购物车里面增加了一个商品。
Phone = Goods()
Phone.g_name="iPhone12"
Phone.save()
Me = Customer.objects.get(c_name="xiaoming")
Me.goods_set.add(Phone)
.remove(Phone)
.set(Phone1,Phone2,...)
当然我们也可以反过来,但是其实这些效果都是一样的,原因看下面的图你就明白了。
那么如果要自己实现的话也是这样开设计,甚至只会更加复杂。
主从表
每次涉及到这多表关系的时候就会出现联表关系的问题,在django当中凡是加了约束的例如人与身份证的那个关系中的身份证表就是从表,当主表删除时,从表也会删除(delete on cascade),这个是默认的,所以有时候为了数据安全,我们应该设置一下,例如只有当从表中对应的数据删了,那么主表才能被删除。
class Person_id(models.Model):
p_id = models.CharField(max_length=32)
p_person = models.OneToOne(Person,ondetele=models.PROTECT)
当然还有其他模式
SETNULL
SET_DEFAULT()
SET()
--------------------------------------------------------------------------------------------------------------
原生缓存
很多人都是django是面向sql框架,原因是django的Model大部分面对的是sql。这个原因其实很多,最主要的其实还是因为大部分情况下我们对sql的依赖要更高,一个网站可以没有redis但是没有sql的话那么压根就搞不下去,除非你只是一个极其简陋的信息展示的静态页面,没有数据交互,或者说交互极少。那么在django当中也是可以使用其他数据库的,只是支持相对于sql没有那么好。
这个缓存服务其实在django当中有个内置的cache,他会把缓存存到数据库当中,当然这样做必然没有redis那么快,不过相对而言硬盘可比内存便宜多了。
建表这个得先建表
python manage.py createcachetable my_table_name
配置:
这个打开设置文件
CHACHES={
'default':{
'BACKEND':"django.core.cache.backends.db.DatabaseCache",
'LOCATION':"my_cache_table",
'TIMEOUT':'60',
}
}
当然这里还有其他的配置,前面那几个基本上够用了。当然你还可以加上option
CHACHES={
'default':{
'BACKEND':"django.core.cache.backends.db.DatabaseCache",
'LOCATION':"my_cache_table",
'TIMEOUT':'60',
'OPTIONS':{
"MAX_ENIRIES":'300',},
'KEY_PREFIX':'Huterox',
}
}
使用缓存加速
这里有很多方法,最简单的就是直接来个缓存装饰器。
def index(request):
sleep(5)
return HttpResponse("Hello")
@chache_page(60,'Huterox')#过期时间,如果你加了option选项那么再把相应的参数给上
def index(request):
sleep(5)
return HttpResponse("Hello")
那么一个完整的流程如下:
自定义缓存
前面的装饰器一刀切,这样显然不太好,而且后面我们做限制用户访问次数的时候也应该自定义一下。
先来几个方法
set(key,value)
get(key)
这玩意就是核心
def index(request):
result=cache.get('index')
if result:
return HttpResponse(result)
else:
#index=....
#此处是index的页面的操作为了简便我就直接让它睡五秒模拟加载的页面很多。
sleep(5)
index="Hello"
cache.set('index',index,timeout=60)
return Httpresponse(index)
使用Redis
由于我们django原生没有redis所以我们要安装第三方django插件
pip install django-redis
pip install django-redis-cache
然后配置
这个配置其实有两种方法
一个是直接配置
CHACHES={
'default':{
'BACKEND':"django_redis.cache.RedisCache",
'LOCATION':"redis://127.0.0.1:6379/1",#数据地址端口和数据库的库(/1第一个库)
"OPTIONS":{
"CLIENT_CLASS":"django_redis.client.DefaultClient",
}
}
}
那么要是这样配置的话,那么还是老方法使用。
还有一种配置是两个一起用。
CHACHES={
'default':{
'BACKEND':"django.core.cache.backends.db.DatabaseCache",
'LOCATION':"my_cache_table",
'TIMEOUT':'60',
}
'redis':{
'BACKEND':"django_redis.cache.RedisCache",
'LOCATION':"redis://127.0.0.1:6379/1",#数据地址端口和数据库的库(/1第一个库)
"OPTIONS":{
"CLIENT_CLASS":"django_redis.client.DefaultClient",
}
}
}
那么在使用的使用我们换一下
如果你想要继续使用原生的那么cache()或者
cache = caches['default']
@cache_page(60,cache='default')
如果用redis的话
cache = caches['redis']
@cache_page(60,cache='redis')
--------------------------------------------------------------------------------------------------------------
中间件
流程图
中间件是什么其实就是面向AOP编程的产物,说通俗一点就是一个装饰器,只不过这个装饰器比较厉害。
那么在Django当中这玩意大概是个什么样的呢,我们先来看看流程图。
那么看清楚了上面的图后,那么接下来就好办了。
自定义中间件
我们在项目的根目录下创建一个文件加 middleware
目录是(相对)
/middleware/LearnMiddle
里面有个类
class HelloMiddle(MiddlewareMiXin)
def process_request(self,request):
pass
然后注册
middleware.LearnMiddle.HelloMiddle
之后我们就可以在中间件中写东西了。
示例(对首页进行限制访问)
class HelloMiddle(MiddlewareMiXin)
def process_request(self,request):
if request.path=='/index/':
black_list = cache.get('black',[])#黑名单,指定返回数据类型为列表
ip = request.META.get("REMOTE_ADDR")
if ip in black_list:
return HttpResponse("你已被列入黑名单")
requests_num = cache.get(ip,[])
while requests_num and time.time()-requests_num>60:
requests_num.pop() #用户访问时间清洗
requests_num.app(time.time())#requests_num.insert(0,time.time())
cache.set(ip,requests_num,timeout=60)
#其实如果timeout=60的话那么就没有必要进行数据清洗了
if len(requests_num)>10:
return HttpResponse("小伙子你的访问太频繁了")
if len(requests_num)>30:
black_list.append(ip)
cache.set("black",black_list,timeout=60*60*24)
其他的看着办就好了。到这里基本上django可以说差不多了,至于其他的还有一些常用插件,和django使用nginx(django自己的runserver性能较低)
总结
基本上差不多就这样吧,后面可能还会对本篇博客更新