Getting Started with Django Rest Framework and AngularJS
Posted 天际凯迪
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了Getting Started with Django Rest Framework and AngularJS相关的知识,希望对你有一定的参考价值。
转载自:http://blog.kevinastone.com/getting-started-with-django-rest-framework-and-angularjs.html
A ReSTful API is becoming a standard component of any modern web application. The Django Rest Framework is powerful framework for developing ReST endpoints for your Django based project. AngularJS is modern javascript framework for creating complex web applications within the browser. It focuses on strong separation of concerns (MVC) and dependency injection to encourage creating maintainable (and testable) modules that can be integrated to develop rich client side functionality.
In this blog post, I‘ll walk through creating a sample project that exposes a ReST API consumed by AngularJS on the client to showcase how to combine the frontend and backend frameworks to simplify creating complex applications. I‘ll make heavy use of code examples to demonstrate both the solution and the process and there‘s a companion Github project with all the code.
Let‘s Build a Sample Django Project
For a sample project, let‘s create a simple photo-sharing app (not unlike a rudimentary Instagram) and build a feed view for a given user to scan through all the photos shared on the site.
All the sample code for this project is available on a GitHub repository. To setup the sample project in your own environment, consult the installation instructions included in the repository. This includes installing AngularJS (and other javascript assets) via bower+grunt.
Finally, there‘s some sample data in fixtures available to demonstrate the API. We have a few users ([‘Bob‘, ‘Sally‘, ‘Joe‘, ‘Rachel‘]
), two posts ([‘This is a great post‘, ‘Another thing I wanted to share‘]
) and some sample photos as well. The included Makefile builds the sample data for you.
Couple notes about the sample code:
- I‘ll skip over the details on configuring, building and running the sample application. There‘s instructions in the repository that covers many of these details. Please report any issues on GitHub and I‘ll be sure to fix them.
- I‘ve written the frontend code using Coffee-Script since I find it more legible and more efficient (and a bit more Pythonic). There‘s a supplied Grunt task that collects all the coffee script files and concats them into a single
script.js
file for inclusion.
Project Data Layer (the Models)
Our model layer is straightforward to what you might find in an introductory tutorial for Django. You have three models of note: User
, Post
, and Photo
. A user can author many posts (as well as have many followers) and a post can showcase many photos (such as a collection or gallery) along with a title and optional description/body.
from django.db import models
from django.contrib.auth.models import AbstractUser
class User(AbstractUser):
followers = models.ManyToManyField(‘self‘, related_name=‘followees‘, symmetrical=False)
class Post(models.Model):
author = models.ForeignKey(User, related_name=‘posts‘)
title = models.CharField(max_length=255)
body = models.TextField(blank=True, null=True)
class Photo(models.Model):
post = models.ForeignKey(Post, related_name=‘photos‘)
image = models.ImageField(upload_to="%Y/%m/%d")
Django Rest Framework based API
The Django Rest Framework (DRF) provides a clean architecture to develop both simple, turn-key API endpoints as well as more complex ReST constructs. The key is a clean separation with Serializer
which describes the mapping between a model and the generalized wire representation (be it JSON, XML or whatever) and separate set of generic Class-Based-Views that can be extended to meet the needs of the specific API endpoint. You also define your own URL structure rather than rely on an auto-generated one. This is what separates DRF from other frameworks like Tastypie and Piston that automate much of the conversion from django models to ReST endpoints, but come at the cost of flexibility in adapting to different use cases (especially around permissions and nested resources).
Model Serializers
The Serializers
in DRF focus on the responsibility to convert django model instances into their representation in the API. This gives us the opportunity to convert any data types, or provide supplemental information in a given model. For example, for the user, we only included some of the fields, stripping private attributes such as password
and email
. For the photo, we converted the ImageField
to return the url of the image (rather than the media path location).
For the PostSerializer
, we elected to embed the author directly in the Post (rather the the common case to provide a hyperlink). This makes that information readily accessible to our client rather than requiring extra API requests at the cost of duplicating users on each post. The alternative hyperlink is listed with a comment for comparison. The power of serializers is that you can extend them to create a derivative version, that uses hyperlinks instead of nested records (say, for the case of listing posts by a specific users‘ feed).
To assign the author
to the PostSerializer
, we‘re gonna have that provided by the API View. So we‘ll make it optional (required=False
) in our serializer and add it to the validation exclusion.
from rest_framework import serializers
from .models import User, Post, Photo
class UserSerializer(serializers.ModelSerializer):
posts = serializers.HyperlinkedIdentityField(‘posts‘, view_name=‘userpost-list‘, lookup_field=‘username‘)
class Meta:
model = User
fields = (‘id‘, ‘username‘, ‘first_name‘, ‘last_name‘, ‘posts‘, )
class PostSerializer(serializers.ModelSerializer):
author = UserSerializer(required=False)
photos = serializers.HyperlinkedIdentityField(‘photos‘, view_name=‘postphoto-list‘)
# author = serializers.HyperlinkedRelatedField(view_name=‘user-detail‘, lookup_field=‘username‘)
def get_validation_exclusions(self):
# Need to exclude `author` since we‘ll add that later based off the request
exclusions = super(PostSerializer, self).get_validation_exclusions()
return exclusions + [‘author‘]
class Meta:
model = Post
class PhotoSerializer(serializers.ModelSerializer):
image = serializers.Field(‘image.url‘)
class Meta:
model = Photo
Okay, given our samples are fixtures are loaded, let‘s play with these serializers. You‘ll likely see DeprecationWarning
because we‘re using HyperlinkedIdentityField
without providing a request object to construct the URL. In the actual views, this is provided, so you can safely ignore.
>>> from example.api.models import User
>>> user = User.objects.get(username=‘bob‘)
>>> # Need to generate a fake request for our hyperlinked results
>>> from django.test.client import RequestFactory
>>> from example.api.serializers import *
>>> context = dict(request=RequestFactory().get(‘/‘))
>>> serializer = UserSerializer(user, context=context)
>>> serializer.data
{‘id‘: 2, ‘username‘: u‘bob‘, ‘first_name‘: u‘Bob‘, ‘last_name‘: u‘‘, ‘posts‘: ‘http://testserver/api/users/bob/posts‘}
>>> post = user.posts.all()[0]
>>> PostSerializer(post, context=context).data
{‘author‘: {‘id‘: 2, ‘username‘: u‘bob‘, ‘first_name‘: u‘Bob‘, ‘last_name‘: u‘‘, ‘posts‘: ‘http://testserver/api/users/bob/posts‘}, ‘photos‘: ‘http://testserver/api/posts/2/photos‘, u‘id‘: 2, ‘title‘: u‘Title #2‘, ‘body‘: u‘Another thing I wanted to share‘}
>>> serializer = PostSerializer(user.posts.all(), many=True, context=context)
>>> serializer.data
[{‘author‘: {‘id‘: 2, ‘username‘: u‘bob‘, ‘first_name‘: u‘Bob‘, ‘last_name‘: u‘‘, ‘posts‘: ‘http://testserver/api/users/bob/posts‘},