django 之(三) --- 会话|关系|静态*

会话技术

  HTTP在web开发中基本都是短连接[一个请求的生命周期都是从request开始到response结束]。

  下次再来请求就是一个新的连接了。为了让服务器端记住用户端是否登陆过就出现了会话技术

 种类:

  • Cookie: 客户端[浏览器端]会话技术。
  • Session:服务端会话技术
  • Token:  服务端会话技术

 总结:

  • Cookie使用更简洁,服务器压力更小,数据不是很安全

  • Session服务器要维护Session,相对安全

  • Token拥有Session的所有优点,自己维护略微麻烦,支持更多的终端

Coookie

 简单介绍

  • Cookie本身是由浏览器生成,客户端[浏览器端]会话技术。
  • Cookie中的数据是以“键值对”方式存储在客户端的。
  • Cookie是可以支持过期时间、默认不支持中文[加盐也不支持]
  • Cookie不可跨域名、不可跨网站、不可跨浏览器使用
  • 默认Cookie会携带本网站所有Cookie通过Response将cookie值写到浏览器端,下一次request访问时浏览器会携带cookie到服务器端验证身份。

 设置Cookie

  • response.set_cookie(key, value [,max_age=None,exprise=None]) 
    • response.set_cookie(key, value, max_age=None, exprise=None)
    • max_age:整数,指定cookie过期时间。[max_age和expries两个任选一个指定]  
      • max_age 设置为0浏览器关闭失效,设置为None永不过期
    • expries:整数,指定过期时间,还支持是datetime或timedelta,可以指定一个具体日期时间 
      • expires = timedelta(days=10) 10天后过期

 获取Cookie:

  • request.COOKIES.get('key')

 简单使用:

  • urls.py
from django.conf.urls import url, include
from django.contrib import admin

urlpatterns = [
    url(r'^admin/', admin.site.urls),
    url(r'App/',include('App.urls',namespace='App')),
]
  • App/urls.py
1 from django.conf.urls import url
2 from App import views
3 
4 urlpatterns=[
5     url(r'^setcookie/',views.set_cookie,name='set_cookie'),
6     url(r'^getcookie/',views.get_cookie,name='get_cookie'),
7 ]
  • App/views.py
 1 # cookile 设置
 2 def set_cookie(request):
 3     response = HttpResponse('设置cookie')
 4     response.set_cookie('username','gypls')
 5     return response
 6 
 7 
 8 # coockie 获取
 9 def get_cookie(request):
10     username = request.COOKIES.get('username')
11     return HttpResponse(username)

  加密 [加盐]

  • response.set_signed_cookie('key', value, '加盐密钥')
    • response.set_signed_cookie('content', uname, 'gypls')

 解密

  • request.get_signed_cookie('key', salt='加盐的密钥')
    • request.get_signed_cookie('content', salt='gypls')

 删除

  • response.delete_cookie('key')
    • response.delete_cookie('content')

 使用方法:

  • App/urls.py 
 1 from django.conf.urls import url
 2 from App import views
 3 
 4 urlpatterns=[
 5     #登陆
 6     url(r'^login/',views.login,name='login'),
 7     url(r'^mine/',views.mine,name='mine'),
 8     #退出
 9     url(r'^logout/',views.logout,name='logout'),
10 ]
  • App/views.py
 1 # 登陆
 2 def login(request):
 3     if request.method == "POST":
 4         uname = request.POST.get('uname')
 5         response = redirect(reverse('app:mine'))
 6         response.set_signed_cookie('content', uname, 'gypls')      # 加盐[加密]
 7         return response
 8     return render(request, 'login.html')
 9 
10 # 个人中心
11 def mine(request):
12     try:
13         uname = request.get_signed_cookie('content', salt='gypls')  # 解密
14         if uname:
15             return render(request, 'mine.html', context={'uname': uname})
16     except Exception as e:
17         print('获取失败')
18     return redirect(reverse('app:login'))
1 def logout(request):
2     response = redirect(reverse('app:login'))
3     response.delete_cookie('content')
4     return response

Session

  简单介绍

  • Session服务端会话技术,数据存储在服务器中。Session原本默认存储在内存[RAM]中,

  • Django中会默认会把Session持久化[存]到数据库session表中,默认过期时间是14天

  • 主键是字符串。数据是使用了数据安全[使用的base64,在前部添加了一个混淆串]

  • Session依赖于Cookie,将Session的session_key赋值到Cookie中某个建传给服务器端。

  • 下次请求的时候,服务器端通过Cookie中的session_key去数据库中查询,有则认证通过
  • 经验之谈:数据中以"="结尾,一般使用的是base64编码

  简单总结

  Session是服务端会话技术,数据存储在服务端。当我们调用request.session['key']时,Django默认会帮我们将这个session键和值加密并持久化的

  存到名为Django_session的数据库表中,"键"存储到一个session_key字段,我们的session数据会存到session_data字段中,同时会有一个过期时

  间字段expire_data,默认设置过期时间为14天。同时通过Cookie将这条数据的唯一标识session_key传给客户端,客户端给这个标识起了一个新的

  名字叫session_id,就把session_key的值存到了cookie的session_id里面。以后再来请求服务器就会带着这个session_id,根据session_id中存放的

  session_key就可以在数据库中的Django_session表中找到我们的session值,从而实现会话技术

 设置Session

  • request.session['key'] = value  [存session是存在服务器端的,所以用request]
    • username = request.POST.get('username')

    • request.session['username'] = username 

 获取Session

  • request.session.get('key')
    • request.session.get('username')

 删除Session  

  •  删除cookie的sessionid键值对来实现session退出登陆。response.delete_cookie('sessionid')
    • 缺陷:django_session表中的数据不会被删除
  •  删除session直接来实现退出登陆。 del request.session['key']
    • 缺陷:django_session表中的数据不会被删除
    • del request.session['username']
  • 全部删除数据 session 和 cookie。    request.session.flush()
    • 最优选择

 简单使用:

  •  urls.py
1 from django.conf.urls import url, include
2 from django.contrib import admin
3 
4 urlpatterns = [
5     url(r'^admin/', admin.site.urls),
6     url(r'Two/',include('Two.urls',namespace='two')),
7 ]
  • Two/urls.py
1 from django.conf.urls import url
2 from Two import views
3 
4 urlpatterns = [
5     url(r'^login/',views.login,name='login'),
6     url(r'^mine/',views.mine,name='mine'),
7     url(r'^logout/',views.logout,name='logout'),
8 ]
  • Two/views.py
 1 from django.shortcuts import render, redirect
 2 from django.urls import reverse
 3 
 4 # 登陆
 5 def login(request):
 6     if request.method == "GET":
 7         return render(request,'two_login.html')
 8     elif request.method == "POST":
 9         username = request.POST.get('username')
10         request.session['username'] = username
11         return redirect(reverse('two:mine'))
12 
13 def mine(request):
14     username = request.session.get('username')
15     # username = request.session['username']  此方法如果key不存在会抛异常,不建议使用
16     return render(request,'two_mine.html',context=locals())
17 
18 # 退出登陆的三种方式
19 def logout(request):
20     response = redirect(reverse('two:login'))
21     # 删除cookie 来实现退出登陆。缺陷,django_session表中还会有数据
22     response.delete_cookie('sessionid')
23     # 删除session 来实现退出登陆。缺陷,django_session表中还会有数据
24     del request.session['username']
25     # 全部删除数据 session 和 cookie。最优,django_session表中的数据也会被清除掉
26     request.session.flush()
27     return response 

总结

 常用操作

  • get(key, default=None)根据键获取会话的值
  • clear()清楚所有会话
  • flush()删除当前的会话数据并删除会话的cookie
  • delete request['session_id'] 删除会话
  • session.session_key获取session的key

 设置数据

  • request.session[‘user’] = username
  • 数据存储到数据库中会进行编码使用的是Base64

Token

 简单介绍: 

  • Token是服务器端会话技术,相当于自定义的session。
  • Token如果在web开发中,使用起来基本和session一致,也是依赖于cookie。
  • Token是在服务器端生成唯一标识"键",真实开发中会Token当作"键"与用户信息当作"值"一起存储到缓存cache中,并设置过期时间。
    • 如果是使用在移动端类型的客户端开发中,通常将Token以json格式传输给移动端,然后移动端需要自己存储Token。
    • 移动端获取需要Token认证的相关数据的时候[如登陆后的界面],主动将移动端存储的Token传递给服务器端进行验证。
    • 服务器端通过移动端传递过来的Token"键",去缓存cache中查找对应的值"用户信息",从而实现移动端和服务器端的会话技术。

 简单使用:

  • Two/urls.py
1 from django.conf.urls import url
2 from Two import views
3 
4 urlpatterns = [
5     url(r'^register/',views.register,name='register'),
6     url(r'^studentlogin/',views.student_login,name='student_login'),
7     url(r'^studentmine/',views.student_mine,name='student_mine'),
8 ]
  • Two/models.py
1 from django.db import models
2 
3 class Student(models.Model):
4     s_name = models.CharField(max_length=16,unique=True)
5     s_password = models.CharField(max_length=128)
  • Two/views.py
from django.core.cache import cache
from django.http import HttpResponse
from django.shortcuts import render, redirect
from django.urls import reverse
from Two.models import Student
import uuid
 1 # token 会话。用户注册
 2 def register(request):
 3     if request.method == 'GET':
 4         return render(request, 'student_register.html')
 5     elif request.method == 'POST':
 6         username = request.POST.get('username')
 7         password = request.POST.get('password')
 8         try:
 9             student = Student()
10             student.s_name = username
11             student.s_password = password
12             student.save()
13         except Exception as e:
14             return redirect(reverse("two:register"))
15         return redirect(reverse("two:student_login"))
 1 # token 会话。用户登陆
 2 def student_login(request):
 3     if request.method == 'GET':
 4         return render(request, 'student_login.html')
 5     elif request.method == 'POST':
 6         username = request.POST.get('username')
 7         password = request.POST.get('password')
 8         students = Student.objects.filter(s_name=username).filter(s_password=password)
 9         if students.exists():
10             student = students.first()
11             names = student.s_name
12             # 生成唯一表示Token
13             token = generate_token()
14             # 将token存到缓存中。cache.set(键[Token],值,过期时间)
15             cache.set(token, names, 60 * 60 * 24)
16             # pc端的请求返回方式
17             response = HttpResponse('用户登陆成功')
18             # 依赖于cookie将token返回给pc浏览器端
19             response.set_cookie('token', token)
20             return response
21         # pc端返回
22         return redirect(reverse('two:student_login'))
23         # 移动端登陆成功返回数据的方法,仅为样式不可执行
24         #     data = {
25         #         'status':200,
26         #         'msg':'login success',
27         #         'token':token
28         #     }
29         #     return JsonResponse(data=data)
30 
31         # 移动端登陆验证失败返回数据的方法,仅为样式不可以执行
32         # data = {
33         #     'status':800,
34         #     'msg':'verify fail',
35         # }
36         # return JsonResponse(data=data)
1 # 自定义tokon
2 def generate_token():
3     # 使用uuid设置token[键]的唯一值
4     token = uuid.uuid4().hex
5     return token
 1 def student_mine(request):
 2     token = request.COOKIES.get('token')
 3     try:
 4         # 验证是否有token值[是否是登陆状态]
 5         sname = cache.get(token)
 6         print(sname)
 7         print(type(sname))  # str
 8     except Exception as e:
 9         return redirect(reverse('two:student_login'))
10     # pc浏览器端返回
11     return HttpResponse("用户的姓名:", sname)
12     # 移动端返回数据样式。此处代码仅为样式不可执行
13     # data = {
14     #     'msg':'ok',
15     #     'status':200,
16     #     'data':{
17     #         'username':student.s_name,
18     #     }
19     # }
20     # return JsonResponse(data=data)

模型关系

 常见的几种数据关系,django都提供了很好的支持。主从表定义:当系统遭遇不可避免的毁灭时多张只能保留一张表,保留的就是主表。[如用户表]

 在企业开发中,一般主表数据不会被随意删除,因为其可能及联好多从表的数据,主表数据一旦删除从表数据也会被及联删除,可能导致系统崩溃

一对一 [1:1]

 业务场景: 

  • 拆分:
    • 比如一张用户表,其功能过于强大,一张用户表中就可能会出现两百多个字段甚至更多,管理起来很麻烦,查询效率也会大大降低。
    • 这时就需要将其表中的数据拆分开,将一张用户表拆分成一张主表和若干张从表,这样每张表有十几个字段,每张表负责不同的功能。
  • 级联:
    • 比如在已经设计好的一张用户表中,开发过程中功能是增量式的操作,需要增加新的字段来实现增加新的功能,比如token认证。
    • 就可以新建一张表来设置这些字段,然后和原来的用户表进行一对一的绑定,就可以实现不改变原来表的基础上增加新字段。

 使用实现

  • 实现: 
    • 在Django模型中使用 models.OneToOneField(要关联表的模型名其他约束) 进行关联。谁声明关系谁是从表
    • 其实还是借助外键实现的。使用外键实现,对外键添加唯一约束。主表数据删除时,从表数据会被及联删除;从表数据删除时,主表数据不会被及联删除
    • 在DDL中 从表声明一个外键和主表进行绑定,开始其实是从表是多对主表是一的关系。之后在从表对多的上面添加了唯一约束,从而实现一对一的约束。
  • 约束:
    • p_id = models.OneToOneField(Person, null=True, blank=True, on_delete=models.PROTECT)

    • on_delete = 以下约束字段
    • models.CASCADE 默认模式。主表数据删除时,从表数据及联删除;从表数据删除,主表数据不受影响

    • models.PROTECT 保护模式。

      • 主表数据删除时,如果主表无从表,主表数据可以删除成功。如果主表有级联从表,从表数据受到保护删除请求会抛出保护异常

      • 开发中为了防止误操作,通常设置成此模式。若必须删除主表数据时,需要提前将主表对应的所有从表的对应数据删除。然后再操作删除主表相应数据
    • models.SET_NULL       置空模式。主表删除时将从表的外键关系设置为空值[前提是此字段允许为NULL]。在一对一关系中常用此设置
    • models.SET_DEFAULT 置默认值。主表删除时将从表的外键关系设置为一个默认值[前提是存在设置的默认值]
    • models.SET()              删除的时候重新动态指向一个实体
 1 from django.db import models
 2 
 3 # 1:1 人员表和身份证表一对一关联
 4 # 人员的表
 5 class Person(models.Model):
 6     p_name = models.CharField(max_length=16)
 7     p_sex = models.BooleanField(default=False)
 8 
 9 # 身份证表
10 class IDCard(models.Model):
11     id_num = models.CharField(max_length=18, unique=True)
12     id_person = models.OneToOneField(Person, null=True, blank=True, on_delete=models.PROTECT)
  • 注意
    • 在迁移同步到mysql数据库中时从表身份证表中声明的外键属性id_person会自动被生成名为id_person_id字段。
    • 当 ‘从表对象.从表模型中定义的外键字段[属性]名’ 时,获取到的是主表的对象,拿着此对象去获取主表中的字段
  •  获取
    • 由主表对象获取从表对象的字段数据:主表对象 . 从表在数据库中的名[隐性属性] . 从表相应的字段名
    • 由从表对象获取主表对象的字段数据:从表对象 . 从表外键字段属性名[显性属性] . 主表相应的字段名
 1 def get_person(request):
 2     idcard = IDCard.objects.last()
 3     person = idcard.id_person
 4     return HttpResponse(person.p_name)
 5 
 6 
 7 def get_idcard(request):
 8     person = Person.objects.last()
 9     idcard = person.idcard
10     return HttpResponse(idcard.id_num)

一对多 [1:N]

 使用实现:

  • 实现:
    • 在django中使用 models.ForeignKey(要关联表的模型名其他约束字段) 进行关联,多是从表。
  • 获取

    • 主获取从: [隐性属性]级联模型对应数据库中的表名_set

      • 如 班级[主]和学生[从]关系模型:student_set

      • 也是Manager的子类,Manager上能使用的函数在这都能使用。如:all、filter、exclude、切片

    • 从获取主: [显性属性]

  • 删除
    • 同一对一操作 

多对多 [N:M]

 使用实现:

  • 实现:
    • 在django中使用 models.ManyToManyField(要关联的表的模型名,其他约束字段)
    • 实际上关系最复杂。开发中很少直接使用多对多属性,而是自己维护多对多的关系[如自己创建一个购物车表存放客户和商品的主键]
    • 迁移同步时,数据库会自己新建一张额外的关系表,关系表中的多个外键字段是需要关联关系的各表的主键,以及和一些自己的字段。
1 # N : M 一类商品可以被多个人购买;一个人可以购买多类商品。产生的关系表就是典型购物车模型
2 class Customer(models.Model):
3     c_name = models.CharField(max_length=16)
4 
5 
6 class Goods(models.Model):
7     g_name = models.CharField(max_length=16)
8     g_customer = models.ManyToManyField(Customer)
  • 注意:
    • 迁移同步在数据库中生成表的时候会单独的生成一张关系表。关系表中的各外键字段不能同时相等

    • 关系表中存储有关联各表的主键,通过多个外键实现的。约束是同时加在各外键上的,多个外键值不能同时相等

    • 在ManyToManyField中,删除一个不存在的数据不会报错,添加一个已存在的数据,不会被添加成功也不报错
  • 获取

    • 主获取从:[隐性属性] 也是Manager子类,操作和从操作主完全一样

    • 从获取主:[显性属性] 也是Manager子类,支持all、filter、exclude、切片使用

    • 方法:级连数据操作的方法有:add、remove、clear、set
 1 def add_to_cart(request):
 2     customer = Customer.objects.last()
 3     goods = Goods.objects.last()
 4     goods.g_customer.add(customer)# 由商品添加顾客,从设置主
 5     customer.goods_set.add(goods) # 由顾客添加商品,主设置从
 6     return HttpResponse('添加成功')
 7 
 8 
 9 # 获取级连数据
10 def get_goods_list(request, customerid):
11     customer = Customer.objects.get(pk=customerid)
12     goods_list = customer.goods_set.all() # 主获取从
13     return render(request, 'goods_list.html', context=locals())
1 # 注意
2 def add_to_cart(request):
3     customer = Customer.objects.last()
4     goods = Goods.objects.last()
5     print(goods.g_customer)       # App.Customer.None
6     print(type(goods.g_customer)) # <class 'django.db.models.fields.related_descriptors.create_forward_many_to_many_manager.<locals>.ManyRelatedManager'>
7     return HttpResponse('添加成功')

  注意:ManyRelatedManager 函数中定义的类,并且父类是一个函数的参数,此方法叫做动态创建。

  

静态

静态资源

  • 配置  

    在settings.py中的底部配置static的文件夹,用来加载些模板中用到的资源提供给全局使用,这个静态文件主要用来配置CSS、HTML、图片、字体文件等。

    和Template的区别在于,不需要MTV渲染就可以显示,只能支持原生的HTML,里面的东西是静态的。

    • STATIC_URL = '/static/'    
    • STATICFILES_DIRS = [os.path.join(BASE_DIR,'static')]   
  • 使用

    之后在模板中,首先加载静态文件,之后调用静态,就不用写绝对全路径了

    • 模板中的声明:{% load static%} 或 {% load staticfiles %}
    • 引用资源时使用:{% static 'xxx' %}    [xxx就是相对于staticfiles_dirs的一个位置]

 文件上传 [头像上传]

  注意 要使用POST方式上传

  • urls.py 
 1 # 主路由
 2 from django.conf.urls import url, include
 3 from django.contrib import admin
 4 
 5 urlpatterns = [
 6     url(r'^admin/', admin.site.urls),
 7     url(r'^App/',include('App.urls',namespace='app')),
 8 ]
 9 
10 # 子路由
11 from django.conf.urls import url
12 from App import views
13 
14 urlpatterns = [
15     url(r'^uploadfile/', views.upload_file, name='upload_file'),
16     url(r'^imagefield/', views.image_field, name='image_filed'),
17 ]
  •  原生写法
    •  views.py
 1 from django.http import HttpResponse
 2 from django.shortcuts import render
 3 
 4 def upload_file(request):
 5     if request.method == 'GET':
 6         return render(request,'upload.html')
 7     elif request.method == 'POST':
 8         icon = request.FILES.get('icon')
 9         # 存储
10         with open('/Users/guoyapeng/pyword/1django1.11/django05/SqlToModel/static/icon.jpg','wb') as save_file:
11             # chunks()函数 是将文件打成一块一块的
12             for part in icon.chunks():
13                 save_file.write(part)
14                 save_file.flush()
15         return HttpResponse('文件上传成功')
    • upload.html
 1 <!DOCTYPE html>
 2 <html lang="en">
 3 <head>
 4     <meta charset="UTF-8">
 5     <title>文件上传</title>
 6 </head>
 7 <body>
 8 {# enctype="multipart/form-data 意思是将图片包打碎加密传输 #}
 9 <form action="{% url 'app:upload_file' %}" method="post" enctype="multipart/form-data">
10    {% csrf_token %}
11     <span>文件:</span>
12     <input type="file" name="icon">
13     <br>
14     <input type="submit" value="上传">
15 </form>
16 
17 </body>
18 </html>
  • 封装实现 
    • 在settings.py中配置指定相对路径位置:MEDIA_ROOT = os.path.join(BASE_DIR,'static/upload')
    • 安装pillow:pip install pillow -i https://pypi.douban.com
    • models.py
1 from django.db import models
2 
3 class UserModel(models.Model):
4     u_name = models.CharField(max_length=16)
5     # upload_to是相对于settions.py配置的MEDIA_ROOT媒体路径的相对路径
6     u_icon = models.ImageField(upload_to='%Y/%m/%d/icon')
    • views.py
 1 from django.http import HttpResponse
 2 from django.shortcuts import render
 3 from App.models import UserModel
 4 # 上传到数据库中
 5 def image_field(request):
 6     if request.method == 'GET':
 7         return render(request,'image_filed.html')
 8     elif request.method == 'POST':
 9         username = request.POST.get('usernmae')
10         icon = request.FILES.get('icon')
11 
12         user = UserModel()
13         user.u_name = username
14         user.u_icon = icon
15         user.save()
16 
17     return HttpResponse('上传成功')
    • image_filed.html
 1 <!DOCTYPE html>
 2 <html lang="en">
 3 <head>
 4     <meta charset="UTF-8">
 5     <title>Title</title>
 6 </head>
 7 <body>
 8 
 9 <form action="{% url 'app:image_filed' %}" method="post" enctype="multipart/form-data">
10    {% csrf_token %}
11     <span>用户名:</span><input type="text" name="username" placeholder="请输入用户名">
12     <br>
13     <span>头像:</span><input type="file" name="icon">
14     <br>
15     <input type="submit" value="上传">
16 </form>
17 
18 </body>
19 </html>
    •  views.py
 1 from django.http import HttpResponse
 2 from django.shortcuts import render
 3 from App.models import UserModel
 4 
 5 
 6 # 获取文件信息[比如头像]
 7 def mine(request):
 8     username = request.GET.get("username")
 9     user = UserModel.objects.get(u_name=username)
10     print("/static/upload/" + user.u_icon.url)
11     data = {
12         "username": username,
13         "icon_url": "/static/upload/" + user.u_icon.url,
14     }
15     return render(request, "mine.html", context=data)
    • mine.html
<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>Mine</title>
</head>
<body>
<h3>{{ username }}</h3>
<img src="{{ icon_url }}" alt="{{ username }}">
</body>
</html>

补充

常用算法

 常用算法:Base64、urlencode

 算法种类:

  • 摘要算法,指纹算法,杂凑算法:
    • MD5:默认是128位的,四位一组变32位
    • SHA:当向不可逆,不叫加密解密、不管输入多长,输出都是固定长度、只要输入有任意的变更,输出都会发生很大的变化
  • 加密算法
    • 对称加密:DES、AES。可以用同一把钥匙加密解密。加密效率高,钥匙一旦丢失数据就全部丢失。
    • 非对称加密:RSA、PGP。一对钥匙,相互制约,公钥加密的数据只有私钥可解开,私钥加密的数据只有公钥才可解开。安全性高,算法复杂需要时间长 

 

生如逆旅 一苇以航
原文地址:https://www.cnblogs.com/TMMM/p/11740379.html