使用Tastypie将POST转换为PUT

时间:2013-02-27 20:20:42

标签: django tastypie

完全披露:Cross发布到Tastypie Google Group

我遇到的情况是我对发送到api的内容的控制有限。基本上,我需要两个Web服务才能接受来自的POST数据。两者都使用普通的POST操作和urlencoded数据(基本表单提交)。

用“卷曲”术语来思考它就像:

curl --data "id=1&foo=2" http://path/to/api

我的问题是我无法使用POST更新记录。所以我需要调整模型资源(我相信),这样如果指定了ID,POST就会充当PUT而不是POST。

api.py

class urlencodeSerializer(Serializer):
    formats = ['json', 'jsonp', 'xml', 'yaml', 'html', 'plist', 'urlencoded']
    content_types = {
        'json': 'application/json',
        'jsonp': 'text/javascript',
        'xml': 'application/xml',
        'yaml': 'text/yaml',
        'html': 'text/html',
        'plist': 'application/x-plist',
        'urlencoded': 'application/x-www-form-urlencoded',
        }
    # cheating
    def to_urlencoded(self,content): 
        pass
    # this comes from an old patch on github, it was never implemented
    def from_urlencoded(self, data,options=None):
        """ handles basic formencoded url posts """
        qs = dict((k, v if len(v)>1 else v[0] )
            for k, v in urlparse.parse_qs(data).iteritems())
        return qs


class FooResource(ModelResource):
    class Meta:
        queryset = Foo.objects.all() # "id" = models.AutoField(primary_key=True)
        resource_name = 'foo'
        authorization = Authorization() # only temporary, I know.
        serializer = urlencodeSerializer()

urls.py

foo_resource = FooResource

...
url(r'^api/',include(foo_resource.urls)),
) 

在Freenode上的#tastypie中,Ghost []建议我通过在模型资源中创建一个函数来覆盖post_list(),但是,我还没有成功使用它。

def post_list(self, request, **kwargs):
    if request.POST.get('id'): 
        return self.put_detail(request,**kwargs) 
    else: 
        return super(YourResource, self).post_list(request,**kwargs)

不幸的是,这种方法对我不起作用。我希望更大的社区可以为这个问题提供一些指导或解决方案。

注意:我无法覆盖来自客户端的标头(根据:http://django-tastypie.readthedocs.org/en/latest/resources.html#using-put-delete-patch-in-unsupported-places

3 个答案:

答案 0 :(得分:1)

我在用户创建方面遇到了类似的问题,我无法检查记录是否已存在。我最终创建了一个自定义验证方法,该方法验证用户是否不存在,在哪种情况下帖子可以正常工作。如果用户确实存在,我从验证方法更新了记录。 api仍然返回400响应,但记录已更新。感觉有点hacky但是......

答案 1 :(得分:1)

from tastypie.validation import Validation

class MyValidation(Validation):

    def is_valid(self, bundle, request=None):
        errors = {}
        #if this dict is empty validation passes. 

        my_foo = foo.objects.filter(id=1)
        if not len(my_foo) == 0: #if object exists      
            foo[0].foo = 'bar'    #so existing object updated
            errors['status'] = 'object updated'  #this will be returned in the api response

        return errors 

    #so errors is empty if object does not exist and validation passes. Otherwise object
    #updated and response notifies you of this

class FooResource(ModelResource):
    class Meta:
        queryset = Foo.objects.all() # "id" = models.AutoField(primary_key=True)
        validation = MyValidation()

答案 2 :(得分:0)

根据Cathal的建议,我能够利用验证功能更新我需要的记录。虽然这不会返回有效的代码......但它有效。

from tastypie.validation import Validation
import string # wrapping in int() doesn't work

class Validator(Validation): 
    def __init__(self,**kwargs):
        pass

    def is_valid(self,bundle,request=None):
        if string.atoi(bundle.data['id']) in Foo.objects.values_list('id',flat=True):
                # ... update code here
        else:
            return {}

确保在validation = Validator()元数据中指定ModelResource