1

У меня есть GET конечную точку, я пытаюсь проверить с помощью APIRequestFactory, следуя методике, описанной в этом SO сообщение: https://stackoverflow.com/a/28429089MultiValueDictKeyError при тестировании GET конечную точку с помощью APIRequestFactory

Во-первых, я знаю, что это конечная точка работает, потому что я получить ожидаемое поведение при отправке запроса GET через cURL.

curl -X GET -H "Content-Type: application/json" -i -d "{\"cardinal_key\":12XXXX3, \"cardinal_type\":\"Npis\", \"terminal_type\":\"Deas\", \"candidate_count\":2}" http://127.0.0.1:8000/v1/ranked_results/ 
HTTP/1.0 200 OK 
Date: Mon, 28 Mar 2016 22:29:29 GMT 
Server: WSGIServer/0.2 CPython/3.4.3 
X-Frame-Options: SAMEORIGIN 
Allow: GET, HEAD, OPTIONS 
Vary: Accept, Cookie 
Content-Type: application/json 

{"npi":"12XXXX3","first_name":"XXXXXX","middle_name":null,"last_name":"XXXXXX","full_name":"XXXXX, XXXX XXXXX","link_candidates":{"FXXXXX9":0.9900022451986468,"BXXXXXXX3":0.8483023431385707}} 

Однако, когда я попытался сделать эмулировать этот запрос, используя завод, я получил ValueError исключение (полный след ниже):

from rest_framework.test import APIRequestFactory 
from linker.views import RankedResults 
factory = APIRequestFactory() 
options = {"cardinal_key":12XXXX3, "cardinal_type":"Npis", "terminal_type":"Deas", "candidate_count":2} 
request = factory.get('/v1/ranked_results/', json.dumps(options), content_type='application/json') 
view = RankedResults.as_view() 
response = view(request) 

ValueError: need more than 1 value to unpack

Моя следующая мысль была что-то было не работает, потому что я пытался отправить запрос как JSON, поэтому я попробовал сделать запрос без JSON, а затем получил MultiValueDictKeyError:

from rest_framework.test import APIRequestFactory 
from linker.views import RankedResults 
factory = APIRequestFactory() 
options = {"cardinal_key":12XXXX3, "cardinal_type":"Npis", "terminal_type":"Deas", "candidate_count":2} 
request = factory.get('/v1/ranked_results/', options) 
view = RankedResults.as_view() 
response = view(request) 

MultiValueDictKeyError: "'cardinal_type'"

Мое понимание MultiValueDictKeyError исключением является то, что это происходит, когда ключ отсутствует в словаре. Более того, в документации указано, что MultiValueDict используется для случая, когда у вас есть несколько значений с одним и тем же ключом. Я не уверен, почему я это вижу, потому что это не так с запросом, который я пытаюсь создать.

Я в затруднении здесь - чего мне не хватает?

вид:

class RankedResults(APIView): 
    def get(self, request): 
     cardinal_instance = apps.get_model('warehouse', request.data['cardinal_type']).objects.get(reference_key=request.data['cardinal_key']) 
     serializer = LinkModelSerializer(cardinal_instance, context=request.data) 
     return Response(serializer.data) 

сериализатору:

class LinkModelSerializer(serializers.ModelSerializer): 
    link_candidates = serializers.SerializerMethodField('get_candidate_links') 

    def get_candidate_links(self, obj): 
     terminal = apps.get_model('warehouse', self.context['terminal_type']) 
     n = self.context['candidate_count'] 
     return dict(obj.top_n_candidates(terminal, n)) 

    class Meta: 
     model = warehouse.Npis 
     fields = ('npi', 'first_name', 'middle_name', 'last_name', 'full_name', 'link_candidates') 
     read_only_fields = ('link_candidates') 

трассировки для случая, когда я пытался отправить запрос JSON:

--------------------------------------------------------------------------- 
ValueError        Traceback (most recent call last) 
<ipython-input-20-6b10ed2040c0> in <module>() 
     2 factory = APIRequestFactory() 
     3 options = {"cardinal_key":12XXXX3, "cardinal_type":"Npis", "terminal_type":"Deas", "candidate_count":2} 
----> 4 request = factory.get('/v1/ranked_results/', json.dumps(options), content_type='application/json') 
     5 view = RankedResults.as_view() 
     6 response = view(request) 

~/lib/python3.4/site-packages/rest_framework/test.py in get(self, path, data, **extra) 
    79  def get(self, path, data=None, **extra): 
    80   r = { 
---> 81    'QUERY_STRING': urlencode(data or {}, doseq=True), 
    82   } 
    83   # Fix to support old behavior where you have the arguments in the url 

~/lib/python3.4/site-packages/django/utils/http.py in urlencode(query, doseq) 
    94   [(force_str(k), 
    95   [force_str(i) for i in v] if isinstance(v, (list, tuple)) else force_str(v)) 
---> 96    for k, v in query], 
    97   doseq) 
    98 

~/lib/python3.4/site-packages/django/utils/http.py in <listcomp>(.0) 
    94   [(force_str(k), 
    95   [force_str(i) for i in v] if isinstance(v, (list, tuple)) else force_str(v)) 
---> 96    for k, v in query], 
    97   doseq) 
    98 

ValueError: need more than 1 value to unpack 

... Наконец след, когда Я попробовал это без запроса JSON:

--------------------------------------------------------------------------- 
KeyError         Traceback (most recent call last) 
~/lib/python3.4/site-packages/django/utils/datastructures.py in __getitem__(self, key) 
    82   try: 
---> 83    list_ = super(MultiValueDict, self).__getitem__(key) 
    84   except KeyError: 

KeyError: 'cardinal_type' 

During handling of the above exception, another exception occurred: 

MultiValueDictKeyError     Traceback (most recent call last) 
<ipython-input-17-559ee170e692> in <module>() 
     4 request = factory.get('/v1/ranked_results/', options) 
     5 view = RankedResults.as_view() 
----> 6 response = view(request) 

~/lib/python3.4/site-packages/django/views/decorators/csrf.py in wrapped_view(*args, **kwargs) 
    56  # function. 
    57  def wrapped_view(*args, **kwargs): 
---> 58   return view_func(*args, **kwargs) 
    59  wrapped_view.csrf_exempt = True 
    60  return wraps(view_func, assigned=available_attrs(view_func))(wrapped_view) 

~/lib/python3.4/site-packages/django/views/generic/base.py in view(request, *args, **kwargs) 
    66    self.args = args 
    67    self.kwargs = kwargs 
---> 68    return self.dispatch(request, *args, **kwargs) 
    69   view.view_class = cls 
    70   view.view_initkwargs = initkwargs 

~/lib/python3.4/site-packages/rest_framework/views.py in dispatch(self, request, *args, **kwargs) 
    464 
    465   except Exception as exc: 
--> 466    response = self.handle_exception(exc) 
    467 
    468   self.response = self.finalize_response(request, response, *args, **kwargs) 

~/lib/python3.4/site-packages/rest_framework/views.py in dispatch(self, request, *args, **kwargs) 
    461     handler = self.http_method_not_allowed 
    462 
--> 463    response = handler(request, *args, **kwargs) 
    464 
    465   except Exception as exc: 

~/views.py in get(self, request) 
    36 class RankedResults(APIView): 
    37  def get(self, request): 
---> 38   cardinal_instance = apps.get_model('warehouse', request.data['cardinal_type']).objects.get(reference_key=request.data['cardinal_key']) 
    39   serializer = LinkModelSerializer(cardinal_instance, context=request.data) 
    40   return Response(serializer.data) 

~/lib/python3.4/site-packages/django/utils/datastructures.py in __getitem__(self, key) 
    83    list_ = super(MultiValueDict, self).__getitem__(key) 
    84   except KeyError: 
---> 85    raise MultiValueDictKeyError(repr(key)) 
    86   try: 
    87    return list_[-1] 

MultiValueDictKeyError: "'cardinal_type'" 
+0

Можете ли вы поэтому напечатайте 'request.data' методом' get', используя первый подход? – AKS

+0

'------------------------------------------------ --------------------------- ' ' AttributeError Traceback (последний последний вызов) ' ' в () ' ' ----> 1 request.data' 'AttributeError: объект WSGIRequest 'не имеет атрибута' data'' –

+0

Однако я могу видеть строку запроса, когда смотрю' request .__ dict__' (это выдержка): '' QUERY_STRING ':' cardinal_type = Npis & cardinal_key = 1XXXXXX3 & terminal_type = Deas & candid_count = 2 ', ' –

ответ

0

Из документации request.data:

request.data returns the parsed content of the request body. This is similar to the standard request.POST and request.FILES attributes...

Так request.data доступен только тогда, когда вы передаете что-то в теле запроса в запросе POST, PUT или PATCH.

Но вы делаете GET запрос вместо:

request = factory.get('/v1/ranked_results/', json.dumps(options), 
         content_type='application/json') 

Для GET запроса правильный путь, чтобы получить строку запроса используется request.query_params:

request.query_params is a more correctly named synonym for request.GET .

For clarity inside your code, we recommend using request.query_params instead of the Django's standard request.GET . Doing so will help keep your codebase more correct and obvious - any HTTP method type may include query parameters, not just GET requests.

Таким образом, вы должны быть в состоянии получите доступ к следующему:

cardinal_type = request.query_params['cardinal_type'] 
# and similarly others 
+0

Спасибо @AKS, я решил это благодаря вашим комментариям, но я не сделал это точно так же, как вы описано. Вы правы в том, что атрибут 'data' недоступен в запросе' GET', однако бит об использовании 'query_params' не работает. Вместо этого я использовал запрос «POST», чтобы отправить запрос в тело запроса, и я смог получить доступ к атрибуту 'data' в' request'. –

+0

Не могли бы вы рассказать мне, в чем проблема, когда вы использовали запрос 'GET' с параметрами запроса? – AKS