Django学习笔记二十二——Django的简易项目之账户注册的实现

借由前面所有的知识点,来做一个简单的项目:用户注册,其中使用的主要知识点和一些其他的需求如下:

利用Form组件生成特定的form标签

用AJAX

有上传头像图片的功能

可以实现数据的校验

用auth组件来管理用户

文件的上传

最简单的文件上传 

如果要实现头像图片的上传,我们要先实现文件的上传功能,这时候一定要记住以前说过的一个知识点:input标签为FILE时,一定要把form的enctype设置合适

<form action="{{file_url}}" method="POST" enctype="multipart/form-data">

这样在后台获取到的数据就是一堆经过编码的数据、,然后我们拿到这些数据就可以写在服务端的硬盘内

所以先看看下面的代码,了解文件是怎么上传到服务器内

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



class File_Upload(View):
    def get(self,request):
       return render(request,'file_update.html',{"file_url":'/file_upload/'})


    def post(self,request):
        file_obj = request.FILES.get('file')
        with open(file_obj.name,'wb') as f:  #file_obj.name为上传数据的文件名
            for line in file_obj:
                f.write(line)
        return HttpResponse('已提交')

    #屏蔽跨站伪装请求
    @csrf_exempt
    def dispatch(self, request, *args, **kwargs):
        return super().dispatch(request, *args, **kwargs)
视图

下面是模板文件代码

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>文件上传</title>
</head>
<body>
    <body>
        <!-- 传文件时一定要加下面的entype -->
        <form action="{{file_url}}" method="POST" enctype="multipart/form-data">
            <p>文件
                <input type="file" name="file"> 
            </p>
            <p>
                <input type="submit" value="提交">
            </p>
        </form>
    </body> 
</body>
</html>
模板

其余有两点要注意的事项:

视图是用CVB的模式构建的,所以要注意路由中的设置

url('^file_upload/',v1.File_Upload.as_view()),

同样,因为CBV架构,注意下如何实现的跨站伪装请求的屏蔽(用的装饰器),但却不是装饰的POST方法而是dispatch

还有因为我们在使用OPEN函数的时候,由于没有定义绝对路径,另存的数据路径就是当前目录.

加了头像预览效果的文件上传

在常规的使用环境个中,我们在传文件的时候是会有个预览的效果,那么这个方法是如何实现的呢?

具体的细节我们先不考虑,这里先看看整个上传图片然后显示预览图的过程是怎么实现的:这里用的是JS实现的

属性为file的input变迁内容发生变化以后,触发事件

事件里先创建一个读取文件的对象

var fileReader = new FileReader()

然后获取到上传文件的路径

var filePath = this.files[0];

下面利用刚才创建的读取文件的对象来读取文件内容

fileReader.readAsDataURL(filePath)

接下来就要把读取的内容加载到img标签里了。但是这里还有一个小知识点:读取数据是需要一定时间的,所以我们必须要等到数据读取完毕以后才给标签添加新的属性。这里就要对fileReader绑定一个另外的函数了(onload),相当于BOM里的ready。

fileReader.onload = function(){   
    $('#img1').attr("src",fileReader.result);
}

这样就有了上传图片然后显示预览的效果了。至于图片大小,上传图片的审核(万一上传的不是图片文件不就崩溃了)啥的我们后面接着再讲。下面把上面说的HTML文件放出来。

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>文件上传</title>
</head>
<body>
    <!-- 传文件时一定要加下面的entype -->
    <form action="{{file_url}}" method="POST" enctype="multipart/form-data">
        <p>头像
            <input type="file" name="file" id="avatar"> 
            <img src="" alt="" id="img1">
        </p>
        <p>
            <input type="submit" value="提交"> 
        </p>
    </form>
</body>

<script src="/static/jquery-3.2.1.min.js"></script>
<script>
$('#avatar').change(function(){var filePath = this.files[0];
                               var fileReader = new FileReader();  //创建读取文件对象
                               fileReader.readAsDataURL(filePath); //读取文件
                               fileReader.onload = function(){     //读取文件需要时间,应该等到读取数据完毕后刷新
                                    $('#img1').attr("src",fileReader.result);}
                                })
</script>
</html>
上传图片文件病显示
基于FORM组件和AJAX的注册

 下面我们看看如何结合FORM表单组件和AJAX来实现注册的效果

FORM组件

第一个版本

为了便于管理,我们可以创建一个forms.py用来管理form类,第一版的form类里是只定义了哥哥字段和最基础的校验规则,里面放了用户名,密码,确认密码,邮箱和头像

显示效果利用了Bootstrap的css和js

"""
用到的form类
"""
from django import forms


class RegForm(forms.Form):
    username = forms.CharField(
        max_length=16,
        label='用户名',
        error_messages={
            'max_length': '用户名最长为16位!',
            'required': '用户名不能为空!'
        },
        widget=forms.widgets.TextInput(
            attrs={
                'class': 'form-control'
            }
        )
    )

    password = forms.CharField(
        min_length=6,
        label='密    码',
        widget=forms.widgets.PasswordInput(
            attrs={
                'class': 'form-control'
            }
        ),
        error_messages={
            'max_length': '用户名最长为16位!',
            'required': '用户名不能为空!'
        }
    )

    re_password = forms.CharField(
        max_length=32,
        label='确认密码',
        widget=forms.widgets.PasswordInput(
            attrs={
                'class': 'form-control'
            }
        ),
        error_messages={
            'max_length': '用户名最长为16位!',
            'required': '用户名不能为空!'
        }
    )

    email = forms.EmailField(
        label='邮箱',
        widget=forms.widgets.EmailInput(
            attrs={
                'class': 'form-control'
            }
        ),
        error_messages={
            'invalid': '邮箱格式不正确',
            'required': '邮箱不能为空'
        }
    )
form组件第一版

补充一点:form内密码和确认密码的错误提示由于是从username里粘过来的,是错的,这里就不改了,在下面的form类里会改正。

还有对应的模板

<!-- 文件名:reg_Form.html -->
<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>FORM控件版注册</title>
    <link rel="stylesheet" href="/static/bootstrap/css/bootstrap.min.css">
    <link rel="stylesheet" href="/static/mystyle.css">
</head>
<body>

    <div class="container">
        <div class="row">
            <div class="col-md-6 col-md-offset-3">
                <form action="{{RegURL}}" method="POST" class="form-horizontal reg-form" enctype="multipart/form-data">

                    {% csrf_token %}

                    <div class="form-group">
                        <label for="{{reg_FORM_obj.username.id_for_label}}" class="col-sm-2 control-label">{{reg_FORM_obj.username.label}}</label>
                        <div class="col-sm-10">
                            {{reg_FORM_obj.username}}
                            <span class="help-block"></span>
                        </div>
                    </div>

                    <div class="form-group">
                        <label for="{{reg_FORM_obj.password.id_for_label}}" class="col-sm-2 control-label">{{reg_FORM_obj.password.label}}</label>
                        <div class="col-sm-10">
                            {{reg_FORM_obj.password}}
                            <span class="help-block"></span>
                        </div>
                    </div>



                    <div class="form-group">
                        <label for="{{reg_FORM_obj.re_password.id_for_label}}" class="col-sm-2 control-label">{{reg_FORM_obj.re_password.label}}</label>
                        <div class="col-sm-10">
                            {{reg_FORM_obj.re_password}}
                            <span class="help-block"></span>
                        </div>
                    </div>

                    <div class="form-group">
                        <label for="{{reg_FORM_obj.email.id_for_label}}" class="col-sm-2 control-label">{{reg_FORM_obj.email.label}}</label>
                        <div class="col-sm-10">
                            {{reg_FORM_obj.email}}
                            <span class="help-block"></span>
                        </div>
                    </div>


                    <div class="form-group">
                        <label for="" 
                               class="col-sm-2 control-label">头像</label>
                        <div class="col-sm-10">
                            <label for="id_avatar"><img src="/static/img/default.png" alt=""></label>
                            <input type="file"  name="avatar" id="id_avatar" style="display: none;">
                            
                        </div>
                    </div>


                    <div class="form-group">
                        <div class="col-sm-offset-2 col-sm-10">
                        <input type="submit" value="注册" class="btn btn-success" id="btn1">
                        </div>
                    </div>


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

    </div>


<script src="/static/jquery-3.2.1.min.js"></script>
<script src="/static/bootstrap/js/bootstrap.js"></script>
</body>
</html>
第一版form对应的模板

在这个模板中,值得注意的地方是在头像哪里设置的点击头像的图标就弹出上传文件的窗口,用了label的for属性,也就是说把img标签嵌套在了label里,然后点击img,也就相当与点击了label标签,效果和点击input标签的效果是一样的,然后把imput的显示属性设置为none隐藏掉,就可以了。就是下面的效果

 在这里有个注意点,我们先看看form里的字段和生成的HTML代码之间的关系

 这里用了下面的方法来创建label标签并且绑定指定的input标签

{{reg_FORM_obj.username.id_for_label}}

 把 里面的这段代码贴出来,着重要注意整个div里label的关系,有两个label,第一个label没有设置for小伙伴,第二个设置的小伙伴是input标签

<div class="form-group">
    <label for="" 
           class="col-sm-2 control-label">头像</label>
    <div class="col-sm-10">
        <label for="id_avatar"><img src="/static/img/default.png" alt="" id="avatar-img"></label>
        <input type="file"  name="avatar" id="id_avatar" style="display: none;">
    </div>
</div>

视图是用的CBV类型的,路由里的设置就不说了,对应的url是/reg2/,视图中也要用到这个url

from reg.forms import RegForm
class Reg2(View):
    def get(self,request):
        reg_FORM_obj = RegForm()

        return render(request,'reg_Form.html',{'RegURL':'/reg2/','reg_FORM_obj':reg_FORM_obj})


    def post(self,request):
        form_obj = RegForm(request.POST)
        if form_obj.is_valid():         #校验数据
            print(form_obj.cleaned_data)
        else:
            print(form_obj.errors)

        return HttpResponse(12345)
视图函数(CBV)

在视图中我们只做了最简单的数据校验,也没有对数据库进行相关操作,并且模板中也没有加上图片预览的效果,然后复杂的校验也没有添加(没有加钩子)。并且错误的提示是弹出提示框的效果

第二个版本

根据上面最后的几个问题,我们升级出来第二个FORM版本

首先,图片的预览效果和上面讲的上传文件然后显示预览效果是一样的,主要就是加了个css效果,限制了图片尺寸(80px*80px),出来的效果

然后就是校验,因为在提交数据以前,各个input标签已经自己做过校验了,但是密码和确认密码还没进行比较,这里必须写一个钩子来对两个字段进行比较,还有就是要把校验的结论显示在字段下面,就是这样的效果

 form的结构就略微的修改了一下

"""
用到的form类
"""
from django import forms
from django.core.exceptions import ValidationError

class RegForm(forms.Form):
    username = forms.CharField(
        max_length=16,
        label='用户名',
        error_messages={
            'max_length': '用户名最长为16位!',
            'required': '用户名不能为空!'
        },
        widget=forms.widgets.TextInput(
            attrs={
                'class': 'form-control'
            }
        )
    )

    password = forms.CharField(
        min_length=6,
        label='密    码',
        widget=forms.widgets.PasswordInput(
            attrs={
                'class': 'form-control'
            }
        ),
        error_messages={
            'min_length': '密码不能少于6位!',
            'required': '密码不能为空!'
        }
    )

    re_password = forms.CharField(
        max_length=32,
        label='确认密码',
        widget=forms.widgets.PasswordInput(
            attrs={
                'class': 'form-control'
            }
        ),
        error_messages={
            'max_length': '用户名最长为16位!',
            'required': '用户名不能为空!'
        }
    )

    email = forms.EmailField(
        label='邮箱',
        widget=forms.widgets.EmailInput(
            attrs={
                'class': 'form-control'
            }
        ),
        error_messages={
            'invalid': '邮箱格式不正确',
            'required': '邮箱不能为空'
        }
    )




#重写全局钩子,对确认密码进行校验
    def clean(self):
        pwd = self.cleaned_data.get('password')
        re_pwd = self.cleaned_data.get('re_password')

        if re_pwd and re_pwd != pwd:
            self.add_error('re_password',ValidationError("两次密码不一致"))

        else:
            return self.cleaned_data
 
form组件第二版

模板的思路就是error信息放在每个字段对应的span标签中,下面就是模板代码

<!-- 文件名:reg_Form.html -->
<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>FORM控件版注册</title>
    <link rel="stylesheet" href="/static/bootstrap/css/bootstrap.min.css">
    <link rel="stylesheet" href="/static/mystyle.css">
</head>
<body>

    <div class="container">
        <div class="row">
            <div class="col-md-6 col-md-offset-3">
                <form action="{{RegURL}}" method="POST"  novalidate class="form-horizontal reg-form" enctype="multipart/form-data">

                    {% csrf_token %}

                    <div class="form-group">
                        <label for="{{reg_FORM_obj.username.id_for_label}}" class="col-sm-2 control-label">{{reg_FORM_obj.username.label}}</label>
                        <div class="col-sm-10">
                            {{reg_FORM_obj.username}}
                            <span class="help-block">{{reg_FORM_obj.username.errors.0}}</span>
                        </div>
                    </div>

                    <div class="form-group">
                        <label for="{{reg_FORM_obj.password.id_for_label}}" class="col-sm-2 control-label">{{reg_FORM_obj.password.label}}</label>
                        <div class="col-sm-10">
                            {{reg_FORM_obj.password}}
                            <span class="help-block">{{reg_FORM_obj.password.errors.0}}</span>
                        </div>
                    </div>



                    <div class="form-group">
                        <label for="{{reg_FORM_obj.re_password.id_for_label}}" class="col-sm-2 control-label">{{reg_FORM_obj.re_password.label}}</label>
                        <div class="col-sm-10">
                            {{reg_FORM_obj.re_password}}
                            <span class="help-block">{{reg_FORM_obj.re_password.errors.0}}</span>
                        </div>
                    </div>


                    <div class="form-group">
                        <label for="{{reg_FORM_obj.email.id_for_label}}" class="col-sm-2 control-label">{{reg_FORM_obj.email.label}}</label>
                        <div class="col-sm-10">
                            {{reg_FORM_obj.email}}
                            <span class="help-block"></span>
                        </div>
                    </div>


                    <div class="form-group">
                        <label for="" 
                               class="col-sm-2 control-label">头像</label>
                        <div class="col-sm-10">
                            <label for="id_avatar"><img src="/static/img/default.png" alt="" id="avatar-img"></label>
                            <input type="file"  name="avatar" id="id_avatar" style="display: none;">
                            
                        </div>
                    </div>


                    <div class="form-group">
                        <div class="col-sm-offset-2 col-sm-10">
                        <input type="submit" value="注册" class="btn btn-success" id="btn1">
                        </div>
                    </div>


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

    </div>


<script src="/static/jquery-3.2.1.min.js"></script>
<script src="/static/bootstrap/js/bootstrap.js"></script>
<script>
$('#id_avatar').change(function(){
    var filePath = this.files[0];
    var fileReader = new FileReader();
    fileReader.readAsDataURL(filePath);
    fileReader.onload = function(){
        $('#avatar-img').attr('src',fileReader.result);
    }
})
</script>
</body>
</html>
模板

上面的模板代码已经包含了头像预览,错误信息提示的功能,

而视图也有对应的修改,对应的URL为/reg3/

class Reg3(View):
    def get(self,request):
        reg_FORM_obj = RegForm()

        return render(request,'reg_Form3.html',{'RegURL':'/reg3/','reg_FORM_obj':reg_FORM_obj})


    def post(self,request):
        reg_FORM_obj = RegForm(request.POST)
        if reg_FORM_obj.is_valid():         #校验数据
            print(reg_FORM_obj.cleaned_data)
        else:
            print(reg_FORM_obj.errors)

        return render(request,'reg_Form3.html',{'RegURL':'/reg3/','reg_FORM_obj':reg_FORM_obj})
第二版form对应视图

AJAX提交数据

上面已经有了大致的过程,但是为了更加好用,我们在这里引入AJAX来提交数据。

使用AJAX,就要把前面的模板中的submit按钮改成button

先放出来模板文件的全部代码,然后对后面的AJAX详细讲一下

<!-- 文件名:reg_Form.html -->
<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>FORM控件版注册</title>
    <link rel="stylesheet" href="/static/bootstrap/css/bootstrap.min.css">
    <link rel="stylesheet" href="/static/mystyle.css">
</head>
<body>

    <div class="container">
        <div class="row">
            <div class="col-md-6 col-md-offset-3">
                <form action="{{RegURL}}" method="POST"  novalidate class="form-horizontal reg-form" enctype="multipart/form-data">

                    {% csrf_token %}

                    <div class="form-group">
                        <label for="{{reg_FORM_obj.username.id_for_label}}" class="col-sm-2 control-label">{{reg_FORM_obj.username.label}}</label>
                        <div class="col-sm-10">
                            {{reg_FORM_obj.username}}
                            <span class="help-block">{{reg_FORM_obj.username.errors.0}}</span>
                        </div>
                    </div>

                    <div class="form-group">
                        <label for="{{reg_FORM_obj.password.id_for_label}}" class="col-sm-2 control-label">{{reg_FORM_obj.password.label}}</label>
                        <div class="col-sm-10">
                            {{reg_FORM_obj.password}}
                            <span class="help-block">{{reg_FORM_obj.password.errors.0}}</span>
                        </div>
                    </div>



                    <div class="form-group">
                        <label for="{{reg_FORM_obj.re_password.id_for_label}}" class="col-sm-2 control-label">{{reg_FORM_obj.re_password.label}}</label>
                        <div class="col-sm-10">
                            {{reg_FORM_obj.re_password}}
                            <span class="help-block">{{reg_FORM_obj.re_password.errors.0}}</span>
                        </div>
                    </div>


                    <div class="form-group">
                        <label for="{{reg_FORM_obj.email.id_for_label}}" class="col-sm-2 control-label">{{reg_FORM_obj.email.label}}</label>
                        <div class="col-sm-10">
                            {{reg_FORM_obj.email}}
                            <span class="help-block"></span>
                        </div>
                    </div>


                    <div class="form-group">
                        <label for="" 
                               class="col-sm-2 control-label">头像</label>
                        <div class="col-sm-10">
                            <label for="id_avatar"><img src="/static/img/default.png" alt="" id="avatar-img"></label>
                            <input type="file"  name="avatar" id="id_avatar" style="display: none;">
                            
                        </div>
                    </div>



                </form>
                <div class="form-group">
                    <div class="col-sm-offset-2 col-sm-10">
                    <button class="btn btn-success" id="btn2">AJAX提交</button>
                    </div>
                </div>
            </div>
        </div>

    </div>


<script src="/static/jquery-3.2.1.min.js"></script>
<script src="/static/bootstrap/js/bootstrap.js"></script>
<script>
$('#id_avatar').change(function(){
    var filePath = this.files[0];
    var fileReader = new FileReader();
    fileReader.readAsDataURL(filePath);
    fileReader.onload = function(){
        $('#avatar-img').attr('src',fileReader.result);
    };
});

$('#btn2').click(function(){

    //点击按钮 ,触发AJAX请求事件
    var username = $('#id_username').val();
    var pwd = $('#id_password').val();
    var re_pwd = $('#id_re_password').val();
    var email = $('#id_email').val();


    $.ajax({
        url:'/reg3/',
        type:'post',
        data:{
            username:username,
            password:pwd,
            re_password:re_pwd,
            email:email
        },
        success:function(data){
            if (data.status){
                //有错误状态,错误为data.msg
                //将错误填写至标签内
                $.each(data.msg,function(k,v){
                    // console.log($('#id_'+k))
                    $('#id_'+k).next('span').text(v[0])
                })

            }
            else{//没有错误跳转到指定页面
                location.href = data.msg
            }
        }
    })
})
//将所有的input框绑定的获取焦点信息
$('input').focus(function(){
     $(this).next('span').text('');
})
</script>
</body>
</html>
AJAX提交的模板

主要是看下面之一段AJAX的代码

 1 $.ajax({
 2         url:'/reg3/',
 3         type:'post',
 4         data:{
 5             username:username,
 6             password:pwd,
 7             re_password:re_pwd,
 8             email:email
 9         },
10         success:function(data){
11             if (data.status){
12                 //有错误状态,错误为data.msg
13                 //将错误填写至标签内
14                 $.each(data.msg,function(k,v){
15                     $('#id_'+k).next('span').text(v[0])
16                 })
17             }
18             else{//没有错误跳转到指定页面
19                 location.href = data.msg
20             }
21         }
22     })
23 })

ajax的请求按POST还是发送给了reg3这个URL,请求带的数据就是input输入框里的内容。然后下面是视图函数

 1 class Reg3(View):
 2     def get(self,request):
 3         reg_FORM_obj = RegForm()
 4 
 5         return render(request,'reg_Form3.html',{'RegURL':'/reg3/','reg_FORM_obj':reg_FORM_obj})
 6 
 7 
 8     def post(self,request):
 9         ret = {'status':0,'msg':''}
10         reg_FORM_obj = RegForm(request.POST)
11         if reg_FORM_obj.is_valid():         #校验数据
12             print(reg_FORM_obj.cleaned_data)
13             ret['msg']='/index/'
14             return JsonResponse(ret)
15         else:
16             ret['status'] = 1
17             print(reg_FORM_obj.errors)
18             ret['msg'] = reg_FORM_obj.errors
19             return JsonResponse(ret)
20 
21     @csrf_exempt
22     def dispatch(self, request, *args, **kwargs):
23         return super().dispatch(request, *args, **kwargs)

备注一下,第13行那个跳转是直接用HttpResponse返回了一个字符串('注册成功!')

视图的流程解析

整个流程主要是判定数据校验是否合格,首先定义一个字典(ret),字典里面就放两个参数,一个是校验状态(status),状态值为1时为校验不合格;还有个参数; 是消息值(msg),校验合格的时候消息值就是要跳转的页面URL,否则就存对应的错误信息。

当数据校验成功的时候(is_valid()==1)就通过AJAX跳转到指定的url。(其实这里还应该有个对应的数据库操作,省略了...) 

如果数据校验不合格的话就把状态值写成1然后把错误的i信息赋值给msg。

最后把ret通过JSONResponse返回给AJAX(因为AJAX里没有指定数据类型,可以直接用JsonResponse返回)。

AJAX流程解析

AJAX在拿到返回值时(ret),先根据ret中的status状态进行判定,如果校验成功(值为0)则用locat.href跳转到msg指定的URL

如果校验不成功,则先拿到错误信息(msg),然后用了jQuery的each方法

var ret = {name:'aa',age:20}
$.each(ret,function(k,v){console.log(k,v)})

//输出值
name aa
age 20

这样就能拿到错误信息的key和value,key就是input的id的一部分,我们用拼接的方法拿到对应的input标签,然后在用next拿到后面的span标签把错误信息放进去。

最后还加了个事件,就是input标签在获取到焦点的时候把下面span标签内的内容删除掉。

带头像文件的AJAX请求

利用AJAX传带文件的请求时,一定要加两个参数

processData:false,
contentType:false,

然后,数据必须是FormData的类型,就不能是data了。

var formData = new FormData()
formData.append('username',$('#id_username').val());
formData.append('password',$('#id_password').val());
formData.append('re_password',$('#id_re_password').val());
formData.append('email',$('#id_email').val());
formData.append('avatar',$('#id_avatar')[0].files[0])

然后把这个formData传就行了。

为了体现效果,我们用一下auth模块,然后添加个头像的字段

from django.db import models

# Create your models here.

from django.contrib.auth.models import AbstractUser


class UserInfo(AbstractUser):
    """
    用户信息表
    """
    nid = models.AutoField(primary_key=True)
    phone = models.CharField(max_length=11, null=True, unique=True)
    avatar = models.FileField(upload_to="avatars/", default="avatars/default.png", verbose_name="头像")
    create_time = models.DateTimeField(auto_now_add=True)

还有一定要记得在settings.py里添加指定的auth

AUTH_USER_MODEL = 'reg.UserInfo'

然后迁移,生成新的数据库

然后是模板和视图

<!-- 文件名:reg_Form.html -->
<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>FORM控件版注册</title>
    <link rel="stylesheet" href="/static/bootstrap/css/bootstrap.min.css">
    <link rel="stylesheet" href="/static/mystyle.css">
</head>
<body>

    <div class="container">
        <div class="row">
            <div class="col-md-6 col-md-offset-3">
                <form action="{{RegURL}}" method="POST"  novalidate class="form-horizontal reg-form" enctype="multipart/form-data">

                    {% csrf_token %}

                    <div class="form-group">
                        <label for="{{reg_FORM_obj.username.id_for_label}}" class="col-sm-2 control-label">{{reg_FORM_obj.username.label}}</label>
                        <div class="col-sm-10">
                            {{reg_FORM_obj.username}}
                            <span class="help-block">{{reg_FORM_obj.username.errors.0}}</span>
                        </div>
                    </div>

                    <div class="form-group">
                        <label for="{{reg_FORM_obj.password.id_for_label}}" class="col-sm-2 control-label">{{reg_FORM_obj.password.label}}</label>
                        <div class="col-sm-10">
                            {{reg_FORM_obj.password}}
                            <span class="help-block">{{reg_FORM_obj.password.errors.0}}</span>
                        </div>
                    </div>



                    <div class="form-group">
                        <label for="{{reg_FORM_obj.re_password.id_for_label}}" class="col-sm-2 control-label">{{reg_FORM_obj.re_password.label}}</label>
                        <div class="col-sm-10">
                            {{reg_FORM_obj.re_password}}
                            <span class="help-block">{{reg_FORM_obj.re_password.errors.0}}</span>
                        </div>
                    </div>


                    <div class="form-group">
                        <label for="{{reg_FORM_obj.email.id_for_label}}" class="col-sm-2 control-label">{{reg_FORM_obj.email.label}}</label>
                        <div class="col-sm-10">
                            {{reg_FORM_obj.email}}
                            <span class="help-block"></span>
                        </div>
                    </div>


                    <div class="form-group">
                        <label for="" 
                               class="col-sm-2 control-label">头像</label>
                        <div class="col-sm-10">
                            <label for="id_avatar"><img src="/static/img/default.png" alt="" id="avatar-img"></label>
                            <input type="file"  name="avatar" id="id_avatar" style="display: none;">
                            
                        </div>
                    </div>



                </form>
                <div class="form-group">
                    <div class="col-sm-offset-2 col-sm-10">
                    <button class="btn btn-success" id="btn2">AJAX提交</button>
                    </div>
                </div>
            </div>
        </div>

    </div>


<script src="/static/jquery-3.2.1.min.js"></script>
<script src="/static/bootstrap/js/bootstrap.js"></script>
<script>
$('#id_avatar').change(function(){
    var filePath = this.files[0];
    var fileReader = new FileReader();
    fileReader.readAsDataURL(filePath);
    fileReader.onload = function(){
        $('#avatar-img').attr('src',fileReader.result);
    };
});

$('#btn2').click(function(){

    //点击按钮 ,触发AJAX请求事件
    var formData = new FormData()
    formData.append('username',$('#id_username').val());
    formData.append('password',$('#id_password').val());
    formData.append('re_password',$('#id_re_password').val());
    formData.append('email',$('#id_email').val());
    formData.append('avatar',$('#id_avatar')[0].files[0])
 
    $.ajax({
        url:'/reg3/',
        type:'post',
        processData:false,
        contentType:false,
        data:formData,
        // data:{
        //     username:username,
        //     password:pwd,
        //     re_password:re_pwd,
        //     email:email,
        //     avatar:$('#id_avatar')[0].files[0]
        // },
        success:function(data){
            if (data.status){
                //有错误状态,错误为data.msg
                //将错误填写至标签内
                $.each(data.msg,function(k,v){
                    $('#id_'+k).next('span').text(v[0])
                })

            }
            else{//没有错误跳转到指定页面
                location.href = data.msg
            }
        }
    })
})
//将所有的input框绑定的获取焦点信息
$('input').focus(function(){
     $(this).next('span').text('');
})
</script>
</body>
</html>
带传文件的模板
from reg import models

class Reg3(View):
    def get(self,request):
        reg_FORM_obj = RegForm()

        return render(request,'reg_Form3.html',{'RegURL':'/reg3/','reg_FORM_obj':reg_FORM_obj})


    def post(self,request):
        ret = {'status':0,'msg':''}
        avatar_img = request.FILES.get('avatar')

        reg_FORM_obj = RegForm(request.POST)
        if reg_FORM_obj.is_valid():         #校验数据
            print(reg_FORM_obj.cleaned_data)
            reg_FORM_obj.cleaned_data.pop('re_password')
            models.UserInfo.objects.create_user(**reg_FORM_obj.cleaned_data,avatar=avatar_img)
            ret['msg']='/index/'
            return JsonResponse(ret)
        else:
            ret['status'] = 1
            ret['msg'] = reg_FORM_obj.errors
            return JsonResponse(ret)

    @csrf_exempt
    def dispatch(self, request, *args, **kwargs):
        return super().dispatch(request, *args, **kwargs)
带传文件的视图

这个头像最后上传在数据库中是按照路径去存的

并且在项目的路径中出现了一个新的文件夹,这是因为我们在定义头像字段的时候有个这个属性:

upload_to="avatars/"

意思就是把图片上传到项目根目录下这个文件夹中,如果没有的话就创建一个。

用户名的校验

我们在注册的时候必须对用户名进行校验,如果用户名已经存在了就要推出相应的错误提示,因为这时候只需要对username一个字段进行校验,所以写一个局部钩子就可以了

#重构局部的钩子对username字段进行校验
    def clean_username(self):
        username = self.cleaned_data.get('username')
        user = models.UserInfo.objects.filter(username=username)
        if user:
            self.add_error('username',ValidationError('用户名已存在!'))
        else:
            return username

注意局部钩子的返回值和全局的钩子的返回值是不一样的,全局钩子返回的是cleaned_data,而局部钩子返回的是被校验的字段。

上面的方法是在提交数据的时候进行校验,还有一种更高级的方法,就是绑定一个对话框的事件,每次修改值就进行校验,下面只放这一部分的AJAX代码和对应的URL视图函数

$('#id_username').on('input',function(){
    var user = $(this).val()
    $.ajax({
        url:'/username_check/',
        type:'POST',
        data:{
            username:user
        },
        success:function(data){

            if(data.status){
                //用户名已经被注册
                $('#id_username').next().text(data.msg);
            }
        }
    })
})

下面是视图函数

@csrf_exempt
def user_check(request):
    ret = {'status':0,'msg':''}

    username = request.POST.get('username')


    user = models.UserInfo.objects.filter(username=username)

    if user:
        ret['status'] = 1
        ret['msg'] = '用户存在'

    return JsonResponse(ret)

整个校验的视图跟前面的视图差不多,唯一一点当时卡住的地方是AJAX里最后一行代码用了$(this)一直不出来错误提示,是但是调试的时候是能获取到错误信息的,是因为这里的this已经不是一开始的input标签了而是success后面的函数了。重新修改一下就解决问题!

原文地址:https://www.cnblogs.com/yinsedeyinse/p/12945367.html