4

我想做的是

1) 允许用户选择图像 2) 获取图像并将其添加到画布 3) 允许在画布内部进行操作(重新调整大小) 4) 推送“上传” 5) 获取画布并生成数据 URI它

这一切在 JS 中都很有效,给我留下了三个隐藏字段:

<input type="hidden" id="imageData" name="imageData" />
<input type="hidden" id="imageName" name="imageName" />
<input type="hidden" id="imageCaption" name="imageCaption" />

这是Python代码

class Image(models.Model):
    filePath = models.CharField(max_length=200)
    imageCaption = models.CharField(max_length=200)
    imageName = models.CharField(max_length=200)

class ImageSerializer(serializers.HyperlinkedModelSerializer):
    class Meta:
        model = Image
        fields = ('filePath', 'imageCaption','imageName')

class ImageViewSet(viewsets.ModelViewSet):
    queryset = Image.objects.all()
    serializer_class = ImageSerializer

我在这里遗漏了一些关键点。

1)我在哪里/如何拦截其余请求以解析/处理传入的 REST 帖子,以便能够分离数据 URI 并将图像存储在磁盘上?

2)我可能不明白这一点 - 所以如果还有其他我遗漏的东西,请告诉我


我认为诀窍在于覆盖序列化程序恢复字段方法。当它查找由model.FileField构建的“文件”字段时,我需要重定向框架以查找作为传递字段的dataUri字段,但我需要实例化一个新字段,没有最大长度的限制。将dataUri拉开,存储文件,将file字段添加到解析资源的字典中,让框架按计划继续。在这种情况下,不需要覆盖 pre_save,因为此代码需要在验证之前执行。

JS:

// angularJs controller submit method, using RESTAngular
$scope.submit = function() { //function(event) {

        var someImg = {
            file: ''
            , dataUri: $scope.fileUrl
            , caption: $scope.caption
        }

        ImagesResource.post(someImg )

    }

Python:

class ImageSerializer(serializers.HyperlinkedModelSerializer):
    class Meta:
        model = Image
        fields = ('file', 'caption','id')

    def saveImage(self, imgFileUri):
        #parse dataUri and save locally, return local path
        return 'somewhereOverTheBlah'

    def restore_fields(self, data, files):
        """
        Core of deserialization, together with `restore_object`.
        Converts a dictionary of data into a dictionary of deserialized fields.
        """
        reverted_data = {}

        if data is not None and not isinstance(data, dict):
            self._errors['non_field_errors'] = ['Invalid data']
            return None

        for field_name, field in self.fields.items():
            print('a: ' + field_name)
            if(field_name == 'file'):
                field_name = 'dataUri'
                field = fields.CharField()
                try:
                    # restore using the built in mechanism
                    field.field_from_native(data, files, field_name, reverted_data)
                    # take the dataUri, save it to disk and return the Path
                    value = reverted_data[field_name]
                    path = self.saveImage(value)
                    # set the file <Path> property on the model, remove the old dataUri
                    reverted_data['file'] = path
                    del reverted_data[field_name]

                except ValidationError as err:
                    self._errors[field_name] = list(err.messages)
            else:
                field.initialize(parent=self, field_name=field_name)
                try:
                    field.field_from_native(data, files, field_name, reverted_data)
                except ValidationError as err:
                    self._errors[field_name] = list(err.messages)

        return reverted_data
4

3 回答 3

4

如果您打算继续使用 a那么您可以使用任何可用于 GenericAPIView 的方法覆盖 -此链接viewsets.ModelViewSet中简要提及并在此处的 GenericAPIView方法部分中完整记录。

对您最有用的可能是框架提供的pre_savepost_save钩子,您可以用自己的方法覆盖它们,从而将您自己的自定义代码添加到视图中。 pre_save,正如您所料,在视图保存发布的数据之前调用。它的参数之一是即将保存的对象:

def pre_save(self, obj):

所以你可以在这一点上执行数据丰富等。

如果这对您来说不够灵活,那么构建自己的自定义视图非常简单,它可以让您完全控制 - 请查看教程的第 3 章以获得比我提供的更好的解释!

更新 - 解决下面评论中提到的验证问题

DRF 在pre_save被调用之前执行验证,因此如果您file的帖子数据中缺少您的字段,您的更新将在您的自定义pre_save代码有机会执行之前被拒绝。有几种方法可以解决这个问题:

  1. 向您的序列化程序添加一个validate_<field_name>(self, attrs, source)方法(validate_file()在您的情况下)。无论该字段是否已填充到您的帖子数据中,这都会被调用,您可以在此处进行处理并在file返回attrs之前添加。请注意,如果file将基于模型中的其他字段,那么实现模型级验证器 ( validate(self, attrs)) 可能更合适。请参阅此处了解更多信息。

  2. 按照上面的建议构建自定义视图。

于 2013-09-16T21:20:55.223 回答
2

在发布的 JSON/XML 与 django 中的模型不一致的情况下,您需要重新调整序列化程序。我使用源并复制了 restore_fields 方法,并重新编码以处理所需的额外代码。

class ImageSerializer(serializers.HyperlinkedModelSerializer):
    class Meta:
        model = Image
        fields = ('file', 'caption','id')

    def saveImage(self, imgFileUri):
        #parse dataUri and save locally, return local path
        return 'somewhereOverTheBlah'

    def restore_fields(self, data, files):
        """
        Core of deserialization, together with `restore_object`.
        Converts a dictionary of data into a dictionary of deserialized fields.
        """
        reverted_data = {}

        if data is not None and not isinstance(data, dict):
            self._errors['non_field_errors'] = ['Invalid data']
            return None

        for field_name, field in self.fields.items():
            """
            So it  is iterating over the fields to serialize, when we find the file field
            do something different (in this case look for the fileUri field, handle it and replace
            it inside of the reverted_data dictionary with the intended file field
            """

            if(field_name == 'file'):
                field_name = 'dataUri'
                field = fields.CharField()
                try:
                    # restore using the built in mechanism
                    field.field_from_native(data, files, field_name, reverted_data)
                    # take the dataUri, save it to disk and return the Path
                    value = reverted_data[field_name]
                    path = self.saveImage(value)
                    # set the file <Path> property on the model, remove the old dataUri
                    reverted_data['file'] = path
                    del reverted_data[field_name]

                except ValidationError as err:
                    self._errors[field_name] = list(err.messages)
            else:
                field.initialize(parent=self, field_name=field_name)
                try:
                    field.field_from_native(data, files, field_name, reverted_data)
                except ValidationError as err:
                    self._errors[field_name] = list(err.messages)

        return reverted_data
于 2013-09-20T15:14:32.153 回答
0

1)我在哪里/如何拦截其余请求以解析/处理传入的 REST 帖子,以便能够分离数据 URI 并将图像存储在磁盘上?

我不清楚 REST 框架如何适应这一点,但表单提交的数据位于[request.POST][request.FILES]( docs ) 中。这些对象将在您的表单发布到的 Django 视图中提供给您。这是文档中的一个简单示例来说明:

from django.shortcuts import render
from django.http import HttpResponseRedirect

def contact(request):
    if request.method == 'POST': # If the form has been submitted...
        form = ContactForm(request.POST) # A form bound to the POST data
        if form.is_valid(): # All validation rules pass
            # Process the data in form.cleaned_data
            # ...
            return HttpResponseRedirect('/thanks/') # Redirect after POST
    else:
        form = ContactForm() # An unbound form

    return render(request, 'contact.html', {
        'form': form,
    })

2)我可能不明白这一点 - 所以如果还有其他我遗漏的东西,请告诉我

可能有帮助的一件事是更深入地研究“绑定和非绑定表单”。哦,我刚刚意识到您可能没有使用Django 表单库;如果没有,那将是一个很好的起点(包括 ModelForms)。此外,还有一项名为Filepicker.io的智能管理文件上传的服务;有一个Django 包可以无缝集成。如果您最终使用带有South的 Filepicker进行数据和模式迁移,您应该阅读以下内容:http: //pydanny.com/filepicker-and-south.html

于 2013-09-16T14:03:32.623 回答