So you're learning to use the Django Web Framework and you're loving it. But you want an attractive, easy to use API for your application? Look no further than the Django Rest Framework (DRF). The DRF is powerful, sophisticated, and surprisingly easy to use. It offers an attractive, web browseable version of your API, and the option of returning raw JSON. The Django Rest Framework provides powerful model serialization, display data using standard function based views, or get granular with powerful class based views for more complex functionality. All in a fully REST compliant wrapper. Let's dig in.
Tuts+ has two excellent videos on how to install virtualenv and virtualenvwrapper. Take a few minutes to walk through those videos to get virtualenv and virtualenvwrapper installed on your machine. If you've already got them installed, then skip the next section.
It doesn't matter where you are in the file system when these commands are run. All virtualenv files are stored in a centralized location and activated on demand.
If at any point you're not getting the expected results, please try switching your local repository's branch to final to see the results:
"Fabric is a Python (2.5 or higher) library and command-line tool for streamlining the use of SSH for application deployment or systems administration tasks."
While a more complete discussion about Fabric is beyond the scope of this article, I've implemented some basic fab commands which make working with this application a little easier. You've seen the
After the shell loads, input the next few lines to retrieve an Author record from the database, which just happens to be mine. What a coincidence. :)
Similarly, you can retrieve all of the Author records from the database with a different command:
Unfortunately this doesn't return data that an AJAX call can understand. So let's add a serializer for Authors. Close out the shell by typing
Without making any more changes, the serializer gives us quite a bit of power. Head back into the shell and let's review.
Let's add a few more lines of code and see what our API will show us in the browser after our data is run through our new
Next, open
Then make sure to add the import for the
The default view for the Django Rest Framework is the APIView. It allows you to define your own
Now that you've made those changes, make sure you've got the server running by typing
Now that we've got the Authors API view in place, try hitting that URL with a
Pretty snazzy eh?
Before we can add books to the
Next, add the following line immediately after the docstring of the
Then add
Reload the
Good guy DRF indeed!
Because the DRF already knows about the properties of the model, it doesn't require us to repeat ourselves. If we wanted, we could be explicit in the
The
Immediately after the docstring of the
Then after the
Then lastly we need to add our new property, to the list of fields. Change this:
to this:
Reload the
Then open
Click one of the author names on the index page and you should see the author Instance page load up.
with this line
Type in a name, yours if you'd like, and hit submit...and presto, you get...an error?
The DRF isn't quite that magical...or is it?
Open
What just happened? The default API View we used only allowed
Keep in mind that while the DRF does enforce database integrity based on the properties of the model, we're not setting any sort of security on who can access or use this form. Diving into security, logging in, and managing permissions is outside the scope of this article, but suffice it to say that DRF does have functionality for allowing access to the views you've been working with and it's fairly trivial to set up.
The DRF has more to it than the few bits we were able to cover, but I hope you'll find it useful for your next application.
Laying the Foundation
When working with Python applications, it's always a good idea to sandbox your development with a virtual environment. It helps prevent version collisions between libraries you need in your application and libraries you might already have installed on your machine, it makes it easy to install dependencies within a virtual env using therequirements.txt
file, and lastly it makes sharing your development environment with other developers a snap.Tuts+ has two excellent videos on how to install virtualenv and virtualenvwrapper. Take a few minutes to walk through those videos to get virtualenv and virtualenvwrapper installed on your machine. If you've already got them installed, then skip the next section.
Setting Up Your Virtual Environment
The first thing we'll do as part of our application is to set up the virtual environment. Enter the following commands in your Terminal.1 2 | $ mkvirtualenv drf $ workon drf |
Installing the Django Application
Since this article isn't about Django itself, I've saved some time by creating a repository containing the app we'll be working in. It's a simple bookshelf application which will allow us to store lists of authors and books. Download the companion repository to this article, into the directory of your choice, then runpip install -r requirements.txt
to install all of the dependencies. Remember to make sure you've activated the virtual environment we set up in the last step. After the installation is complete, you should be able to type fab runserver
to start a local web server and open a web browser pointing to http://127.0.0.1:8000/
. If you see a list of authors on screen then you're good to go.If at any point you're not getting the expected results, please try switching your local repository's branch to final to see the results:
git checkout final
.Fab? What's That?
Fab == Fabric, a Python task runner. From the docs:"Fabric is a Python (2.5 or higher) library and command-line tool for streamlining the use of SSH for application deployment or systems administration tasks."
While a more complete discussion about Fabric is beyond the scope of this article, I've implemented some basic fab commands which make working with this application a little easier. You've seen the
fab runserver
command. There's also the fab shell
command which brings up an interactive iPython shell within the context of the application and the fab syncdb
command which runs Django's syncdb
command to sync changes in models to the database.Working With Serialization
One powerful feature of the Django Rest Framework is the built in model serialization it offers. With just a few lines of code you can compose powerful representations of your data that can be delivered in a number of formats. As previously mentioned, our application will be a simple bookshelf app, with Authors and Books. I've already created theAuthor
and Book
models for you, so open up /app/bookreview/models.py
. There are already a few Authors stored in the local SQLite database, so let's open up an interactive shell for our app and poke around. Switch to your Terminal window, make sure you're in the ./app
directory and type in the following command.1 | $ fab shell |
1 2 3 4 5 6 7 8 | $ from bookreview.models import Author $ author = Author.objects.get(pk=1) $ author. id > 1 $ author.first_name > u 'Andy' $ author.last_name > u 'Matthews' |
1 2 3 4 | $ from bookreview.models import Author $ authors = Author.objects.all() $ authors > [<Author: Andy Matthews>, <Author: China Mieville>, <Author: Neil Gaiman>, <Author: Veronica Roth>, <Author: Suzanne Collins>, <Author: Brandon Sanderson>, <Author: Rick Riordan>, <Author: Phillip K. Dick>, <Author: John Scalzi>, <Author: Jesse Petersen>] |
quit
and open up bookreview/serializers.py
. Type, or paste, the next few lines of code and save the file.1 2 3 4 5 6 7 | class AuthorSerializer(serializers.ModelSerializer): """ Serializing all the Authors """ class Meta: model = Author fields = ( 'id' , 'first_name' , 'last_name' ) |
1 2 3 4 5 6 | $ from bookreview.models import Author $ from bookreview.serializers import AuthorSerializer $ author = Author.objects.get(pk=1) $ serialized = AuthorSerializer(author) $ serialized.data > { 'id' : 1, 'first_name' : u 'Andy' , 'last_name' : u 'Matthews' } |
AuthorSerializer
. Checking Out the Web Browseable API
First, openbookreview/urls.py
and add the following line just after the index_view
route:1 | url(r '^authors/$' , views.AuthorView.as_view(), name = 'author-list' ), |
bookreview/views.py
and add these lines to the end of the file:1 2 3 4 5 6 | class AuthorView(generics.ListAPIView): """ Returns a list of all authors. """ model = Author serializer_class = AuthorSerializer |
AuthorSerializer
at the top of the page:1 | from bookreview.serializers import AuthorSerializer |
get
, put
, and delete
methods. It's a good way to get base functionality but still have control over the end result. In our case though, we're letting the DRF do the heavy lifting for us by extending the ListAPIView. We just need to provide a few bits of information to allow the DRF to connect the pieces. We give it the Author
model so that it knows how to talk to the database, and the AuthorSerializer
so that the DRF knows how to return the information. We'll only be working with a few of the built in APIViews, but you can read about all of the options on the Django Rest Framework website.Now that you've made those changes, make sure you've got the server running by typing
fab runserver
then enter the URL http://127.0.0.1:8000/authors/
. You should see an attractively designed API view page containing a list of all the authors in the database.Now that we've got the Authors API view in place, try hitting that URL with a
curl
command:1 2 | $ curl http: //127 .0.0.1:8000 /authors/ > [{ "id" : 1, "first_name" : "Andy" , "last_name" : "Matthews" },..., { "id" : 10, "first_name" : "Jesse" , "last_name" : "Petersen" }] |
Giving the Authors Some Books!
While this API view is pretty slick, it's one-for-one with the database. Let's kick up our API view by composing a more complex data set for Authors by including a list of all of their books. Openbookreview/serializers.py
and add the following line of code before the AuthorSerializer
class definition.1 2 3 4 5 6 7 | class BookSerializer(serializers.ModelSerializer): """ Serializing all the Books """ class Meta: model = Book fields = ( 'id' , 'title' , 'isbn' ) |
AuthorSerializer
, we have to serialize Books. This should look completely familiar to you. Because it's almost identical to the AuthorSerializer
, we're not going to discuss it.Next, add the following line immediately after the docstring of the
AuthorSerializer
class:1 | books = BookSerializer(many = True ) |
books
to the fields property of the inner Meta class of the AuthorSerializer
:1 | fields = ( 'id' , 'first_name' , 'last_name' , 'books' ) |
/authors/
endpoint and you should now see an array of books coming in for each author. Not bad for just a few more lines of code eh?.Good guy DRF indeed!
Use SerializerMethodField to Create Custom Properties
The serializer is clever...when we indicate which model it should serialize within the inner meta class, it knows everything about that model...properties, lengths, defaults, and so on. Notice that we're not defining any of the properties found on the model directly within the serializer, we're only indicating which fields should be returned to the API in thefields
property.Because the DRF already knows about the properties of the model, it doesn't require us to repeat ourselves. If we wanted, we could be explicit in the
BookSerializer
and add the following lines...and the DRF would be just as happy.1 2 | title = serializers.Field(source = 'title' ) isbn = serializers.Field(source = 'isbn' ) |
serializers.field
method allows you to point to an existing property of the model, the source
field, and allows you to explicitly name it something else when returning it to the end user. But what about serializers.SerializerMethodField
? That allows you to essentially create a custom property, one that's not directly tied to the model, whose content is the result of a method call. In our case, we're going to return a URL which contains a list of places you could go to purchase the book. Let's add that custom method now.Immediately after the docstring of the
BookSerializer
add the following string:1 | search_url = serializers.SerializerMethodField( 'get_search_url' ) |
class Meta
definition of the BookSerializer
add the following lines:1 2 | def get_search_url( self , obj): |
1 | fields = ( 'id' , 'title' , 'isbn' ) |
1 | fields = ( 'id' , 'title' , 'isbn' , 'search_url' ) |
/authors/
endpoint and you should now see a URL coming back along with the other information about the book.Adding an Author Endpoint
We already have a list of authors, but it would be nice for each author to have their own page...just like MySpace right? Lets add an API endpoint to view a single author. Openurls.py
and add the following line after the author-list
route:1 | url(r '^authors/(?P<pk>[\d]+)/$' , views.AuthorInstanceView.as_view(), name = 'author-instance' ), |
views.py
and add the following lines after the AuthorView
class:1 2 3 4 5 6 7 | class AuthorInstanceView(generics.RetrieveAPIView): """ Returns a single author. Also allows updating and deleting """ model = Author serializer_class = AuthorSerializer |
Refactoring for the Win!
Now would be a good time to do a quick bit of refactoring. Since Django offers the option of naming your routes, we can reference the route by that name. This prevents us from having to build the URL manually. Opentemplates/index.html
and swap out the following piece:1 | < a href = "/authors/{{author.id}}/" >{{author.first_name}} {{author.last_name}}</ a > |
1 | < a href = "{% url 'author-instance' author.id %}" >{{author.first_name}} {{author.last_name}}</ a > |
Saving Data: Let the DRF Work for You!
Up until now our app has been read only. It's time to start saving some data. Opentemplates/index.html
and add the following lines underneath the authors header:Type in a name, yours if you'd like, and hit submit...and presto, you get...an error?
The DRF isn't quite that magical...or is it?
Open
views.py
, change the class that AuthorView
extends from generics.ListAPIView
to generics.ListCreateAPIView
. Then try your request again. Boom! You're an author! And your fourth grade gym teacher said you'd never amount to anything. But what did he know, he has to work around sweaty socks all day. Go back to the main author's page to see your name in lights.What just happened? The default API View we used only allowed
GET
requests to the authors endpoint. By changing it to ListCreateAPIView, we told DRF we wanted to also allow POST
requests. It does everything else for us. We could just as easily define our own post
method within the AuthorView
and do some extra stuff there. It might look like this:1 2 | def post( self , * args, * * kwargs): import pdb; pdb.set_trace() |
Finishing Up
You've learned quite a lot about the Django Rest Framework now: how to implement a web-viewable API which can return JSON for you, how to configure serializers to compose and transform your data, and how to use class based views to abstract away boilerplate code.The DRF has more to it than the few bits we were able to cover, but I hope you'll find it useful for your next application.
If you'd like an alternative to casually flirting with girls and trying to find out the right thing to say...
ReplyDeleteIf you'd rather have women chase YOU, instead of spending your nights prowling around in filthy bars and restaurants...
Then I encourage you to view this short video to learn a shocking little secret that has the potential to get you your own harem of hot women:
FACEBOOK SEDUCTION SYSTEM...