【问题标题】:django set image delete old reference and prevent delete defaultdjango 设置图像删除旧引用并防止删除默认值
【发布时间】:2023-01-18 06:50:48
【问题描述】:

尽管许多现代网站都使用 OSS 来提供图像服务,但我仍然想构建一个后端来在本地管理小缩略图。

然而,django 图像字段有点棘手。

我可能会更改图像参考的三个视图:

  • models.py
  • views.py
  • forms.py

我曾经通过以下方式简单地做到这一点:

forms.py

request.user.profile.image = self.files['image']

我总是有一个默认值

models.py

class Profile(models.Model):
    user = models.OneToOneField(User, on_delete=models.CASCADE)
    image = ProcessedImageField(
        default='profile/default.jpg', 
        upload_to='profile', 
        processors=[ResizeToFill(*THUMBNAIL_SIZE)],
        format='JPEG',
        options={'quality': THUMBNAIL_QUALITY}, 
    )

经过大量的测试,我发现它总是导致一个问题,可以是:

  • 默认图像文件被删除。

  • 如果图像之前已经设置过,它保存的值不是默认值,当我重置它时,旧的引用文件不会被删除并且会占用磁盘存储空间。

为了完美地做到这一点,我决定为导入编写一个全局函数,每当我想设置图像时,调用它

from django.conf import settings

def setImage(instance, attr, file):
    """ instance will be saved """
    if file:
        ifield = getattr(instance, attr)
        # old reference, can be default
        iurl = ifield.url
        # default
        durl = settings.MEDIA_URL + instance._meta.get_field(attr).get_default()
        if iurl != durl:
            # old reference is not default
            # delete old reference and free up space
            ifield.delete(save=True)
        # set new file 
        setattr(ifield, attr, file)
    instance.save()

非常简单。然而,在测试中,我发现图像永远不会被设置。以下是我排除的可能原因:

  • 形成multipartenctype属性
  • ajax processData, contentType 设置正确
  • 模型类中的 save 未被覆盖

如果一切正常,哪里出了问题?我注销了所有的值。

setImage(self.user.profile, 'image', self.files['image'])
# self.files['image'] has valid value and is passed 
# to setImage, which I think, is not garbage collected
def setImage(instance, attr, file):
    """ instance will be saved """
    print('======')
    print(file)
    if file:
        ifield = getattr(instance, attr)
        iurl = ifield.url
        durl = settings.MEDIA_URL + instance._meta.get_field(attr).get_default()
        print(iurl)
        print(durl)
        if iurl != durl:
            ifield.delete(save=True)
            print(f'--- del {iurl}')
        setattr(ifield, attr, file)
        print('----res')
        print(getattr(ifield, attr))
        print(ifield.image)
    print('--- ins')
    print(instance)
    instance.save()
    print('--- after save')
    print(instance.image.url)
    print(getattr(instance, attr))

该字段有一个默认值,我在测试中上传了屏幕截图。

======
Screen Shot 2022-11-03 at 10.59.41 pm.png
/media/profile/default.jpg
/media/profile/default.jpg
----res
Screen Shot 2022-11-03 at 10.59.41 pm.png
Screen Shot 2022-11-03 at 10.59.41 pm.png
--- ins
tracey
--- after save
/media/profile/default.jpg
profile/default.jpg

为什么图像没有设置,有人有任何想法吗?

【问题讨论】:

    标签: python django django-models imagefield


    【解决方案1】:

    老实说,我没有测试你的功能来说明它有什么问题。相反,我以您的模型为基础以我自己的方式实现了它。如果你真的想要一个 set_image(instance, attr, file) 函数,你可以从这个答案 create_profile_ajax at views.py 中改编它。

    设置.py

    DEFAULT_IMAGE_URL = 'profile/default.jpg'
    

    模型.py

    class Profile(models.Model):
        user = models.OneToOneField(User, on_delete=models.CASCADE)
        image = ProcessedImageField(
            default=settings.DEFAULT_IMAGE_URL,
            upload_to='avatars',
            processors=[ResizeToFill(100, 50)],
            format='JPEG',
            options={'quality': 60},
            blank=True,
            null=True
        )
    
        @staticmethod
        def default_image_absolute_url():
            return settings.MEDIA_URL + settings.DEFAULT_IMAGE_URL
        
        @staticmethod
        def default_image_url():
            return settings.DEFAULT_IMAGE_URL
    

    表单.py

    class ProfileForm(forms.ModelForm):
    
        class Meta:
            model = Profile
            fields = ['image']
    

    视图.py

    @login_required
    def create_profile(request):
        form = ProfileForm()
        return render(request, 'profile/create.html', {'form': form})
    
    
    def create_profile_ajax(request):
        image = request.FILES.get('image')
        profile, created = Profile.objects.get_or_create(user=request.user)
    
        if image:
            if profile.image.url == Profile.default_image_absolute_url():
                profile.image = image
            else:
                profile.image.delete()
                profile.image = image
        else:
            profile.image = Profile.default_image_url()
        
        profile.save()
        profile.refresh_from_db()
    
        return JsonResponse({'new_image_url': profile.image.url})
    

    模板.html (csrf with ajax)

    {% extends 'base.html' %}
    
    {% block content %}
    {{form.as_p}}
    <input type="submit" value="Create" onclick="sendProfile()">
    
    <img src="{{request.user.profile.image.url}}" 
        id="thumb" 
        width="500" 
        height="600" 
        {% if not request.user.profile.image %} hidden {% endif %} 
        style="object-fit: contain;">
    
    
    <script>
        function getCookie(name) {
            ...
        }
    
        function sendProfile() {
            const csrftoken = getCookie('csrftoken');
            var input = document.getElementById('id_image');
            var data = new FormData()
            data.append('image', input.files[0])
    
            fetch('/image/create/ajax/', {
            method: 'POST',
            headers: {
                'X-CSRFToken': csrftoken
            },
            body: data
            })
            .then((response) => response.json())
            .then((data) => {
                var thumb = document.getElementById('thumb');
                thumb.src = data.new_image_url;
                thumb.hidden = false;
                input.value = '';
            });
        }
    </script>
    {% endblock %}
    

    引用FormData文档:

    使用 FormData 使用 XMLHttpRequest 或提交 POST 请求时 具有 multipart/form-data Content-Type 的 Fetch_API(例如,当 上传文件和 Blob 到服务器),不要显式设置 请求中的 Content-Type 标头。

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 2019-06-02
      • 1970-01-01
      • 1970-01-01
      • 2018-07-17
      • 2011-03-22
      • 2016-03-13
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多