【BBS】第02回 注册相关功能实现

1. 补充知识

1.1 去IOE

1. 它是阿里巴巴造出的概念。其本意是,在阿里巴巴的IT架构中,去掉IBM的小型机、Oracle数据库、EMC存储设备,代之以自己在开源软件基础上开发的系统
2、去IOE意味着接下来政府必须将数据安全牢牢掌握在国内企业手中,防止数据丢失造成的一系列严重后果。也是在此背景之下,阿里云与12306以及之前的中国气象总局、药监局展开深度合作才显得水到渠成。

1.2 ForeignKey:on_delete

当删除关联表中的数据时,当前表与其关联的行的行为。

models.CASCADE
删除关联数据,与之关联也删除

models.DO_NOTHING
删除关联数据,引发错误IntegrityError

models.PROTECT
删除关联数据,引发错误ProtectedError

models.SET_NULL
删除关联数据,与之关联的值设置为null(前提FK字段需要设置为可空)

models.SET_DEFAULT
删除关联数据,与之关联的值设置为默认值(前提FK字段需要设置默认值)

models.SET
删除关联数据
a. 与之关联的值设置为指定值,设置:models.SET(值)
b. 与之关联的值设置为可执行对象的返回值,设置:models.SET(可执行对象)

1.3 字段类有很多属性

null             数据库字段是否可以为空
db_column        数据库中字段的列名
default          数据库中字段的默认值
primary_key      数据库中字段是否为主键
db_index         数据库中字段是否可以建立索引
unique           数据库中字段是否可以建立唯一索引
erbose_name      Admin中显示的字段名称
blank            Admin中是否允许用户输入为空
editable         Admin中是否可以编辑
help_text        Admin中该字段的提示信息
choices          Admin中显示选择框的内容,用不变动的数据放在内存中从而避免跨表操作


db_constraint    是否在数据库中创建外键约束,默认为True。放在ForeignKey,不建立外键关联

1,4 外键

1. OneToOneField: 一对一就是ForeignKey+unique
2. ManyToManyField:多对多
   自动创建中间表
   手动创建中间表:中间表中除了关联字段外,还有其他字段
   写出中间表ManyToManyField的属性:
       through:通过哪个中间表做关联
       through_fields:设置关联的字段
3. OneToOneField,ForeignKey,ManyToManyField
   related_name:反向操作时,使用的字段名,用于代替原反向查询时的’表名_set’。
   related_query_name:反向查询操作时,使用的连接前缀,用于替换表名

2. 注册forms编写

from django import forms
from django.forms import widgets
from django.core.exceptions import ValidationError
from .models import UserInfo


class RegisterFom(forms.Form):
    username = forms.CharField(max_length=8, min_length=3, required=True, label='用户名', error_messages={
        'max_length': '太长了',
        'min_length': '太短了',
        'required': '这个必填',
    }, widget=widgets.TextInput(attrs={'class': 'form-control'}))
    password = forms.CharField(max_length=8, min_length=3, required=True, label='密码', error_messages={
        'max_length': '太长了',
        'min_length': '太短了',
        'required': '这个必填',
    }, widget=widgets.PasswordInput(attrs={'class': 'form-control'}))
    re_password = forms.CharField(max_length=8, min_length=3, required=True, label='确认密码', error_messages={
        'max_length': '太长了',
        'min_length': '太短了',
        'required': '这个必填',
    }, widget=widgets.PasswordInput(attrs={'class': 'form-control'}))
    email = forms.EmailField(label='邮箱', widget=widgets.EmailInput(attrs={'class': 'form-control'}))

    # 局部钩子校验,验证用户名是否重复
    # 方案一
    def clean_username(self):
        username = self.cleaned_data.get('username')
        user = UserInfo.objects.filter(username=username).first()
        if user:
            # 已存在,不合理
            raise ValidationError('该用户名已经存在')
        else:
            return username

    # 方式二·
    # def clean_username(self):
    #     username = self.cleaned_data.get('username')
    #     try:
    #         UserInfo.objects.get(username=username)  # 有且只有一条才正常,否则就抛出异常
    #         raise ValidationError('该用户名已经存在')
    #     except Exception:
    #         return username

    # 全局钩子校验,验证两次密码是否一致

    def clean(self):
        # 比较两个密码是否一致  :cleaned_data存的是校验过后的数据,是个字典
        password = self.cleaned_data.get('password')
        re_password = self.cleaned_data.get('re_password')
        if password == re_password:
            # 合理,返回校验过后的数据
            return self.cleaned_data
        else:
            # 不合理,抛出校验失败的异常
            raise ValidationError('两次密码不一致')

3. 注册页面搭建

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>Title</title>
    <script src="https://cdn.bootcdn.net/ajax/libs/jquery/3.6.0/jquery.js"></script>
    <link rel="stylesheet" href="/static/bootstrap-3.4.1-dist/css/bootstrap.min.css">
    <script src="https://stackpath.bootstrapcdn.com/bootstrap/3.4.1/js/bootstrap.min.js"></script>
</head>
<body>
     <div class="container-fluid">
         <div class="row">
             <div class="col-md-6 col-md-offset-3">
                 <h1 class="text-center">注册功能</h1>
                 <form id="id_form">
                     {% for item in form  %}
                         <div class="form-group">
                             <label for="{{ item.id_for_label }}">{{ item.label }}</label>
                             {{ item }}
                             <span class="pull-right text-danger"></span>
                         </div>
                     {% endfor %}
                     <div class="form-group">
                         <label for="id_file">头像
                             <img src="/static/img/default.png" alt="" height="80px" width="80px" style="margin-left: 10px" id="id_img">
                         </label>
                         <input type="file" id="id_file" accept="image/*" style="display: none">
                     </div>
                     <div class="form-group text-center">
{#                         如果input类型是submit或者button标签,放在form表单中,如果点提交,触发form的提交,如果我们写了ajax提交,会触发两次提交#}
                         <input type="button" value="注册" class="btn btn-danger" id="id_submit">
                         <span class="text-danger"></span>
                     </div>
                 </form>
             </div>
         </div>
     </div>
</body>
</html>
from .forms import RegisterFom


def register(request):
    if request.method == 'GET':
        register_form = RegisterFom()
        # context:上下文
        return render(request, 'register.html', context={'form': register_form})

3. 头像动态显示

<script>
    $("#id_file").change(function () {
        // 把当前图片,放到img标签中
        // 把图片地址放到img标签上,标签就显示了图片
        //$("#id_img")[0].src='https://tva1.sinaimg.cn/large/00831rSTly1gd1u0jw182j30u00u043b.jpg'

        // 1 id_file这个标签的图片读出来,借助于文件阅读器,js提供的一个类
        var reader=new FileReader()
        // 2 拿到文件对象,赋值给一个变量
        var file =$("#id_file")[0].files[0]
        // 3 把文件读到文件阅读器中
        reader.readAsDataURL(file)
        // 4 等读完后,把文件阅读器的内容写到img标签上
        //$("#id_img")[0].src=reader.result
        reader.onload=function (){
            //$("#id_img")[0].src=reader.result
            $('#id_img').attr('src', reader.result)
        }


    })

</script>

5. 注册功能后端

from django.shortcuts import render
from django.http import JsonResponse
# Create your views here.

from .forms import RegisterFom
from .models import UserInfo

def register(request):
    if request.method == 'GET':
        register_form = RegisterFom()
        # context:上下文
        return render(request, 'register.html', context={'form': register_form})
    else:  # post请求的时候
        res = {'code':100, 'msg': '注册成功'}
        # 取出用户名密码,使用form校验数据,如果校验通过,存到数据库中,如果校验不通过,返回错误信息
        register_form = RegisterFom(data=request.POST)
        if register_form.is_valid():
            # 数据字段自己的规则,局部钩子,全局钩子都校验过后,通过了
            # 1 re_password字段,不存在数据库的,剔除
            register_data = register_form.cleaned_data
            register_data.pop('re_password')
            # 2 头像:如果携带了要存,头像是文件,在request.FILES中
            my_file = request.FILES.get('my_file')
            if my_file:
                register_data['avatar'] = my_file
            # 3 存到数据库
            UserInfo.objects.create_user(**register_data)
            # UserInfo.objects.create_user(username=register_data.get('username'),) 等同于上面,但是麻烦
            return JsonResponse(res)
        else:
            res['code'] = 101
            res['msg'] = '注册失败'
            res['errors'] = register_form.errors
            return JsonResponse(res)

6. 注册功能前端

    // 当点击注册按钮,发送ajax请求到后端的注册功能
    $("#id_submit").click(function (){
        // 上传文件,借助于formdata对象
        var formdata = new FormData()
        // 方式一

        {#formdata.append('username',$('#id_username').val())#}
        {#formdata.append('password',$('#id_username').val())#}
        {#formdata.append('re_password',$('#id_username').val())#}
        {#formdata.append('my_file',$('#id_file')[0].files[0])#}
        {#// csrftoken也要加上#}
        // 方式二:借助于form表单批量处理
        var data = $("#id_form").serializeArray()
        console.log(data)
        //使用for循环,把data中得到的数据,转存到formdata中 jquery的each循环
        $.each(data, function (i, v){
            formdata.append(v.name, v.value)
        })
        // 文件单独载放进去
        formdata.append('my_file', $('#id_file')[0].files[0])


        // 使用ajax向后端发送请求
        // 1 三种编码格式:urlencoded, from-data,json
        $.ajax({
            url:'/register/',
            type:'post',
            processData: false,
            contentType: false,
            data: formdata,
            success: function (data){
                console.log(data)
                if(data.code==100){
                    // 表示注册成功,跳转到登录页面
                    location.href='/login/'
                }else {
                    //在前端显示错误信息
                    console.log(data);
                    //两次密码不一致的错误渲染
                    /*
                    if(data.errors['__all__']){
                        $(".error").html(data.errors['__all__'][0])
                    }
                    */
                    // 其他标签的错误渲染
                    $.each(data.errors,function (k,v) {
                        if (k=='__all__'){
                            $(".error").html(v[0])
                        }else{
                             // 链式调用,在对应的input后的span中插入错误文字,把父div加入has-error类,整个框变红
                            $("#id_"+k).next().html(v[0]).parent().addClass('has-error')
                        }
                    })
                    // 起一个定时任务,把错误信息去掉
                    setTimeout(function (){
                        // 把所有span的文字去掉,把父div中得has-error类去掉
                        $('.text-danger').html("").parent().removeClass('has-error')
                    },3000)

                }

            }

        })

    })

7. 注册错误信息渲染

    // 当点击注册按钮,发送ajax请求到后端的注册功能
    $("#id_submit").click(function (){
        // 上传文件,借助于formdata对象
        var formdata = new FormData()
        // 方式一

        {#formdata.append('username',$('#id_username').val())#}
        {#formdata.append('password',$('#id_username').val())#}
        {#formdata.append('re_password',$('#id_username').val())#}
        {#formdata.append('my_file',$('#id_file')[0].files[0])#}
        {#// csrftoken也要加上#}
        // 方式二:借助于form表单批量处理
        var data = $("#id_form").serializeArray()
        console.log(data)
        //使用for循环,把data中得到的数据,转存到formdata中 jquery的each循环
        $.each(data, function (i, v){
            formdata.append(v.name, v.value)
        })
        // 文件单独载放进去
        formdata.append('my_file', $('#id_file')[0].files[0])


        // 使用ajax向后端发送请求
        // 1 三种编码格式:urlencoded, from-data,json
        $.ajax({
            url:'/register/',
            type:'post',
            processData: false,
            contentType: false,
            data: formdata,
            success: function (data){
                console.log(data)
                if(data.code==100){
                    // 表示注册成功,跳转到登录页面
                    location.href='/login/'
                }else {
                    //在前端显示错误信息
                    console.log(data);
                    //两次密码不一致的错误渲染
                    /*
                    if(data.errors['__all__']){
                        $(".error").html(data.errors['__all__'][0])
                    }
                    */
                    // 其他标签的错误渲染
                    $.each(data.errors,function (k,v) {
                        if (k=='__all__'){
                            $(".error").html(v[0])
                        }else{
                             // 链式调用,在对应的input后的span中插入错误文字,把父div加入has-error类,整个框变红
                            $("#id_"+k).next().html(v[0]).parent().addClass('has-error')
                        }
                    })
                    // 起一个定时任务,把错误信息去掉
                    setTimeout(function (){
                        // 把所有span的文字去掉,把父div中得has-error类去掉
                        $('.text-danger').html("").parent().removeClass('has-error')
                    },3000)

                }

            }

        })

    })


8. 用户存在校验功能


def check_username(request):
    res = {'code': 100, 'msg': '用户存在'}
    username = request.GET.get('username')
    user = UserInfo.objects.filter(username=username).first()
    if user:
        # 用户存在
        return JsonResponse(res)
    else:
        # 用户不存在
        res['code'] = 101
        res['msg'] = '用户不存在'
        return JsonResponse(res)
    // username输入框,失去焦点,触发ajax执行
    $("#id_username").blur(function (){
        $.ajax({
            url:'/check_username/?username=' + $(this).val(),
            type: 'grt',
            success:function (data){
                if (data.code == 100){
                    // 在span的中插入错误信息
                    {# alert(data.msg) #}
                    $("#id_username").next().html(data.msg)
                }
            }
        })

    })

流程


	-注册功能
    	-1 forms表单的使用:字段类,字段属性
        	- 作用:数据校验,渲染前端页面,渲染前端页面错误信息
        -2 for循环form对象渲染页面
        -3 头像上传:头像动态显示
        -4 ajax提交注册信息
        	-ajax上传文件:FormData    form表单上传文件:指定编码
        	-form表单中:var data = $("#id_form").serializeArray()
		    -jq的循环$.each(data,function(i,v){})
            -登录成功: location.href = '/login/'
            -错误:渲染再页面上
            -定时任务:3s后清空错误信息
       -5 后端
    		-取出前端传进的数据,校验通过后:剔除re_pwd,把头像给avatar
        	-create_user创建用户
            
    -登录
    	-布局使用bootstrap实现的
        -图片验证码:自己写的
        	-保存再session中
            
        -前端图片验证码的刷新
        	-只要src对应的路径发生变化,就会重新加载 
        -ajax提交登录信息
        	-用户名,密码和验证码到后端
	   -登录接口后端
    		-authenticate 验证用户名密码是否正确(密码是加密的)
        	-登录成功,调用auth_login(request, user)---》再session中写入数据,cookie中写入
            
   -首页
		-布局
    		-bootstrap的导航条
        	-左中右的栅格布局
            -中间位置上下:上面是轮播图,下面是文章列表
            	-文章列表:模板语法的for循环 ,media媒体组
                -优化字段:点赞,点踩,评论数量
        -后端接口
        	-查询所有文章(缺分页)
            -轮播图内容:模板语法写的====》使用ajax加载轮播图
        -登录成功显示用户名字,退出
        -退出接口
        -没有登录显示登录注册
        
 -个人站点
	-路由设计:一定要放在最下面
	-布局
    	-base.html:用来做继承
    	-不同用户顶部颜色不一样,不同人博客样式不一样
    -右侧文章:把首页文章复制过来的,最后一行布局推到右侧去了
    -左侧:
    	-标签,分类,随笔档案的渲染---》后端接口查询出
        -后端:3个分组查询,TruncMonth后再分组
        -前端:通过后端查出来的渲染
        -使用inclusion_tag重写了左侧栏的渲染
        
        -左侧的过滤
        	-3条路由
            -汇总成1条路由
    	
 -文章详情
	-布局:base.html
    	-文章真正的渲染  |safe   文章内容是html
    	-点赞点踩的布局:复制粘贴过来的
    -点赞点踩的后端
    	-如果点过了就不能点了
        -没登录不能点
        -事务
    -评论
    	-render显示根和子评论
        -评论ajax提交根评论
        -评论的ajax提交子评论
        -根评论和子评论的ajax显示
        
-后台管理
	-布局
    -文章列表的显示:表格
    -删除文章
    -新增文章:富文本编辑器,标签,分类的渲染
    -处理xss攻击
    -修改文章
    -修改头像
    -修改密码
    -django发送邮件
    
    
    
    
# 可以扩展什么
	-首页轮播图ajax加载
    -新增标签,分类
    -输入密码错误3次,锁定账号,解锁账号
    -修改密码:之前用过的密码不能用了
    -异地登录,发邮件提醒
    	-ip地址根之前不同
	-文章存到草稿
# 学长的项目
	
posted @ 2022-09-15 23:04  |相得益张|  阅读(31)  评论(0编辑  收藏  举报