Skip to content

Commit

Permalink
Added support to Django's field: GenericRel.
Browse files Browse the repository at this point in the history
Improve model's fields calculation for to add all possible related and reverse fields.
Improved documentation translation.
  • Loading branch information
Ernesto Perez Amigo committed Oct 31, 2017
1 parent 58e1f99 commit 6e68e52
Show file tree
Hide file tree
Showing 8 changed files with 154 additions and 139 deletions.
95 changes: 52 additions & 43 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -5,9 +5,9 @@


This package add some extra functionalities to graphene-django to facilitate the graphql use without Relay:
1. Allow pagination and filtering on Queries.
2. Allow to define DjangoRestFramework serializers based Mutations.
3. Add support to Subscription's requests and its integration with websockets using Channels package. :muscle:
1. Allows pagination and filtering on Queries.
2. Allows to define DjangoRestFramework serializers based Mutations.
3. Adds support to Subscription's requests and its integration with websockets using Channels package. :muscle:

## Installation

Expand Down Expand Up @@ -45,8 +45,8 @@ pip install graphene-django-extras

### Queries and Mutations examples:

This is a basic example of graphene-django-extras package use. You can configure some pagination
default values on settings.py like this:
This is a basic example of graphene-django-extras package use. You can configure global params
for DjangoListObjectType classes pagination definitions on settings.py like this:

```python
GRAPHENE_DJANGO_EXTRAS = {
Expand Down Expand Up @@ -85,19 +85,19 @@ class UserListType(DjangoListObjectType):
pagination = LimitOffsetGraphqlPagination(page_size=20)
```

#### 2- Input Types can be defined for use on mutations:
#### 2- You can to define InputTypes for use on mutations:

```python
from graphene_django_extras import DjangoInputObjectType


class UserInput(DjangoInputObjectType):
class Meta:
description = " User InputType to use as input on Arguments classes on traditional Mutations "
description = " User InputType definition to use as input on an Arguments class on traditional Mutations "
model = User
```

#### 3- You can define traditional mutations that use InputTypes or Mutations based on DRF SerializerClass:
#### 3- You can define traditional mutations that use InputTypes or Mutations based on DRF serializers:

```python
import graphene
Expand All @@ -113,7 +113,7 @@ class UserSerializerMutation(DjangoSerializerMutation):
DjangoSerializerMutation auto implement Create, Delete and Update functions
"""
class Meta:
description = " Serializer based Mutation for Users "
description = " DRF serializer based Mutation for Users "
serializer_class = UserSerializer


Expand All @@ -128,7 +128,7 @@ class UserMutation(graphene.Mutation):
new_user = graphene.Argument(UserInput)

class Meta:
description = " Traditional graphene mutation for Users "
description = " Graphene traditional mutation for Users "

@classmethod
def mutate(cls, root, info, *args, **kwargs):
Expand All @@ -153,22 +153,22 @@ class Queries(graphene.ObjectType):
all_users3 = DjangoListObjectField(UserListType, filterset_class=UserFilter, description=_('All Users query'))

# Defining a query for a single user
# The DjangoObjectField have a ID input field, that allow filter by id and is't necessary resolve method
# The DjangoObjectField have a ID type input field, that allow filter by id and is't necessary to define resolve function
user = DjangoObjectField(UserType, description=_('Single User query'))

# Another way to define a single user query
user1 = DjangoObjectField(UserListType.getOne(), description=_('User List with pagination and filtering'))
# Another way to define a query to single user
user1 = DjangoObjectField(UserListType.getOne(), description=_('User list with pagination and filtering'))


class Mutations(graphene.ObjectType):
user_create = UserSerializerMutation.CreateField(deprecation_reason='Some deprecation message')
user_create = UserSerializerMutation.CreateField(deprecation_reason='Some one deprecation message')
user_delete = UserSerializerMutation.DeleteField()
user_update = UserSerializerMutation.UpdateField()

traditional_user_mutation = UserMutation.Field()
```

#### 5- Examples of queries:
#### 5- Queries's examples:
```js
{
allUsers(username_Icontains:"john"){
Expand Down Expand Up @@ -209,7 +209,7 @@ class Mutations(graphene.ObjectType):
}
```

#### 6- Examples of Mutations:
#### 6- Mutations's examples:

```js
mutation{
Expand Down Expand Up @@ -251,7 +251,7 @@ mutation{

### **Subscriptions:**

This first approach to support Graphql subscriptions with Channels in **graphene-django-extras**, use **channels-api** package.
This first approach to add Graphql subscriptions support with Channels in **graphene-django-extras**, use **channels-api** package.

#### 1- Defining custom Subscriptions classes:

Expand All @@ -268,18 +268,18 @@ class UserSubscription(Subscription):
class Meta:
mutation_class = UserMutation
stream = 'users'
description = 'Subscription for users'
description = 'User Subscription'


class GroupSubscription(Subscription):
class Meta:
mutation_class = GroupMutation
stream = 'groups'
description = 'Subscriptions for groups'
description = 'Group Subscription'

```

Add ours subscriptions definitions into our app schema:
Add the subscriptions definitions into your app's schema:

```python
# app/graphql/schema.py
Expand All @@ -292,7 +292,7 @@ class Subscriptions(graphene.ObjectType):
GroupSubscription = PersonSubscription.Field()
```

Add your app schema into your project root schema:
Add the app's schema into your project root schema:

```python
# schema.py
Expand All @@ -302,17 +302,17 @@ import custom.app.route.graphql.schema

class RootQuery(custom.app.route.graphql.schema.Query, graphene.ObjectType):
class Meta:
description = 'Root Queries for my Project'
description = 'The project root query definition'


class RootSubscription(custom.app.route.graphql.schema.Mutation, graphene.ObjectType):
class Meta:
description = 'Root Mutations for my Project'
description = 'The project root mutation definition'


class RootSubscription(custom.app.route.graphql.schema.Subscriptions, graphene.ObjectType):
class Meta:
description = 'Root Subscriptions for my Project'
description = 'The project root subscription definition'


schema = graphene.Schema(
Expand All @@ -324,7 +324,7 @@ schema = graphene.Schema(

#### 2- Defining Channels settings and custom routing config ( *For more information see Channels documentation* ):

Defining our app routing, like custom apps urls:
We define app routing, as if they were app urls:

```python
# app/routing.py
Expand All @@ -345,7 +345,7 @@ app_routing = [
]
```

Defining our project routing, like custom root project urls:
We define project routing, as if they were project urls:

```python
# project/routing.py
Expand All @@ -358,7 +358,7 @@ project_routing = [

```

You should add **channels** and **channels_api** modules into your INSTALLED_APPS setting and you must defining your routing project definition into the CHANNEL_LAYERS setting:
You should put into your INSTALLED_APPS the **channels** and **channels_api** modules and you must to add your project's routing definition into the CHANNEL_LAYERS setting:

```python
# settings.py
Expand All @@ -384,17 +384,18 @@ CHANNEL_LAYERS = {
...
```

#### 3- Examples of Subscriptions:
#### 3- Subscription's examples:

In your client you must define websocket connection to: *'ws://host:port/custom_websocket_path'*.
When the connection is established, the server return a websocket message like this:
In your WEB client you must define websocket connection to: *'ws://host:port/custom_websocket_path'*.
When the connection is established, the server return a websocket's message like this:
*{"channel_id": "GthKdsYVrK!WxRCdJQMPi", "connect": "success"}*, where you must store the **channel_id** value to later use in your graphql subscriptions request for subscribe or unsubscribe operations.
The Subscription accept five possible parameters:

The graphql's subscription request accept five possible parameters:
1. **operation**: Operation to perform: subscribe or unsubscribe. (*required*)
2. **action**: Action you wish to subscribe: create, update, delete or all_actions. (*required*)
3. **channelId**: Websocket connection identification. (*required*)
4. **id**: ID field value of model object that you wish to subscribe to. (*optional*)
5. **data**: List of desired model fields that you want in subscription's notification. (*optional*)
2. **action**: Action to which you wish to subscribe: create, update, delete or all_actions. (*required*)
3. **channelId**: Identification of the connection by websocket. (*required*)
4. **id**: Object's ID field value that you wish to subscribe to. (*optional*)
5. **data**: Model's fields that you want to appear in the subscription notifications. (*optional*)

```js
subscription{
Expand All @@ -412,7 +413,9 @@ subscription{
}
```

In this case, the subscription request sanded return a websocket message to client like this: *{"action": "update", "operation": "subscribe", "ok": true, "stream": "users", "error": null}* and each time than the user with id=5 get modified, you will receive a message through websocket's connection with the following format:
In this case, the subscription request sent return a websocket message to client like this:
*{"action": "update", "operation": "subscribe", "ok": true, "stream": "users", "error": null}*
and from that moment each time than the user with id=5 get modified, you will receive a message through websocket's connection with the following format:

```js
{
Expand All @@ -432,7 +435,7 @@ In this case, the subscription request sanded return a websocket message to clie
}
```

For unsubscribe you must send a graphql subscription request like this:
For unsubscribe you must send a graphql request like this:

```js
subscription{
Expand All @@ -449,14 +452,17 @@ subscription{
}
```

**NOTE:** Each time than the Graphql server restart, you must to reestablish the websocket's connection and resend the subscription graphql request with a new websocket connection id.
**NOTE:** Each time than the graphql's server restart, you must to reestablish the websocket connection and resend the graphql's subscription request with the new websocket connection id.


## Change Log:

#### v0.1.0-alpha12:
1. Added MAX_PAGE_SIZE configuration field to global dict GRAPHENE_DJANGO_EXTRAS
configuration for better customize paginations.
1. Added new settings param: MAX_PAGE_SIZE, to use on GRAPHENE_DJANGO_EXTRAS
configuration dict for better customize DjangoListObjectType's pagination.
2. Added support to Django's field: GenericRel.
3. Improve model's fields calculation for to add all possible related and reverse fields.
4. Improved documentation translation.

#### v0.1.0-alpha11:
1. Improved ordering for showed fields on graphqli's IDE.
Expand All @@ -482,7 +488,8 @@ subscription{
1. Add queryset options to DjangoListObjectType Meta class for specify wanted model queryset.
2. Add AuthenticatedGraphQLView on graphene_django_extras.views for use
'permission', 'authorization' and 'throttle' classes based on the DRF settings. Special thanks to
[@jacobh](https://github.com/jacobh) for this [comment](https://github.com/graphql-python/graphene/issues/249#issuecomment-300068390)
[@jacobh](https://github.com/jacobh) for this
[comment](https://github.com/graphql-python/graphene/issues/249#issuecomment-300068390)

#### v0.1.0-alpha3:
1. Fixed bug on subscriptions when not specified any field in "data" parameter to bean return on notification
Expand Down Expand Up @@ -512,8 +519,10 @@ subscription{

#### v0.0.1:
1. Fixed bug on DjangoInputObjectType class that refer to unused interface attribute.
2. Added support to create nested objects like in [DRF](http://www.django-rest-framework.org/api-guide/serializers/#writable-nested-representations),
it's valid to SerializerMutation and DjangoInputObjectType, only is necessary to specify nested_fields=True on its Meta class definition.
2. Added support to create nested objects like in
[DRF](http://www.django-rest-framework.org/api-guide/serializers/#writable-nested-representations),
it's valid to SerializerMutation and DjangoInputObjectType, only is necessary to specify nested_fields=True
on its Meta class definition.
3. Added support to show, only in mutations types to create objects and with debug=True on settings,
inputs autocomplete ordered by required fields first.
4. Fixed others minors bugs.
Expand Down
Loading

0 comments on commit 6e68e52

Please sign in to comment.