Giter Club home page Giter Club logo

django-rest-framework-json-api's Introduction

JSON:API and Django REST framework

Tests Read the docs PyPi Version

Overview

JSON:API support for Django REST framework

By default, Django REST framework will produce a response like:

{
    "count": 20,
    "next": "https://example.com/api/1.0/identities/?page=3",
    "previous": "https://example.com/api/1.0/identities/?page=1",
    "results": [{
        "id": 3,
        "username": "john",
        "full_name": "John Coltrane"
    }]
}

However, for an identity model in JSON:API format the response should look like the following:

{
    "links": {
        "prev": "https://example.com/api/1.0/identities",
        "self": "https://example.com/api/1.0/identities?page=2",
        "next": "https://example.com/api/1.0/identities?page=3",
    },
    "data": [{
        "type": "identities",
        "id": "3",
        "attributes": {
            "username": "john",
            "full-name": "John Coltrane"
        }
    }],
    "meta": {
        "pagination": {
          "count": 20
        }
    }
}

Goals

As a Django REST framework JSON:API (short DJA) we are trying to address following goals:

  1. Support the JSON:API spec to compliance

  2. Be as compatible with Django REST framework as possible

    e.g. issues in Django REST framework should be fixed upstream and not worked around in DJA

  3. Have sane defaults to be as easy to pick up as possible

  4. Be solid and tested with good coverage

  5. Be performant

Requirements

  1. Python (3.8, 3.9, 3.10, 3.11, 3.12)
  2. Django (4.2, 5.0)
  3. Django REST framework (3.14, 3.15)

We highly recommend and only officially support the latest patch release of each Python, Django and REST framework series.

Generally Python and Django series are supported till the official end of life. For Django REST framework the last two series are supported.

For optional dependencies such as Django Filter only the latest release is officially supported even though lower versions should work as well.

Installation

Install using pip...

$ pip install djangorestframework-jsonapi
$ # for optional package integrations
$ pip install djangorestframework-jsonapi['django-filter']
$ pip install djangorestframework-jsonapi['django-polymorphic']
$ pip install djangorestframework-jsonapi['openapi']

or from source...

$ git clone https://github.com/django-json-api/django-rest-framework-json-api.git
$ cd django-rest-framework-json-api
$ pip install -e .

and add rest_framework_json_api to your INSTALLED_APPS setting below rest_framework.

INSTALLED_APPS = [
    ...
    'rest_framework',
    'rest_framework_json_api',
    ...
]

Running the example app

It is recommended to create a virtualenv for testing. Assuming it is already installed and activated:

$ git clone https://github.com/django-json-api/django-rest-framework-json-api.git
$ cd django-rest-framework-json-api
$ pip install -Ur requirements.txt
$ django-admin migrate --settings=example.settings
$ django-admin loaddata drf_example --settings=example.settings
$ django-admin runserver --settings=example.settings

Browse to

Usage

rest_framework_json_api assumes you are using class-based views in Django REST framework.

Settings

One can either add rest_framework_json_api.parsers.JSONParser and rest_framework_json_api.renderers.JSONRenderer to each ViewSet class, or override settings.REST_FRAMEWORK

REST_FRAMEWORK = {
    'PAGE_SIZE': 10,
    'EXCEPTION_HANDLER': 'rest_framework_json_api.exceptions.exception_handler',
    'DEFAULT_PAGINATION_CLASS':
        'rest_framework_json_api.pagination.JsonApiPageNumberPagination',
    '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_json_api.renderers.BrowsableAPIRenderer',
    ),
    'DEFAULT_METADATA_CLASS': 'rest_framework_json_api.metadata.JSONAPIMetadata',
    'DEFAULT_FILTER_BACKENDS': (
        'rest_framework_json_api.filters.QueryParameterValidationFilter',
        'rest_framework_json_api.filters.OrderingFilter',
        'rest_framework_json_api.django_filters.DjangoFilterBackend',
        'rest_framework.filters.SearchFilter',
    ),
    'SEARCH_PARAM': 'filter[search]',
    'TEST_REQUEST_RENDERER_CLASSES': (
        'rest_framework_json_api.renderers.JSONRenderer',
    ),
    'TEST_REQUEST_DEFAULT_FORMAT': 'vnd.api+json'
}

This package provides much more including automatic inflection of JSON keys, extra top level data (using nested serializers), relationships, links, paginators, filters, and handy shortcuts. Read more at https://django-rest-framework-json-api.readthedocs.org/

django-rest-framework-json-api's People

Contributors

abdulhaq-e avatar alig1493 avatar amw avatar anton-shutik avatar arttuperala avatar bor3ham avatar bpleshakov avatar chrisclark avatar dependabot[bot] avatar gaker avatar jamesturk avatar jarekwg avatar jerel avatar jsenecal avatar leo-naeka avatar lucacorti avatar martinmaillard avatar mblayman avatar n2ygk avatar nattyg93 avatar platinumazure avatar pyup-bot avatar safaalfulaij avatar santiavenda2 avatar schtibe avatar scottfisk avatar sliverc avatar tbartelmess avatar tenzer avatar tpilara avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

django-rest-framework-json-api's Issues

Automated testing

I have made a pull request #66 in which I included automated testing. This will hopefully satisfy the requirement for the v2.0 roadmap #40. A few notes regarding the PR:

1- I have used pytest as the test tool. It's better imo than Django Testing. The original tests have not been touched as py.test is compatible with them. Nonetheless, if you don't like it, automation can be done without it and I can edit the PR (see the examples on the pytest website)

2- pytest is able to generate a runtests script which I did, so python runtests.py would run the tests without the user invoking py.test manually.

3- Tox has been used to test different environments. It's a great tool for such purposes and it goes well with pytest but it's not a necessary. (see docs).

Now this is the worse part:

4- If we're to test a combination of python2.7, python3.2, python3.3, python3.4, django1.6, django1.7, django1.8, drf2.4, drf3.0, drf3.1, drf3.2 Then only a handful of these tests will pass. Anything other than python{27,33,34}, django18, drf{31,32} will FAIL. I had a quick look at the failures, some of them are easy to fix and some are not. However, I didn't fix anything in order to follow a branch per feature process.

Finally, thanks to drf-json-api as I got the idea for automated testing from it. That project is identical to this but this is more mature and active and the code is quite clean (despite the fact that the above tests all work in drf-json-api excluding drf2.4) . Sad fragmentation in OSS.

Included compound documents are being included recursively

On a model having a relation with itself, including the relation will have the consequence of including the fields in the relation itself.

http://localhost:8000/api/contacts/organizations/534?format=vnd.api%2Bjson&include=resellers,people&fields[organization]=resellers,people&fields[person]=full_name

{
    "data": {
        "type": "organization",
        "id": "534",
        "attributes": {},
        "relationships": {
            "resellers": {
                "data": [
                    {
                        "type": "organization",
                        "id": "4"
                    }
                ],
                "links": {
                    "self": "http://api-dev.zerofail.com:8080/api/contacts/organizations/534/relationships/resellers?format=vnd.api%2Bjson",
                    "related": "http://api-dev.zerofail.com:8080/api/contacts/organizations/534/resellers?format=vnd.api%2Bjson"
                },
                "meta": {
                    "count": 1
                }
            },
            "people": {
                "data": [
                    {
                        "type": "person",
                        "id": "628"
                    },
                    {
                        "type": "person",
                        "id": "1022"
                    }
                ],
                "links": {
                    "self": "http://api-dev.zerofail.com:8080/api/contacts/organizations/534/relationships/people?format=vnd.api%2Bjson",
                    "related": "http://api-dev.zerofail.com:8080/api/contacts/organizations/534/people?format=vnd.api%2Bjson"
                },
                "meta": {
                    "count": 2
                }
            }
        },
        "links": {
            "self": "http://api-dev.zerofail.com:8080/api/contacts/organizations/534?format=vnd.api%2Bjson"
        }
    },
    "included": [
        {
            "type": "organization",
            "id": "4",
            "attributes": {},
            "relationships": {
                "resellers": {
                    "data": [
                        {
                            "type": "organization",
                            "id": "504"
                        }
                    ],
                    "links": {
                        "self": "http://api-dev.zerofail.com:8080/api/contacts/organizations/4/relationships/resellers?format=vnd.api%2Bjson",
                        "related": "http://api-dev.zerofail.com:8080/api/contacts/organizations/4/resellers?format=vnd.api%2Bjson"
                    },
                    "meta": {
                        "count": 1
                    }
                },
                "people": {
                    "data": [
                        {
                            "type": "person",
                            "id": "111"
                        },
                        {
                            "type": "person",
                            "id": "1783"
                        },
                        {
                            "type": "person",
                            "id": "1985"
                        },
                        {
                            "type": "person",
                            "id": "648"
                        },
                        {
                            "type": "person",
                            "id": "421"
                        },
                        {
                            "type": "person",
                            "id": "353"
                        }
                    ],
                    "links": {
                        "self": "http://api-dev.zerofail.com:8080/api/contacts/organizations/4/relationships/people?format=vnd.api%2Bjson",
                        "related": "http://api-dev.zerofail.com:8080/api/contacts/organizations/4/people?format=vnd.api%2Bjson"
                    },
                    "meta": {
                        "count": 6
                    }
                }
            },
            "links": {
                "self": "http://api-dev.zerofail.com:8080/api/contacts/organizations/4?format=vnd.api%2Bjson"
            }
        },
        {
            "type": "person",
            "id": "1022",
            "attributes": {
                "full_name": "Neppie Abernathy"
            },
            "links": {
                "self": "http://api-dev.zerofail.com:8080/api/contacts/people/1022?format=vnd.api%2Bjson"
            }
        },
        {
            "type": "person",
            "id": "111",
            "attributes": {
                "full_name": "Mrs. Ernestine Bartoletti"
            },
            "links": {
                "self": "http://api-dev.zerofail.com:8080/api/contacts/people/111?format=vnd.api%2Bjson"
            }
        },
        {
            "type": "person",
            "id": "1783",
            "attributes": {
                "full_name": "Adrain Franecki"
            },
            "links": {
                "self": "http://api-dev.zerofail.com:8080/api/contacts/people/1783?format=vnd.api%2Bjson"
            }
        },
        {
            "type": "person",
            "id": "1985",
            "attributes": {
                "full_name": "Mr. Dandre Haley"
            },
            "links": {
                "self": "http://api-dev.zerofail.com:8080/api/contacts/people/1985?format=vnd.api%2Bjson"
            }
        },
        {
            "type": "person",
            "id": "353",
            "attributes": {
                "full_name": "Mrs. Harlene VonRueden"
            },
            "links": {
                "self": "http://api-dev.zerofail.com:8080/api/contacts/people/353?format=vnd.api%2Bjson"
            }
        },
        {
            "type": "person",
            "id": "421",
            "attributes": {
                "full_name": "Miss Jasmin Kutch"
            },
            "links": {
                "self": "http://api-dev.zerofail.com:8080/api/contacts/people/421?format=vnd.api%2Bjson"
            }
        },
        {
            "type": "person",
            "id": "628",
            "attributes": {
                "full_name": "Dr. Beckham Abernathy"
            },
            "links": {
                "self": "http://api-dev.zerofail.com:8080/api/contacts/people/628?format=vnd.api%2Bjson"
            }
        },
        {
            "type": "person",
            "id": "648",
            "attributes": {
                "full_name": "Creed Kuhn"
            },
            "links": {
                "self": "http://api-dev.zerofail.com:8080/api/contacts/people/648?format=vnd.api%2Bjson"
            }
        }
    ]
}

Handling Side-Loading Nicely

Hi all. I wanted to get a little discussion going about how to handle side-loading nicely. After mucking around in the DRF and DJA internals for a bit, I've come to the conclusion that the Renderer is the right place to handle this (mostly).

Below is an example of a JSONRenderer that examines a sideloads dictionary on an APIView or ViewSet instance. It only supports ModelSerializer subclasses for use sideloading.

My use of an additional query for all of the related ids is a hack. Ideally, this should work "in Python" without touching the database again. That way, one can use select_related and prefetch_related in the view queryset (e.g. MyModel.objects.select_related('some_o2o').prefetch_related('some_m2m').all()) for performance tuning without changing the semantics.

It also assumes the use of PrimaryKeyRelatedField, and doesn't yet support hyperlinks. That shouldn't be too hard to add.

Simplistic, but it's a place to start. Thoughts?

class MyViewSet(ModelViewSet):
    sideloads = { 'foo': FooSerializer, 'bar': BarSerializer }
    ...
class SideloadingJSONRenderer(JSONRenderer):
    def render(self, data, accepted_media_type=None, renderer_context=None):
        view = renderer_context.get('view')
        queryset = view.get_queryset()
        resource_name = get_resource_name(view)

        # for various reasons, we probably don't want to sideload in this case.
        if resource_name == False:
            return super(JSONRenderer, self).render(
                data, accepted_media_type, renderer_context)

        try:
            content = data.pop('results')
            resource_name = format_resource_name(content, resource_name)
            data = {resource_name: content, "meta": data}
        except (TypeError, KeyError, AttributeError) as e:

            # Default behavior
            if not resource_name == 'data':
                format_keys(data, 'camelize')
                resource_name = format_resource_name(data, resource_name)

            data = {resource_name: data}

        # Sideloading
        for attr, serializer in view.sideloads.items():
            rel_objs = getattr(serializer.Meta, 'model') \
                .objects \
                .filter(id__in=queryset.values_list(attr, flat=True).distinct())

            rel_data = serializer(rel_objs, many=True).data
            rel_name = format_resource_name(rel_data, attr)

            data.update({rel_name: rel_data})

        data = format_keys(data, 'camelize')

        return super(JSONRenderer, self).render(
            data, accepted_media_type, renderer_context)

Serializer `resource_name` for nested objects is not used

Nested serializers don't use resource_name for the type. In the example below the type for the child would show something like nestedmodels.

class NestedSerializer(serializers.ModelSerializer):
    resource_name = "pretty_nested"
    class Meta:
        model = NestedModel

class ParentSerializer(serializers.ModelSerializer):
    nested = NestedSerializer(source='some_field', many=True)
    class Meta:
        model = ParentModel

Looking at the code it looks like in utils.py the only function that performs the correct checks to get the type is get_resource_name(). All the other functions simply do something like relation_type = inflection.pluralize(relation_model.__name__).lower() without checking for a resource_name on the serializer as the docs suggest. I could submit a PR if it makes sense to put these checks in place everywhere. Ideally a function like get_related_resource_type() should act as a getter for type so that we don't see these inconsistencies.

ResourceRelatedField is too rigid for type validation

Opening this on behalf of @martinmaillard (#122 (comment))

ResourceRelatedField is way too rigid when it checks the valid type. It gets the expected type from the queryset, which means that:

  • it's not possible to customize the type of a relation (what is allowed by the resource_name on the serializer)
  • it's not possible to allow multiple types to be valid for one relation (useful with polymorphic relationships)

Including data fails on lists

If the inclusion parameter is used in retrieving a list of models, every relation is only considered once (utils.py line 417 removes the relation once)

        try:
            included_resources.remove(field_name)
            new_included_resources = [key.replace('%s.' % field_name, '', 1) for key in included_resources]
            relation_instance_or_manager = getattr(resource_instance, field_name)
            serializer_data = resource.get(field_name)
        except ValueError:
            # Skip fields not in requested included resources
            continue

Example:

Assuming table Person:

pk fruit (fk to Fruit) Person
1 1 John
2 1 Jack
3 2 Jill

and table Fruit:

pk name
1 apple
2 banana

Now let's do GET /api/v1/person?include=fruit which is programmed to return all persons

Gives roughly following result (I wrote this from memory ):

    {
        "data": [{
                "type": "persons",
                "id": 1,
                "attributes": { "name": "John" },
                "relationships": {
                    "fruit": {
                        "data": { "type": "fruit", "id": "1" }
                    }
                }
            },
            {
                "type": "persons",
                "id": 2,
                "attributes": { "name": "Jack" },
                "relationships": {
                    "fruit": {
                        "data": { "type": "fruit", "id": "1" }
                    }
                }
            },
            {
                "type": "persons",
                "id": 3,
                "attributes": { "name": "Jill" },
                "relationships": {
                    "fruit": {
                        "data": { "type": "fruit", "id": "2" }
                    }
                }
            }
        # so far so good, now here it gets interesting
        ],
        "included": [{
            "type": "fruit",
            "id": 1,
            "attributes": {
                "name": "apple"
            }
        }]
        # banana is missing, even though the person 3 references it
    },

Rename package

We made a mistake when initially naming this package with making it so EmberJS-centric.

With a 2.0 release, let's include what is currently in master, plus renaming. To maintain backwards compatibility and make things not too difficult, we can do a 1.6 release with what is in master as well.

Package naming suggestions, as well as thoughts/concerns are appreciated.

Handling errors from Django

Hi,

How would one handle errors sent via the server from Django Rest Framework when using Rest Framework Ember? Could someone please assist me with pointing me in the right direction as to how exactly this is done?

PS. I am quite new to Ember and Django Rest Framework :)

Thanks!

Confusing validation error when submitting JSON in Django REST API format

Thanks for creating this project! I really like both Django REST Framework and the design of jsonapi.org, so being able to use them together is excellent.

However, with the current 2.0.0a2 release, I found an interesting problem with the interaction between DRFJA's validation errors, and DRF's presentation of those errors through the browsable API: encode/django-rest-framework#3446

What appears to be happening is that when a create request is submitted using DRF's default format (i.e. an attribute mapping at the top level of the JSON request), DRFJA is raising ValidationError with the details attribute set to a list, which DRF then attempts to treat as a mapping.

Assuming I've identified the origins of the error correctly, If the exception details stored a mapping instead, that could both report the field name that was expected, as well as avoid the secondary failure in DRF's error handling.

sideloaded models (compound documents)

class PostSerializer(serializers.ModelSerializer):
    class Meta:
        model = Post
        fields = ('name', 'comments')

    comments = CommetSerializer(many=True,read_only=True)

This works properly for http://127.0.0.1:8000/posts/1 (ie has the appropriate comments in included at the top level).

However http://127.0.0.1:8000/posts returns the error 'NoneType' object has no attribute 'comments'.

Issue with relationships in POST/PATCH/PUT requests

If I do a POST request with data containing a relationship:

{
    "data": {
        "type": "comments",
        "attributes": {
            "body": "Hi"
        },
        "relationships": {
            "entry": {
                "data": {
                    "id": "12",
                    "type": "entries"
                }
            }
        }
    }
}

I get the following error:

{
    "errors": [{
        "status": "400",
        "source": {"pointer": "/data/attributes/entry"}, 
        "detail": "Incorrect type. Expected pk value, received OrderedDict."
    }]
}

I understand why this is happening, but I think this is a mistake.

RelationshipView in serializer

I have set up a RelationshipView view with a working url. How do I specify in a serializer to use that url for the relationship?

comments = serializers.HyperlinkedIdentityField(view_name='post-relationships',lookup_field='comments')

throws

Could not resolve URL for hyperlinked relationship using view name "post-relationships". You may have failed to include the related model in your API, or incorrectly configured the `lookup_field` attribute on this field.`

I'd be happy to update the documentation.

Including data fails on a List endpoint

/api/widgets?include=parent_widget

rest_framework_json_api/utils.py", line 419, in extract_included
    relation_instance_or_manager = getattr(resource_instance, field_name)
AttributeError: 'NoneType' object has no attribute 'parent_widget'

301 Moved Permanently Response

The RESTAdapter doesn't append a slash "/" to the end of collection requests, which Django REST is expecting. I have worked around the problem by overwriting the buildURL method of the RESTAdapter using the following:

buildURL: function(type, id) {
    var url = this._super(type, id);
    if (url.charAt(url.length -1) !== '/') {
        url += '/';
    }
    return url;
}

Is it possible to change the API behaviour within the scope of this package? If not, then it would seem this RESTAdapter change is required.

Python 3 compatibility

Hi,

I ran into a python 3 issue with the use of basestring, but upon checking utils.py in master I see it's already fixed. Is there a plan to do a bug-fix release soon?

Feedback requested: what version of Django REST Framework do you use?

How many of you are using DRF 3.0.x or 2.4.x? Since the JSON API spec just recently stabilized on 1.0 I'm guessing that most JSON API compliant APIs will be built using one of the latest versions of DRF.

If that is the case I would like to just support DRF 3.1 and newer. Django (1.6+) and Python (2.7+) support should remain unchanged.

Incorrect type. Expected pk value, received OrderedDict when updating a relationship

I get this error:

{'errors': [{'detail': 'Incorrect type. Expected pk value, received '
                   'OrderedDict.',
         'source': {'pointer': '/data/attributes/title'},
         'status': '400'}]}

since commit d654c75 when I run my unit test. The data I send is

    data = {
        "data": {
            "type": "customers",
            "id":   self.lukeId,
            "relationships": {
                "title": {
                    "data": { "type": "titles", "id": "2" }
                }
            }
        }
    }

What is that change for / is my data wrong?

Document ResourceRelatedField

One needs to use ResourceRelatedField (from rest_framework_json_api.relations) for the relationship fields to be able to parse incoming Resource Identifier Objects.
#111 and #122 are both issues created because of that lack of documentation

POST with nested relationships isn't functional

DRF expects nested objects to be dictionaries, and we're passing them as Strings (the id)

screen shot 2015-08-18 at 5 46 34 pm

Doesn't appear there are any tests for this either. Anyone working this? I'll throw up a PR if not.

get_related_resource_type returns parent model name

This is a simplified version of my API:

class Survey(models.Model):
    pass

class Question(models.Model):
    survey = models.ForeignKey(Survey, related_name='questions')

class SurveySerializer(serializers.ModelSerializer):
    class Meta:
        model = Survey
        fields = ('id', 'questions')
        read_only_fields = ('questions',)

class SurveyViewSet(viewsets.ModelViewSet):
     queryset = Survey.objects.all()
     serializer_class = SurveySerializer

Now if I request a survey, the type of the questions is not correct :

>>> GET '/surveys/1'
{
    'data': {
        'id': '1',
        'type': 'surveys',
        'attributes': {},
        'relationships': {
            'questions': {
                'data': [
                    {
                        'id': '1',
                        'type': 'surveys',
                        'attributes': {},
                    },
                ],
            },
        },
    },
}

The issue seems to be in utils.get_related_resource_type. I would submit a PR, but I'm not entirely sure how to fix it without breaking something else...

Also, I'm guessing that it will be necessary to be able to define a resource_name for related models and the relationships handling code will change, but this is still a big issue.

When using the ListCreateApiView, the action has to be differentiated

I had the problem that I couldn't use the generics.ListCreateAPIView for both, getting a list of all models and posting a new model.

If I set the action to 'list', I could correctly deliver a JsonAPI list of all the models; but when posting, the JSONRenderer had a problem on line 63 (results = (data["results"] if isinstance(data, dict) else data)) because there is no 'results' in the data dict.

However, when I removed the action = 'list' from my view, the post was done correctly, but the list of models wasn't in JsonAPI.

I now set up my view like this:

class AddressList(generics.ListCreateAPIView):
    """List of addresses"""
    serializer_class = models.AddressSerializer

    def get(self, request, *args, **kwargs):
        self.action = 'list'
        return super(AddressList, self).get(request, *args, **kwargs)

        [...]

which works, but I wonder, if this is the best way to go...

'ReturnList' object has no attribute 'get'

I am using the develop branch and get this in return when accessing a list:

'ReturnList' object has no attribute 'get'

                json_api_included.extend(included)
        else:
            json_api_data = data
    # Make sure we render data in a specific order
    render_data = OrderedDict()
                if data.get('links'): ...
        render_data['links'] = data.get('links')
    render_data['data'] = json_api_data
    if len(json_api_included) > 0:
        # Iterate through compound documents to remove duplicates

With the actual variable being

data
[OrderedDict([(u'id', 2), (u'user', 2), (u'slug', u'asdfasdfsadf'),...])]

Using the latest DRF @ 18c93912180aa93d1120495d4f7e47d7bed025f4

I am using a ModelViewSet and the detail view works.

Basic ember setup

Hej guys,

I'm fully new to ember and drf, so I hope I won't bother. I had some problems getting used to the available adapters, so I was lucky when I found your app. I used your example to integrate it. I got the basic api running, which returns a queryset.

{
    "meta": {
        "next": null, 
        "next_link": null, 
        "page": 1, 
        "previous": null, 
        "previous_link": null, 
        "count": 1, 
        "total": 1
    }, 
    "recipe": [
        {
            "pk": 1, 
            "name": "Foo", 
            "ingredients": [
                2, 
                1
            ], 
            "portions": 1, 
            "date_created": "2014-12-30T20:54:32.523138Z", 
            "date_updated": "2014-12-30T20:54:32.523138Z"
        }
    ]
}

Now I had to setup ember like that to make use of the api response:

    App = Ember.Application.create();

    App.Router.map(function() {
        this.resource('index', {path: '/'});
    });

    App.IndexRoute = Ember.Route.extend({
        model: function() {
            return $.getJSON('/api/recipes').then(function(data) {
                return data.recipe;
            });
        }
    });

Now I would like to ask, if there's any way to generate the ember routes dynamically?
Do you know an app example, which includes the ember setup and which helps greenhorns like me to kickstart their ember-drf experience?

Thanks a lot!

format_keys should use OrderedDict

When having REST_EMBER_FORMAT_KEYS=True in settings the order of the fields are not in the same order as defined on the serializer. This should be easily fixed by using rest_framework.compat.OrderedDict in rest_framework.utils.py line 52.

Customizing relationships

Problem

extract_relationships should be able to handle specialized cases, like allowing a links or meta object instead of a data object. For example, in our application it needs to:

  1. recognize other field types as relationships
  2. return a links object instead of a data object with type/id

The relationship data is read-only.

Desired

screen shot 2015-08-04 at 3 28 42 pm

Potential solutions

@jerel suggested a serializer hook like add_relationships for customization.

According to the spec, under relationships you must include a links, data, or meta object. http://jsonapi.org/format/#document-resource-object-relationships. The current extract_relationships just allows for a data object. It would be valuable for users to be able to have a links object, instead.

Better error handling

Hey,

So recently I started looking at improving error handling in a project that uses this and I realized that currently if I do e.g. raise BadRequest('You've got an error'), the response might look like:

{
    'someResourceName': {
        'detail': 'You've got an error'
    }
}

when in fact what I want is:

{
    'error': {
        'message': 'You've got an error'
    }
}

So the second part is easily changed by implementing a custom exception handler but the first part which is important because I want to implement consistent error responses site-wide, is not so intuitive to solve.

Do you guys have any ideas on how we can override the resource name automatically to handle error responses?

The only other way so far I know to do it is to manually add self.resource_name = 'error' before raising an error, but that seems like an ugly thing to have to add in my code.

Will keep looking into it...

Cheers,
-Eric

Overriding built-in actions

I'm overriding the .list() action on my ModelViewSet with a bit of custom logic. However, when I do so I lose the 'emberfication' provided by this library (i.e., no 'meta.). I have both the defaults set, and also added the parser/renderer classes directly for good measure -- no such luck. If I remove the custom action all returns as I expect.

Perhaps an exception is being returned per lines 28:29 of the renderer and so it's breaking silently?

How to filter relationship?

If I am getting the details of an object (e.g. Build), which has a one-to-many relationship with another object (e.g. BuildArtifact), how do I limit what related objects are listed in the relationships key?

For example, I if BuildArtifact.type can be both PUBLIC and PRIVATE, and if I want to show certain users only the PUBLIC relationships. To do this, I have to access the request.user, and adjust the relationship queryset.

Attempting to do this in the ModelSerializer.get_fields() method seems like it should work, but doesn't because the extract_relationships() method used to build the response JSON object goes directly the model instance and queries the relationship directly. (See this section of extract_relationships() for the relevant code).

Json-API in combination of django-rest-framework-jwt

In my unit tests, when I do a post, I get the following error:

======================================================================
ERROR: test_get_one_vehicletype_in_french (masterdata.tests.VehicleTypeTest)
----------------------------------------------------------------------
Traceback (most recent call last):
  File "/usr/src/app/cardispo2/masterdata/tests.py", line 206, in setUp
    super(VehicleTypeTest, self).setUp()
  File "/usr/src/app/cardispo2/cardispo2/test_helper.py", line 83, in setUp
    self.client.login(username='tester', password='blabla')
  File "/usr/src/app/cardispo2/cardispo2/test_helper.py", line 57, in login
    response = self.post('/api/v1/auth/login', {"username": username, "password": password})
  File "/usr/local/lib/python3.4/site-packages/rest_framework/test.py", line 168, in post
    path, data=data, format=format, content_type=content_type, **extra)
  File "/usr/local/lib/python3.4/site-packages/rest_framework/test.py", line 89, in post
    data, content_type = self._encode_data(data, format, content_type)
  File "/usr/local/lib/python3.4/site-packages/rest_framework/test.py", line 64, in _encode_data
    ret = renderer.render(data)
  File "/usr/local/lib/python3.4/site-packages/rest_framework_json_api/renderers.py", line 36, in render
    resource_name = utils.get_resource_name(renderer_context)
  File "/usr/local/lib/python3.4/site-packages/rest_framework_json_api/utils.py", line 32, in get_resource_name
    view = context.get('view')
AttributeError: 'NoneType' object has no attribute 'get'

If you look at the last three steps in the trace, there is ret = renderer.render(data) which calls rest_framework_json_api.JSONRenderer.render without a renderer_context argument. It then passes None (default value) to rest_framework_json_api.utils.get_resource_name where it tries to call view = context.get('view') on None... which of course can't work.

My DRF settings:

REST_FRAMEWORK = {
    'PAGINATE_BY': 30,
    'PAGINATE_BY_PARAM': 'page_size',
    'MAX_PAGINATE_BY': 100,
    #'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.MultiPartParser',
    ),
    'DEFAULT_RENDERER_CLASSES': (
        'rest_framework_json_api.renderers.JSONRenderer',
        'rest_framework.renderers.BrowsableAPIRenderer',
    ),
    'TEST_REQUEST_RENDERER_CLASSES': (
        'rest_framework_json_api.renderers.JSONRenderer',
    ),
    'DEFAULT_AUTHENTICATION_CLASSES': (
        'rest_framework_jwt.authentication.JSONWebTokenAuthentication',
    ),
    'DEFAULT_PERMISSION_CLASSES': (
        'rest_framework.permissions.IsAuthenticated',
    ),
    'EXCEPTION_HANDLER': 'cardispo2.exceptions.custom_exception_handler',
    'TEST_REQUEST_DEFAULT_FORMAT': 'json'
}

[bug] Using swagger for API docs

Problem

We use Swagger for API documentation. https://github.com/marcgibbons/django-rest-swagger. There are two problems when trying to use this package in conjunction with Swagger.

  1. Since the package enforces the media-type 'application/vnd.api+json', it will cause Swagger to throw an error. {"errors":{"detail":"Could not satisfy the request Accept header."}} I've worked around this in the past using custom content negotiation.

screen shot 2015-08-10 at 4 08 12 pm

  1. Provided the first issue is fixed, Swagger hangs with a fetching resource list message.
    screen shot 2015-08-10 at 4 12 21 pm

Potential Solutions

I haven't dug into this much, but one thing I've noticed is that this package is looking for views to have an action. I assume this means even SwaggerViews need an action. If I add an action to the SwaggerView, it still hangs.

PyPi package name is wrong in the docs

The line

pip install rest_framework_json_api

should be pip install djangorestframework-jsonapi

Either that or rename the PyPi package name to conform with the documentation.

v2.0 (JSON API support) roadmap

This is a meta issue to track the tasks that need to be completed before we ship a stable version.

  • format simple serializers as { data: { type, id, attributes } for GET and POST
  • change the root key from data to error if HTTP status is >= 300
  • format exceptions (validation errors, etc) to match the spec. #44
  • write beginner friendly documentation for Read The Docs. #28
  • automate testing against each Python version >= 2.7 and DRF >= 2.4 #67
  • add an example blog app with fixtures that can be used for tests and as an example #42
  • add tests for more complex serializer scenarios (relationships, included)

If you are working on an item create an issue for it and ask to be assigned. I will update the list item with a link to it.

For general questions or help visit Gitter

Parser does not correctly convert to underscores in case where numbers are a word boundary

Say we have a model with the following field:

address_1 = models.CharField(max_length=255)

The Renderer will correctly transform that field to address1. If we send back an address1 field in a POST/PUT, however, our model will fail to validate, complaining that address_1 is missing.

This is due to a limitation of inflection, which does not consider numbers to be word boundaries when underscoring. I'm not sure whether that's an "issue" from the perspective of the inflection maintainers (presumably most users are fine with this behavior, since no one seems to be complaining about it). It does pose a problem for this library, however: field names that are perfectly acceptable to Django will result in obtuse errors in rest_framework_ember.

There's a few possible solutions -- we could wrap underscore() with some extra regex magic to find numbers, for instance. That would break field names like 5spooky3me = BooleanField(), however, so it'd have to be a switch. If nothing else, it'd be worth a loud callout in the README, to steer users away from names like address_1 in their models when using rest_framework_ember.

Sideloading Resources

Hi there,

I am using rest_framework_ember and am at the point where I'd like to start side loading resources.

I am using viewsets to setup my resource endpoints:

class DocumentViewSet(viewsets.ModelViewSet):
    """
    API endpoint that allows users to be viewed or edited.
    """
    queryset = Document.objects.all()
    serializer_class = DocumentSerializer

class ContactViewSet(viewsets.ModelViewSet):
    """
    API endpoint that allows users to be viewed or edited.
    """
    queryset = Contact.objects.all()
    serializer_class = ContactSerializer

A document model has a relationship with a contact model so in order to sideload this data I'd have to somehow get something like this:

{
  "document": {
    "id": 1,
    "title": "This is a document",
    "contacts": [1, 2]
  },

  "contacts": [{
    "id": 1,
    "first_name": "my name"
  },
  {
    "id": 2,
    "first_name": "your name"
  }]
}

Can this be accomplished using rest_framework_ember? I saw that there is a section in the readme about side loading relationships but it didn't exactly makes sense when it comes to my problem.

Any help in this regard would be greatly appreciated.

Thanks!

What format is expected when doing a post?

I am trying to migrate to this json api format, when using rest_auth https://github.com/Tivix/django-rest-auth

Everything works with the standard DRF, but blocks when using drf-json-api. I tracked it down to having an empty self.request.DATA in my post method, which leads me to think that I might have to encapsulate my post request data?

Anyhow, some docs on this would be great!

how to implement Sparse Fieldsets?

I would like the Sparse Fieldsets feature:
http://jsonapi.org/format/#fetching-sparse-fieldsets

I tried to implement it and thought creating a custom serializer and override rest_framework.serializers.Serializer.fields property.

However, we need the resource_name to get the fields[TYPE] query parameter value (TYPE=resource_name).
I find rest_framework_json_api.utils.get_resource_name, however its argument rendrerer_context is created after the serializer returns data. For example, in ListModelMixin, rendered_context is created in Response(serializer.data) at https://github.com/tomchristie/django-rest-framework/blob/8cae462b3a3d724cf83fd33572c0ea23f91f2278/rest_framework/mixins.py#L47-L48)

So how should we implement the Sparse Fieldsets feature?

Query params - Ember Data relationships

One of the key things for getting relationships to work with ember data is allowing for API calls like this:

If I have a shelf object that returns this:

{
    "shelf": {
        "id": 10, 
        "address": "123 City St, Capitol City, State, 1234", 
        "books": [11,12, 13,14]
     }
} 

The Ember Data model looks like this:

App.Shelf = DS.Model.extend({
    address: DS.attr(),
    books: DS.hasMany('book', {async: true})
});

Then the following endpoint will be called when referencing a child route like this:

this.modelFor('shelf').get('books');

Results in this API call:

http://localhost:9000/api/books?ids[]=11&ids[]=12&ids[]=13&ids[]=14

I am probably going to implement this myself as a mixin with the get_queryset method overridden to support this. Let me know if you are interested in a PR or can think of another architecture for achieving this.

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. 📊📈🎉

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google ❤️ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.