上节回顾
# 0 中间件:概念很大,介于某两个东西之间
-服务器中间件 介于http请求和程序之间例如uWSGI,tomcat
-数据库中间件 介于应用程序和数据库之间例如orm框架,mycat(做分库分表)
-消息队列中间件 介于应用程序和应用程序之间例如将程序之间的通信从同步变成异步(RabbitMQ,Kafka)
# 1 django中间件(全局请求和响应的处理)
-写一个类,继承一个类,把类配置在setting中,先后顺序
-process_request:所有请求来了,都会走它(尽量加判断),如果返回HttpResponse对象,不再继续往下走
-process_response:所有请求走,都会经过它,加入响应头,设置cookie,session
-process_view:process_request和路由匹配成功,视图函数执行之前
-process_excption:视图函数出异常,会执行它
# csrf:跨站请求伪造(浏览器的问题)只要在浏览器登录了一个网站,下次只要往这个网站发送请求,就会携带者cookie过去,不管这个请求是从哪个网站发出的(只要是在同一个浏览器中)就会把cookie带过去
-方案:每次发请求,都携带一个服务端给的随机字符串(放在头,放在体中)
-django处理csrf攻击,放在头或者数据中都可以,这个随机字符串是后端给的(cookie中取,页面中隐藏的,标签渲染上的)# 这个字符串给的过程是在是数据在返回的过程中经过中间件时给的
-csrf全局使用,局部禁用,csrf全局禁用,局部使用,cbv加装饰器
今日内容
1 auth组件介绍
1 我们需要实现包括用户注册、用户登录、用户认证、注销、修改密码等功能,内置了强大的用户认证系统--auth其实就是一个app,在setting中注册了这个app,是django.contrib.auth
2 内置属性方法
authenticate用户认证
# 数据迁移以后使用,好多和用户相关的表不用自己创建,因为在setting的app中注册了好多app,如果将其注释掉,那么那些迁移数据之后再数据库能看到的表就不见了,例如如果注释了session的表,那么则需要再写入session的时候自己先创建对应的表
创建超级用户可以直接在运行manage.py之后的终端界面创建createsuperuser,填写信息,那么表中就会多了一个用户,而且用户的密码是密文
from django.contrib import auth # 导入auth里面创建的表
def login(request):
if request.method=='GET':
return render(request,'login.html')
else:
name=request.POST.get('name')
password=request.POST.get('password') # 明文
## 方案行不通,密码是密文的,永远匹配不成功
# user=User.objects.filter(username=name,password=password)
## 使用此方案
## 第一个参数必须是request对象,后面的必须使用username和password字段名作为变量名(因为内部表中的字段名是username和password)
user=auth.authenticate(request,username=name,password=password) # 本质原理就是将密码取出,然后加密和表中的加密了的密码比较然后返回结果,此处只是校验,并没有登录
if user:
return HttpResponse('登录成功')
else:
return HttpResponse('用户名或密码错误')
login 登录
user=auth.authenticate(request,username=name,password=password) # 本质原理就是将密码取出,然后加密和表中的加密了的密码比较然后返回结果
if user:
auth.login(request,user) # 调了此表示用户登录了,存了session,以后所有的视图函数,都可以使用request.user(登陆成功后返回了一个session的id,下次请求来的时候根据id从数据库中取出数据,不但将数据放到session,而且将数据转成了user的对象,放到了request.user中,这是中间件处理的),它就是当前登录用户,这也是为什么返回响应对象的时候都会携带request的参数,因为可以在后面的html模板渲染页面中可以直接使用如{{ request.user.username }}(必须登录后才能使用)
return HttpResponse('登录成功')
else:
return HttpResponse('用户名或密码错误')
# 两个浏览器中登录不同的用户查看自己登录后的订单类,是根据自己的sessionid区分是哪个用户,在request.user中存储的就是那个请求发过来的用户
logout 退出登录
def logout(request):
auth.logout(request) # 后续再访问视图函数,就没有当前登录用户了request.user(只要登陆就是当前用户,没有登录就是匿名用户AnonymousUser(因为如果是空,那么request.user一取就会报错,这也是为什么在模板中退出了继续使用{{request.user.username}}也不会报错,只是变成了空))
return redirect('/index/')
is_authenticated 是否登录
if request.user.is_authenticated: # is_authenticated 登陆成功返回True,匿名用户返回False,判断用户是否登录,用在视图中登陆成功返回
print('用户登录了')
else:
print('用户没有登录,匿名用户')
# 主要用在模板中
{% if request.user.is_authenticated %}
{{ request.user.username }} 登录了
{% else %}
<a href="/login/">去登录</a>
{% endif %}
login_requierd 未登录禁止访问
1 装饰器,装饰在视图函数上,只要没有登录,就进不来
# 必须登录后才能访问,需要导入 from django.contrib.auth.decorators import login_required
@login_required(login_url='/login/') # 括号中的是如果没有登录就重定向到那个地址(只是携带了数据,并不会主动跳转到指定的页面,需要自己手动在登录页面写)
create_user 注册用户
# 使用内置create_user或者create_superuser方法创建的用户权限不一样
user=User.objects.create_user(username=name,password=password) # 需要先导入from django.contrib.auth.models import User
# user=User.objects.create_superuser(username=name,password=password)
# 如果要求注册成功后自动跳转到首页,可以在后面接着写auth.login(request,user),然后返回跳转页面(一般不用,都是要求注册后,再次主动重新登录才行)
# 注册一个账户,将管理员的密码拿出来保存,然后将自己密码密文给管理员账户替换,那么此时管理员账户的密码就是自己填的,将自己的操作完成后,最后将管理员的密码重新填写回去
check_password
## 有了用户,校验密码是否正确
# 先获取到用户对象
user = User.objects.filter(username=name).first()
# 判断密码是否正确
flag=user.check_password(password)
set_password 修改密码
def change_password(request):
if request.method == 'GET':
return render(request, 'change_pwd.html')
else:
old_pwd = request.POST.get('old_pwd')
new_pwd = request.POST.get('new_pwd')
re_new_pwd = request.POST.get('re_new_pwd')
if request.user.check_password(old_pwd): # 一般要求登录以后才能修改密码
# 密码正确再修改
request.user.set_password(new_pwd) # request.user._password 原始密码
# 记住保存
request.user.save()
return redirect('/login/')
else:
return HttpResponse('原来密码错误')
3 User对象的属性
is_staff : 用户是否拥有网站的管理权限,是否可以登录到后台管理
is_superuser:是否是超级管理员(如果is_staff=1,可以任意增删查改任何表数据)
is_active : 是否允许用户登录, 设置为 False,可以在不删除用户的前提下禁止用户登录(三次密码输入错误禁用用户)
# 超级管理员禁用用户,通过id将user.is_active=False,user.save()
# 普通用户密码错误三次,禁用一段时间,一般的做法是将错误的次数存到redis中,一定时间后重置为0
4 扩展默认的auth_user表
1 内置的auth_user表,要加字段,加不了,扩展该表
-方式一:一对一
from django.contrib.auth.models import User
class UserDetail(models.Model):
phone = models.CharField(max_length=32)
addr = models.CharField(max_length=32)
user= models.OneToOneField(to=User)
# 方式二:通过继承,一定要记住在setting中配置(空地方写),使用这种方式,一开始就要使用,否则会报错,或者重新迁移数据之后才行,扩写之后的数据还是从request.user里面拿
from django.contrib.auth.models import AbstractUser
class User(AbstractUser):
# id=models.AutoField(primary_key=True)
# username = models.CharField(max_length=128)
phone = models.CharField(max_length=32)
addr = models.CharField(max_length=32)
## setting.py中固定的名字加app的名字.类名
AUTH_USER_MODEL = "app01.User"
如果项目一开始没有扩展auth_user表,后期想扩展的操作步骤
1 备份--删库---》重新创建出数据库,然后所有app的数据迁移记录删除,migrations下除了__init__.py都删除
(重要)去源码中删除auth和admin 这俩app的migrations下除了__init__.py都删除
数据迁移,同步到数据库,备份的数据,恢复回去
5 自定义中间表(中介模型)
1 多对多关系中,第三张表的建立
-默认使用ManyToMany,自动创建
-使用中介模型
-即手动创建第三张表加一些额外的字段,又要使用好用的查询(关联查询和__查询),但是add ,remove, clear,set不能用了(因为额外的字段没办法使用这些)
-完全自己写第三张表
# 使用中介模型
class Author(models.Model):
nid = models.AutoField(primary_key=True)
name = models.CharField(max_length=32)
age = models.IntegerField()
author_detail = models.OneToOneField(to='AuthorDatail', to_field='nid', unique=True, on_delete=models.CASCADE)
class AuthorDatail(models.Model):
nid = models.AutoField(primary_key=True)
telephone = models.BigIntegerField()
birthday = models.DateField()
addr = models.CharField(max_length=64)
class Book(models.Model):
nid = models.AutoField(primary_key=True)
name = models.CharField(max_length=32)
price = models.DecimalField(max_digits=5, decimal_places=2)
publish_date = models.DateField()
publish = models.ForeignKey(to='Publish', to_field='nid', on_delete=models.CASCADE)
# 此处是中介模型,第一个参数to是关联的表,第二个参数through后面写第三张表名,第三个参数through_field关联字段,元组中的第一个参数是当前所在表和第三张对应关联的id即为第三张表中的book_id,第二个写author_id;如果此处的中间模型写在了author表中,那么里面的对应的所有关联也相应的更改。admin的后台管理中,字段后面verbose_name='标题'表示字段在后台显示名字从默认值更改为自己设置的值,help_text='提示',会显示提示的信息。
authors=models.ManyToManyField(to='Author',through='AuthorToBook',through_fields=('book_id','author_id'))
def __str__(self):
return self.name # 是将选项的类显示成类中自己的名字
class Publish(models.Model):
nid = models.AutoField(primary_key=True)
name = models.CharField(max_length=32)
city = models.CharField(max_length=32)
email = models.EmailField()
# 手动创建第三张表
class AuthorToBook(models.Model):
nid = models.AutoField(primary_key=True)
book_id = models.ForeignKey(to=Book, to_field='nid', on_delete=models.CASCADE) # 外键,此处相当于一对多
author_id = models.ForeignKey(to=Author, to_field='nid', on_delete=models.CASCADE) # 外键,此处相当于一对多
date=models.DecimalField()
# s1.py
import os
if __name__ == '__main__':
os.environ.setdefault("DJANGO_SETTINGS_MODULE", "day76.settings")
import django
django.setup()
from app01 import models
# 金梅这本书是lqz和egon写的
# book=models.Book.objects.get(pk=1)
# # book.authors.add(1,2) # 用不了了
# # --------手动模型
# models.AuthorToBook.objects.create(book_id_id=1,author_id_id=1)
# models.AuthorToBook.objects.create(book_id_id=1,author_id_id=2)
# 金梅这本书所有的作者
# book = models.Book.objects.get(pk=1)
# res=models.AuthorToBook.objects.filter(book_id=book) # 基于对象的跨表查询
# print(res)
# ----------中介模型的查询
# book = models.Book.objects.get(pk=1)
# print(book.authors.all())
#add ,remove, clear,set这些不能用了,但是连表操作,book.authors这些都能用
book = models.Book.objects.get(pk=1)
book.authors.add(1,2) # 不能用了