• BBS


      

    一.基于forms组件的注册页面设计

      点击头像==点击input

      头像预览:

        修改  获取用户选中的文件对象;获取文件对象的路径;修改img标签的src属性,让src=文件对象路径。

      register.html

    <!DOCTYPE html>
    <html lang="en">
    <head>
        <meta charset="UTF-8">
        <title>Title</title>
        <link rel="stylesheet" href="/static/blog/bs/css/bootstrap.css">
        <style>
            #avatar{ {# 隐藏file的input标签 #}
                display: none
            }
            #avatar_img{  {# 默认头像右移20px #}
                margin-left: 20px;
            }
            .error_info{
                color: red;
            }
    
        </style>
    </head>
    <body>
    
        <h3>注册页面</h3>
    
        <div class="container">
            <div class="row">
                <div class="col-md-6 col-md-offset-3" >
                    <form id="form">
                        {% csrf_token %}
    
                        {% for filed in form %} // for循环这个form就是有user,pwd,re_pwd,email这些字段,进行渲染
                            <div class="form-group">
    {#                            <label for="">{{ filed.label }}</label>#}
                                <label for="{{ filed.auto_id }}">{{ filed.label }}</label>
                                {# 由于forms组件渲染的input标签中的id都会自动加上_id,所以在label标签这里的for为了能保持一致,#}
                                {#写上filed.auto_id ,会自动的补上_id#}
                                {{ filed }} // 它渲染出来有input标签,会跟字段的名字一致,只不过这里要发ajax
                            </div>
    
                        {% endfor %}
    
                        <div class="form-group">  {# 头像没有放到input里边,把它单独处理,它不需要检验#}
                            <label for="avatar">  {# input中的id和label中的for一致,则点击头像和图片都可以跳转到input上面 #}
                                头像
                                <img width="60" height="60" id="avatar_img" src="/static/blog/imgs/default.png" alt="">
                            </label>
    
                            <input type="file" id="avatar" >  {# 此时这个标签没有意义了,需要隐藏起来,style="display:None" #}
                        </div>
    
                        <input type="button" class="btn btn-default reg_btn " value="submit"><span class="error"></span>
    
                    </form>
                </div>
            </div>
        </div>
    
    </body>
    </html>

      Myforms.py

    # -*- coding:utf-8 -*-
    
    from django import forms
    from django.forms import widgets
    
    from blog.models import UserInfo
    
    from django.core.exceptions import NON_FIELD_ERRORS, ValidationError
    
    
    class UserForm(forms.Form):
    
        user = forms.CharField(max_length=32,
                               error_messages={"required": "用户名不能为空!"},
                               label="用户名",  # label标签,这样子就可以渲染出中文了
                               widget=widgets.TextInput(attrs={"class": "form-control"})
                               )  # 为这个字段添加类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"})
                                 )

    二.注册页面的默认头像

      把img标签写到label里边去,点击头像就相当于点击input标签

      http://127.0.0.1:8000/static/blog/img/default.png,这样子就可以访问到这个默认的注册头像

      很多网站是这样子:把input标签的长宽跟这个头像一致,然后利用css里边定位position=absolute,把这两个标签定位到一个位置上去。再把input标签透明度显示为0,这时候它俩叠到一起了,你点击头像也就点击了input标签。   另外一种思路是:利用label标签的for的值与input标签的id值一致就可以了,点击label相当于点击input标签。

    三.注册页面的头像预览功能 

      Console---》

    $("#avatar")
    r.fn.init [input#avatar]0: input#avatarlength: 1__proto__: Object(0)
    $("#avatar")[0]
    <input type=​"file" id=​"avatar">​
    $("#avatar")[0].files
    FileList {0: File(593239), length: 1}0: File(593239) {name: "distance.png", lastModified: 1510650980163, lastModifiedDate: Tue Nov 14 2017 17:16:20 GMT+0800 (中国标准时间), webkitRelativePath: "", size: 593239, …}length: 1__proto__: FileList
    $("#avatar")[0].files[0]
    File(593239) {name: "distance.png", lastModified: 1510650980163, lastModifiedDate: Tue Nov 14 2017 17:16:20 GMT+0800 (中国标准时间), webkitRelativePath: "", size: 593239, …}

      头像预览:

        修改用户选中的文件对象;获取文件对象的路径;修改img的src属性,src=文件对象路径。

    <script src="/static/js/jquery-3.3.1.js"></script>
    
        <script>
    
            $("#avatar").change(function () {  // change事件,选中文本之后事件就发生了,
                // 头像预览:
                        // 1.获取用户选中的文件对象
                        var file_obj = $(this)[0].files[0];
    
                        // 2.获取文件对象的路径
                        // 基于文件阅读器 FileReader,new一个实例对象
                        var reader = new FileReader();
    
                        reader.readAsDataURL(file_obj);// 读完之后没有返回值,而是默认加到你的对象里面
                                                       // FileReaderURL是读取文件的URL
                        // 3.修改img的src属性,src=文件对象的路径
                        // attr给属性赋值
                        // reader.readAsDataURL(file_obj)读的时间很长,而且他们都是异步的,还没有读完,就已经执行下面的替换操作了,会找不到路径
    
                        // $("#avatar_img").attr("src", reader.result)
    
                        // 等读完之后在执行下面这句代码
                        reader.onload = function () {
                            $("#avatar_img").attr("src", reader.result)  // 结果在reader.result里边
    
                        }
            });
    </script>

    四.基于Ajax提交formdata数据和注册页面显示错误信息

      点击提交(什么都不输入直接submit)的错误信息在这里边:---->>> { user:null,msg:{ } }

    <script>
        // 基于ajax提交数据
            $(".reg_btn").click(function () {
                // ajax上传文件类型一定要换成formdata格式
                // 使用formdata,必须加上这两个,
                // contentType:false,
                // processData:false,
                // 否则会报错
    
                var formdata = new FormData();
    {#            formdata.append("user", $("#id_user").val());#}
    {#            formdata.append("pwd", $("#id_pwd").val());#}
    {#            formdata.append("re_pwd", $("#id_re_pwd").val());#}
    {#            formdata.append("email", $("#id_email").val());#}
    {#            formdata.append("csrfmiddlewaretoken", $("[name='csrfmiddlewaretoken']").val());#}
    
                formdata.append("avatar", $("#avatar")[0].files[0]);
    
                // 循环往formdata添加数据
    {#            console.log($("#form").serializeArray()); // 打印form表单所有的键值对#}
                var request_data = $("#form").serializeArray();
                $.each(request_data, function (index,data) {
                    formdata.append(data.name, data.value);
                });
    
                $.ajax ({
                    url:"",
                    type:"post",
                    contentType:false,
                    processData:false,
                    data:formdata,  // 把上边的formdata构建出来以后,加到这里边来
                    success:function (data) {
                        //console.log(data)
                        if (data.user){
                            // 注册成功,跳转到登录页面
                            location.href = "/login/"
                        }
                        else { // 注册失败
                            // 由于每次展示的时候会将错误的信息保存在span中,需要清空,否则会出现问题
                            $("span.error_info").text("");
                            $(".form-group").removeClass("has-error");  //展示之前,移除类has-error
                            // 循环展示此次提交的错误信息
                            $.each(data.msg, function (field, error_list){
                                // 判断是不是个全局的
                                if (field=="__all__"){
                                    $("#id_re_pwd").next().text(error_list[0]).parent().addClass("has-error"); // 链式操作
                                }
                                // 显示错误信息:next()是找到下一个标签
                                $("#id_"+field).next().text(error_list[0]);
                                // 显示红色边框:找到父标签,然后添加一个bootstrap的类has-error
                                $("#id_"+field).parent().addClass("has-error");
                        })
                        }
    
                    }
    
                })
            })
    </script>

      views.py

    def register(request):
    
        # if request.method == "POST": # 在这里二者都可以
        if request.is_ajax(): # 点击那个按钮,既是Ajax请求,又是post请求
            print(request.POST)
            form = UserForm(request.POST)  # 通过UserForm得到用户输入的数据进行校验
    
            response = {"user": None, "msg": None}
            if form.is_valid():
                response["user"] = form.cleaned_data.get("user")
    
                # 生成一条用户记录
               else:
                print(form.cleaned_data)  # 干净数据都在这里边
                print(form.errors)  # 错误数据都在这里边
                response["msg"] = form.errors   # 失败之后把所有的错误信息放到msg里边
    
            return JsonResponse(response)
    
        form = UserForm()
        print(form)
    
        return render(request, "register.html", {"form": form})

    五.forms组件的局部钩子与全局钩子的应用

      Myforms.py

    # 加局部钩子,验证用户是否注册
        def clean_user(self):
            val = self.cleaned_data.get("user")  # 取到已经在上面通过基础的格式验证的user
    
            user = UserInfo.objects.filter(username=val).first()  # 在数据库中过滤是否有一致的用户名
    
            if not user:
                return val
            else:
                raise ValidationError("该用户已注册!")
    
        # 加全局钩子,验证两次密码是否一致,全局错误信息是放在__all__中
    
        def clean(self):
            pwd = self.cleaned_data.get("pwd")
            re_pwd = self.cleaned_data.get("re_pwd")
    
            if pwd and re_pwd:  # 当两次密码输入都有值的时候才会对两者进行比较
                if pwd == re_pwd:
                    return self.cleaned_data
                else:
                    raise ValidationError("两次密码输入不一致!")
            else:
                return self.cleaned_data

      全局错误可以在视图里边取出来,跟之前form表单提交错误信息不一样,之前是把错误信息放到视图里边,放到模板里边去。这里可以直接在register里边

    错误信息在errors里边,里边有个键叫__all__,对应列表error_list的信息,发给ajax了,ajax拿到数据做下判断---->>>

     if (field == "__all__"){   //处理全局错误信息;下面找到它的标签,父标签给它加上
            $("#id_re_pwd").next().html(error_list[0]).parent().addClass("has-error"); //给它放到re_pwd确认密码的下面 
                            }

    六.FileField(可以上传任何文件)与ImageFiled(只能上传图片)

     class UserInfo(AbstractUser):  # 使用用户认证组件需要使用auth_user表,扩展这个表需要继承AbstractUser,User继承的就是AbstractUser
                    """
                    用户信息
                    用户信息表和博客信息表是一对一的关系
                    """
                    nid = models.AutoField(primary_key=True)
                    telephone = models.CharField(max_length=11, null=True, unique=True)
                    avatar = models.FileField(upload_to='avatars/', default="/avatars/default.png")  # 存本地用户图像字段
    
                avatar_obj = request.FILES.get("avatar")
                UserInfo.objects.create_user(username=user,password=pwd, email=email, avatar=avatar_obj)  # 这里avatar一定要接收一个文件对象
    
                Django实现:
                    会将文件对象下载到项目的根目录中avatars文件夹中(如果没有avatars文件夹,Django会自动创建),
                user_obj的avatar存的是文件的相对路径

      

      views.py

    def register(request):
    
        # if request.method == "POST": # 在这里二者都可以
        if request.is_ajax():
            print(request.POST)
            form = UserForm(request.POST)  # 通过UserForm都到用户输入的数据
    
            response = {"user": None, "msg": None}
            if form.is_valid():
                response["user"] = form.cleaned_data.get("user")
    
                # 生成一条用户记录
                user = form.cleaned_data.get("user")
                pwd = form.cleaned_data.get("pwd")
                email= form.cleaned_data.get("email")
    avatar_obj
    = request.FILES.get("avatar") UserInfo.objects.create_user(username=user,password=pwd, email=email, avatar=avatar_obj) else: print(form.cleaned_data) print(form.errors) response["msg"] = form.errors return JsonResponse(response) form = UserForm() print(form) return render(request, "register.html", {"form": form})

    七. Media配置之MEDIA_ROOT

    Django有两种静态文件:

      /static/   :js,css,img

      /media/   :用户上传文件;  用户默认头像图片应该在media/avatar里边存一份。

    class UserInfo(AbstractUser): 
    '''用户信息'''
    nid = models.AutoField(primary_key=True)
    telephone = models.CharField(max_length=11, null=True, unique=True)
    avatar = models.FileField(upload_to='avatars/', default="/avatars/default.png")
    avatar_obj = request.FILES.get("avatar")
    user_obj = UserInfo.objects.create_user(username = user, password = pwd, email = email, avatar = avatar_obj)

    用户一旦配置了 

    MEDIA_ROOT = os.path.join(BASE_DIR, "media") 这个路径(在settings里边)
    Django会实现:
      会将文件对象下载到MEDIA_ROOT中avatars文件夹中(如果没有avatar文件夹,Django会自动创建),user_obj的avatar存的是文件的相对路径。

      settings.py

    复制代码
    STATIC_URL = '/static/'
    
    STATICFILES_DIRS = [
        os.path.join(BASE_DIR,"static")
    ]
    
    MEDIA_ROOT = os.path.join(BASE_DIR, "media")
    复制代码

    八. Media配置之MEDIA_URL

      访问http://127.0.0.1:8000/static/blog/img/default.png是可以直接访问到的,如果是http://127.0.0.1:8000/blog/views.py/是访问不到的,不能把你的源代码暴露出来啊,static里边的无所谓,而static是Django给你配置好的,应该让用户看到。同样的media里边应该也让用户看到。  浏览器如何能直接访问到media中的数据呢?

      settings.py

    MEDIA_URL = '/media/'
    #与用户上传相关的配置
    MEDIA_ROOT = os.path.join(BASE_DIR, "media")
    MEDIA_URL = '/media/'

      urls.py

    #media配置
    re_path(r"media/(?P<path>.*)$", serve, {"document_root":settings.MEDIA_ROOT})

    浏览器访问http://127.0.0.1:8000/media/avatars/11.jpg可以直接访问media里边的文件了

    注意规范问题:

    优化代码问题:

      views.py

    def register(request):
    
        # if request.method == "POST": # 在这里二者都可以
        if request.is_ajax():
            print(request.POST)
            form = UserForm(request.POST)  # 通过UserForm都到用户输入的数据
    
            response = {"user": None, "msg": None}
            if form.is_valid():
                response["user"] = form.cleaned_data.get("user")
    
                # 生成一条用户记录
                user = form.cleaned_data.get("user")
                pwd = form.cleaned_data.get("pwd")
                email= form.cleaned_data.get("email")
                avatar_obj = request.FILES.get("avatar")
    
                # 如果用户没有上传图像怎么办?
                # 没有会有个默认的图像,但是下面这段代码已经上传了一个空的avatar_obj,不会走到默认
                # 所以需要加一个判断
                # UserInfo.objects.create_user(username=user,password=pwd, email=email, avatar=avatar_obj)
    
                # 重复代码太多,优化
                # if avatar_obj:
                #     UserInfo.objects.create_user(username=user,password=pwd, email=email, avatar=avatar_obj)
                # else:
                #     UserInfo.objects.create_user(username=user, password=pwd, email=email)
    
                # def create_user(self, username, email=None, password=None, **extra_fields):
                # create_user中有个参数**extra_fields,所以可以下面这样来优化
                extra = {}
    
                if avatar_obj:
                    extra['avatar'] = avatar_obj
    
                UserInfo.objects.create_user(username=user, password=pwd, email=email, **extra)
    
            else:
                print(form.cleaned_data)
                print(form.errors)
                response["msg"] = form.errors
    
            return JsonResponse(response)
    
        form = UserForm()
        print(form)
    
        return render(request, "register.html", {"form": form})
  • 相关阅读:
    linux卸载mysql,apache,php
    iOS 秒数转换成时间,时,分,秒
    iOS 正则表达式判断邮箱、身份证..是否正确
    ios 删除系统从相册压缩的视频
    iOS 视频选择压缩
    iOS 从相册中拿到 图片名 ,截取后缀,图片名
    ios 根据颜色生成图片,十六进制颜色。
    ios 友盟第三方登录遇到的各种坑。
    项目适配iOS9遇到的一些问题及解决办法 ,以及URL 白名单配置方法
    ios 设置head请求头,自定义head, read response header
  • 原文地址:https://www.cnblogs.com/mumupa0824/p/10441083.html
Copyright © 2020-2023  润新知