zoukankan      html  css  js  c++  java
  • 博客园-注册(form组件,ajax提交数据,头像上传,media)

    博客园-注册(form组件,ajax提交数据,头像上传,media)

    form类创建

    使用form组件需要我们自己创建一个类

    复制代码
    import re
    from django import forms
    from django.forms import widgets
    from django.core.exceptions import ValidationError
    from blog import models
    # Create your views here.
    
    
    class RegForm(forms.Form):
        user = forms.CharField(
            max_length=20,
            min_length=6,
            error_messages={
                "required": "用户名不能为空",
                "max_length": "用户名不能超过20位",
                "min_length": "用户名不能少于6位"
            },
            widget=widgets.TextInput(attrs={"class": "form-control"})
        )
        nickname = forms.CharField(
            max_length=20,
            min_length=3,
            error_messages={
                "required": "用户名不能为空",
                "max_length": "用户名不能超过20位",
                "min_length": "用户名不能少于3位"
            },
            widget=widgets.TextInput(attrs={"class": "form-control"})
        )
        pwd = forms.CharField(
            min_length=6,
            widget=widgets.PasswordInput(attrs={"class": "form-control"}),
            error_messages={
                "required": "密码不能为空",
                "min_length": "密码不能少于6位"
            }
        )
        repeat_pwd = forms.CharField(
            widget=widgets.PasswordInput(attrs={"class": "form-control"}),
            error_messages={
                "required": "确认密码不能为空"
            }
        )
        email = forms.EmailField(
            error_messages={
                "invalid": "格式错误",
                "required": "邮箱不能为空"
            },
            widget=widgets.EmailInput(attrs={"class": "form-control"})
        )
        tel = forms.IntegerField(
            error_messages={
                "required": "手机号不能为空"
            },
            widget=widgets.NumberInput(attrs={"class": "form-control"})
        )
    
        def clean_user(self):
            user = self.cleaned_data.get("user")
            if models.UserInfo.objects.filter(username=user).exists():
                raise ValidationError("用户名已存在")
            else:
                return user
        
        def clean_nickname(self):
            nickname = self.cleaned_data.get("nickname")
            if models.UserInfo.objects.filter(nickname=nickname).exists():
                raise ValidationError("昵称已存在")
            else:
                return nickname
    
        def clean_tel(self):
            tel = self.cleaned_data.get("tel")
            if re.search("^1[3458]d{9}$", str(tel)):
                return tel
            else:
                raise ValidationError("手机号格式错误")
    
        def clean(self):
            pwd = self.cleaned_data.get("pwd")
            repeat_pwd = self.cleaned_data.get("repeat_pwd")
            if pwd == repeat_pwd:
                return self.cleaned_data
            else:
                raise ValidationError("密码不一致")
    复制代码

    在这个类中我们定义好每一个字段的类型及一些判断条件

    前端页面渲染

    复制代码
    {% load static %}
    <!DOCTYPE html>
    <html lang="zh-CN">
    <head>
        <meta charset="UTF-8">
        <meta http-equiv="x-ua-compatible" content="IE=edge">
        <meta name="viewport" content="width=device-width, initial-scale=1">
        <title>注册页面</title>
        <link rel="stylesheet" href="{% static 'bootstrap-3.3.7/css/bootstrap.min.css' %}">
        <script src="{% static 'jquery-3.2.1.min.js' %}"></script>
        <script src="{% static 'bootstrap-3.3.7/js/bootstrap.min.js' %}"></script>
        <style>
            body{
                background-color: #eeeeee;
            }
            .d1{
                margin-top: 100px;
            }
            span{
                color: red;
            }
            .form-group{
                margin-bottom: 20px;
            }
        </style>
    </head>
    <body>
    <div class="container d1">
        <div class="row">
            <div class="col-sm-3 col-sm-offset-3">
                <h3>注册</h3>
            </div>
        </div>
    </div>
    <div class="container">
        <div class="row">
            <div class="col-sm-offset-1 col-sm-10">
                <form class="form-horizontal">
                    {% csrf_token %}
                    <div class="form-group">
                        <label for="id_user" class="col-sm-2 control-label">用户名</label>
                        <div class="col-sm-8">
                            {{ form_obj.user }}
                            <span class="pull-right">&nbsp;</span>
                        </div>
                    </div>
    
                       <div class="form-group">
                        <label for="id_nickname" class="col-sm-2 control-label">昵称</label>
                        <div class="col-sm-8">
                            {{ form_obj.nickname }}
                            <span class="pull-right">&nbsp;</span>
                        </div>
                    </div>
                    <div class="form-group">
                        <label for="id_pwd" class="col-sm-2 control-label">密码</label>
                        <div class="col-sm-8">
                            {{ form_obj.pwd }}
                            <span class="pull-right">&nbsp;</span>
                        </div>
                    </div>
                    <div class="form-group">
                        <label for="id_repeat_pwd" class="col-sm-2 control-label">确认密码</label>
                        <div class="col-sm-8">
                            {{ form_obj.repeat_pwd }}
                            <span class="pull-right">&nbsp;</span>
                        </div>
                    </div>
                    <div class="form-group">
                        <label for="id_email" class="col-sm-2 control-label">邮箱</label>
                        <div class="col-sm-8">
                            {{ form_obj.email }}
                            <span class="pull-right">&nbsp;</span>
                        </div>
                    </div>
                    <div class="form-group">
                        <label for="id_tel" class="col-sm-2 control-label">手机号</label>
                        <div class="col-sm-8">
                            {{ form_obj.tel }}
                            <span class="pull-right">&nbsp;</span>
                        </div>
                    </div>
                    <div class="form-group">
                        <label for="avatar" class="col-sm-2 control-label">头像</label>
                        <div class="col-sm-8">
                            <label for="avatar"><img src="" alt="" width="60" height="60" id="avatar_img"></label>
                            <input type="file" style="display: none;" id="avatar">
                        </div>
                    </div>
                    <div class="form-group">
                        <div class="col-sm-offset-2 col-sm-5">
                            <input type="button" class="btn btn-primary btn-block" value="注册">
                        </div>
                    </div>
                </form>
            </div>
        </div>
    </div>
    复制代码

    通过form组件定义的类实例化一个对象,使用这个对象对前端页面进行渲染

    头像选择

    在前端页面中我们可以看到有一个头像的选项,一般情况下用户点击头像的默认框就可以选择自己要上传的头像

    为了实现这个功能,我们先写一个隐藏的input框(type=file),再利用label标签与他绑定关系,将img标签写入label标签内

    这样便实现了点击img图片也能上传头像的功能

    复制代码
    <div class="form-group">
        <label for="avatar" class="col-sm-2 control-label">头像</label>
        <div class="col-sm-8">
            <label for="avatar"><img src="" alt="" width="60" height="60" id="avatar_img"></label>
            <input type="file" style="display: none;" id="avatar">
        </div>
    </div>
    复制代码

    头像预览功能

    头像预览功能与后端没有什么关系,可以通过前端获取用户选择的图片的路径,然后直接将img标签的src属性改为用户选择图片的路径,实现图片的预览功能

    复制代码
    // 头像预览
        $("#avatar").change(function () {
            var choose_file = $(this)[0].files[0];
            // 实例化一个阅读器对象
            var reader = new FileReader();
            // 读取文件的路径,没有返回值,结果在reader.result里
            reader.readAsDataURL(choose_file);
            // 读取需要时间,读完后再修改图片路径
            reader.onload=function () {
                $("#avatar_img").attr("src",this.result)
            }
    
        });
    复制代码

    这里我们用到了一个取用户选择的文件对象的方法,先使用$("#avatar")取到上传文件的input标签的jquery对象,通过$("#avatar")[0]取到该标签的DOM对象

    再通过DOM对象的files方法拿到上传文件的数组,最后通过索引获取上传的文件对象,即为$("#avatar")[0].files[0]

    得到这个对象后,为了获取该文件的路径,我们使用阅读器的功能

    先实例化一个阅读器对象var reader = new FileReader(),在利用阅读器对象的readAsDATAURL方法读取文件的路径

    由于读取文件路径需要时间,所以我们对该阅读器对象绑定了一个onload方法,待读取完成后,再将img标签的src属性更改为文件的路径

    这样便实现了头像预览的功能

    ajax传文件

     由于需要传文件,如果用form表单传,需要设置enctype="multipart/form-data"

    我们这里使用ajax发送数据,需要用到FormData

    复制代码
    $(":button").click(function () {
            // 实例一个对象,用它来封装数据
            var formdata = new FormData();
            formdata.append("user",$("#id_user").val());
            formdata.append("nickname",$("#id_nickname").val());
            formdata.append("pwd",$("#id_pwd").val());
            formdata.append("repeat_pwd",$("#id_repeat_pwd").val());
            formdata.append("email",$("#id_email").val());
            formdata.append("tel",$("#id_tel").val());
            formdata.append("csrfmiddlewaretoken",$("[name='csrfmiddlewaretoken']").val());
            formdata.append("avatar",$("#avatar")[0].files[0]);
            $.ajax({
                url: "/register/",
                type: "post",
                data: formdata,
                contentType: false,
                processData: false,
                success: function (data) {
                    var data = JSON.parse(data);
                    if (data.reg){
                        location.href="/login/"
                    }else{
                        // 清空上次错误信息
                        $("form span").html("&nbsp;");
                        $(".form-group").removeClass("has-error");
                        var errors = data.error_msg;
                        for (var i in errors){
                            if (i === "__all__"){
                                // 边框变红
                                $("#id_repeat_pwd").parent().parent().addClass("has-error");
                                $("#id_repeat_pwd").next().text(errors[i][0])
                            }else{
                                $("#id_"+i).parent().parent().addClass("has-error");
                                $("#id_"+i).next().text(errors[i][0])
                            }
                        }
                    }
                    // 还可以使用each循环
    {#                $.each(errors, function (field,error) {#}
    {#                    $("#id_"+field).next().text(error[0])#}
    {#                })#}
                }
            })
        })
    复制代码

    注意,这里要加contentType: false, processData: false,否则会出错

    拿到后端传回的数据后

    先将后端传回的数据进行范序列化,再通过传回数据的键值对进行判断

    如果数据没问题,则进行跳转

    如果数据有问题,需要将错误信息显示到页面上

    由于后端传回的错误信息的键值对的键对应的都是form类中的字段,而通过form类实例化的对象渲染出的页面的input标签的id为id_字段,可以通过字符串拼接的方式,得到每个input框的id

    从而取到每个input框的jquery对象,再将相应的错误信息显示到每个input框后面即可

    后端数据处理

    复制代码
    def register(request):
        reg_info = {"reg": True, "error_msg": ""}
        if request.method == "POST":
            form_obj = RegForm(request.POST)
            if form_obj.is_valid():
                user = form_obj.cleaned_data.get("user")
                nickname = form_obj.cleaned_data.get("nickname")
                pwd = form_obj.cleaned_data.get("pwd")
                email = form_obj.cleaned_data.get("email")
                tel = form_obj.cleaned_data.get("tel")
                avatar_obj = request.FILES.get("avatar")  # 图片对象
                models.UserInfo.objects.create_user(username=user, password=pwd, email=email, telephone=tel, nickname=nickname, avatar=avatar_obj)  # 添加avatar字段时会自动下载文件,放到upload_to后的路径里
            else:
                reg_info["error_msg"] = form_obj.errors
                reg_info["reg"] = False
            return HttpResponse(json.dumps(reg_info))
        form_obj = RegForm()
        return render(request, "register.html", locals())
    复制代码

    通过前端传来的数据,实例化一个form_obj对象,在通过这个对象的is_vaild()方法对数据进行校验,如果数据没问题,则从form_obj.cleaned_data中取出数据,使用UserInfo.objects.create_user方法添加数据

    这里需要注意的是,avatar字段,这里我们直接使用的是avatar=avatar_obj这个文件对象,其实在数据库中该字段还是会存放这个文件的路径,并将用户上传的文件放到相应的路径下

    media

    在之前的配置中,我们在settings文件中配置过静态文件的存放目录和别名,用来专门存放一些静态文件

    而在django中,也会将用户上传的文件统一放到一个地方(类似static配置)

    复制代码
    class UserInfo(AbstractUser):
        """
        用户信息
        """
        nid = models.AutoField(primary_key=True)
        nickname = models.CharField(verbose_name='昵称', max_length=32)
        telephone = models.CharField(max_length=11, null=True, unique=True)
        avatar = models.FileField(upload_to='avatar_dir/', default="/avatar/default.png")
        create_time = models.DateTimeField(verbose_name='创建时间', auto_now_add=True)
        blog = models.OneToOneField(to='Blog', to_field='nid', null=True)
    复制代码

    针对FileField,Imagefield字段:
    avatar = models.FileField(upload_to = 'avatars/',default="/avatar/default.png")
    默认会将FileField字段中的upload_to参数对应的值avatars文件下载到项目的根目录下
    if 在settings配置了一句:
    MEDIA_ROOT=os.path.join(BASE_DIR,"blog","media")
    将FileField字段中的upload_to参数对应的值avatars下载到MEDIA_ROOT路径下

    MEDIA_URL = "/media/"  # 别名
    MEDIA_ROOT = os.path.join(BASE_DIR, "blog", "media")

    这里的配置与static静态文件的配置类型,只不过static配置了下面的内容后

    STATIC_URL = '/static/'
    STATICFILES_DIRS = [
        os.path.join(BASE_DIR, "static")
    ]

    用户可以直接通过/static/接文件的路径访问到静态文件,这是因为django在这里自动帮你配置了url

    而media这里django并未帮你配置,所以我们需要自己在url里配置

    复制代码
    from django.conf.urls import url
    from django.contrib import admin
    
    from blog import views
    from django.views.static import serve
    from s8_cnblog import settings
    
    urlpatterns = [
        url(r'^admin/', admin.site.urls),
        url(r'^login/', views.log_in),
        url(r'^get_valid_img/', views.get_valid_img),
        url(r'^index/', views.index),
        url(r'^logout/', views.logout),
        url(r'^register/', views.register),
    
        # media 配置
        url(r'^media/(?P<path>.*)$', serve, {'document_root': settings.MEDIA_ROOT}),
    ]
    复制代码

    这样我们便可以通过/media/接具体的路径找到用户上传的文件了

  • 相关阅读:
    mt7601u: probe of xxxx failed with error -2
    error: 'ENOSYS' undeclared (first use in this function)
    backports移植rtlwifi驱动
    Buildroot 指定内核版本
    Buildroot 使用默认配置
    Uncaught TypeError: jQuery.i18n.browserLang is not a function
    Web APi之控制器创建过程及原理解析(八)
    Web APi之手动实现JSONP或安装配置Cors跨域(七)
    Web APi之Web Host消息处理管道(六)
    Web APi之消息处理管道(五)
  • 原文地址:https://www.cnblogs.com/QQ279366/p/8424227.html
Copyright © 2011-2022 走看看