django博客项目-注册功能实现

注册功能描述

使用者输入用户数据进行注册,对用户信息校验(forms组件),若用户输入数据不符合规范,系统提示用户错误信息和正确输入格式(ajax局部刷新),输入数据没有问题,将用户数据写入数据库,并跳转到登录界面。

设计注册页面

代码

前端代码:

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>blog</title>
    <link rel="stylesheet" href="/static/css/bootstrap.min.css">
    <script src="/static/js/bootstrap.min.js"></script>
    <style>
        #avatar_img{
            margin-left: 20px
        }
    </style>
</head>
<body>
<h1>注册</h1>
<div class="container">
<div class="row">
    <div class="col-md-6 col-lg-offset-3">
        <form>
            {% csrf_token %}
            {% for field in form %}
                <div class="form-group">
<!--                    当lable中for的值和其他标签id的值相同时,点击lable就相当于点击了对应标签-->
                    <label for="{{ field.auto_id }}">{{ field.label }}</label>
                    {{ field }}
                </div>
            {% endfor %}
            <div class="form-group">
                <label for="avatar">
                    头像
                    <img id="avatar_img" width="60px" height="60px" src="/static/blog/img/default.png">
                </label>

                <input type="file" id="avatar" style="display:none">
            </div>
            <input type="button" class="btn btn-default login_btn" value="注册">
        </form>

    </div>
</div>
</div>

</body>
</html>

后端代码

from django import forms
from django.forms import widgets
class UserForm(forms.Form):
    user = forms.CharField(max_length=32,
                           label='用户名',
                           widget=widgets.TextInput(attrs={'class':'form-control'})
                           )
    pwd = forms.CharField(max_length=32,
                          label='密码',
                          widget=widgets.PasswordInput(attrs={'class': 'form-control'})
                          )
    re_pwd = forms.CharField(max_length=32,
                             label='确认密码',
                             widget=widgets.PasswordInput(attrs={'class': 'form-control'}))
    email = forms.EmailField(max_length=32,
                             label='邮箱',
                             widget=widgets.EmailInput(attrs={'class': 'form-control'}))

def register(request):
    form = UserForm()
    return render(request, 'register.html', {'form': form})

知识点

forms组件的前端渲染
label 标签for属性的功能实现头像点击上传

效果预览

在这里插入图片描述

注册界面头像预览功能

代码

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>blog</title>
    <link rel="stylesheet" href="/static/css/bootstrap.min.css">
    <script src="/static/js/bootstrap.min.js"></script>
    <style>
        #avatar_img{
            margin-left: 20px
        }
    </style>
</head>
<body>
<h1>注册</h1>
<div class="container">
<div class="row">
    <div class="col-md-6 col-lg-offset-3">
        <form>
            {% csrf_token %}
            {% for field in form %}
                <div class="form-group">
<!--                    当lable中for的值和其他标签id的值相同时,点击lable就相当于点击了对应标签-->
                    <label for="{{ field.auto_id }}">{{ field.label }}</label>
                    {{ field }}
                </div>
            {% endfor %}
            <div class="form-group">
                <label for="avatar">
                    头像
                    <img id="avatar_img" width="60px" height="60px" src="/static/blog/img/default.png">
                </label>

                <input type="file" id="avatar" style="display:none" onload="/static">
            </div>
            <input type="button" class="btn btn-default login_btn" value="注册">
        </form>

    </div>
</div>
</div>

<script src="/static/js/jquery.min.js"></script>
<script>
    $('#avatar').change(function (){
        // 获取用户选中的文件对象
        var file_obj = $(this)[0].files[0]
        // 获取文件对象的路径
        var reader = new FileReader()
        reader.readAsDataURL(file_obj)
        reader.result
        // 修改img的src属性
        reader.onload=function(){
            $('#avatar_img').attr("src", reader.result)
        }

    })
</script>

</body>
</html>

知识点

ajax 的change事件
ajax 的onload事件

ajax数据提交

代码

前端代码:

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>blog</title>
    <link rel="stylesheet" href="/static/css/bootstrap.min.css">
    <script src="/static/js/bootstrap.min.js"></script>
    <style>
        #avatar_img{
            margin-left: 20px
        }
    </style>
</head>
<body>
<h1>注册</h1>
<div class="container">
<div class="row">
    <div class="col-md-6 col-lg-offset-3">
        <form id="form">
            {% csrf_token %}
            {% for field in form %}
                <div class="form-group">
<!--                    当lable中for的值和其他标签id的值相同时,点击lable就相当于点击了对应标签-->
                    <label for="{{ field.auto_id }}">{{ field.label }}</label>
                    {{ field }}
                </div>
            {% endfor %}
            <div class="form-group">
                <label for="avatar">
                    头像
                    <img id="avatar_img" width="60px" height="60px" src="/static/blog/img/default.png">
                </label>

                <input type="file" id="avatar" style="display:none" onload="/static">
            </div>
            <input type="button" class="btn btn-default register_btn" value="注册">
        </form>

    </div>
</div>
</div>

<script src="/static/js/jquery.min.js"></script>
<script>
    $('#avatar').change(function (){
        // 获取用户选中的文件对象
        var file_obj = $(this)[0].files[0]
        // 获取文件对象的路径
        var reader = new FileReader()
        reader.readAsDataURL(file_obj)
        reader.result
        // 修改img的src属性
        reader.onload=function(){
            $('#avatar_img').attr("src", reader.result)
        }
    })
    $('.register_btn').click(function (){
        // var form = new FormData()
        // form.append('user', $('#id_user').val())
        // form.append('pwd', $('#id_pwd').val())
        // form.append('re_pwd', $('#id_re_pwd').val())
        // form.append('email', $('#id_email').val())
        // form.append('avatar', $('#avatar')[0].files[0])
        // form.append('csrfmiddlewaretoken', $("[name='csrfmiddlewaretoken']").val())

        // 优化数据封装进FormData中的方式,优化代码冗余
        var form = new FormData();
        var request_data = $('#form').serializeArray();
        // 循环处理
        $.each(request_data, function(index, data){
            form.append(data.name, data.value)
        });
        form.append('avatar', $('#avatar')[0].files[0])

        $.ajax({
            url:"",
            method:"post",
            data:form,
            contentType:false,
            processData:false,
            success: function(data){
                console.log(data)
            }
        })
    })
</script>

</body>
</html>

后端代码

def register(request):
    form = UserForm()
    if request.is_ajax():
        form = UserForm(request.POST)
        response = {'user': None, 'msg': None}
        if form.is_valid():
            pass
        else:
            response['msg'] = form.errors
        return JsonResponse(response)
    return render(request, 'register.html', {'form': form})

知识点

ajax 提交formdata类型数据,拓展到前后端传输的三种编码格式。
ajax 提交文件数据

ajax 显示错误提示信息

在后端返回后,ajax的success函数中进行判断如果用户有输入错误信息,对其进行显示到对应标签的下方。对错误信息有误的input框使其飘红。

代码

在forms组件前端渲染的input后添加span占位标签,并为其增加样式

<head>
    <meta charset="UTF-8">
    <title>blog</title>
    <link rel="stylesheet" href="/static/css/bootstrap.min.css">
    <script src="/static/js/bootstrap.min.js"></script>
    <style>
        #avatar_img{
            margin-left: 20px
        }
        .error{
            color: red
        }
    </style>
</head>
<body>
<h1>注册</h1>
<div class="container">
<div class="row">
    <div class="col-md-6 col-lg-offset-3">
        <form id="form">
            {% csrf_token %}
            {% for field in form %}
                <div class="form-group">
<!--                    当lable中for的值和其他标签id的值相同时,点击lable就相当于点击了对应标签-->
                    <label for="{{ field.auto_id }}">{{ field.label }}</label>
                    {{ field }} <span class="error pull-right"></span>
                </div>
            {% endfor %}
            <div class="form-group">
                <label for="avatar">
                    头像
                    <img id="avatar_img" width="60px" height="60px" src="/static/blog/img/default.png">
                </label>

                <input type="file" id="avatar" style="display:none" onload="/static">
            </div>
            <input type="button" class="btn btn-default register_btn" value="注册">
        </form>

    </div>
</div>
</div>

ajax对内容进行显示和标签渲染

<script src="/static/js/jquery.min.js"></script>
<script>
    $('.register_btn').click(function (){
        // var form = new FormData()
        // form.append('user', $('#id_user').val())
        // form.append('pwd', $('#id_pwd').val())
        // form.append('re_pwd', $('#id_re_pwd').val())
        // form.append('email', $('#id_email').val())
        // form.append('avatar', $('#avatar')[0].files[0])
        // form.append('csrfmiddlewaretoken', $("[name='csrfmiddlewaretoken']").val())

        // 优化数据封装进FormData中的方式,优化代码冗余
        var form = new FormData();
        var request_data = $('#form').serializeArray();
        // 循环处理
        $.each(request_data, function(index, data){
            form.append(data.name, data.value)
        });
        form.append('avatar', $('#avatar')[0].files[0])

        $.ajax({
            url:"",
            method:"post",
            data:form,
            contentType:false,
            processData:false,
            success: function(data){
                // console.log(data)
                if (data.user){
                    // 用户登录成功
                }
                else {
                    // 注册失败,错误信息处理
                    // 清空错误信息
                    $("span.error").html("")
                    $(".form-group").removeClass("has-error")
                    // 展现错误信息
                    $.each(data.msg, function(field, error_list){
                        // 找到对应input标签的下一个span标签
                        $('#id_'+field).next().html(error_list[0])
                        $('#id_'+field).parent().addClass("has-error")
                    })
                }
            }
        })
    })
</script>

效果展示

在这里插入图片描述

forms 组件对用户数据添加校验规则,全局钩子,局部钩子

代码

前端代码,在ajax中添加对全局钩子错误信息显示的特殊处理

<script src="/static/js/jquery.min.js"></script>
<script>
    $('.register_btn').click(function (){
        // var form = new FormData()
        // form.append('user', $('#id_user').val())
        // form.append('pwd', $('#id_pwd').val())
        // form.append('re_pwd', $('#id_re_pwd').val())
        // form.append('email', $('#id_email').val())
        // form.append('avatar', $('#avatar')[0].files[0])
        // form.append('csrfmiddlewaretoken', $("[name='csrfmiddlewaretoken']").val())

        // 优化数据封装进FormData中的方式,优化代码冗余
        var form = new FormData();
        var request_data = $('#form').serializeArray();
        // 循环处理
        $.each(request_data, function(index, data){
            form.append(data.name, data.value)
        });
        form.append('avatar', $('#avatar')[0].files[0])

        $.ajax({
            url:"",
            method:"post",
            data:form,
            contentType:false,
            processData:false,
            success: function(data){
                // console.log(data)
                if (data.user){
                    // 用户登录成功
                }
                else {
                    // 注册失败,错误信息处理
                    // 清空错误信息
                    $("span.error").html("")
                    $(".form-group").removeClass("has-error")
                    // 展现错误信息
                    $.each(data.msg, function(field, error_list){
                        // 全局钩子错误信息处理
                        if (field == "__all__"){
                            $("#id_re_pwd").next().html(error_list[0]).parent().addClass("has-error")
                        }
                        // 找到对应input标签的下一个span标签
                        $('#id_'+field).next().html(error_list[0])
                        $('#id_'+field).parent().addClass("has-error")
                    })
                }
            }
        })
    })
</script>

后端代码,添加全局钩子和局部钩子。另forms组件内建议放在app下的Myforms.py文件中,而不是views.py文件中

from django import forms
from django.forms import widgets
from django.core.exceptions import ValidationError
from blog.models import UserInfo
class UserForm(forms.Form):
    user = forms.CharField(max_length=32,
                           label='用户名',
                           error_messages={'required': "字段不能为空"},
                           widget=widgets.TextInput(attrs={'class':'form-control'})
                           )
    pwd = forms.CharField(max_length=32,
                          label='密码',
                          widget=widgets.PasswordInput(attrs={'class': 'form-control'})
                          )
    re_pwd = forms.CharField(max_length=32,
                             label='确认密码',
                             widget=widgets.PasswordInput(attrs={'class': 'form-control'}))
    email = forms.EmailField(max_length=32,
                             label='邮箱',
                             widget=widgets.EmailInput(attrs={'class': 'form-control'}))

    def clean_user(self):
        user = self.cleaned_data.get('user')
        user_obj = UserInfo.objects.filter(username=user).first()
        if not user_obj:
            return user
        else:
            raise ValidationError("该用户已注册!")

    def clean(self):
        pwd = self.cleaned_data.get('pwd')
        re_pwd = self.cleaned_data.get('re_pwd')
        if re_pwd == pwd:
            return self.cleaned_data
        else:
            raise ValidationError("两次密码不一致")

知识点

forms组件局部钩子,全局钩子

效果展示

在这里插入图片描述

用户上传头像保存,生成用户数据

django有两种静态文件

  • /static/ 是js,css,img 系统用到的静态文件和第三方框架
  • /media/ 是用户上传的文件

media配置

配置MEDIA_ROOT

在settings.py中配置 MEDIA_ROOT=os.path.join(BASE_DIR, "media"),一旦对其进行了配置,只要执行:

avatar_obj = request.FILES.get("avatar")
user_obj = UserInfo.objects.create_user(username=user,password=password,email=email,avatar=avatar_obj)

django会将文件对象下载到MEDIA_ROOT中的avatar文件中(如果没有avatar文件夹,django会自动创建),user_obj的avatar必须接收的是一个文件对象,其在数据库存储的是文件的相对路径。

配置MEDIA_URL

浏览器可以直接访问到media中的数据,即为media资源开设访问接口。
在settings.py 中配置MEDIA_URL=/media/
在全局路由urls.py中添加media配置:

from django.views.static import serve
from cnblog import settings
url(r'media/(?P<path>.*)$', serve, {"document_root":settings.MEDIA_ROOT})

代码

前端代码:增加注册成功后跳转到登录界面

location.href="/login/"

后端代码:

def register(request):
    form = UserForm()
    if request.is_ajax():
        form = UserForm(request.POST)
        response = {'user': None, 'msg': None}
        if form.is_valid():
            # 生成一条用户记录
            user = form.cleaned_data.get("user")
            pwd = form.cleaned_data.get("pwd")
            email = form.cleaned_data.get("email")
            avatar_obj = request.FILES.get("avatar")

            response['user'] = user

            # 检测用户是否上传了头像,如果没有使用默认的
            # if avatar_obj:
            #     user_obj = UserInfo.objects.create_user(
            #         username=user,
            #         password=pwd,
            #         email=email,
            #         avatar=avatar_obj
            #     )
            # else:
            #     user_obj = UserInfo.objects.create_user(
            #         username=user,
            #         password=pwd,
            #         email=email
            #     )
            # 用户存储数据代码优化
            extra = {}
            if avatar_obj:
                extra['avatar'] = avatar_obj
            UserInfo.objects.create_user(username=user, password=pwd, email=email, **extra)
        else:
            response['msg'] = form.errors
        return JsonResponse(response)
    return render(request, 'register.html', {'form': form})
  • 1
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值