Django on heroku with s3 for media files. Have to edit model save method to switch between dev and prod?

时间:2022-10-24 14:20:32

Hopefully someone can help with this. Thanks for having a look!

希望有人可以帮助解决这个问题。谢谢你看看!

I am fairly new to django and programming in general so please bear with me. I am building a custom Profile app that has an avatar ImageField. I set it all up in development to resize and properly orient the image. Went to push it to production on heroku and had to set up aws s3 to serve media.

我对django和编程都很新,所以请耐心等待。我正在构建一个具有头像ImageField的自定义配置文件应用程序。我在开发中将其全部设置为调整大小并正确定位图像。在heroku上将其推向生产并且必须设置aws s3来提供媒体服务。

I got an error with the save method of my avatar using Image.open(self.avatar.path). The solution that ended up working was here. However, having changed the models save method i get the following error when trying to upload locally. There must be a better solution than having multiple save methods for production vs development. But I can't seem to find any info that addresses both cases. Thanks!

我使用Image.open(self.avatar.path)使用我的头像的save方法出错。最终工作的解决方案就在这里。但是,更改了模型保存方法,我尝试在本地上传时收到以下错误。必须有一个比生产与开发的多种保存方法更好的解决方案。但我似乎无法找到解决这两种情况的任何信息。谢谢!

[22/Mar/2016 17:33:24] "GET /profiles/you/ HTTP/1.1" 200 6735
Internal Server Error: /profiles/you/
Traceback (most recent call last):
  File "/home/vagrant/virtualenvs/venv_philabucks/lib/python3.4/site-packages/django/core/handlers/base.py", line 149, in get_response
    response = self.process_exception_by_middleware(e, request)
  File "/home/vagrant/virtualenvs/venv_philabucks/lib/python3.4/site-packages/django/core/handlers/base.py", line 147, in get_response
    response = wrapped_callback(request, *callback_args, **callback_kwargs)
  File "/home/vagrant/virtualenvs/venv_philabucks/lib/python3.4/site-packages/django/views/generic/base.py", line 68, in view
    return self.dispatch(request, *args, **kwargs)
  File "/home/vagrant/virtualenvs/venv_philabucks/lib/python3.4/site-packages/django/views/generic/base.py", line 88, in dispatch
    return handler(request, *args, **kwargs)
  File "/vagrant/projects/philabucks/profiles/views.py", line 29, in post
    form.save()
  File "/vagrant/projects/philabucks/profiles/forms.py", line 30, in save
    user.save()
  File "/vagrant/projects/philabucks/profiles/models.py", line 108, in save
    image.save(fh, format)
  File "/home/vagrant/virtualenvs/venv_philabucks/lib/python3.4/site-packages/PIL/Image.py", line 1675, in save
    save_handler(self, fp, filename)
  File "/home/vagrant/virtualenvs/venv_philabucks/lib/python3.4/site-packages/PIL/PngImagePlugin.py", line 690, in _save
    fp.write(_MAGIC)
TypeError: must be str, not bytes
[22/Mar/2016 17:33:45] "POST /profiles/you/ HTTP/1.1" 500 92102

Here are the relevant parts of my Profile Model

以下是我的个人资料模型的相关部分

from django.db import models
from django.contrib.auth.models import BaseUserManager, AbstractBaseUser
from PIL import Image, ExifTags
from PIL.ImageOps import fit
from django.core.files.storage import default_storage as storage

avatar = models.ImageField(upload_to='images/', null=True, blank=True)

def save(self, **kwargs):
    super(Profile, self).save()
    if self.avatar:
        image = Image.open(storage.open(self.avatar.name))
        try:
            for orientation in ExifTags.TAGS.keys():
                if ExifTags.TAGS[orientation]=='Orientation':
                    break
            exif=dict(image._getexif().items())
            if exif[orientation] == 3:
                image=image.rotate(180, expand=True)
            elif exif[orientation] == 6:
                image=image.rotate(270, expand=True)
            elif exif[orientation] == 8:
                image=image.rotate(90, expand=True)
        except (AttributeError, KeyError, IndexError):
            # cases: image don't have getexif
            pass

        image = fit(image, (200, 200), Image.ANTIALIAS)
        fh = storage.open(self.avatar.name, "w")
        format = 'png'  # You need to set the correct image format here
        image.save(fh, format)
        fh.close()

Here's the s3 settings from settings/production.py

这是来自settings / production.py的s3设置

INSTALLED_APPS += ['storages',]

AWS_QUERYSTRING_AUTH = False
AWS_ACCESS_KEY_ID = os.environ['AWS_ACCESS_KEY_ID']
AWS_SECRET_ACCESS_KEY = os.environ['AWS_SECRET_ACCESS_KEY']
AWS_STORAGE_BUCKET_NAME = os.environ['S3_BUCKET_NAME']
MEDIA_URL = 'http://%s.s3.amazonaws.com/your-folder/' % AWS_STORAGE_BUCKET_NAME
DEFAULT_FILE_STORAGE = "storages.backends.s3boto.S3BotoStorage"


AWS_AUTO_CREATE_BUCKET = True

AWS_HEADERS = {
    "Cache-Control": "public, max-age=86400",
}

AWS_S3_FILE_OVERWRITE = False

AWS_S3_SECURE_URLS = True

AWS_REDUCED_REDUNDANCY = False

AWS_IS_GZIPPED = False

Here's the media storage from settings/local.py:

这是来自settings / local.py的媒体存储:

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

1 个解决方案

#1


0  

I got this working by changing this line:

我通过更改此行来实现此目的:

    fh = storage.open(self.avatar.name, "w")

to this

    fh = storage.open(self.avatar.name, "wb")

after finding this wonderfully helpful answer. I'm still not sure why it worked in heroku but not locally but the "wb" opens it in binary and makes it work in both cases.

找到这个非常有用的答案之后。我仍然不确定为什么它在heroku中工作但不在本地,但“wb”以二进制打开它并使其在两种情况下都有效。

#1


0  

I got this working by changing this line:

我通过更改此行来实现此目的:

    fh = storage.open(self.avatar.name, "w")

to this

    fh = storage.open(self.avatar.name, "wb")

after finding this wonderfully helpful answer. I'm still not sure why it worked in heroku but not locally but the "wb" opens it in binary and makes it work in both cases.

找到这个非常有用的答案之后。我仍然不确定为什么它在heroku中工作但不在本地,但“wb”以二进制打开它并使其在两种情况下都有效。