我是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?...)以及它如何在上下文中工作? ......我只需要帮助理解并解决这个问题。
谢谢!
答案 0 :(得分:7)
如果你没问题,那就准备好了一个Django应用程序,正是你想要的: https://github.com/sorl/sorl-thumbnail
答案 1 :(得分:4)
如果上传的图片发生了变化,这就是我在模型中用来保存新缩略图的内容。它是基于另一个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
答案 2 :(得分:2)
不确定您发送的代码,因为我从不使用这样的模型,但还有另一种方法。
您可以实施自己的FileUploadHandler
来处理图片文件上传。例子是
here。
在第37行(dest.close()
)之后使用thumbnail(upload_dir + upload.name)
函数(您发送的函数)。
希望它对你有所帮助。
答案 3 :(得分:2)
一个关键问题是:何时应该生成缩略图?
如果(1)我建议您创建一个映射到网址/flagthumbnail/countryid
的视图。然后视图方法必须:
每当您需要显示缩略图标记时,只需使用<a href="/flagthumbnail/countryid">
。
如果(2),您可以连接到Django的django.db.models.signals.post_save
信号,并在信号处理程序中创建并保存缩略图文件。
答案 4 :(得分:2)
我想这取决于您使用缩略图的方式和时间。
如果您想在每次保存国家/地区时创建一些缩略图,您可以这样做:
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上的一个版本)。您可以创建一个获取图像和大小的模板标签,然后根据需要创建并保存适当大小的图像,或者显示之前创建的图像(如果有)。它运作得很好。
答案 5 :(得分:2)
覆盖save方法是一个不错的选择,但在这种情况下我更倾向于使用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)
答案 6 :(得分:2)
记住django不会为你清理旧图像,所以除非你有脚本来检查图像/缩略图是否仍在使用中并清除任何不是你将泄漏的磁盘空间。 (根据图像大小和更新频率,这可能会或可能不会对您造成影响)
我不知道你怎么能用post_save信号做到这一点,而且我不太了解信号(那是今晚的研究!)来知道是否有合适的pre_save信号。如果我找到一个,那么我将重新编写上面的代码,将信号用作通用预保存列表。
答案 7 :(得分:2)
答案 8 :(得分:1)
另一种选择是直接使用Imagemagick,如果你想避免Pillow和Python 3的几个困难,例如this one。
from subprocess import call
call(['convert', img_path_file_name, '-thumbnail', target_size_str, '-antialias', style_path_file_name])
您可以在模型保存或模板标记上调用此方法,以文件缓存方式生成原始文件的一次性操作副本,甚至是芹菜任务。
您可以在我的一个项目中找到我如何使用它的示例:
答案 9 :(得分:0)
我也发誓贾斯汀德里斯科尔的django-photologue也很适合调整大小。它:
基本上它太棒了。
答案 10 :(得分:0)
一种非常简单的方法是使用django-imagefit调整图片大小和/或裁剪图片。
它将保留原始图像,因此您可以拥有多个版本,稍后重构您的前端,它也适用于非模型图像。
答案 11 :(得分:0)
您可以在使用“枕头”保存数据后更改照片大小,例如我要 将用户个人资料图片更改为300 x 300:
首先确保安装了“枕头”。
2-这是我的代码:
from PIL import Image
class Profile(models.Model):
user_group_rule_options = [
('site_manager', 'site_manager'),
('site_sales', 'site_sales'),
('site_technical_support', 'site_technical_support'),
('site_user', 'site_user'),
]
PRF_user = models.OneToOneField(User, on_delete=models.CASCADE)
PRF_user_group = models.CharField(max_length=30, choices=user_group_rule_options, default='site_user')
PRF_image = models.ImageField(upload_to='profile_img', blank=True, null=True)
PRF_country = CountryField()
PRF_address = models.CharField(max_length=100)
PRF_join_date = models.DateTimeField(auto_now_add=True)
PRF_slug = models.SlugField(blank=True, null=True)
def save(self , *args , **kwargs):
# change profile image size
img = Image.open(self.PRF_image.path)
if img.width > 300 or img.height > 300:
output_size = (300, 300)
img.thumbnail(output_size)
img.save(self.PRF_image.path)