Django 实现注册时选择头像,头像预览在页面,并且在数据库中写入头像文件,Ajax参数及上传文件方式

使用场景:用户注册等

1、表结构

class UserInfo(AbstractUser):
    """
    用户信息表,继承auth认证模块中的默认表
    """
    nid = models.AutoField(primary_key=True)
    phone = models.CharField(max_length=11, unique=True)
    avatar = models.FileField(upload_to='static/img', default='static/img/default.png', verbose_name='头像')  #upload_to值为头像存在本地的地址
    create_time = models.DateTimeField(auto_now_add=True)  # 注册时间

    class Meta:
        verbose_name = '用户表'
        verbose_name_plural = verbose_name

    def __str__(self):
        return self.username

2、forms表单验证注册

from django import forms
from blog import models
from django.contrib.auth.backends import ModelBackend
from django.db.models import Q
from .models import UserInfo


from django.core.validators import RegexValidator
from django.core.exceptions import ValidationError

"""
注册页表单
"""

class Register(forms.Form):
    username = forms.CharField(max_length=15, min_length=2,
                               label='用户名',
                               error_messages={
                                   'max_length': '最长不能超过15位',
                                   'min_length': '最短不能少于2位',
                                   'required': '用户名不能为空'
                               },
                               widget=forms.widgets.TextInput(
                                   attrs={"class": "form-control"},
                               )
                               )

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

    rep_password = forms.CharField(min_length=6,
                                   label='确认密码',
                                   error_messages={
                                       'min_length': '确认密码长度小于6',
                                       'required': '确认密码不能为空'
                                   },
                                   widget=forms.widgets.PasswordInput(
                                       attrs={"class": "form-control"},
                                   )
                                   )

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

    phone = forms.CharField(  # max_length=11,
        label='手机号',
        validators=[RegexValidator(r'^[0-9]+$', '请输入数字'),
                    RegexValidator(r'^0?(13|14|15|17|18|19)[0-9]{9}', '手机号有误')],
        error_messages={
            'max_length': '手机号不正确',
            'required': '邮箱不能为空'
        },
        widget=forms.widgets.TextInput(
            attrs={"class": "form-control"},
        )
    )

    # 重写全局的钩子函数,对确认密码做校验
    def clean(self):
        password = self.cleaned_data.get("password")
        rep_password = self.cleaned_data.get("rep_password")

        if rep_password and rep_password != password:
            self.add_error("rep_password", ValidationError("两次密码不一致"))

        else:
            return self.cleaned_data

    # 重写局部钩子函数,对手机号进行验证
    def clean_phone(self):
        phone = self.cleaned_data.get("phone")
        if models.UserInfo.objects.filter(phone=phone):
            raise ValidationError("手机号已被注册")
        else:
            return phone

    # 重写局部钩子函数,对用户名进行验证
    def clean_username(self):
        username = self.cleaned_data.get("username")
        if models.UserInfo.objects.filter(username=username):
            raise ValidationError("用户名已被注册")
        else:
            return username

    # 重写局部钩子函数,对邮箱进行验证
    def clean_email(self):
        email = self.cleaned_data.get("email")
        if models.UserInfo.objects.filter(email=email):
            raise ValidationError("邮箱已被注册")
        else:
            return email

3、通过Ajax上传数据

<script>
    $('#id_register').on('click', function () {
        //注册一个formData对象 利用它来提交表单、模拟表单提交,最大用法是可以上传二进制文件,把所有数据添加到formData对象就可以
        var formData = new FormData();
        // 把注册数据添加到formData对象
        formData.append('username', $('#id_username').val())
        formData.append('password', $('#id_password').val())
        formData.append('rep_password', $('#id_rep_password').val())
        formData.append('email', $('#id_email').val())
        formData.append('phone', $('#id_phone').val())
        //把头像文件添加到formData对象
        formData.append('avatar', $("#id_avatar")[0].files[0])

        $.ajax({
            url:'/register/',
            type:'post',
            processData: false,  // 告诉jQuery不要去处理发送的数据 传文件必须设置为false
            contentType: false,  // 告诉jQuery不要去设置Content-Type请求头 传文件必须设置为false
            data:formData,
            success:function (ret) {
                if (ret.state == 1){
                    // 如果状态码为1,代表有错误,循环存有错误信息的对象,将错误信息添加到span标签,并给输入框加上has-error样式类
                    //each 循环函数
                    $.each(ret.msg, function (k,v) {
                        $('#id_'+k).next().text(v).parent().parent().addClass('has-error')
                    })
                }else {
                    // 否则输入信息正确
                    alert('注册成功')
                    location.href='/login/'
                }
            }

        })
    })


    // 将默认头像替换成用户选中的头像:值被修改事件
    $('#id_avatar').on('change', function () {
        //创建一个读取文件对象
        var fileReader = new FileReader()
        //取到当前选中的头像文件
        //console.log(this.files[0])
        //读取选中的头像文件 ,读取文件需要时间,需要等待读取完成才能加载到img标签中
        fileReader.readAsDataURL(this.files[0])
        //等待读取完毕后,将文件加载到img标签中
        fileReader.onload = function () {
            $('#id_img').attr('src',fileReader.result)
        }
    })

</script>

4、视图views处理

def register(request):
    forms_obj = forms.Register()
    if request.method == 'POST':
        ret = {
            'state': 0,
            'msg': '',
        }
        forms_obj = forms.Register(request.POST)
        if forms_obj.is_valid():
            # 验证通过,将数据写入数据库
            forms_obj.cleaned_data.pop('rep_password')  # 删除重复密码 cleaned_data存放forms对象的数据
            avatar = request.FILES.get('avatar', None)  # 获取头像文件
            # print(avatar, type(avatar))
            # 将头像 密码等,写入数据库
            models.UserInfo.objects.create_user(avatar=avatar, **forms_obj.cleaned_data)
            return JsonResponse(ret)
        else:
            print(forms_obj.errors, type(forms_obj.errors))  # forms_obj.errors返回一个存有所有验证错误信息的对象
            ret = {
                'state': 1,
                'msg': forms_obj.errors
            }
            return JsonResponse(ret)
    return render(request, 'register.html', {'forms_obj': forms_obj})

 

知识点:

  1、forms模块的使用,手机号字段自定义正则做校验,局部钩子与全局钩子做校验。

  2、Ajax上传文件的方式:利用FormData方法

  3、前端预览选中的文件方式:利用FileReader方法,需要注意读取文件需要时间,需要把文件读取完毕才进行显示

  4、后端接收文件及处理

 

Ajax的参数:

  url:往哪请求

  type:请求方式

  dataType:json  接收的数据必须是json格式,接收后会自动反序列化,如果不是json数据,反序列化失败

  data:需要发送的数据

更多参数:点击

  

 
posted @ 2020-02-12 22:18  aikell  阅读(679)  评论(0编辑  收藏  举报