58、csrf的相关装饰器、Auth模块、扩展默认的auth_user表

117 阅读8分钟

csrf的相关装饰器

1.csrf的相关装饰器与csrf验证相关
2.当我们把scrf的中间件打开之后,我们的方法都需要验证
3.打开csrf的中间件,有几个方法不验证--->csrf_exemp
4.关闭csrf的中间件之后,有几个方法要验证--->csrf_protect
5.模块:from django.views.decorators.csrf import csrf_protect,csrf_exemp		

FBV

局部禁用:用装饰器(在FBV中使用)

需求:注释scrf中间件,让func方法经过验证----->导模块:from django.views.decorators.csrf import csrf_protect

from django.shortcuts import render

from django.views.decorators.csrf import csrf_protect
@csrf_protect
def func(request):
    return render(request,'func.html')

需求:打开scrf中间件,让func方法不经过验证----->导模块:from django.views.decorators.csrf import csrf_exempt


from django.views.decorators.csrf import csrf_exempt
@csrf_exempt
def func(request):
    return render(request,'func.html')

CBV

需求:让Login不验证---->导模块

第一种方式添加装饰器不可行

from django.views import View
from django.utils.decorators import method_decorator
from django.views.decorators.csrf import csrf_exempt,csrf_protect

class Login(View):

    def get(self,request):
        print('get')
        return HttpResponse('get')

    @method_decorator(csrf_exempt)
    def post(self,request):
        print('post')
        return HttpResponse('post')

第二种方式添加装饰器不可行

from django.views import View
from django.utils.decorators import method_decorator
from django.views.decorators.csrf import csrf_exempt,csrf_protect


# @method_decorator(csrf_exempt,name='post')
class Login(View):

    def get(self,request):
        print('get')
        return HttpResponse('get')

    def post(self,request):
        print('post')
        return HttpResponse('post')

第三种方法可行


from django.views import View
from django.utils.decorators import method_decorator
from django.views.decorators.csrf import csrf_exempt,csrf_protect

class Login(View):

    @method_decorator(csrf_exempt)
    def dispatch(self, request, *args, **kwargs):
         return super(Login, self).dispatch(request, *args, **kwargs)

    def get(self,request):
        print('get')
        return HttpResponse('get')

    def post(self,request):
        print('post')
        return HttpResponse('post')
from django.views import View
from django.utils.decorators import method_decorator
from django.views.decorators.csrf import csrf_exempt,csrf_protect


@method_decorator(csrf_exempt,name='dispatch')
class Login(View):

    def get(self,request):
        print('get')
        return HttpResponse('get')

    def post(self,request):
        print('post')
        return HttpResponse('post')
      
总结: 1.CBV的csrf装饰器,只能加载类上(指定方法为dispatch)和dispatch方法上(django的bug)
      2.给get方法使用csrf_token检测

Auth模块

1.Auth模块:是Django自带的用户认证模块
		1.我们在开发一个网站的时候,无可避免的需要设计实现网站的用户系统。此时我们需要实现包括用户注册、用户登录、用户认证、注销、修改密码等功能,这是个麻烦的事情。
    2.Django作为一个完美主义者的终极框架,当然也会想到用户的这些痛点。它内置了强大的用户认证系统–auth,它默认使用 auth_user 表来存储用户数据。
    
2.django自带的后台管理系统依赖的就是auth_user表,要想登陆django的后台管理系统,就需要创建一个超级用户
		创建超级用户步骤:
  		1.终端输入:pthon manage.py createsuperuser
      2.输入用户、邮箱、password(echoed打印)
      3.再输入一次密码
      4.成功
      
 ps:django_migrations  --->里面的数据是有关下面应用的记录
		"""
		INSTALLED_APPS = [
    'django.contrib.admin',
    'django.contrib.auth',
    'django.contrib.contenttypes',
    'django.contrib.sessions',
    'django.contrib.messages',
    'django.contrib.staticfiles',
    'app01'
]
		"""

用户登录

1.比较用户名和密码
   1. 去哪个表里查询数据------>auth_user表
   2. 密码如何比较
   解决:借助于auth模块
      1.导模块:from django.contrib import auth
      2.比较数据:user_obj=auth.authenticate(request,username=user,password=pwd)
  		ps:返回的是用户对象,当用户不存在的时候,返回的是false
      
2.保存用户信息
	1.使用session--->request.session['username'] = user_obj.username
  2.使用auth.login--->auth.login(request,用户对象)
  ps:如果你用了auth,就用一整套。只要你用了auth.login方法,后续就可以在任何方法里面通过:request.user拿到用户对象。eg:request.user.username --->user_obj.username
  
from django.contrib import auth
def login(request):
    if request.method == 'POST':
        # print(request.POST)
        user = request.POST.get('username')
        pwd = request.POST.get('password')

        # 比对数据
        user_obj = auth.authenticate(request,username=user,password=pwd)
        # print(user_obj)  # nana  __str__
        # print(user_obj.username)  # nana
        if user_obj:
             # 保存用户的信息
             # 1.request.session['username'] = user_obj.username
             # 2.auth.login(request,user_obj)
            auth.login(request,user_obj)
            return redirect('/home/')
        return HttpResponse('用户名或密码错误')
    return render(request,'login.html')


def home(request):
    return render(request,'home.html')

用户认证

需求:登陆成功后才能访问home

1.如何判断用户是否登录了
    1.1 session request.session.get('username')
    1.2 auth模块:request.user.is_authenticated()-->True,False
    ps:request.user.is_authenticated()在django1中加括号,在django2中不加括号
2.认证装饰器
		1.1 导模块:from django.contrib.auth.decorators import login_required
    1.2 装饰器:@login_required(),默认提交:/account/login/
    		1.局部添加:@login_required(login_url ='/xxx/')-->未登陆跳转/xxx/
        2.全局添加:@login_required()-->在配置文件中加:LOGIN_URL = '/login/',未登陆跳转
  
	
def home(request):
    # 1.需求:登陆成功后才能访问home页面
    # 1.1 session request.session.get('username')
    # 1.2 auth模块:request.user.is_authenticated()
    print(request.user.is_authenticated())  # True
    if request.user.is_authenticated():
        return render(request,'home.html')
    return redirect('/login/')
from django.contrib.auth.decorators import login_required
@login_required(login_url='/xxxx/')   # 局部添加
def home(request):
        return render(request,'home.html')


@login_required   # 全局添加,在配置文件中加:LOGIN_URL = '/login/',未登陆跳转
def index(request):
    return HttpResponse('index')

修改密码

1.验证老密码:request.user.check_password(旧密码) --->True,False
2.修改密码:
	1.request.user.set_password(新密码)-->这一句没有真正的操作数据库(update)
  2.request.user.save()-->真正的操作数据库(update)
3.request.user-->返回的是用户对象;如果返回的是AnonymousUser,就是匿名用户,未登陆
@login_required
def set_password(request):
  	print(request.user)  # AnonymousUser 匿名用户  nana
    if request.method == 'POST':
        print(request.POST.get('username'))  # None 禁用
        print(request.POST.get('password'))  # 123
        # 1.验证老密码是否正确
        old_pwd = request.POST.get('old_password')
        is_right = request.user.check_password(old_pwd)
        print(is_right)
        if not is_right:
            return HttpResponse('原密码错误')

        # 2.先判断两次密码是否一致
        new_pwd = request.POST.get('new_password')
        if old_pwd == new_pwd:
            return HttpResponse('新密码不能和旧密码一致')

        # 3.验证新密码和第三次输入的密码是否一致
        confirm_pwd = request.POST.get('confirm_password')
        if new_pwd != confirm_pwd:
            return HttpResponse('两次密码不一致')

        # 4.修改密码
        request.user.set_password(new_pwd) # 这一句没有真正的操作数据库(update)
        request.user.save()  # 真正的操作数据库(update)
        return HttpResponse('修改成功')
        
    return render(request,'set_password.html',locals())

注销登录

1.注销用户:
	1.导模块:from django.contrib import auth
	2.注销用户:auth.logout(request)
2.当调用该函数时,当前请求的session信息会全部清除
from django.contrib.auth import logout

def logout(request):
    auth.logout(request) # request.session.flush
    return redirect('/home/')

注册用户

1.导模块:from django.contrib.auth.models import User
2.注册用户名密码
	创建不加密:creat()-->User.objects.create(username=username,password=password)
  创建加密的:creat_user()-->User.objects.create_user(username=username,password=password)
  创建超级用户:create_superuser() --> User.objects.create_superuser(username=username,email='123@qq.com',password=password # 邮箱必须填
from django.contrib.auth.models import User
def register(request):
    if request.method =='POST':
        username = request.POST.get('username')
        password = request.POST.get('password')
        # 方法1:创建普通用户(密码不加密)
        # User.objects.create(username=username,password=password)  # 不加密

        # 方法2:创建普通用户(密码加密)
        # User.objects.create_user(username=username,password=password) # 加密

        # 方法3:创建超级用户
        User.objects.create_superuser(username=username,email='123@qq.com',password=password) # 加密

        return redirect('/home/')


    return render(request,'register.html')

方法总结

https://www.yuque.com/liyangqit/lb35ya/mmq0y5

扩展默认的auth_user表

1.由于auth_user表是django创建的,后续我们有扩展字段的需求,所以我们对这张表进行扩展
2.如何扩展
	1.在models.py写类,建表
  2.扩展auth_user表,就必须继承AbstractUser类,不再继承models.Model;导模块:from django.contrib.auth.models import AbstractUser
3.我们在扩展字段的时候,默认的字段我们不要动,只写我们要增加的字段
		ps:
    1. 扩展字段的时候,默认的字段不要动,只写自己要增加的字段
    2. 如果扩展了这张表,那么,auth_user表就不存在了,有的是我们自己新建的这张表
    3. 新建出来的这张表里面的字段,有之前auth_user表的所有字段加自己添加的字段
    4. 需要在配置文件中添加一个变量,告诉django我要用现在新的表替代原来的auth_user表
        AUTH_USER_MODEL = '应用名.类名'
        eg:AUTH_USER_MODEL = 'app01.UserInfo'
        
    5. 如果在扩展这张表之前已经执行了数据库迁移命令,需要从新换库
    6. 如果你已经迁移了,还想扩展,怎么办?
      1. 换库(应用下的migration删除)
      2. 删所有应用下的migrations和django自动创建的migrantion表数据
    7. 得出结论:在执行迁移命令之前就扩展好(应用下的migration删除)
from django.db import models
from django.contrib.auth.models import AbstractUser

class aaa(AbstractUser):
    phone = models.CharField(max_length=64)
    avatar = models.CharField(max_length=64)
    test = models.CharField(max_length=64)
    
# settings.py
AUTH_USER_MODEL = 'app01.aaa'
import os
if __name__ == "__main__":
    os.environ.setdefault("DJANGO_SETTINGS_MODULE", "django12.settings")
    import django
    django.setup()
    from app01 import models

    fields = models.aaa._meta.fields
    l1 = []
    for field in fields:
        print(field.name)
        l1.append(field.name)
    print(l1)  # ['id', 'password', 'last_login', 'is_superuser', 'username', 'first_name', 'last_name', 'email', 'is_staff', 'is_active', 'date_joined', 'phone', 'avatar', 'test']

BBS

1.对于任何一个项目来说,最终的是不是业务逻辑,而是表结构的设计,以及表关系的设计
我们以后拿到一个新的需求的时候,我们首先考虑的是表结构和表关系

表关系

分析BBS需要哪些表以及表与表之间的关系!!!

1. 用户表
	扩展auth_user表
	扩展字段:
		phone
		avatar
		create_time
		...
	# 表关系
		和站点表是一对一的关系

2. 站点表
	site_name 站点名称
	site_title 站点标题
	site_style 站点样式

3. 文章表
	 title  文章标题
	 desc 文章摘要
	 content 文章内容
	 create_time 文章发表的时间
	 
	  # 数据库字段设计优化
        up_num = models.BigIntegerField(verbose_name='点赞数',default=0)
        down_num = models.BigIntegerField(verbose_name='点踩数',default=0)
        comment_num = models.BigIntegerField(verbose_name='评论数',default=0)
    
	 # 表关系 	
	 文章和标签是多对多的关系
	 文章和分类表是一对多的关系
	 站点表和文章表是一对多的关系	 
	 
4. 文章标签表
	标签名称
  # 表关系
	标签表和站点表是一对多的关系

5. 分类表
	分类名称
  # 表关系
	分类表和站点表是一对多的关系


6. 点赞点踩表
	ps:谁在什么时间点赞了哪篇文章
	user
	article
	is_up 
	create_time
	

7. 评论表comment
	ps:谁在什么时间给哪篇文章评论了什么内容
	user
	artilce
	content
	create_time
  
  ps: 1.根评论和子评论的概念
			   根评论:评论文章的评论
				 子评论:评论文章的评论的评论
         根评论和子评论:一对多	
       eg:	1. PHP是世界上最好的语言
		           1.1  python是最好的
			         1.2 java是最好的额        
 """
 	id   user   article   content  creare_time    parent_id
	1	     1		  1			     a		   ''			
	2      2		  1          b		   ''			        1
	3      3      1			     c 			 ''             2
	parent = models.ForeignKey(to='comment')
	parent = models.ForeignKey(to='self')  # 自关联
 """

表结构

from django.db import models

# Create your models here.
"""
先写普通字段
之后再写外键字段
"""
from django.contrib.auth.models import AbstractUser


class UserInfo(AbstractUser):
    phone = models.BigIntegerField(verbose_name='手机号', null=True)
    # 头像
    """
        upload_to:指定图片上传的路径
        default='avatar/default.png' 默认图片
        avatar:存的是文件的路径 avatar/123.png
    """
    avatar = models.FileField(upload_to='avatar/', default='static/img/default.jpg', verbose_name='用户头像')
    """
    给avatar字段传文件对象 该文件会自动存储到avatar文件下 然后avatar字段只保存文件路径avatar/default.png
    """

    # auto_now_add:自定添加当前时间,如果不人为的去修改,这个时间就不会变
    create_time = models.DateTimeField(auto_now_add=True)

    blog = models.OneToOneField(to='Blog', null=True)

# 站点表
class Blog(models.Model):
    site_name = models.CharField(verbose_name='站点名称', max_length=32)
    site_title = models.CharField(verbose_name='站点标题', max_length=32)
    # 简单模拟 带你认识样式内部原理的操作
    site_theme = models.CharField(verbose_name='站点样式', max_length=64)  # 存css/js的文件路径  css/my.css


class Category(models.Model):
    name = models.CharField(verbose_name='文章分类', max_length=64)
    blog = models.ForeignKey(to='Blog', null=True)


class Tag(models.Model):
    name = models.CharField(verbose_name='文章标签', max_length=32)
    blog = models.ForeignKey(to='Blog', null=True)


class Article(models.Model):
    title = models.CharField(verbose_name='文章标题', max_length=128)
    desc = models.CharField(verbose_name='文章简介', max_length=255)
    # 文章内容有很多 一般情况下都是使用TextField
    content = models.TextField(verbose_name='文章内容')  # content text
    create_time = models.DateField(auto_now_add=True)

    # 数据库字段设计优化
    up_num = models.BigIntegerField(verbose_name='点赞数', default=0)
    down_num = models.BigIntegerField(verbose_name='点踩数', default=0)
    comment_num = models.BigIntegerField(verbose_name='评论数', default=0)

    # 外键字段
    blog = models.ForeignKey(to='Blog', null=True)
    category = models.ForeignKey(to='Category', null=True)
    tags = models.ManyToManyField(to='Tag',
                                  through='Article2Tag',
                                  through_fields=('article', 'tag')
                                  )


class Article2Tag(models.Model):
    article = models.ForeignKey(to='Article')
    tag = models.ForeignKey(to='Tag')


class UpAndDown(models.Model):
    user = models.ForeignKey(to='UserInfo')
    article = models.ForeignKey(to='Article')
    is_up = models.BooleanField()  # 传布尔值 存0/1


class Comment(models.Model):
    user = models.ForeignKey(to='UserInfo')
    article = models.ForeignKey(to='Article')
    content = models.CharField(verbose_name='评论内容', max_length=255)
    comment_time = models.DateTimeField(verbose_name='评论时间', auto_now_add=True)
    # 自关联
    parent = models.ForeignKey(to='self', null=True)  # 有些评论就是根评论