Django ImageField: полный или относительный путь? - PullRequest
0 голосов
/ 01 мая 2018

Я пытаюсь реализовать загрузку и обработку изображений в моем проекте Django. Проблема с путем для ImageField:

Вариант 1.

Если я использую путь относительно папки media, то image.url приятно возвращает /media/img_name.jpg, и Django правильно обрабатывает изображения. Но я не могу использовать image.width свойство, это приводит к ошибке:

SuspiciousFileOperation
Объединенный путь (/user_test/3d55d527-109d-4a07-b0f6-4de0304d41f6.jpg) находится вне компонента базового пути (/ Users / Aivan / Desktop / Code / MyProject / media)


Вариант 2.

Если я использую полный путь, я могу легко получить доступ к свойству image.width. Но image.url возвращает полный путь, добавленный в папку media: http://127.0.0.1:8000/media/Users/Aivan/Desktop/Code/MyProject/media/user_test/8f3219cd-0333-4488-9d29-2c5506707afb.jpg


Вопрос:

Что я должен сделать для правильного доступа к image.url и image.width?

Конечно, я всегда могу установить полный путь и добавить сохранить относительный путь в некоторых CharField или добавить собственный метод для извлечения относительного URL, но, вероятно, такие решения плохие.

Полагаю, это потому, что Django использует относительные пути для медиа-файлов, но для работы с изображениями он использует Pillow, которому нужны полные пути ... Может быть, это ошибка?


Мой класс картинок:

def path_user_photo(instance, filename):
    return 'user_{0}'.format(instance.account.user.username)

class Photo(models.Model):
    image = models.ImageField(upload_to=path_user_photo)
    name = models.CharField(max_length=32, unique=True)

И это статический метод загрузки изображений:

    @classmethod
    def create_file(cls, fl, user, written_name, ext):
        folder = '/user_{0}'.format(user.username)
        realname = '/' + str(uuid.uuid4()) + '.' + ext
        write_path = settings.MEDIA_ROOT + folder
        # Create directory if it doesn't exist
        if not os.path.exists(write_path):
            os.makedirs(write_path)
        # Write data
        write_path = write_path + realname
        with open(write_path, 'wb+') as destination:
            for chunk in fl.chunks():
                destination.write(chunk)

        res = cls()
        # Option 1. Relative path
        res.image = folder + realname
        # Option 2. Full path
        res.image = write_path
        res.name = written_name
        res.save()
        return res

Который я вызываю из вида:

Photo.create_file(
    # the file
    request.FILES['file_img'],
    # current user
    request.user,
    # written name
    request.POST['written_name'],
    # real file extension
    file.name.split('.')[-1]
)

1 Ответ

0 голосов
/ 01 мая 2018

Проблема здесь, я думаю, в том, что res.image = folder + realname технически является абсолютным путем, потому что folder начинается с жесткого кода /.

Способ решения проблемы - объединить пути, используя os.path.join вместо явного /, и тогда вы можете безопасно использовать относительный путь при сохранении изображения.

См. Ниже - я прокомментировал измененные строки.

import os.path

@classmethod
def create_file(cls, fl, user, written_name, ext):
    folder = 'user_{0}'.format(user.username)  # Drop the leading slash
    realname = str(uuid.uuid4()) + '.' + ext  # Drop the leading slash
    write_path = os.path.join(settings.MEDIA_ROOT, folder)  # Join the paths
    # Create directory if it doesn't exist
    if not os.path.exists(write_path):
        os.makedirs(write_path)
    # Write data
    write_path = os.path.join(write_path, realname)  # Join the path and name
    with open(write_path, 'wb+') as destination:
        for chunk in fl.chunks():
            destination.write(chunk)

    res = cls()
    # Option 1. Relative path
    res.image = os.path.join(folder, realname)  # Join the folder and name
    res.name = written_name
    res.save()
    return res
...