Skip to content

AttributeError: 'NoneType' object has no attribute 'get' #275

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Closed
z0al opened this issue Sep 8, 2016 · 12 comments
Closed

AttributeError: 'NoneType' object has no attribute 'get' #275

z0al opened this issue Sep 8, 2016 · 12 comments

Comments

@z0al
Copy link

z0al commented Sep 8, 2016

Hi, there

Since yesterday, I'm stuck on this error in my test script:

Creating test database for alias 'default'...
E
======================================================================
ERROR: test_create_account (api.accounts.tests.test_crud.AccountTests)
----------------------------------------------------------------------
Traceback (most recent call last):
  File "/app/api/accounts/tests/test_crud.py", line 22, in test_create_account
    response = self.client.post(url, data)
  File "/usr/local/lib/python3.5/site-packages/rest_framework/test.py", line 169, in post
    path, data=data, format=format, content_type=content_type, **extra)
  File "/usr/local/lib/python3.5/site-packages/rest_framework/test.py", line 90, in post
    data, content_type = self._encode_data(data, format, content_type)
  File "/usr/local/lib/python3.5/site-packages/rest_framework/test.py", line 65, in _encode_data
    ret = renderer.render(data)
  File "/usr/local/lib/python3.5/site-packages/rest_framework_json_api/renderers.py", line 419, in render
    view = renderer_context.get("view", None)
AttributeError: 'NoneType' object has no attribute 'get'

----------------------------------------------------------------------
Ran 1 test in 0.019s

FAILED (errors=1)
Destroying test database for alias 'default'...

Particularly, this is the line of my code causing the error:

response = self.client.post(url, data)

And this is the line of your code that responsible of the error

view = renderer_context.get("view", None)

Simply because renderer_context is None

All my test was passing before switching to this library (using default DRF renders)

Here is my configurations:

REST_FRAMEWORK = {
    'PAGE_SIZE': 5,
    'EXCEPTION_HANDLER': 'rest_framework_json_api.exceptions.exception_handler',
    'DEFAULT_PAGINATION_CLASS':
        'rest_framework_json_api.pagination.PageNumberPagination',
    'DEFAULT_PARSER_CLASSES': (
        'rest_framework_json_api.parsers.JSONParser',
        'rest_framework.parsers.FormParser',
        'rest_framework.parsers.MultiPartParser'
    ),
    'DEFAULT_RENDERER_CLASSES': (
        'rest_framework_json_api.renderers.JSONRenderer',
        'rest_framework.renderers.BrowsableAPIRenderer',
    ),
    'DEFAULT_METADATA_CLASS': 'rest_framework_json_api.metadata.JSONAPIMetadata',

    'TEST_REQUEST_RENDERER_CLASSES':(
        'rest_framework_json_api.renderers.JSONRenderer',
    ),
    'TEST_REQUEST_PARSER_CLASSES':(
        'rest_framework_json_api.parsers.JSONParser',
    ),
    'TEST_REQUEST_DEFAULT_FORMAT' : 'vnd.api+json',
}

Am I doing something wrong ?

@abdulhaq-e
Copy link
Contributor

Did you use the serialiazers and views provided by rest_frame_work_json_api?

@z0al
Copy link
Author

z0al commented Sep 9, 2016

@abdulhaq-e Oh, I wasn't using them.

I changed the code to use serializers from (from rest_framework_json_api import serializers ) and views from (from rest_framework_json_api import views).

This is the view:

from rest_framework_json_api import views

from .models import Account
from . import serializers


class AccountViewSet(views.ModelViewSet):
    """
    A viewset that provides the standard actions
    """
    serializer_class = serializers.AccountSerializer
    queryset = Account.objects.all()
    lookup_field = 'username'

And this is my serializer:

from rest_framework_json_api import serializers
from .models import Account
from . import validators
class AccountSerializer(serializers.ModelSerializer):
    """
    A serializer for `accounts.models.Account` model.
    """
    ...

But still, the same error occurs :/

BTW, it works perfectly in browser (and any client), and even works using original rest_framework views, and serializers. it only has testing problem when I switched to this library .

@abdulhaq-e
Copy link
Contributor

Just a wild guess, can you try:

response = self.client.post(url, data, content_type='application/vnd.api+json')

@abdulhaq-e
Copy link
Contributor

abdulhaq-e commented Sep 10, 2016

I think the solution I gave you in the previous comment will work.

In DRF test module, the render method of arenderer instance is being called here. As you can see, only data is passed to this method. There is no renderer_context which then raises an error when using drf-json-api renderer. I gave the JSONRenderer in DRF a quick look and it seems that it works fine if its render method isn't given a renderer_context. In fact, I think all renderers in DRF behave the same with regards to this aspect. Perhaps it was designed like this for testing purposes. They all have the following line in common:

renderer_context = renderer_context or {}

Anyway, if what I'm saying is correct, passing content_type directly to post leads to a different path where renderer.render is not called, see here

The tests written for this package either used the standard Django Unittest or pytest and some used the testing classes provided by DRF. For the latter, content_type was used when sending data.

@z0al
Copy link
Author

z0al commented Sep 10, 2016

@abdulhaq-e shouldn't 'TEST_REQUEST_DEFAULT_FORMAT' : 'vnd.api+json', do the same ???

Anyway, I tried it, it didn't raise the same error, but the test failed

        response = self.client.post(url, data, content_type='application/vnd.api+json')
>       self.assertEqual(response.status_code, status.HTTP_201_CREATED)
E       AssertionError: 400 != 201

Response body:

[{'source': {'pointer': '/data'}, 'status': '400', 'detail': 'JSON parse error - Expecting property name enclosed in double quotes: line 1 column 2 (char 1)'}]

data:

{'last_name': 'Ramirez', 'confirm_password': '#0trFCKmah', 'user_permissions': [], 'email':'maureen.ramirez@example.com', 'is_staff': False, 'is_superuser': False, 'username': 'fakeuser', 'last_login': None, 'first_name': 'Maureen', 'groups': [], 'password': '#0trFCKmah', 'id': None, 'date_joined': '2016-09-10T03:14:36.042945Z'}

@abdulhaq-e
Copy link
Contributor

Easy fix, you can't pass the data as a python object, you need to serialise it to json

import json
response = self.client.post(url, json.dumps(data), content_type='application/vnd.api+json')

@abdulhaq-e
Copy link
Contributor

shouldn't 'TEST_REQUEST_DEFAULT_FORMAT' : 'vnd.api+json', do the same ???

After inspecting DRF test module, no it's not the same. By passing content_type, the data is converted to bytestring without using a renderer.

@z0al
Copy link
Author

z0al commented Sep 10, 2016

@abdulhaq-e Now I'm confused :/

The original (using default DRF) test was:

def test_create_account(self):
    u = AccountFactory.build(username="fakeuser")
    s = AccountSerializer(u)
    data = s.data
    data['confirm_password'] = data['password']
    url = reverse('accounts:account-list')
    response = self.client.post(url, data)
    self.assertEqual(response.status_code, status.HTTP_201_CREATED)
    self.assertEqual(Account.objects.count(), 1)

There was no need to json.dumps(data) thing, why we need it here ?

@z0al
Copy link
Author

z0al commented Sep 10, 2016

@abdulhaq-e Btw, using json.dumps(data) will raise:

400: Received document does not contain primary data

So, I have to manually put the data in JSON API format, before json.dumps it, something like

manually= {
    "data": {
        "type": "Account",
        "id": None,
        "attributes": data
    }
}

Then serialize it, and your test will finally pass :)

@abdulhaq-e
Copy link
Contributor

400: Received document does not contain primary data

For creating/updating resources, you have to send a resource object which has to exist under a top level member named data. This is a requirement in JSON API specs.

There was no need to json.dumps(data) thing, why we need it here ?

The renderer does that for you when passing format rather than content_type. To confirm this, go back to DRF serializers and views and try post or patch without json.dumps(data):

response = self.client.post(url, data, content_type='application/vnd.api+json')

An error will be raised.

I believe the renderer in DRF Json API can be enhanced to allow tests such as yours.

@z0al
Copy link
Author

z0al commented Sep 10, 2016

@abdulhaq-e I do agree that DRF JSON needs some enhancements regarding to testing

So, I will leave this issue open

Thanks @abdulhaq-e :)

@jerel
Copy link
Member

jerel commented Sep 26, 2016

I'm going to close this issue since it's a rather generic thread about test configuration. If you feel like we could implement something better with regards to renderer defaults then you're definitely welcome to open a PR or a more specific issue.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

3 participants