跳到主要內容

[Django教學9]6個步驟搞懂Django上傳圖片的功能

Photo by Mel Poole on Unsplash
在許多知名社交平台或網站,像是InstagramPinterest等,都是以圖片的形式來分享資訊,深受大眾的喜愛,也因此有越來越多的網站都具備上傳圖片的功能,而如果要使用Python來建立具有此功能的網站,該如何達成呢?

本文將實作一個簡單的圖片牆網站,利用6個步驟,在PythonDjango網站框架上,開發上傳圖片的功能,並且顯示出來,其中的步驟包含:
  • 安裝Pillow圖片函式庫
  • 建立Django圖片欄位的資料模型
  • 建立Django上傳圖片的表單
  • 設定Django存放圖片的路徑
  • 儲存Django上傳的圖片至資料庫
  • 顯示Django資料庫中的圖片

一、安裝Pillow圖片函式庫

首先,如果要在Django的資料模型中使用圖片類型的欄位(ImageField),則需要安裝Pillow圖片函式庫,可以透過pip指令來進行安裝,如下範例:

$ pip install pillow

如果有為Django專案建立虛擬環境的話,則可以使用pipenv指令安裝,如下範例:

$ pipenv install pillow

二、建立Django圖片欄位的資料模型

PythonPillow圖片函式庫安裝完成後,接下來,在Django應用程式(APP)下的models.py中,建立圖片欄位(ImageField)的屬性,如下範例:
from django.db import models
from django.utils import timezone


class Photo(models.Model):
    image = models.ImageField(upload_to='image/', blank=False, null=False)
    upload_date = models.DateField(default=timezone.now)
範例中,圖片欄位的upload_to參數設定圖片上傳後所存放的Django專案路徑,之後的blanknull參數代表圖片欄位是否允許空值,如果為必填,則需設定為False

而第二個upload_date屬性為新增圖片至資料庫時,預設寫入當地時區的當下時間。接下來,使用DjangoMigration(資料遷移)指令,將資料模型中定義的欄位,同步至資料庫中,如下指令:

$ python manage.py makemigrations

$ python manage.py migrate

這時候,啟動本地端伺服器,連線至Django Administration(管理員後台),即可看到剛剛所建立的資料模型,如下圖:

三、建立Django上傳圖片的表單

要讓使用者能夠上傳圖片至網站,就需要建立可以上傳圖片的表單,而本文利用Django ModelForm(資料模型表單)來進行開發,其中的使用方式及觀念可以參考[Django教學7]透過Django ModelForm快速開發CRUD應用程式教學文章,如下範例:
from django import forms
from .models import Photo


class UploadModelForm(forms.ModelForm):

    class Meta:
        model = Photo
        fields = ('image',)
        widgets = {
            'image': forms.FileInput(attrs={'class': 'form-control-file'})
        }
表單建立完成後,就可以開啟Django應用程式(APP)下的views.py,將上傳圖片的表單傳送至Django Template(樣板)來進行顯示,如下範例:
from django.shortcuts import render
from .forms import UploadModelForm


def index(request):

    form = UploadModelForm()

    context = {
        'form': form
    }

    return render(request, 'photos/index.html', context)
接下來,在共用樣板(base.html)中,定義了兩個區塊,分別為「上傳圖片區塊」及「顯示圖片區塊」,如下範例:
<html lang="en">

<head>
    <!-- Required meta tags -->
    <!-- Bootstrap CSS -->
    <link crossorigin="anonymous" href="https://stackpath.bootstrapcdn.com/bootstrap/4.4.1/css/bootstrap.min.css" integrity="sha384-Vkoo8x4CGsO3+Hhxv8T/Q5PaXtkKtu6ug5TOeNV6gBiFeWPGFN9MuhOf23Q9Ifjh" rel="stylesheet"></link>
    

    <title>ImageWall</title>
</head>

<body>

    <section class="text-center">
        <div class="container">
            <h1>
                ImageWall</h1>
            <div class="lead text-muted">
                Share your photos around the world.
            </div>
            {% block upload %}
            {% endblock  %}

        </div>
    </section>

    <div class="container">

        {% block content %}
        {% endblock  %}

    </div>
    <!-- Optional JavaScript -->
    <!-- jQuery first, then Popper.js, then Bootstrap JS -->
    <script crossorigin="anonymous" integrity="sha384-J6qa4849blE2+poT4WnyKhv5vZF5SrPo0iEjwBvKU7imGFAV0wwj1yYfoRSJoZ+n" src="https://code.jquery.com/jquery-3.4.1.slim.min.js"></script>
    <script crossorigin="anonymous" integrity="sha384-Q6E9RHvbIyZFJoft+2mJbHaEWldlvI9IOYy5n3zV9zzTtmI3UksdQRVvoxMfooAo" src="https://cdn.jsdelivr.net/npm/popper.js@1.16.0/dist/umd/popper.min.js"></script>
    <script crossorigin="anonymous" integrity="sha384-wfSDF2E50Y2D1uUdj0O3uMBJnjuUD4Ih7YwaYd1iqfktj0Uod8GCExl3Og8ifwB6" src="https://stackpath.bootstrapcdn.com/bootstrap/4.4.1/js/bootstrap.min.js"></script>
</body>
</html>
所以在Django應用程式(APP)的首頁(index.html),需繼承自共用樣板(base.html),並且在「上傳圖片區塊」中,顯示上傳圖片的表單,如下範例:
{% extends 'base.html' %}

{% block upload %}
<form action="" enctype="multipart/form-data" method="POST">
    {% csrf_token %}
    <br />
    <div class="form-row">
        <div class="form-group col-md-4">
        </div>
        <div class="form-group col-md-4">
            <input class="btn btn-primary" type="submit" value="上傳" />
        </div>
        <div class="form-group col-md-4">
        </div>
    </div>
    <div class="form-row">
        <div class="form-group col-md-4">
        </div>
        <div class="form-group col-md-4">
            {{ form.image }}
        </div>
        <div class="form-group col-md-4">
        </div>
    </div>
</form>
{% endblock  %}


{% block content %}

{% endblock  %}
由於表單中包含了上傳的元件,所以這邊要特別注意,在<form>標籤的地方,要加上enctype="multipart/form-data"最後,設定Django應用程式(APP)的首頁網址,如下範例:
from django.urls import path
from . import views


urlpatterns = [
    path('', views.index, name='Index')
]

四、設定Django存放圖片的路徑

使用者將圖片上傳後,這時候會將圖片存放在Django專案中的某個路徑底下,而這個路徑在定義資料模型時,利用upload_to參數進行設定,除此之外,要讓Django認識這個路徑,就需要在settings.py中新增以下的設定:

MEDIA_URL = '/media/'
MEDIA_ROOT = os.path.join(BASE_DIR, 'media')

只要使用者上傳圖片後,Django就會自動建立media資料夾,並且在它底下建立資料模型中所定義的路徑(image/),來存放圖片。當然,實務上通常不會將所有圖片都存放在Django專案中,而會整合雲端的儲存空間,例如Amazon S3Google Cloud Platform等,來增加儲存空間的彈性,這部份往後會來跟大家分享。

五、儲存Django上傳的圖片至資料庫

除了將上傳的圖片檔存放在Django專案中,也會需要儲存至資料庫,提供網站其它的應用。

當使用者選擇圖片並且點擊上傳按鈕後,就會使用POST請求方法呼叫檢視函式(View Function),接著利用傳送過來的POST資料及檔案來建立ModelForm(資料模型表單)物件,通過驗證就把資料儲存至資料庫中,最後返回首頁,如下範例:
def index(request):

    form = UploadModelForm()

    if request.method == "POST":
        form = UploadModelForm(request.POST, request.FILES)
        if form.is_valid():
            form.save()
            return redirect('/photos')

    context = {
        'photos': photos,
        'form': form
    }

    return render(request, 'photos/index.html', context)

六、顯示Django資料庫中的圖片

最後一個步驟,就是把如存在資料庫中的圖片顯示在首頁。首先,在檢視函式(View Function)中,查詢資料模型中的所有資料,如下範例:
def index(request):

    photos = Photo.objects.all()  #查詢所有資料

    form = UploadModelForm()

    if request.method == "POST":
        form = UploadModelForm(request.POST, request.FILES)
        if form.is_valid():
            form.save()
            return redirect('/photos')

    context = {
        'photos': photos,
        'form': form
    }

    return render(request, 'photos/index.html', context)
接著,開啟Django首頁樣板(index.html),在「顯示圖片區塊」中,透過for樣板語法,將所有圖片物件的路徑讀取出來,並且設定在<img>標籤的url屬性中,如下範例:
{% extends 'base.html' %}

{% block upload %}
<form action="" enctype="multipart/form-data" method="POST">
    {% csrf_token %}
    <br />
    <div class="form-row">
        <div class="form-group col-md-4">
        </div>
        <div class="form-group col-md-4">
            <input class="btn btn-primary" type="submit" value="上傳" />
        </div>
        <div class="form-group col-md-4">
        </div>
    </div>
    <div class="form-row">
        <div class="form-group col-md-4">
        </div>
        <div class="form-group col-md-4">
            {{ form.image }}
        </div>
        <div class="form-group col-md-4">
        </div>
    </div>
</form>
{% endblock  %}


{% block content %}

<div class="row">
    {% for photo in photos %}

    <br />
    <div class="col-md-4">
        <div class="card mb-4 shadow-sm">
            <img class="card-img-top" src="{{ photo.image.url }}" />
        </div>
    </div>
    {% endfor %}
</div>

{% endblock  %}
執行結果

七、小結

以上就是透過6個步驟,來實作一個簡單的圖片牆,藉此來學習Django上傳圖片的功能,詳細的程式碼可以參考下方GitHub網址。如果在練習的過程中,有碰到任何問題,歡迎留言提問或分享。

如果您喜歡我的文章,請幫我按五下Like(使用GoogleFacebook帳號免費註冊),支持我創作教學文章,回饋由LikeCoin基金會出資,完全不會花到錢,感謝大家的收看


有想要看的教學內容嗎?歡迎利用以下的Google表單讓我知道,將有機會成為教學文章,分享給大家😊

你可能有興趣的文章









留言

  1. MIKE大您好,我實作最後的功能,可以成功上傳並儲存到media資料夾,但在首頁卻會顯示裂掉的小圖示而顯示不了圖片,想要請問問題可能為何?

    回覆刪除
    回覆
    1. XIAO您好,感覺上是網頁圖片的src路徑發生錯誤,檢查一下上傳圖片後,專案中的image資料夾下是否有上傳的圖片,以及前往Django Administration的Photos資料模型中,image欄位是否有成功存入圖片的路徑,如果還是無法解決的話,可以將問題截圖,到Learn Code With Mike粉絲專頁私訊我,將會協助您解決,謝謝您 :)

      刪除
  2. 圖片儲存路徑需要在urls.py中增加以下程式碼
    from django.conf import settings
    from django.conf.urls.static import static

    urlpatterns = [
    .......
    ]

    urlpatterns += static(settings.MEDIA_URL, document_root=settings.MEDIA_ROOT)

    回覆刪除
    回覆
    1. 我也碰到圖片上的問題 剛好可以解決! 謝謝分享!!!

      刪除

張貼留言