読者です 読者をやめる 読者になる 読者になる

Djangoroidの奮闘記

python,django,angularJS1~三十路過ぎたプログラマーの奮闘記

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のようなものを作るとか。