Django e-commerce part17 Image Uploads
画像のアップロードについて
models.py に画像アップ用のclassを作成する
とりあえず、以下のような感じでシンプルに書いてみる。
... class ProductImage(models.Model): product = models.ForeignKey(Product) image = models.ImageField(upload_to='products/') def __str__(self): return self.product.tittle ...
プロダクト1対多 productimage imageは暫定的に入れてある。
ちなみに、画像処理用のpillowを入れてるあると、imagefieldを指定できる。そっちの方が多分、いろいろと処理しやすいと予想。
Pillowをinstall
Pillowをinstallする手順は、以下のサイトを参考:
Guides/imagefield_and_pillow.md at master · codingforentrepreneurs/Guides · GitHub
admin.py に,ProductImageを追加
from .models import Product, Variation, ProductImage admin.site.register(Product) admin.site.register(Variation) admin.site.register(ProductImage)
試しに何件かアップロードしてみる。
product_detail.htmlを編集して、画像を表示させてみる。
<div class='col-sm-8'> <h2>{{ object.title }}</h2> {% if object.productimage_set.count > 0 %} # ここで、productのforeignkeyのproductimage_setを使って、image数を数える。 {% for img in object.productimage_set.all %} # 同様にsetで、全てのimgへのpathをimgに代入して、1つずつ表示する。 {{ img.image }} <img class='img-responsive' src='{{ img.image }}' /> #bootstapのimg-responsiveクラスを挿入する {% endfor %} {% endif %} <p class='lead'>{{ object.description }}</p> </div>
ただ、これだけだと、画像は上手く表示されない。なぜか? 以下のように、imageのfileとurlを比べてみるとわかる。
{% for img in object.productimage_set.all %} {{ img.image.file }} {{ img.image.url }} <img class='img-responsive' src='{{ img.image }}' /> {% endfor %}
fileは、fileが置いてあるpath urlは、fileを表示するための、url そのため、urlを表示しないと、いけないかな。
<img class='img-responsive' src='{{ img.image.url }}' />
ちなみにそれぞれ以下のような感じで表示される。
/users/desktop/ecommerce-2/static_in_env/media_root/products/headgehog.jpg /media/products/headgehog.jpg
ProductImageのupload_to の場所を変更する。より動的にする。
from django.utils.text import slugify #slugifyをimportする。 ... def image_upload_to(instance, filename): title = instance.product.title slug = slugify(title) return "products/%s/%s" %(slug, filename) # Product Images class ProductImage(models.Model): product = models.ForeignKey(Product) image = models.ImageField(upload_to=image_upload_to) ...
解説: * image_upload_toというfunctionを作る
def image_upload_to(instance, filename):
は、instanceは、classのinstanceを想定?例えば、ProductImage().product.titleのようなものをイメージ。slugは、そのtitleをslugifyしたもの。upload_to=image_upload_toは、なぜ()で引数を与えなくていいのか??
仮説としては、image_upload_to(instance, filename)が本来の正しい引数の渡し方だが、upload_toの後では、instance→classのinstance、filename→アップロードされたファイル名が自動的に渡される。
ちなみにファイルが保存されるstorageは、static_in_env/media_root/products/%s/%s になっているはず。要はsettings.pyで、セットしてある、media_rootに保存されるようになっている。デフォだと。
titleと、titleをslugに変換する, slugifyを使う(ただ、これ日本語だと反応しないからちょっと日本語→ローマ字→slugifyにしないといけないな。)
応急手段として、uriエンコードを利用するなどができるけど、ちょっとイマイチなので、なんか考えないといけないな。pythonでutf-8日本語文字列を、URIエンコード・URLデコードする
今回は、titleを全部英語に変えていきます。
さらにちょっとproducts/models.py を修正する。
.... def image_upload_to(instance, filename): title = instance.product.title slug = slugify(title) basename, file_extension = filename.split(".") # これでfileの名前をnameと拡張子に分ける new_filename = "%s-%s.%s" %(slug, instance.id, file_extension) # instance.idを挿入する。(ここでは、ProductImage().idを想定している。) return "products/%s/%s" %(slug, new_filename) ...
課題・問題点
- titleが日本語の場合、titleのslugifyが利用できない。そのため、別の方法を考える必要があるかも。例えば、idとは別に、product_idのようなものを作るとか。