Skip to content

Commit

Permalink
v2.0.0-beta.2 -> v2.0.0
Browse files Browse the repository at this point in the history
* docs: note about importing serializers

* Document ResourceRelatedField and RelationshipView

* Updated pip install instructions for 2.0.0-beta.2

* Add LimitOffsetPagination

* Dont let the offset go into negative space

* Add basic unit test for LimitOffsetPagination

* Support deeply nested includes
Allow skipping of intermediate included models

* Add current tox.ini directory to PYTHONPATH in order to use imports form there
Fix regression on PY3 caused by unicode_literals

* [FEATURE]: support using get_serializer_class on view

* fixed extract_root_meta for lists

* Fixed get_resource_name in case of non-model backed serializer.

Closes #219

* ResourceRelatedField now accepts serializer methods when many=True

* Rename "suggested" posts to "featured" so we can use suggested as many=True

* Updated SerializerMethodResourceRelatedField to allow many=True

Issue #151
Closes #220

* Correct error responses for projects with different DRF-configurations (#222)

* [#214] Add error messages tests.

* [#214] Extract formatting DRF errors.

* Add example view with custom handle_exception.

* Use HTTP 422 for validation error responses.

* Add full example of class-configured json api view.

* Fixed naming that suggested settings were used to inflect relationship names.

JSON_API_FORMAT_RELATION_NAME actually inflected the `type` instead.
The relation name is not changable at this time although if it woudl
be useful to someone it would be fine to implement it.
Closes #136.

* Updated changelog

* Added a doc note to prefer setting resource_name on serializers or models.

Closes #207

* Added get_related_field_name method to RelationshipView

* Added get_related_field_name method to RelationshipView

* Added docs about field_name_mapping

* Updated the readme for testing (#234)

* Allow exception handler to be used by normal DRF views: (#233)

* Add top-level 'errors' object to non-JSON-API responses
* Allow configuring the exception handler to be used _only_
  in JSON API views or uniformly across all views

* Fix included resource type inconsistency (#229)

When setting `resource_name = None`, the related instance's resource
name is used in `relationships`, but `None` is used in `included`.

This is related to #94 and #124

* Fixes #230. Keep write only fields from having an attribute key

* Release v2.0.0

* Update setup.py to classify as production/stable
  • Loading branch information
jerel committed Apr 29, 2016
1 parent 8c4db64 commit b9ccd9d
Showing 31 changed files with 851 additions and 209 deletions.
3 changes: 3 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
@@ -30,3 +30,6 @@ pip-delete-this-directory.txt

# Tox
.tox/

# VirtualEnv
.venv/
14 changes: 14 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,4 +1,18 @@

v2.0.0

* Fixed bug where write_only fields still had their keys rendered
* Exception handler can now easily be used on DRF-JA views alongside regular DRF views
* Added `get_related_field_name` for views subclassing RelationshipView to override
* Renamed `JSON_API_FORMAT_RELATION_KEYS` to `JSON_API_FORMAT_TYPES` to match what it was actually doing
* Renamed `JSON_API_PLURALIZE_RELATION_TYPE` to `JSON_API_PLURALIZE_TYPES`
* Documented ResourceRelatedField and RelationshipView
* Added LimitOffsetPagination
* Support deeply nested `?includes=foo.bar.baz` without returning intermediate models (bar)
* Allow a view's serializer_class to be fetched at runtime via `get_serializer_class`
* Added support for `get_root_meta` on list serializers


v2.0.0-beta.2

* Added JSONAPIMeta class option to models for overriding `resource_name`. #197
9 changes: 7 additions & 2 deletions README.rst
Original file line number Diff line number Diff line change
@@ -80,7 +80,7 @@ From PyPI

::

$ pip install djangorestframework-jsonapi==2.0.0-beta.1
$ pip install djangorestframework-jsonapi


From Source
@@ -107,9 +107,14 @@ Browse to http://localhost:8000
Running Tests
^^^^^^^^^^^^^

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

::

$ python runtests.py
$ pip install -e .
$ pip install -r requirements-development.txt
$ py.test


-----
3 changes: 1 addition & 2 deletions docs/api.md
Original file line number Diff line number Diff line change
@@ -38,7 +38,7 @@ Gathers the data from serializer fields specified in `meta_fields` and adds it t

#### extract_root_meta

`extract_root_meta(serializer, resource, meta)`
`extract_root_meta(serializer, resource)`

Calls a `get_root_meta` function on a serializer, if it exists.

@@ -47,4 +47,3 @@ Calls a `get_root_meta` function on a serializer, if it exists.
`build_json_resource_obj(fields, resource, resource_instance, resource_name)`

Builds the resource object (type, id, attributes) and extracts relationships.

208 changes: 195 additions & 13 deletions docs/usage.md
Original file line number Diff line number Diff line change
@@ -34,6 +34,18 @@ record count and a `links` object with the next, previous, first, and last links
Pages can be selected with the `page` GET parameter. Page size can be controlled
per request via the `PAGINATE_BY_PARAM` query parameter (`page_size` by default).

### Serializers

It is recommended to import the base serializer classes from this package
rather than from vanilla DRF. For example,

```python
from rest_framework_json_api import serializers

class MyModelSerializer(serializers.ModelSerializers):
# ...
```

### Setting the resource_name

You may manually set the `resource_name` property on views, serializers, or
@@ -75,7 +87,10 @@ class Me(models.Model):
If you set the `resource_name` on a combination of model, serializer, or view
in the same hierarchy, the name will be resolved as following: view >
serializer > model. (Ex: A view `resource_name` will always override a
`resource_name` specified on a serializer or model)
`resource_name` specified on a serializer or model). Setting the `resource_name`
on the view should be used sparingly as serializers and models are shared between
multiple endpoints. Setting the `resource_name` on views may result in a different
`type` being set depending on which endpoint the resource is fetched from.


### Inflecting object and relation keys
@@ -143,20 +158,20 @@ Example - With format conversion set to `dasherize`:
}
```

#### Relationship types
#### Types

A similar option to JSON\_API\_FORMAT\_RELATION\_KEYS can be set for the relationship names:
A similar option to JSON\_API\_FORMAT\_KEYS can be set for the types:

``` python
JSON_API_FORMAT_RELATION_KEYS = 'dasherize'
JSON_API_FORMAT_TYPES = 'dasherize'
```

Example without format conversion:

``` js
{
"data": [{
"type": "identities",
"type": "blog_identity",
"id": 3,
"attributes": {
...
@@ -179,7 +194,7 @@ When set to dasherize:
``` js
{
"data": [{
"type": "identities",
"type": "blog-identity",
"id": 3,
"attributes": {
...
@@ -198,7 +213,7 @@ When set to dasherize:
It is also possible to pluralize the types like so:

```python
JSON_API_PLURALIZE_RELATION_TYPE = True
JSON_API_PLURALIZE_TYPES = True
```
Example without pluralization:

@@ -245,8 +260,168 @@ When set to pluralize:
}
```

Both `JSON_API_PLURALIZE_RELATION_TYPE` and `JSON_API_FORMAT_RELATION_KEYS` can be combined to
achieve different results.
### Related fields

Because of the additional structure needed to represent relationships in JSON
API, this package provides the `ResourceRelatedField` for serializers, which
works similarly to `PrimaryKeyRelatedField`. By default,
`rest_framework_json_api.serializers.ModelSerializer` will use this for
related fields automatically. It can be instantiated explicitly as in the
following example:

```python
from rest_framework_json_api import serializers
from rest_framework_json_api.relations import ResourceRelatedField

from myapp.models import Order, LineItem, Customer


class OrderSerializer(serializers.ModelSerializer):
class Meta:
model = Order

line_items = ResourceRelatedField(
queryset=LineItem.objects,
many=True # necessary for M2M fields & reverse FK fields
)

customer = ResourceRelatedField(
queryset=Customer.objects # queryset argument is required
) # except when read_only=True

```

In the [JSON API spec](http://jsonapi.org/format/#document-resource-objects),
relationship objects contain links to related objects. To make this work
on a serializer we need to tell the `ResourceRelatedField` about the
corresponding view. Use the `HyperlinkedModelSerializer` and instantiate
the `ResourceRelatedField` with the relevant keyword arguments:

```python
from rest_framework_json_api import serializers
from rest_framework_json_api.relations import ResourceRelatedField

from myapp.models import Order, LineItem, Customer


class OrderSerializer(serializers.ModelSerializer):
class Meta:
model = Order

line_items = ResourceRelatedField(
queryset=LineItem.objects,
many=True,
related_link_view_name='order-lineitems-list',
related_link_url_kwarg='order_pk',
self_link_view_name='order_relationships'
)

customer = ResourceRelatedField(
queryset=Customer.objects,
related_link_view-name='order-customer-detail',
related_link_url_kwarg='order_pk',
self_link_view_name='order-relationships'
)
```

* `related_link_view_name` is the name of the route for the related
view.

* `related_link_url_kwarg` is the keyword argument that will be passed
to the view that identifies the 'parent' object, so that the results
can be filtered to show only those objects related to the 'parent'.

* `self_link_view_name` is the name of the route for the `RelationshipView`
(see below).

In this example, `reverse('order-lineitems-list', kwargs={'order_pk': 3}`
should resolve to something like `/orders/3/lineitems`, and that route
should instantiate a view or viewset for `LineItem` objects that accepts
a keword argument `order_pk`. The
[drf-nested-routers](https://github.com/alanjds/drf-nested-routers) package
is useful for defining such nested routes in your urlconf.

The corresponding viewset for the `line-items-list` route in the above example
might look like the following. Note that in the typical use case this would be
the same viewset used for the `/lineitems` endpoints; when accessed through
the nested route `/orders/<order_pk>/lineitems` the queryset is filtered using
the `order_pk` keyword argument to include only the lineitems related to the
specified order.

```python
from rest_framework import viewsets

from myapp.models import LineItem
from myapp.serializers import LineItemSerializer


class LineItemViewSet(viewsets.ModelViewSet):
queryset = LineItem.objects
serializer_class = LineItemSerializer

def get_queryset(self):
queryset = self.queryset

# if this viewset is accessed via the 'order-lineitems-list' route,
# it wll have been passed the `order_pk` kwarg and the queryset
# needs to be filtered accordingly; if it was accessed via the
# unnested '/lineitems' route, the queryset should include all LineItems
if 'order_pk' in self.kwargs:
order_pk = self.kwargs['order_pk']
queryset = queryset.filter(order__pk=order_pk])

return queryset
```

### RelationshipView
`rest_framework_json_api.views.RelationshipView` is used to build
relationship views (see the
[JSON API spec](http://jsonapi.org/format/#fetching-relationships)).
The `self` link on a relationship object should point to the corresponding
relationship view.

The relationship view is fairly simple because it only serializes
[Resource Identifier Objects](http://jsonapi.org/format/#document-resource-identifier-objects)
rather than full resource objects. In most cases the following is sufficient:

```python
from rest_framework_json_api.views import RelationshipView

from myapp.models import Order


class OrderRelationshipView(RelationshipView):
queryset = Order.objects

```

The urlconf would need to contain a route like the following:

```python
url(
regex=r'^orders/(?P<pk>[^/.]+/relationships/(?P<related_field>[^/.]+)$',
view=OrderRelationshipView.as_view(),
name='order-relationships'
)
```

The `related_field` kwarg specifies which relationship to use, so
if we are interested in the relationship represented by the related
model field `Order.line_items` on the Order with pk 3, the url would be
`/order/3/relationships/line_items`. On `HyperlinkedModelSerializer`, the
`ResourceRelatedField` will construct the url based on the provided
`self_link_view_name` keyword argument, which should match the `name=`
provided in the urlconf, and will use the name of the field for the
`related_field` kwarg.
Also we can override `related_field` in the url. Let's say we want the url to be:
`/order/3/relationships/order_items` - all we need to do is just add `field_name_mapping`
dict to the class:
```python
field_name_mapping = {
'line_items': 'order_items'
}
```


### Meta

@@ -260,10 +435,17 @@ added to the `meta` object within the same `data` as the serializer.
To add metadata to the top level `meta` object add:

``` python
def get_root_meta(self, obj):
return {
'size': len(obj)
}
def get_root_meta(self, resource, many):
if many:
# Dealing with a list request
return {
'size': len(resource)
}
else:
# Dealing with a detail request
return {
'foo': 'bar'
}
```
to the serializer. It must return a dict and will be merged with the existing top level `meta`.

2 changes: 1 addition & 1 deletion example/factories/__init__.py
Original file line number Diff line number Diff line change
@@ -1,5 +1,4 @@
# -*- encoding: utf-8 -*-
from __future__ import unicode_literals

import factory
from faker import Factory as FakerFactory
@@ -22,6 +21,7 @@ class Meta:
name = factory.LazyAttribute(lambda x: faker.name())
email = factory.LazyAttribute(lambda x: faker.email())

bio = factory.RelatedFactory('example.factories.AuthorBioFactory', 'author')

class AuthorBioFactory(factory.django.DjangoModelFactory):
class Meta:
Loading

0 comments on commit b9ccd9d

Please sign in to comment.