2013-09-16 42 views
2

同样,我仍在学习python,因此请和我一起裸照。我想这样做是Django REST框架和文件上传(数据URI)

1)允许用户选择图像 2)获得的图像,并将其添加到画布 3)允许处理(重新调整大小)的canavs 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)我也许不明白一堆这一点 - 因此,如果有别的我失去了一些东西,请让我知道


我认为,关键是在覆盖串行恢复领域的方法。当它查找由file.FileField构建的“file”字段时,我需要重定向框架以查找dataUri字段,该字段是要通过的字段,但我需要实例化一个新字段,其中没有限制最大长度。将dataUri分开,存储文件,并将文件字段添加到解析资源的字典中,并让框架按计划继续。在这种情况下,重写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 

回答

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 
0

1)式中/我怎么拦截其余请求解析/处理 传入REST后能够拉开Data URI并将 图像存储在磁盘上?

的REST架构如何适应这一点,但表单提交的数据是在任一[request.POST],或[request.FILES]docs)这我不清楚。这些对象将在您的表单发布到的Django视图中提供给您。这里有一个简单的例子from the documentation来说明:

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)我也许不明白一堆这样 - 所以如果有 别的东西我失踪,请让我知道

有一点可以帮助的是深入探究“Bound and Unbound Forms”。哦,我刚刚意识到你可能不会使用Django forms library;如果不是,那将是一个很好的开始(包括ModelForms)。此外,还有一项智能管理文件上传的服务,名为Filepicker.io;有一个Django package,使整合,非常无缝。如果你最终使用Filepicker与South的数据和架构迁移,你应该阅读:http://pydanny.com/filepicker-and-south.html

+0

谢谢我使用传统的Web POST实现类似的东西。转向基于REST的方法 - 并将数据发送出去很困难。在这种情况下,我不需要持续处理数据,它工作正常。但在这种情况下,我想将文件保存到FS(而不是数据库),在这种情况下,我需要在正确的位置挂入DJANGO REST FRAMEWORK ... – akaphenom

+0

感谢您的表单链接。今天下午肯定会阅读。 – akaphenom

4

如果您打算继续使用viewsets.ModelViewSet那么你可以使用任何可用的GenericAPIView方法覆盖的 - 在简要提及this link并完全记录在GenericAPIView 方法部分here

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

def pre_save(self, obj): 

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

如果这对于您来说不够灵活,那么创建您自己的自定义视图可以让您完全控制 - 查看chapter 3 of the tutorial以获得比我所能提供的更好的解释!

更新 - 避开以下在评论中提到的验证问题

DRF执行pre_save之前验证被调用,所以如果您的file场是从您的文章数据丢失的更新将您的自定义pre_save代码之前被拒绝有机会被执行。有两种方法解决此问题:

  1. 添加validate_<field_name>(self, attrs, source)方法的串行器(在你的情况validate_file())。无论该字段是否已填充到您的发布数据中,您都可以调用此函数,并且您可以在此处执行处理,并在返回之前将file添加到attrs。请注意,如果file将基于模型中的其他字段,那么实现模型级验证器(validate(self, attrs))可能更合适。有关更多信息,请参阅here

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

+0

很酷 - 所以我认为这种方法可行,但显然堆栈上的某些内容正在验证此输入,然后再进入pre_save。它返回结果:{“file”:[“该字段是必需的。“]}这是真的,我没有在文章中传递文件变量(现在是fileURI),我想要做的就是截取这个文件并在验证之前创建文件变量(并且可能仍然未通过验证)。想法? – akaphenom

+0

我已经编辑了上面的答案 –

+0

谢谢。我试过了,恢复字段没有将字段添加到字典中,因为它是零长度的字符串,我需要进一步向上移动堆栈。在还原字段阶段,允许构建文件字段的替代逻辑,并仍允许进行验证(而不必更改模型)。谢谢! – akaphenom