Django REST Framework JSON APIis a library for creating JSON:API backends using thedjango structure, built on top ofDjango REST Frameworklibrary.
To test it, let's create a web service for rating dishes in restaurants. Let's call it "Ate Opinion".
First,Install Python.
Create a new Python project with a virtual environment:
psmkdirdjango_jsonapipscddjango_jsonapipspython3-metrovenvenvironmentpssource environment/bin/activate
Install Django itself:
psnuggetinstallDjango
Now create a Django project and application:
psdjango-admin startproject django_jsonapi. # Notice the ending '.' characterpsdjango-admin startapp look_ate
Updatedjango_jsonapi/settings.py
to indicate thatopinar_ate
is an installed application:
INSTALLED_APPLICATIONS = [+ 'opinion_ate.apps.OpinionAteConfig','django.contrib.admin',
models
Django maintains the data in the database using classes called Models. The Django REST Framework JSON API uses the same templates, so to start building our app, we'll create templates in the typical Django way.
Replace the content ofopinion_ate/modelos.py
with the following:
of django.db matter modelsclassroom restaurant(models.Model): name = models.CharFieldName(maximum length=200) ADDRESS = models.CharFieldName(maximum length=200)classroom Prato(models.Model): name = models.CharFieldName(maximum length=200) assessment = models.CampoEntero() restaurant = models.foreign key(restaurant, on_delete=models.WATERFALL)
This defines two new models,restaurant
miPrato
.restaurant
It has onename
miADDRESS
field in it, both character fields.Prato
It has onename
character field, aassessment
integer and a foreign key pointing to the relatedrestaurant
.
Then create a database migration to update the database to match our current model definitions:
pspython manage.py makemigrations opinion_ate
By default, a new Django application is configured with an SQLite database, which is just a flat file. This is the easiest way to do it for experimentation purposes. If you want to use another SQL database like Postgres or MySQL, follow the Django docs atinstall the appropriate database links.
Run the following command to perform these migrations on the SQLite database:
psmove python manage.py
Now that our templates are set up, we can create some records. You can do this manually, but Django has the concept of plugin files, which allow you to import sample data into Django's database. We are going to use this to configure some data. Create oneopinion_ate/accessories/
folder then arestaurants.json
file inside it with the following content:
[ { "model": "opinion_ate.restaurante", "package": 1, "Campos": { "name": "sushi place", "ADDRESS": "123 Main Street" } }, { "model": "opinion_ate.prato", "package": 1, "Campos": { "name": "Rollo Volcano", "assessment": 3, "restaurant_id": 1 } }, { "model": "opinion_ate.prato", "package": 2, "Campos": { "name": "Salmon nigiri", "assessment": 4, "restaurant_id": 1 } }, { "model": "opinion_ate.restaurante", "package": 2, "Campos": { "name": "burger place", "ADDRESS": "456 another street" } }, { "model": "opinion_ate.prato", "package": 3, "Campos": { "name": "BBQ Burger", "assessment": 5, "restaurant_id": 2 } }, { "model": "opinion_ate.prato", "package": 4, "Campos": { "name": "Slider", "assessment": 3, "restaurant_id": 2 } }]
Load the data from the device with the following command:
pspython manage.py loaddata restaurants
Web service configuration
Now that we have all of our data set up, let's set up the Django REST Framework JSON API (DJA) so that we can access it through a web service.
Install the following dependencies:
psnuggetinstalldjangorestframeworkpsnuggetinstalldjangorestframework-jsonapipsnuggetinstalldjango filter
To addrest_framework
as an installed application for your project indjango_jsonapi/settings.py
:
INSTALLED_APPS = [ 'opinion_ate.apps.OpinionAteConfig',+ 'rest_frame','django.contrib.admin',
Then configure Django REST Framework to use the JSON API by pasting this large part of the configuration indjango_jsonapi/settings.py
. This comes directly from the DJA docs, with one exception: we've disabled pagination here for simplicity.
REST_FRAMEWORK = { 'EXCEPTION_HANDLER': 'rest_framework_json_api.excepciones.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', # If you are testing performance, you may want to use the browsable API # without forms, since forms can generate their own queries. # If you are testing performance, enable: # 'ejemplo.utils.BrowsableAPIRendererWithoutForms', # Otherwise, to play with the browsable API, enable: 'rest_framework.renders.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', ), 'BUSCAR_PARAM': 'filter [search]', 'TEST_REQUEST_RENDERER_CLASSES': ( 'rest_framework_json_api.renderers.JSONRenderer', ), 'TEST_REQUEST_DEFAULT_FORMAT': 'vnd.api+json'}
To set up a DJA web service, we must first create "serializers", which translate the templates into their format for the end user. Create oneseem_ate/serializers.py
file and add the following content:
of rest_framework_json_api matter serializersof models_ate.models review matter restaurant, Pratoclassroom RestaurantSerializer(serializers.HyperlinkedModelSerializerHyperlinkedModelSerializer): classroom meta: model = restaurant Campos = ('name', 'ADDRESS', 'plate_set')classroom DishSerializer(serializers.HyperlinkedModelSerializerHyperlinkedModelSerializer): classroom meta: model = Prato Campos = ('name', 'assessment', 'restaurant')
DJA doesn't want to make assumptions about the data we want to expose to end users; we have to say this explicitly. EITHERCampos
The values we provide mean that these fields will be exposed to the end user.
but what is thecrockery
field inrestaurant
? This is the inverse ofplate.restaurant
foreign key; is the set of dishes associated with a restaurant. We didn't need to declare this in the model; it is automatically available under a dish in a restaurant.
Now that our serializers are defined, we need to create views that handle HTTP requests for restaurants and dishes. DJA provides basic viewset classes that will give us what we need with just a little configuration. To replaceopinion_ate/views.py
with the following:
of models_ate.models review matter restaurant, Pratoof parecer_ate.serializers matter RestaurantSerializer, DishSerializerof rest_framework matter see setsclassroom RestauranteVistaConjunto(see sets.ModelViewSet): query set = restaurant.objects.all() serializer_class = RestaurantSerializerclassroom PlatoVerConjunto(see sets.ModelViewSet): query set = Prato.objects.all() serializer_class = DishSerializer
The last piece of the puzzle is connecting the URLs. Opendjango_json / URL
and replace the content with the following:
of django.urls matter include, caminoof rest_framework matter routersof opinar_ate matter Points of viewrouter = routers.default router()router.record(r'restaurants', Points of view.RestauranteVistaConjunto)router.record(r'dishes', Points of view.PlatoVerConjunto)url pattern = [ camino('', include(router.URL)),]
This will connect all the necessary URLs. For example, for restaurants, the following main URLs are now available:
- GET /restaurants/ — lists all restaurants
- POST /restaurants/ — creates a new restaurant
- GET /restaurants/:id — get a restaurant
- PATCH /restaurants/:id — updates a restaurant
- DELETE /restaurants/:id — deletes a restaurant
That's a lot we can accomplish without having to write almost any code!
experiencing
Now let's try. Start the Django server:
pspython manage.py runtime server
Visithttp://localhost:8000/platos/1
in your browser. You should see something like the following:
{ "data": { "type": "Praton", "I was going": "1", "attributes": { "name": "Rollo Volcano", "assessment": 3 }, "relations": { "restaurant": { "data": { "type": "Restaurant", "I was going": "1" }, "connections": { "related": "http://localhost:8000/restaurantes/1/" } } } }}
if you are usingfire foxyou should see the JSON data formatted correctly. If your browser doesn't automatically format JSON, you can find a browser extension to do it. For example, for Chrome you can useJSONVer.
This is a JSON:API response for a single record. Let's talk about what's going on here:
- the upper level
data
The property contains the main data of the response. In this case, it's a record; can also be an array. - Although you can infer the record type from the context, JSON:API records always have a
type
field record of what type they are. In some contexts, records of different types will be mixed in an array, so this keeps them distinct. - The record contains a
I was going
A property that provides the publicly exposed ID of the record, which by default is the full database ID. But JSON:API IDs are always exposed as strings, to allow for the possibility of slugs or UUIDs. attributes
is an object that contains all the attributes that we have exposed. They are nested instead of being directly in the record to avoid collision with other standard JSON:API properties, such astype
.relations
provides data about relationships for this record. In this case, the record has arestaurant
relationship. We receive a "resource identifier object", which contains the type and id of the record, but not its attributes.
try to visithttp://localhost:8000/restaurantes/
, in the browser. You will see the following:
{ "data": [ { "type": "Restaurant", "I was going": "1", "attributes": { "name": "sushi place", "ADDRESS": "123 Main Street" }, "relations": { "plate_game": { "data": [ { "type": "Praton", "I was going": "1" }, { "type": "Praton", "I was going": "2" } ], "meta": { "say": 2 } } } }, { "type": "Restaurant", "I was going": "2", "attributes": { "name": "burger place", "ADDRESS": "456 another street" }, "relations": { "plate_game": { "data": [ { "type": "Praton", "I was going": "3" }, { "type": "Praton", "I was going": "4" } ], "meta": { "say": 2 } } } } ]}
Note that this time thedata
is an array of two records. Each of them also has its ownrelations
going back to the dishes associated with the restaurant. These relationships are where DJA really shines. Instead of manually creating URLs, views, and queries for all of these relationships, DJA exposes them for you. And since it uses the standard JSON:API format, there are pre-built client tools that can save the same type of code on the front-end!
Next, let's try to create a record. We won't be able to do this in the browser; we'll need a more sophisticated web service client to do this. A good option isMailman- download and run it.
You can also use Postman for GET requests: set up a GET request forhttp://localhost:8000/restaurantes/
and see how it shows the same data as the browser.
Next, let's create a POST request to the same URL,http://localhost:8000/restaurantes/
. Go to the Headers tab and enter the key "Content-type" and the value "application/vnd.api+json"; this is the content type that JSON:API requires.
Then switch to the Body tab. Click the "none" dropdown menu and change it to "raw." Another "Text" dropdown will appear; change it to "JSON". Enter the following:
{ "data": { "type": "Restaurant", "attributes": { "name": "Spaghetti Place", "ADDRESS": "Calle 3 789", "plate_game": [] } }}
Please note that you do not need to provide aI was going
because we trust the server to generate it. And we don't have to provide therelations
olinks
, soloattributes
we want to set the new record.
Now that our request is set up, click Submit and you should get a "201 Created" response, with the following body:
{ "data": { "type": "Restaurant", "I was going": "3", "attributes": { "name": "Spaghetti Place", "ADDRESS": "Calle 3 789" }, "relations": { "plate_game": { "data": [], "meta": { "say": 0 } } } }}
Our new record is created and the data is returned to us!
If you want to try updating and deleting records:
- make a
CORRECTION
applicationhttp://localhost:8000/restaurantes/3/
, passing updatedattributes
. - make a
DELETE
applicationhttp://localhost:8000/restaurantes/3/
there is no body to clear the record.
Has more
We saw a lot of help from the Django REST Framework JSON API: the ability to create, read, update, and delete records, including relationships between records. But it also offers much more! It allows you to request only a subset of the required fields, allows you to include related records in the response, as well as sort, filter, and paginate. For more information, seethe DJ guide.
Now that you have a JSON:API backend, try connecting to it from the frontend. Choose a tutorialAs JSON: API home page!