Skip to content

Commit 46b0ec1

Browse files
committed
Initial commit for the sample django rest framework API and AngularJS project.
0 parents  commit 46b0ec1

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

50 files changed

+1098
-0
lines changed

.bowerrc

+3
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
{
2+
"directory": "example/assets"
3+
}

.gitignore

+18
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,18 @@
1+
*~
2+
.DS_Store
3+
*.csv
4+
*.pyc
5+
*.swp
6+
*.egg-info
7+
build
8+
dist
9+
.coverage
10+
nosetests.xml
11+
lettucetests.xml
12+
coverage.xml
13+
ghostdriver.log
14+
.harvest
15+
pep8.report
16+
.env
17+
*.sqlite
18+
node_modules

Gruntfile.coffee

+12
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,12 @@
1+
module.exports = (grunt) ->
2+
grunt.initConfig(
3+
pkg: grunt.file.readJSON('package.json')
4+
coffee:
5+
files:
6+
src: ['example/src/js/**/*.coffee']
7+
dest: 'example/assets/js/script.js'
8+
)
9+
10+
grunt.loadNpmTasks('grunt-contrib-coffee')
11+
12+
grunt.registerTask('default', ['coffee'])

Makefile

+14
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,14 @@
1+
clean:
2+
rm -f example.sqlite
3+
4+
create_database:
5+
./manage.py syncdb --noinput
6+
./manage.py migrate --noinput
7+
./manage.py createsuperuser --username=root [email protected] --noinput
8+
9+
make_fixtures:
10+
DJANGO_SETTINGS_MODULE='example.settings' ./scripts/create_users.py
11+
DJANGO_SETTINGS_MODULE='example.settings' ./scripts/create_posts.py
12+
DJANGO_SETTINGS_MODULE='example.settings' ./scripts/create_photos.py
13+
14+
all: clean create_database make_fixtures

Readme.markdown

+34
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,34 @@
1+
# Django API with Django Rest Framework and AngularJS-Resource
2+
3+
This sample project is the companion of a [blog post](http://kevinastone.github.io/getting-started-with-django-rest-framework-and-angularjs.html) on how to get started with Django Rest Framework and AngularJS.
4+
5+
## Dependencies
6+
7+
To setup and run the sample code, you're going to need `npm` from NodeJS available to install the frontend code.
8+
9+
## Setup
10+
11+
You're encouraged to setup a `virtualenv` to work in prior to configuring the dependencies.
12+
13+
1. Install Python Requirements
14+
15+
pip install -r requirements.txt
16+
python setup.py develop
17+
18+
2. Install Assets
19+
20+
npm install
21+
bower install
22+
23+
3. Compile Assets
24+
25+
grunt
26+
27+
4. Setup the Database
28+
29+
make create_database; make make_fixtures
30+
31+
5. Run the Server
32+
33+
./manage.py runserver
34+

bower.json

+11
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,11 @@
1+
{
2+
"name": "example",
3+
"version": "1.0.0",
4+
"dependencies": {
5+
"angular": "1.2.0-rc.2",
6+
"angular-resource": "1.2.0-rc.2",
7+
"underscore": "",
8+
"bootstrap": ">=3.0"
9+
},
10+
"private": true
11+
}

example/__init__.py

Whitespace-only changes.

example/api/__init__.py

Whitespace-only changes.

example/api/admin.py

+8
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,8 @@
1+
from django.contrib import admin
2+
3+
from .models import User, Post, Photo
4+
5+
6+
admin.site.register(User)
7+
admin.site.register(Post)
8+
admin.site.register(Photo)

example/api/api.py

+75
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,75 @@
1+
from rest_framework import generics, permissions
2+
3+
4+
from .serializers import UserSerializer, PostSerializer, PhotoSerializer
5+
from .models import User, Post, Photo
6+
from .permissions import PostAuthorCanEditPermission
7+
8+
9+
class UserList(generics.ListAPIView):
10+
model = User
11+
serializer_class = UserSerializer
12+
permission_classes = [
13+
permissions.AllowAny
14+
]
15+
16+
17+
class UserDetail(generics.RetrieveAPIView):
18+
model = User
19+
serializer_class = UserSerializer
20+
lookup_field = 'username'
21+
22+
23+
class PostMixin(object):
24+
model = Post
25+
serializer_class = PostSerializer
26+
permission_classes = [
27+
PostAuthorCanEditPermission
28+
]
29+
30+
def pre_save(self, obj):
31+
"""Force author to the current user on save"""
32+
obj.author = self.request.user
33+
return super(PostMixin, self).pre_save(obj)
34+
35+
36+
class PostList(PostMixin, generics.ListCreateAPIView):
37+
pass
38+
39+
40+
class PostDetail(PostMixin, generics.RetrieveUpdateDestroyAPIView):
41+
pass
42+
43+
44+
class UserPostList(generics.ListAPIView):
45+
model = Post
46+
serializer_class = PostSerializer
47+
48+
def get_queryset(self):
49+
queryset = super(UserPostList, self).get_queryset()
50+
return queryset.filter(author__username=self.kwargs.get('username'))
51+
52+
53+
class PhotoList(generics.ListCreateAPIView):
54+
model = Photo
55+
serializer_class = PhotoSerializer
56+
permission_classes = [
57+
permissions.AllowAny
58+
]
59+
60+
61+
class PhotoDetail(generics.RetrieveUpdateDestroyAPIView):
62+
model = Photo
63+
serializer_class = PhotoSerializer
64+
permission_classes = [
65+
permissions.AllowAny
66+
]
67+
68+
69+
class PostPhotoList(generics.ListAPIView):
70+
model = Photo
71+
serializer_class = PhotoSerializer
72+
73+
def get_queryset(self):
74+
queryset = super(PostPhotoList, self).get_queryset()
75+
return queryset.filter(post__pk=self.kwargs.get('pk'))

example/api/auth.py

+10
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,10 @@
1+
from .models import User
2+
3+
4+
class AlwaysRootBackend(object):
5+
def authenticate(self, *args, **kwargs):
6+
"""Always returns the `root` user. DO NOT USE THIS IN PRODUCTION!"""
7+
return User.objects.get(username='root')
8+
9+
def get_user(self, user_id):
10+
return User.objects.get(username='root')
+145
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,145 @@
1+
# -*- coding: utf-8 -*-
2+
import datetime
3+
from south.db import db
4+
from south.v2 import SchemaMigration
5+
from django.db import models
6+
7+
8+
class Migration(SchemaMigration):
9+
10+
def forwards(self, orm):
11+
# Adding model 'User'
12+
db.create_table(u'api_user', (
13+
(u'id', self.gf('django.db.models.fields.AutoField')(primary_key=True)),
14+
('password', self.gf('django.db.models.fields.CharField')(max_length=128)),
15+
('last_login', self.gf('django.db.models.fields.DateTimeField')(default=datetime.datetime.now)),
16+
('is_superuser', self.gf('django.db.models.fields.BooleanField')(default=False)),
17+
('username', self.gf('django.db.models.fields.CharField')(unique=True, max_length=30)),
18+
('first_name', self.gf('django.db.models.fields.CharField')(max_length=30, blank=True)),
19+
('last_name', self.gf('django.db.models.fields.CharField')(max_length=30, blank=True)),
20+
('email', self.gf('django.db.models.fields.EmailField')(max_length=75, blank=True)),
21+
('is_staff', self.gf('django.db.models.fields.BooleanField')(default=False)),
22+
('is_active', self.gf('django.db.models.fields.BooleanField')(default=True)),
23+
('date_joined', self.gf('django.db.models.fields.DateTimeField')(default=datetime.datetime.now)),
24+
))
25+
db.send_create_signal(u'api', ['User'])
26+
27+
# Adding M2M table for field groups on 'User'
28+
m2m_table_name = db.shorten_name(u'api_user_groups')
29+
db.create_table(m2m_table_name, (
30+
('id', models.AutoField(verbose_name='ID', primary_key=True, auto_created=True)),
31+
('user', models.ForeignKey(orm[u'api.user'], null=False)),
32+
('group', models.ForeignKey(orm[u'auth.group'], null=False))
33+
))
34+
db.create_unique(m2m_table_name, ['user_id', 'group_id'])
35+
36+
# Adding M2M table for field user_permissions on 'User'
37+
m2m_table_name = db.shorten_name(u'api_user_user_permissions')
38+
db.create_table(m2m_table_name, (
39+
('id', models.AutoField(verbose_name='ID', primary_key=True, auto_created=True)),
40+
('user', models.ForeignKey(orm[u'api.user'], null=False)),
41+
('permission', models.ForeignKey(orm[u'auth.permission'], null=False))
42+
))
43+
db.create_unique(m2m_table_name, ['user_id', 'permission_id'])
44+
45+
# Adding M2M table for field followers on 'User'
46+
m2m_table_name = db.shorten_name(u'api_user_followers')
47+
db.create_table(m2m_table_name, (
48+
('id', models.AutoField(verbose_name='ID', primary_key=True, auto_created=True)),
49+
('from_user', models.ForeignKey(orm[u'api.user'], null=False)),
50+
('to_user', models.ForeignKey(orm[u'api.user'], null=False))
51+
))
52+
db.create_unique(m2m_table_name, ['from_user_id', 'to_user_id'])
53+
54+
# Adding model 'Post'
55+
db.create_table(u'api_post', (
56+
(u'id', self.gf('django.db.models.fields.AutoField')(primary_key=True)),
57+
('author', self.gf('django.db.models.fields.related.ForeignKey')(to=orm['api.User'])),
58+
('title', self.gf('django.db.models.fields.CharField')(max_length=255)),
59+
('body', self.gf('django.db.models.fields.TextField')(null=True, blank=True)),
60+
))
61+
db.send_create_signal(u'api', ['Post'])
62+
63+
# Adding model 'Photo'
64+
db.create_table(u'api_photo', (
65+
(u'id', self.gf('django.db.models.fields.AutoField')(primary_key=True)),
66+
('post', self.gf('django.db.models.fields.related.ForeignKey')(related_name='photos', to=orm['api.Post'])),
67+
('image', self.gf('django.db.models.fields.files.ImageField')(max_length=100)),
68+
))
69+
db.send_create_signal(u'api', ['Photo'])
70+
71+
72+
def backwards(self, orm):
73+
# Deleting model 'User'
74+
db.delete_table(u'api_user')
75+
76+
# Removing M2M table for field groups on 'User'
77+
db.delete_table(db.shorten_name(u'api_user_groups'))
78+
79+
# Removing M2M table for field user_permissions on 'User'
80+
db.delete_table(db.shorten_name(u'api_user_user_permissions'))
81+
82+
# Removing M2M table for field followers on 'User'
83+
db.delete_table(db.shorten_name(u'api_user_followers'))
84+
85+
# Deleting model 'Post'
86+
db.delete_table(u'api_post')
87+
88+
# Deleting model 'Photo'
89+
db.delete_table(u'api_photo')
90+
91+
92+
models = {
93+
u'api.photo': {
94+
'Meta': {'object_name': 'Photo'},
95+
u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
96+
'image': ('django.db.models.fields.files.ImageField', [], {'max_length': '100'}),
97+
'post': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "'photos'", 'to': u"orm['api.Post']"})
98+
},
99+
u'api.post': {
100+
'Meta': {'object_name': 'Post'},
101+
'author': ('django.db.models.fields.related.ForeignKey', [], {'to': u"orm['api.User']"}),
102+
'body': ('django.db.models.fields.TextField', [], {'null': 'True', 'blank': 'True'}),
103+
u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
104+
'title': ('django.db.models.fields.CharField', [], {'max_length': '255'})
105+
},
106+
u'api.user': {
107+
'Meta': {'object_name': 'User'},
108+
'date_joined': ('django.db.models.fields.DateTimeField', [], {'default': 'datetime.datetime.now'}),
109+
'email': ('django.db.models.fields.EmailField', [], {'max_length': '75', 'blank': 'True'}),
110+
'first_name': ('django.db.models.fields.CharField', [], {'max_length': '30', 'blank': 'True'}),
111+
'followers': ('django.db.models.fields.related.ManyToManyField', [], {'related_name': "'followees'", 'symmetrical': 'False', 'to': u"orm['api.User']"}),
112+
'groups': ('django.db.models.fields.related.ManyToManyField', [], {'to': u"orm['auth.Group']", 'symmetrical': 'False', 'blank': 'True'}),
113+
u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
114+
'is_active': ('django.db.models.fields.BooleanField', [], {'default': 'True'}),
115+
'is_staff': ('django.db.models.fields.BooleanField', [], {'default': 'False'}),
116+
'is_superuser': ('django.db.models.fields.BooleanField', [], {'default': 'False'}),
117+
'last_login': ('django.db.models.fields.DateTimeField', [], {'default': 'datetime.datetime.now'}),
118+
'last_name': ('django.db.models.fields.CharField', [], {'max_length': '30', 'blank': 'True'}),
119+
'password': ('django.db.models.fields.CharField', [], {'max_length': '128'}),
120+
'user_permissions': ('django.db.models.fields.related.ManyToManyField', [], {'to': u"orm['auth.Permission']", 'symmetrical': 'False', 'blank': 'True'}),
121+
'username': ('django.db.models.fields.CharField', [], {'unique': 'True', 'max_length': '30'})
122+
},
123+
u'auth.group': {
124+
'Meta': {'object_name': 'Group'},
125+
u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
126+
'name': ('django.db.models.fields.CharField', [], {'unique': 'True', 'max_length': '80'}),
127+
'permissions': ('django.db.models.fields.related.ManyToManyField', [], {'to': u"orm['auth.Permission']", 'symmetrical': 'False', 'blank': 'True'})
128+
},
129+
u'auth.permission': {
130+
'Meta': {'ordering': "(u'content_type__app_label', u'content_type__model', u'codename')", 'unique_together': "((u'content_type', u'codename'),)", 'object_name': 'Permission'},
131+
'codename': ('django.db.models.fields.CharField', [], {'max_length': '100'}),
132+
'content_type': ('django.db.models.fields.related.ForeignKey', [], {'to': u"orm['contenttypes.ContentType']"}),
133+
u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
134+
'name': ('django.db.models.fields.CharField', [], {'max_length': '50'})
135+
},
136+
u'contenttypes.contenttype': {
137+
'Meta': {'ordering': "('name',)", 'unique_together': "(('app_label', 'model'),)", 'object_name': 'ContentType', 'db_table': "'django_content_type'"},
138+
'app_label': ('django.db.models.fields.CharField', [], {'max_length': '100'}),
139+
u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
140+
'model': ('django.db.models.fields.CharField', [], {'max_length': '100'}),
141+
'name': ('django.db.models.fields.CharField', [], {'max_length': '100'})
142+
}
143+
}
144+
145+
complete_apps = ['api']

example/api/models.py

+18
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,18 @@
1+
from django.db import models
2+
3+
from django.contrib.auth.models import AbstractUser
4+
5+
6+
class User(AbstractUser):
7+
followers = models.ManyToManyField('self', related_name='followees', symmetrical=False)
8+
9+
10+
class Post(models.Model):
11+
author = models.ForeignKey(User, related_name='posts')
12+
title = models.CharField(max_length=255)
13+
body = models.TextField(blank=True, null=True)
14+
15+
16+
class Photo(models.Model):
17+
post = models.ForeignKey(Post, related_name='photos')
18+
image = models.ImageField(upload_to="%Y/%m/%d")

example/api/permissions.py

+21
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,21 @@
1+
from rest_framework import permissions
2+
3+
4+
class SafeMethodsOnlyPermission(permissions.BasePermission):
5+
"""Only can access non-destructive methods (like GET and HEAD)"""
6+
def has_permission(self, request, view):
7+
return self.has_object_permission(request, view)
8+
9+
def has_object_permission(self, request, view, obj=None):
10+
return request.method in permissions.SAFE_METHODS
11+
12+
13+
class PostAuthorCanEditPermission(SafeMethodsOnlyPermission):
14+
"""Allow everyone to list or view, but only the other can modify existing instances"""
15+
def has_object_permission(self, request, view, obj=None):
16+
if obj is None:
17+
# Either a list or a create, so no author
18+
can_edit = True
19+
else:
20+
can_edit = request.user == obj.author
21+
return can_edit or super(PostAuthorCanEditPermission, self).has_object_permission(request, view, obj)

0 commit comments

Comments
 (0)