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),

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM