如何使PATCH方法对Django应用程序的部分更新有效?没有ImageField更新,它将无法正常工作

时间:2018-08-22 23:29:28

标签: python django rest django-rest-framework patch

我正在尝试在Django Web应用程序中启用部分更新(使用Django Rest Framework)。

我要更新的模型包括ImageField。
当我更新ImageField时,PATCH方法有效,但是当我不更新ImageField时,PATCH方法不起作用。
错误消息说
AttributeError:'NoneType' object has no attribute 'items'

我该如何解决?

型号:

class Work(models.Model):
    owner       = models.ForeignKey(settings.AUTH_USER_MODEL, on_delete=models.CASCADE, null=True, blank=True)
    title       = models.CharField(max_length=120)
    made_date   = models.DateField(default=datetime.date.today, null=True, blank=True)
    note        = models.TextField(max_length=2000, null=True, blank=True)
    image       = models.ImageField(upload_to='work_pic', default='default_image.png')

    def __str__(self):
        return self.title

    def save(self, *args, **kwargs):
        if self.image and self.image.name.lower().endswith(('.jpg', '.jpeg')):
            pilImage = Img.open(BytesIO(self.image.read()))
            for orientation in ExifTags.TAGS.keys():
                if ExifTags.TAGS[orientation] == 'Orientation':
                    break
            exif = dict(pilImage._getexif().items())

            if exif[orientation] == 3:
                pilImage = pilImage.rotate(180, expand=True)
            elif exif[orientation] == 6:
                pilImage = pilImage.rotate(270, expand=True)
            elif exif[orientation] == 8:
                pilImage = pilImage.rotate(90, expand=True)

            output = BytesIO()
            pilImage.save(output, format='JPEG', quality=75)
            output.seek(0)
            self.image = File(output, self.image.name)

        return super(Work, self).save(*args, **kwargs)

序列化器:

class WorkSerializer(serializers.ModelSerializer):
    owner = serializers.HiddenField(default=serializers.CurrentUserDefault())

    class Meta:
        model = Work
        fields = '__all__'

    def create(self, validated_data):
        return Work.objects.create(**validated_data)

ViewSet:

class WorkViewSet(viewsets.ModelViewSet):
    queryset = Work.objects.all()
    serializer_class = WorkSerializer

    def partial_update(self, request, *args, **kwargs):
        kwargs['partial'] = True
        return self.update(request, *args, **kwargs)

发送请求的方法(Vue.js):

updateWork: function() {
    let formData  = new FormData();

    //Initial value of currentWork.image is text (path to the original image in the media folder). 
    //When user upload new image, the file is set to currentWork.image. So condition is match to the if statement bellow.
    if (this.currentWork.image instanceof File) {
        formData.append("image", this.currentWork.image);
    }

    formData.append("title", this.currentWork.title);
    formData.append("made_date", this.currentWork.made_date);
    formData.append("note", this.currentWork.note);
    this.loading = true;
    axios.patch(`/api/work/${this.currentWork.id}/`, formData, {
        headers: {
            'Content-Type': 'multipart/form-data'
        }
    })
        .then((response) => {
            this.loading = false;
        })
        .catch((err) => {
            this.loading = false;
            console.log(err);
        })

错误消息:

Internal Server Error: /api/work/61/    
Traceback (most recent call last):  
  File "/Users/rami/Dev/MySweetsBase/myvenv/lib/python3.6/site-packages/django/core/handlers/exception.py", line 35, in inner  
    response = get_response(request)  
  File "/Users/rami/Dev/MySweetsBase/myvenv/lib/python3.6/site-packages/django/core/handlers/base.py", line 128, in _get_response    
    response = self.process_exception_by_middleware(e, request)  
  File "/Users/rami/Dev/MySweetsBase/myvenv/lib/python3.6/site-packages/django/core/handlers/base.py", line 126, in _get_response  
    response = wrapped_callback(request, *callback_args, **callback_kwargs)  
  File "/Users/rami/Dev/MySweetsBase/myvenv/lib/python3.6/site-packages/django/views/decorators/csrf.py", line 54, in wrapped_view  
    return view_func(*args, **kwargs)  
  File "/Users/rami/Dev/MySweetsBase/myvenv/lib/python3.6/site-packages/rest_framework/viewsets.py", line 103, in view  
    return self.dispatch(request, *args, **kwargs)  
  File "/Users/rami/Dev/MySweetsBase/myvenv/lib/python3.6/site-packages/rest_framework/views.py", line 483, in dispatch  
    response = self.handle_exception(exc)  
  File "/Users/rami/Dev/MySweetsBase/myvenv/lib/python3.6/site-packages/rest_framework/views.py", line 443, in handle_exception  
    self.raise_uncaught_exception(exc)  
  File "/Users/rami/Dev/MySweetsBase/myvenv/lib/python3.6/site-packages/rest_framework/views.py", line 480, in dispatch  
    response = handler(request, *args, **kwargs)  
  File "/Users/rami/Dev/MySweetsBase/works/viewsets.py", line 13, in partial_update  
    return self.update(request, *args, **kwargs)  
  File "/Users/rami/Dev/MySweetsBase/myvenv/lib/python3.6/site-packages/rest_framework/mixins.py", line 70, in update  
    self.perform_update(serializer)  
  File "/Users/rami/Dev/MySweetsBase/myvenv/lib/python3.6/site-packages/rest_framework/mixins.py", line 80, in perform_update  
    serializer.save()  
  File "/Users/rami/Dev/MySweetsBase/myvenv/lib/python3.6/site-packages/rest_framework/serializers.py", line 209, in save  
    self.instance = self.update(self.instance, validated_data)  
  File "/Users/rami/Dev/MySweetsBase/myvenv/lib/python3.6/site-packages/rest_framework/serializers.py", line 981, in update  
    instance.save()  
  File "/Users/rami/Dev/MySweetsBase/works/models.py", line 26, in save   
    exif = dict(pilImage._getexif().items())  
AttributeError: 'NoneType' object has no attribute 'items'  

1 个答案:

答案 0 :(得分:0)

class Work(models.Model):
    owner       = models.ForeignKey(settings.AUTH_USER_MODEL, on_delete=models.CASCADE, null=True, blank=True)
    title       = models.CharField(max_length=120)
    made_date   = models.DateField(default=datetime.date.today, null=True, blank=True)
    note        = models.TextField(max_length=2000, null=True, blank=True)
    image       = models.ImageField(upload_to='work_pic', default='default_image.png')

    def __str__(self):
        return self.title

    def save(self, *args, **kwargs):

        if not self.id:
            # add
            self.set_image()
        else:
            # change
            this = Work.objects.get(id=self.id)
            if this.image != self.image:
                self.set_image()
        return super(Work, self).save(*args, **kwargs)

    def set_image(self):
        if self.image and self.image.name.lower().endswith(('.jpg', '.jpeg')):
            pilImage = Img.open(BytesIO(self.image.read()))
            for orientation in ExifTags.TAGS.keys():
                if ExifTags.TAGS[orientation] == 'Orientation':
                    break
            exif = dict(pilImage._getexif().items())

            if exif[orientation] == 3:
                pilImage = pilImage.rotate(180, expand=True)
            elif exif[orientation] == 6:
                pilImage = pilImage.rotate(270, expand=True)
            elif exif[orientation] == 8:
                pilImage = pilImage.rotate(90, expand=True)

            output = BytesIO()
            pilImage.save(output, format='JPEG', quality=75)
            output.seek(0)
            self.image = File(output, self.image.name)

尝试一下