Ответ 1
Если все в порядке, есть приложение Django, готовое сделать именно то, что вы хотите: https://github.com/sorl/sorl-thumbnail
Я новичок в Django (и Python), и я сам пытался решить некоторые вещи, прежде чем переходить на использование приложений других людей. У меня возникли проблемы с пониманием того, где вещи "подходят" в способе Django (или Python) делать вещи. То, что я пытаюсь решить, - это изменить размер изображения после его загрузки. У меня хорошая настройка модели и подключен к администратору, и изображение загружается в каталог:
from django.db import models
# This is to list all the countries
# For starters though, this will be just United Kingdom (GB)
class Country(models.Model):
name = models.CharField(max_length=120, help_text="Full name of country")
code = models.CharField(max_length=2, help_text="This is the ISO 3166 2-letter country code (see: http://www.theodora.com/country_digraphs.html)")
flag = models.ImageField(upload_to="images/uploaded/country/", max_length=150, help_text="The flag image of the country.", blank=True)
class Meta:
verbose_name_plural = "Countries"
def __unicode__(self):
return self.name
Теперь у меня возникают проблемы с этим файлом и созданием нового файла в миниатюре. Как я уже сказал, я хотел бы знать, как это сделать, не используя приложения других (пока). У меня есть этот код из DjangoSnippets:
from PIL import Image
import os.path
import StringIO
def thumbnail(filename, size=(50, 50), output_filename=None):
image = Image.open(filename)
if image.mode not in ('L', 'RGB'):
image = image.convert('RGB')
image = image.resize(size, Image.ANTIALIAS)
# get the thumbnail data in memory.
if not output_filename:
output_filename = get_default_thumbnail_filename(filename)
image.save(output_filename, image.format)
return output_filename
def thumbnail_string(buf, size=(50, 50)):
f = StringIO.StringIO(buf)
image = Image.open(f)
if image.mode not in ('L', 'RGB'):
image = image.convert('RGB')
image = image.resize(size, Image.ANTIALIAS)
o = StringIO.StringIO()
image.save(o, "JPEG")
return o.getvalue()
def get_default_thumbnail_filename(filename):
path, ext = os.path.splitext(filename)
return path + '.thumb.jpg'
... но это в конечном итоге смутило меня... Как я не знаю, как это "подходит" к моему приложению Django? И действительно, это лучшее решение для простого создания эскиза изображения, которое было успешно загружено? Может ли кто-нибудь показать мне хороший, солидный, достойный способ, чтобы начинающий, как я, мог научиться делать это правильно? Как, знаете, где поставить такой код (models.py? Forms.py?...) и как он будет работать в контексте?... Мне просто нужно немного помочь понять и решить эту проблему.
Спасибо!
Если все в порядке, есть приложение Django, готовое сделать именно то, что вы хотите: https://github.com/sorl/sorl-thumbnail
Это то, что я использую в своих моделях для сохранения нового эскиза, если загруженное изображение изменилось. Он основан на другом DjangoSnippet, но я не могу вспомнить, кто написал оригинал - если вы знаете, добавьте комментарий, чтобы я мог кредитовать их.
from PIL import Image
from django.db import models
from django.contrib.auth.models import User
import os
import settings
class Photo_Ex(models.Model):
user = models.ForeignKey(User, blank=True, null=True)
photo = models.ImageField(upload_to='photos')
thumbnail = models.ImageField(upload_to='profile_thumb', blank=True,
null=True, editable=False)
def save(self, *args, **kwargs):
size = (256,256)
if not self.id and not self.photo:
return
try:
old_obj = Photo_Ex.objects.get(pk=self.pk)
old_path = old_obj.photo.path
except:
pass
thumb_update = False
if self.thumbnail:
try:
statinfo1 = os.stat(self.photo.path)
statinfo2 = os.stat(self.thumbnail.path)
if statinfo1 > statinfo2:
thumb_update = True
except:
thumb_update = True
pw = self.photo.width
ph = self.photo.height
nw = size[0]
nh = size[1]
if self.photo and not self.thumbnail or thumb_update:
# only do this if the image needs resizing
if (pw, ph) != (nw, nh):
filename = str(self.photo.path)
image = Image.open(filename)
pr = float(pw) / float(ph)
nr = float(nw) / float(nh)
if image.mode not in ('L', 'RGB'):
image = image.convert('RGB')
if pr > nr:
# photo aspect is wider than destination ratio
tw = int(round(nh * pr))
image = image.resize((tw, nh), Image.ANTIALIAS)
l = int(round(( tw - nw ) / 2.0))
image = image.crop((l, 0, l + nw, nh))
elif pr < nr:
# photo aspect is taller than destination ratio
th = int(round(nw / pr))
image = image.resize((nw, th), Image.ANTIALIAS)
t = int(round(( th - nh ) / 2.0))
image = image.crop((0, t, nw, t + nh))
else:
# photo aspect matches the destination ratio
image = image.resize(size, Image.ANTIALIAS)
image.save(self.get_thumbnail_path())
(a, b) = os.path.split(self.photo.name)
self.thumbnail = a + '/thumbs/' + b
super(Photo_Ex, self).save()
try:
os.remove(old_path)
os.remove(self.get_old_thumbnail_path(old_path))
except:
pass
def get_thumbnail_path(self):
(head, tail) = os.path.split(self.photo.path)
if not os.path.isdir(head + '/thumbs'):
os.mkdir(head + '/thumbs')
return head + '/thumbs/' + tail
def get_old_thumbnail_path(self, old_photo_path):
(head, tail) = os.path.split(old_photo_path)
return head + '/thumbs/' + tail
Не уверен в том, что вы отправили код, потому что я никогда не использую модели как таковые, но есть и другой метод.
Вы можете реализовать свой собственный FileUploadHandler
для обработки загрузок файлов изображений. Пример.
здесь.
Сразу после строки 37 (dest.close()
) используйте функцию thumbnail(upload_dir + upload.name)
(тот, который вы отправили).
Надеюсь, это поможет вам.
Ключевой вопрос: , когда должен быть создан эскиз?
Если (1) я предлагаю вам создать представление, которое отображает URL-адрес /flagthumbnail/countryid
. Тогда метод просмотра должен был бы:
Всякий раз, когда вам нужно отобразить значок эскиза, просто используйте <a href="/flagthumbnail/countryid">
.
Если (2), вы можете подключиться к сигналу Django django.db.models.signals.post_save
, а в обработчике сигнала создать и сохранить файл миниатюр.
Я думаю, это зависит от того, как и когда вы используете свои миниатюры.
Если вы хотите создать несколько миниатюр каждый раз, когда Страна сохранена, вы можете сделать это так:
from django.db import models
# This is to list all the countries
# For starters though, this will be just United Kingdom (GB)
class Country(models.Model):
name = models.CharField(max_length=120, help_text="Full name of country")
code = models.CharField(max_length=2, help_text="This is the ISO 3166 2-letter country code (see: http://www.theodora.com/country_digraphs.html)")
flag = models.ImageField(upload_to="images/uploaded/country/", max_length=150, help_text="The flag image of the country.", blank=True)
class Meta:
verbose_name_plural = "Countries"
def __unicode__(self):
return self.name
def save(self, force_insert=False, force_update=False):
resize_image(self.flag)
super(Country, self).save(force_insert, force_update)
Если вы не на 100% уверены, какие размеры вам понадобятся вам, вы можете изменить их размер в последнюю минуту. Я видел это эффективно с помощью templatetag (я верю в версию на Pinax). Вы создаете templatetag, который принимает изображение и размер, затем создавайте и сохраняйте изображение соответствующего размера, если вам нужно, или отображаете ранее созданный, если он есть. Он работает очень хорошо.
Переопределение метода сохранения является хорошим вариантом, но в этом случае у меня было бы больше соблазна использовать signal. Сигналы Django позволяют вам "прослушивать" данный тип типа различных событий; в этом случае вас будет интересовать событие post_save
.
Я обычно подписываюсь на такие сигналы в моем файле models.py
. Код для вас выглядит примерно так:
from django.db.models.signals import post_save
from models import Country
def resize_image(sender, **kwargs):
country = kwargs["instance"]
resize_image(country.flag) # where resize_image generates a thumbnail given a Country instance
post_save.connect(resize_image, sender=Country)
Ryan - правильные сигналы, это лучший способ пойти, однако преимущество завышенной экономии заключается в том, что мы можем получить старые и новые пути к изображению, посмотреть, изменилось ли изображение (и если оно создало новый миниатюру), сохраните экземпляр модели, а затем удалите старое изображение и миниатюру.
Помните, что django не очищает старые изображения для вас, поэтому, если у вас есть script, чтобы проверить, что изображения/эскизы все еще используются, и очистите все, что не приведет к утечке дискового пространства. (Это может или не может быть проблемой для вас в зависимости от размера и частоты обновлений)
Я не уверен, как вы могли бы сделать это с помощью сигнала post_save, и я не знаю достаточно о сигналах (это исследование сегодня!), чтобы узнать, есть ли подходящий сигнал pre_save. Если я найду его, я переписал вышеприведенный код, чтобы использовать сигналы как общий предварительный список.
Вы можете попробовать:
функции:
Другой альтернативой является использование Imagemagick напрямую, если вы хотите избежать нескольких трудностей с Pillow и Python 3, таких как этот.
from subprocess import call
call(['convert', img_path_file_name, '-thumbnail', target_size_str, '-antialias', style_path_file_name])
Вы можете вызвать это при сохранении модели или в теге шаблона, чтобы создать одноразовую управляемую копию исходного файла в режиме кэширования файлов или даже задачу сельдерея.
Вы можете получить пример того, как я использовал это в одном из моих проектов:
Я также клянусь Джастином Дрисколлом django-photologue также отлично подходит для изменения размера. Это:
В принципе это потрясающе.