April 13, 2015 in Django
I designed drf-generators to allow you (the developer) to spend less time worrying about views and serializers and more time creating rock-solid data models for your application.
If you know a bit about Django, you can create a basic API in minutes. This tutorial will walk you through creating a complete API using the Django rest framework and drf-generators to jump-start your development.
First we need to set up our django application. My recommendation with every new project is to create a virtualenv to manage your depnedencies.
$ virtualenv -p python3 venv $ source venv/bin/activate
Next, install drf-generators. This installation will also install its dependencies, including Django and django-rest-framework.
$ pip install drf-generators
Create a Django project and api app.
$ django-admin.py startproject example_api $ cd example_api $ python manage.py startapp api
Finally add "api", "rest_framework", and "drf_generators" to the INSTALLED_APPS in your settings
INSTALLED_APPS = ( ... 'rest_framework', 'drf_generators', 'api', )
Now our project is all set up and ready to go!
Now that the environment is set up, we can begin constructing the application. Let's start with the models. For this example, I will be designing a basic blogging system with Entries and Cateogries. Feel free to follow along with your own models.
The slug field in each of the models is for urls. When they are saved in the Django admin, slugify will create a "lisp-case" (i.e. /how-to-do-the-thing) slug for use in urls.
# api/models.py from django.template.defaultfilters import slugify from django.contrib.auth.models import User from django.db import models class Category(models.Model): name = models.CharField(max_length=32) slug = models.SlugField(max_length=32, default='', blank=True) parent = models.ForeignKey('self', blank=True, null=True) def __str__(self): return self.name def save(self, *args, **kwargs): self.slug = slugify(self.name) super().save(*args, **kwargs) class Meta: verbose_name_plural = 'Categories' class Entry(models.Model): title = models.CharField(max_length=64) slug = models.SlugField(max_length=32, default='', blank=True) created = models.DateTimeField(auto_now_add=True) updated = models.DateTimeField(auto_now=True) content = models.TextField() category = models.ForeignKey(Category) def __str__(self): return self.title def save(self, *args, **kwargs): self.slug = slugify(self.title) super().save(*args, **kwargs) class Meta: verbose_name_plural = 'Entries'
Additionally, let's go ahead and register the models with the admin so we can enter some test data
# api/admin.py from django.contrib import admin from api.models import Category, Entry admin.site.register(Category) admin.site.register(Entry)
Now that we have defined the models, let's make our migrations
$ python manage.py makemigrations # Migrations for 'api': # 0001_initial.py: # - Create model Category # - Create model Entry $ python manage.py migrate # Applying ..... OK # Applying api.0001_initial... OK
Now we can load some initial data. To do this save the following json in data.json in your project root and load it using the 'loaddata' command.
$ python manage.py loaddata data.json > Installed 4 object(s) from 1 fixture(s)
data.json
[ { "fields":{ "slug":"programming", "name":"Programming", "parent":null }, "model":"api.category", "pk":1 }, { "fields":{ "slug":"python", "name":"Python", "parent":1 }, "model":"api.category", "pk":2 }, { "fields":{ "slug":"django", "name":"Django", "parent":2 }, "model":"api.category", "pk":3 }, { "fields":{ "updated":"2015-04-13T17:52:38.404Z", "slug":"writing-an-api-in-django", "title":"Writing an API in Django", "category":3, "created":"2015-04-13T17:52:38.404Z", "content":"Django is pretty neat, eh." }, "model":"api.entry", "pk":1 } ]
Now that the models are well defined, we can generate and start working with the API. Drf-generators makes creating Views, Serializers and URLs dead simple. Simply run the following command. Here we are going to use the "modelviewset" format for simplicity. Other formats include "viewset", "function", and "apiview". If you have existing files, it ask you if you want to overwrite them.
$ python manage.py generate api --format modelviewset # Are you sure you want to overwrite views.py? (y/n): y # - writing serializers.py # - writing views.py # - writing urls.py
Sweet! Now we have a base to work on. Let's examine the generated files.
The serializers.py file contains classes that extends rest_framework's ModelSerializer. These classes define how the models are serialized into JSON. The Meta class defines the model that will be serialized. This is a basic as ModelSerializers come. For more options and customization, check out the Django Rest Framework documentation
# api/serializers.py from rest_framework.serializers import ModelSerializer from api.models import Category, Entry class CategorySerializer(ModelSerializer): class Meta: model = Category class EntrySerializer(ModelSerializer): class Meta: model = Entry
The views.py contains your API views. This is where the magic happens. Since we used the "modelviewset" format, our Views are based on rest_framework's ModelViewSet class. With the ModelViewSet class, all we have to do is define the queryset and serializer class and rest_framework does the rest. The views support get, post, put, delete, and list methods that we will explore later
# api/views.py from rest_framework.viewsets import ModelViewSet from api.serializers import CategorySerializer, EntrySerializer from api.models import Category, Entry class CategoryViewSet(ModelViewSet): queryset = Category.objects.all() serializer_class = CategorySerializer class EntryViewSet(ModelViewSet): queryset = Entry.objects.all() serializer_class = EntrySerializer
Lastly the generators created a urls.py that contains the API routes. The routes bind the url to the corresponding ViewSet for each model.
# api/urls.py from rest_framework.routers import SimpleRouter from api import views router = SimpleRouter() router.register(r'category', views.CategoryViewSet) router.register(r'entry', views.EntryViewSet) urlpatterns = router.urls
The last thing we need to do before testing the api, is reference the API's urls from the project's urls. To do this, add an api route so your urls look like the following
from django.conf.urls import include, url from django.contrib import admin urlpatterns = [ url(r'^admin/', include(admin.site.urls)), url(r'^api/', include('api.urls')), ]
Now it's time to run the project and test the api. To spin up the local development server execute the runserver command
$ python manage.py runserver
Now navigate to http://127.0.0.1:8000/api/entry/. You should be presented with the rest_framework's browsable API with a list of Entries. Append '?format=json' to the url and your output should look like the following.
[ { "id": 1, "title": "Writing an API in Django", "slug": "writing-an-api-in-django", "created": "2015-04-13T17:52:38.404000Z", "updated": "2015-04-13T17:52:38.404000Z", "content": "Django is pretty neat, eh.", "category": 3 } ]
If your output looks like this, congrats! You have successfully created a simple API with drf-generators.
Thanks for following along this tutorial. Drf-generators is a side-project of mine that I am constantly working to improve. If you come across an issue or bug, please submit an issue on Github. If you find a way to make drf-generators or would like to contribute, feel free to fork it and submit a pull request.