問題は、Content-Type
リクエストのヘッダーが正しく設定されていないことです。【参考】
Tastypie は、、、および のみxml
を認識しjson
ます。そのため、POST リクエストを送信するときは、リクエスト ヘッダーにそれらのいずれかを設定する必要があります (例: )。yaml
bplist
Content-Type
application/json
編集:
Tastypie を介してファイルを含むマルチパート フォームを送信しようとしているようです。
ロードマップ 1.0 最終版 (まだリリースされていません) に対する Issac Kelly による Tastypie のファイル アップロード サポートの背景:
- PUT/POST 用に base64 でエンコードされたファイル (第 42 号のものなど) を受け入れ、GET 要求の URL を提供する Base64FileField を実装します。これはメインのtastypieレポの一部になります。
- 他の実装を独立したプロジェクトとして実装することをお勧めします。これを行うにはいくつかの方法があり、それらのほとんどは少し厄介で、すべて異なる欠点があります。他のオプションを用意し、それぞれの長所と短所を文書化したいと考えています
つまり、少なくとも現時点では、Tastypie はマルチパート ファイルのアップロードを正式にサポートしていません。ただし、実際にはうまく機能していると思われるフォークがあり、これはその 1 つです。私はそれをテストしていません。
それでは、なぜそのエラーが発生するのかを説明してみましょう。
Tastypieresource.py
の 452 行目:
def dispatch(self, request_type, request, **kwargs):
"""
Handles the common operations (allowed HTTP method, authentication,
throttling, method lookup) surrounding most CRUD interactions.
"""
allowed_methods = getattr(self._meta, "%s_allowed_methods" % request_type, None)
if 'HTTP_X_HTTP_METHOD_OVERRIDE' in request.META:
request.method = request.META['HTTP_X_HTTP_METHOD_OVERRIDE']
request_method = self.method_check(request, allowed=allowed_methods)
method = getattr(self, "%s_%s" % (request_method, request_type), None)
if method is None:
raise ImmediateHttpResponse(response=http.HttpNotImplemented())
self.is_authenticated(request)
self.is_authorized(request)
self.throttle_check(request)
# All clear. Process the request.
request = convert_post_to_put(request)
response = method(request, **kwargs)
# Add the throttled request.
self.log_throttled_access(request)
# If what comes back isn't a ``HttpResponse``, assume that the
# request was accepted and that some action occurred. This also
# prevents Django from freaking out.
if not isinstance(response, HttpResponse):
return http.HttpNoContent()
return response
convert_post_to_put(request)
ここから呼び出されます。そして、ここにコードがあります
convert_post_to_put
:
# Based off of ``piston.utils.coerce_put_post``. Similarly BSD-licensed.
# And no, the irony is not lost on me.
def convert_post_to_VERB(request, verb):
"""
Force Django to process the VERB.
"""
if request.method == verb:
if hasattr(request, '_post'):
del(request._post)
del(request._files)
try:
request.method = "POST"
request._load_post_and_files()
request.method = verb
except AttributeError:
request.META['REQUEST_METHOD'] = 'POST'
request._load_post_and_files()
request.META['REQUEST_METHOD'] = verb
setattr(request, verb, request.POST)
return request
def convert_post_to_put(request):
return convert_post_to_VERB(request, verb='PUT')
また、このメソッドは、メソッドがフラグをに設定するrequest.body
ため
、それ以上のアクセスを防ぐという副作用があるため、マルチパートを処理することを実際には意図していません:_load_post_and_files()
_read_started
True
ジャンゴrequest.body
と_load_post_and_files()
:
@property
def body(self):
if not hasattr(self, '_body'):
if self._read_started:
raise Exception("You cannot access body after reading from request's data stream")
try:
self._body = self.read()
except IOError as e:
six.reraise(UnreadablePostError, UnreadablePostError(*e.args), sys.exc_info()[2])
self._stream = BytesIO(self._body)
return self._body
def read(self, *args, **kwargs):
self._read_started = True
return self._stream.read(*args, **kwargs)
def _load_post_and_files(self):
# Populates self._post and self._files
if self.method != 'POST':
self._post, self._files = QueryDict('', encoding=self._encoding), MultiValueDict()
return
if self._read_started and not hasattr(self, '_body'):
self._mark_post_parse_error()
return
if self.META.get('CONTENT_TYPE', '').startswith('multipart'):
if hasattr(self, '_body'):
# Use already read data
data = BytesIO(self._body)
else:
data = self
try:
self._post, self._files = self.parse_file_upload(self.META, data)
except:
# An error occured while parsing POST data. Since when
# formatting the error the request handler might access
# self.POST, set self._post and self._file to prevent
# attempts to parse POST data again.
# Mark that an error occured. This allows self.__repr__ to
# be explicit about it instead of simply representing an
# empty POST
self._mark_post_parse_error()
raise
else:
self._post, self._files = QueryDict(self.body, encoding=self._encoding), MultiValueDict()
したがって、Tastypie の
convert_post_to_VERB()
メソッドをrequest._body
呼び出して設定すること
でモンキー パッチを適用しrequest.body
、すぐに設定して、から読み取って設定しない
_read_started=False
よう
にすることができます (おそらくそうすべきではありません) 。_load_post_and_files()
_body
_read_started=True
def convert_post_to_VERB(request, verb):
"""
Force Django to process the VERB.
"""
if request.method == verb:
if hasattr(request, '_post'):
del(request._post)
del(request._files)
request.body # now request._body is set
request._read_started = False # so it won't cause side effects
try:
request.method = "POST"
request._load_post_and_files()
request.method = verb
except AttributeError:
request.META['REQUEST_METHOD'] = 'POST'
request._load_post_and_files()
request.META['REQUEST_METHOD'] = verb
setattr(request, verb, request.POST)
return request