• Django的文件上传以及预览、存储


    思路:

    文件上传通过前端的input标签,input设置display:none属性。
    
    内容显示需要让前端通过<img>标签读取图片内容,可以通过<label>标签连接<img>和<input>属性。
    
    文件上传后通过ajax提交到后台,验证成功后,通过locations.href实现页面跳转。

    前端需要使用的方法:

    # 当标签内的内容出现变化时,要触发相应的方法。
    $("#id").change(function(){
        ...
    })
    
    # 创建一个读取文件的对象
    var obj = new FileReader();
    
    # 当全部文件取完毕后,把图片加载到img标签中
    onload
    $("id").attr("src", "图片路径")
    
    # ajax提交图片文件
    var formData = new FormData();
    
    # input框绑定获取焦点的事件
    $('form input').focus(function(){
        ...
    })

    设计数据库模型

    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)
    
        blog = models.OneToOneField(to="Blog", to_field="nid", null=True, on_delete=models.CASCADE)
    
        def __str__(self):
            return self.username
    
    
    class Blog(models.Model):
        """
        博客信息
        """
        nid = models.AutoField(primary_key=True)
        title = models.CharField(max_length=64)  # 个人博客标题
        site = models.CharField(max_length=32, unique=True)  # 个人博客后缀
        theme = models.CharField(max_length=32)  # 博客主题
    
        def __str__(self):
            return self.title
    
    
    class Category(models.Model):
        """
        个人博客文章分类
        """
        nid = models.AutoField(primary_key=True)
        title = models.CharField(max_length=32)  # 分类标题
        blog = models.ForeignKey(to="Blog", to_field="nid", on_delete=models.CASCADE)  # 外键关联博客,一个博客站点可以有多个分类
    
        def __str__(self):
            return self.title
    
    
    class Tag(models.Model):
        """
        标签
        """
        nid = models.AutoField(primary_key=True)
        title = models.CharField(max_length=32)  # 标签名
        blog = models.ForeignKey(to="Blog", to_field="nid", on_delete=models.CASCADE)  # 所属博客
    
        def __str__(self):
            return self.title
    
    
    class Article(models.Model):
        """
        文章
        """
        nid = models.AutoField(primary_key=True)
        title = models.CharField(max_length=50)  # 文章标题
        desc = models.CharField(max_length=255)  # 文章描述
        create_time = models.DateTimeField()  # 创建时间
    
        category = models.ForeignKey(to="Category", to_field="nid", null=True, on_delete=models.CASCADE)
        user = models.ForeignKey(to="UserInfo", to_field="nid", on_delete=models.CASCADE)
        tags = models.ManyToManyField(  # 中介模型
            to="Tag",
            through="Article2Tag",
            through_fields=("article", "tag"),  # 注意顺序!!!
        )
    
        def __str__(self):
            return self.title
    
    
    class ArticleDetail(models.Model):
        """
        文章详情表
        """
        nid = models.AutoField(primary_key=True)
        content = models.TextField()
        article = models.OneToOneField(to="Article", to_field="nid", on_delete=models.CASCADE)
    
    
    class Article2Tag(models.Model):
        """
        文章和标签的多对多关系表
        """
        nid = models.AutoField(primary_key=True)
        article = models.ForeignKey(to="Article", to_field="nid", on_delete=models.CASCADE)
        tag = models.ForeignKey(to="Tag", to_field="nid", on_delete=models.CASCADE)
    
        class Meta:
            unique_together = (("article", "tag"),)
    
    
    class ArticleUpDown(models.Model):
        """
        点赞表
        """
        nid = models.AutoField(primary_key=True)
        user = models.ForeignKey(to="UserInfo", null=True, on_delete=models.CASCADE)
        article = models.ForeignKey(to="Article", null=True, on_delete=models.CASCADE)
        is_up = models.BooleanField(default=True)
    
        class Meta:
            unique_together = (("article", "user"),)
    
    
    class Comment(models.Model):
        """
        评论表
        """
        nid = models.AutoField(primary_key=True)
        article = models.ForeignKey(to="Article", to_field="nid", on_delete=models.CASCADE)
        user = models.ForeignKey(to="UserInfo", to_field="nid", on_delete=models.CASCADE)
        content = models.CharField(max_length=255)  # 评论内容
        create_time = models.DateTimeField(auto_now_add=True)
        parent_comment = models.ForeignKey("self", null=True, on_delete=models.CASCADE)
    
        def __str__(self):
            return self.content
    models.py

     # upload_to参数会自动将图片下载到本地服务器,这里为avatar目录

    通过forms设置前端字段,实现输入内容过滤:

    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.TextInput(
                attrs={"class":"form-control", "placeholder": "用户名"},
            )
        )
    
        password = forms.CharField(
            min_length=6,
            label="密码",
            error_messages={
                "required": "密码不能为空",
                "min_length": "密码不能少于6位",
            },
            widget = forms.PasswordInput(
                attrs={"class": "form-control", "placeholder": "密码"},
            )
        )
    
        re_password = forms.CharField(
            min_length=6,
            label="确认密码",
            error_messages={
                "required": "密码不能为空",
                "min_length": "密码不能少于6位",
            },
            widget=forms.PasswordInput(
                attrs={"class": "form-control", "placeholder": "密码"},
            )
        )
    
        email = forms.EmailField(
            label="邮箱",
            error_messages={
                "invalid": "请输入正确的邮箱格式",
                "required": "邮箱不能为空",
            },
            widget=forms.EmailInput(
                attrs={"class": "form-control", "placeholder": "邮箱"},
            )
        )
    
        # 重写全局的钩子函数,对确认密码做校验
        def clean(self):
            password = self.cleaned_data.get("password")
            re_password = self.cleaned_data.get("re_password")
    
            if re_password and re_password != password:
                self.add_error("re_password", ValidationError("两次密码不一致"))
    
            else:
                return self.cleaned_data

    后台方法

    def register(request):
        if request.method == "POST":
            ret = {"status": 0, "msg": ""}
            form_obj = forms.RegForm(request.POST)
            # print(request.POST)
            if form_obj.is_valid():
                # 数据库中没有re_password字段,需要从字典中剔除re_password字段
                form_obj.cleaned_data.pop("re_password")
                # 接收从ajax发送过来的的图片数据
                avatar_img = request.FILES.get("avatar")
                print(avatar_img)
                models.UserInfo.objects.create_user(**form_obj.cleaned_data, avatar=avatar_img)
                ret["msg"] = "/reg/"
                return JsonResponse(ret)
            else:
                ret["status"] = 1
                ret["msg"] = form_obj.errors
                print(ret)
                return JsonResponse(ret)
    
        form_obj = forms.RegForm()
        return render(request, 'register.html', {"form_obj": form_obj})
    
    def reg(request):
        return render(request, 'index.html')

    前端代码

    <!DOCTYPE html>
    <html lang="en">
    <head>
        <meta charset="UTF-8">
        <title>注册</title>
    </head>
    <body>
        <link rel="stylesheet" href="/static/css/bootstrap.min.css">
        <link rel="stylesheet" href="/static/css/backend.css">
    
        <div class="container register">
            <div class="row">
                <div class="col-md-6 col-md-offset-3">
    {#                form添加novalidate参数,代表取消前端h5的验证,比如邮箱格式验证#}
                    <form novalidate action="/register/" method="post" class="form-horizontal" enctype="multipart/form-data">
                        {% csrf_token %}
                        <div class="form-group">
                            <label for="{{ form_obj.username.id_for_label }}" class="col-sm-2 control-label">
                                {{ form_obj.username.label }}
                            </label>
                            <div class="col-sm-8">
                                {{ form_obj.username }}
                                <span class="help-block">{{ form_obj.username.errors.0 }}</span>
                            </div>
                        </div>
    
    
                        <div class="form-group">
                            <label for="{{ form_obj.password.id_for_label }}" class="col-sm-2 control-label">
                                {{ form_obj.password.label }}
                            </label>
                            <div class="col-sm-8">
                                {{ form_obj.password }}
                                <span class="help-block">{{ form_obj.password.errors.0 }}</span>
                            </div>
                        </div>
    
                        <div class="form-group">
                            <label for="{{ form_obj.re_password.id_for_label }}" class="col-sm-2 control-label">
                                {{ form_obj.re_password.label }}
                            </label>
                            <div class="col-sm-8">
                                {{ form_obj.re_password }}
                                <span class="help-block">{{ form_obj.re_password.errors.0 }}</span>
                            </div>
                        </div>
    
                        <div class="form-group">
                            <label for="{{ form_obj.email.id_for_label }}" class="col-sm-2 control-label">
                                {{ form_obj.email.label }}
                            </label>
                            <div class="col-sm-8">
                                {{ form_obj.email }}
                                <span class="help-block">{{ form_obj.email.errors.0 }}</span>
                            </div>
                        </div>
    
                        <div class="form-group">
                            <label class="col-sm-2 control-label">
                                头像
                            </label>
                            <div class="col-sm-8">
                                <label for="id_avatar"><img id="avatar-img" src="/static/img/default.png"></label>
                                <input type="file" id="id_avatar" style="display: none;" name="avatar">
                            </div>
                        </div>
    
                        <div class="form-group">
                            <div class="col-sm-offset-2 col-sm-8">
                                <button id="reg-submit" type="button" class="btn btn-success">注册</button>
                            </div>
                        </div>
                    </form>
                </div>
            </div>
        </div>
    
    
    {#    <script src="/static/js/bootstrap.min.js"></script>#}
        <script src="/static/js/jquery-1.12.4.js"></script>
        <script>
            $("#id_avatar").change(function () {
                // 创建一个文件读取对象
                var fileReader = new FileReader;
                // 在更改前端图片之前,把文件内容读取完
                fileReader.readAsDataURL(this.files[0]); // 读取文件是需要时间的
                // 文件读取完后,重新加载到img当中
                fileReader.onload = function () {
                    $("#avatar-img").attr("src", fileReader.result);
                }
            });
    
            $("#reg-submit").click(function () {
                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());
                // 这里传递的是文件对象,是为了能够定位文件,后台获取到这个文件对象后会通过models字段单中的upload_to="avator"参数传递到服务器目录。
                formData.append("avatar", $("#id_avatar")[0].files[0]);
                formData.append("csrfmiddlewaretoken", $("[name='csrfmiddlewaretoken']").val());
    
                /*
                console.log($("#id_avatar"));
                // 获取到了是一个jquery对象
                // jQuery.fn.init [input#id_avatar, context: document, selector: "#id_avatar"]
    
                console.log($("#id_avatar")[0]);
                // 获取到了input整个标签
                // <input type="file" id="id_avatar" style="display:none" name="avatar">
    
                console.log($("#id_avatar")[0].files);
                // 获取了input标签当中的type="file"类型中的内容
                //FileList {0: File, length: 1}
                //    0: File {name: "风景.jpg", lastModified: 1553135747721, lastModifiedDate: Thu Mar 21 2019 10:35:47 GMT+0800 (中国标准时间), webkitRelativePath: "", size: 27556, …}
                //    length: 1
                //    __proto__: FileList
    
                console.log($("#id_avatar")[0].files[0]);
                // 获取到了整个上传的文件内容
                // File {name: "风景.jpg", lastModified: 1553135747721, lastModifiedDate: Thu Mar 21 2019 10:35:47 GMT+0800 (中国标准时间), webkitRelativePath: "", size: 27556, …}
                */
    
                $.ajax({
                    url: "/register/",
                    type: "post",
                    // 当需要传输图片的时候,需要将processData和contentType设置为false
                    processData: false,
                    contentType: false,
                    data: formData,
                    success:function (data) {
                        // 这里data是后端返回的一个字典ret = {"status": 0, "msg": "/reg/"}
                        if (data.status){
                            // 有错误就展示错误
                            // console.log(data.msg);
                            // 将报错信息填写到页面上
                            $.each(data.msg, function (k,v) {
                                // console.log("id_"+k, v[0]);
                                // console.log($("#id_"+k));
                                $("#id_"+k).next("span").text(v[0]).parent().parent().addClass("has-error");
                            })
                            //console.log(123)
                        }else {
                            // 没有错误就跳转到指定页面,这里data是后端返回的一个字典ret = {"status": 0, "msg": "/reg/"}
                            location.href = data.msg;
                        }
                    }
                })
            });
    
            // 当input获取焦点的事件,移除报错的样式,并且晴空报错信息。
            $("form input").focus(function () {
                $(this).next().text("").parent().parent().removeClass("has-error");
            })
        </script>
    </body>
    </html>

    路由设置

    path('register/', backend.register),
    path('reg/', backend.reg),
  • 相关阅读:
    第一次作业
    第四次作业
    第五次作业《构建之法》心得体会
    第三次作业
    第二次作业
    第一次作业
    个人简介
    读《构建之法》有感
    第四次博客园作业(2)
    第四次博客园作业(1)
  • 原文地址:https://www.cnblogs.com/ttyypjt/p/10572712.html
Copyright © 2020-2023  润新知