Created
May 6, 2013 18:08
-
-
Save shofetim/5526906 to your computer and use it in GitHub Desktop.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| """ | |
| If this proves useful, move it to lib or something more suitable. | |
| The intention is that we need a good API, one that can be used for | |
| developing complex beehive UIs with backbone.js and that can be | |
| exposed to customers. | |
| (See wiki.aquameta.com/Letters_from_a_front-end_guy#5._Stuff_that_hearts_Backbone.js_and_iCanHaz, | |
| there is abunch of stuff that is not yet spec'd) | |
| We don't have anything good in place, but there are a few good options | |
| available. We could use an existing API app / library. TastyPieAPI | |
| tastypieapi.org, Django REST framework django-rest-framework.org | |
| Django Piston bitbucket.org/jespern/django-piston/wiki/Home are the | |
| leading ones. These, especially TastyPie, are nice. But they come with | |
| a lot that we don't need: multiple formats, custom authentication, | |
| etc. They also do not produce a standard JSON format that is | |
| compatible with backbone.js or similar (JSON API jsonapi.org). We | |
| could modify backbone sync (github.com/PaulUithol/backbone-tastypie or | |
| do it ourselves) to work with Django's or the Django apps default data | |
| representation. But that is just pushing the problem around, not | |
| fixing it. | |
| So we roll our own. | |
| Lets use the existing cookie based auth and CSRF until and unless they | |
| prove problematic. | |
| Django forms don't look like the right way to handle input. They | |
| expect urlencoded data by default, not JSON, their error message | |
| generation doesn't really fit in the API responses, and they aren't | |
| required for type coercion. | |
| """ | |
| #-----------------------------------------------------------------------------# | |
| # The first challange is that Django's built in serializer doesn't | |
| # produce compatible / reasonable JSON output. And that | |
| # "serializers.serialize('json'" is abit verbose. | |
| # | |
| # The following serializes in Backbone.js / JSON API format, and is terse. | |
| #-----------------------------------------------------------------------------# | |
| from django.core import serializers | |
| def to_json(queryset): | |
| """Creates a string that can be parsed as JSON from a queryset""" | |
| return serializers.serialize("api", queryset) | |
| def parse_json(data): | |
| """Parse JSON string into Python values""" | |
| return json.loads(data) | |
| #-----------------------------------------------------------------------------# | |
| # Next we may want a class/mixin to help with the boilerplate | |
| # GET/PUT/POST/DELETE pattern. | |
| #-----------------------------------------------------------------------------# | |
| class BaseView(object): | |
| """ | |
| BaseView provides a class that is instantiated and then | |
| called like a function. | |
| __init__ called without arguments. | |
| You just (must) override the __call__ method. | |
| """ | |
| def __new__(cls, *args, **kwargs): | |
| obj = super(BaseView, cls).__new__(cls) | |
| return obj(*args, **kwargs) | |
| def __call__(self, *args, **kwargs): | |
| pass | |
| from django.http import HttpResponse | |
| class REST(BaseView): | |
| """ | |
| A class to handle CRUD boilerplate for Backbone.js requests. | |
| Override methods as needed, be sure to set the model class in the child class. | |
| """ | |
| def __call__(self, request, *args, **kwargs): | |
| func = self.getattr(self, request.method.lower(), "get") | |
| return func(request, *args, **kwargs) | |
| model_class = None #Override in child classes. | |
| # HTTP methods www.w3.org/Protocols/rfc2616/rfc2616-sec9.html | |
| def get(request, *args, **kwargs): | |
| if oid: #"object id" since id is a reserved word | |
| data = self.model_class.objects.get(pk=oid) | |
| else: | |
| data = self.model_class.objects.all() | |
| return HttpResponse(to_json(data), content_type="application/json") | |
| def post(request, *args, **kwargs): | |
| data = parse_json(request.body) | |
| try: | |
| obj = this.model_class(**data) | |
| obj.save() | |
| except Exception as e: #TODO | |
| print e | |
| print data | |
| return HttpResponse(to_json(obj), content_type="application/json") | |
| def put(request, *args, **kwargs): | |
| data = parse_json(request.body) | |
| obj = self.model_class.objects.get(pk=oid) | |
| for field in data.keys: | |
| setattr(obj, field = data[field]) | |
| obj.save() | |
| return self.post(to_json(obj), *args, **kwargs) | |
| def delete(request, *args, **kwargs): | |
| obj = self.model_class.objects.get(pk=oid) | |
| obj.delete() | |
| return HttpResponse("", content_type="application/json") | |
| def head(request, *args, **kwargs): | |
| return self.get(request, *args, **kwargs) | |
| def options(request, *args, **kwargs): | |
| raise NotImplementedError | |
| def trace(request, *args, **kwargs): | |
| raise NotImplementedError | |
| def connect(request, *args, **kwargs): | |
| raise NotImplementedError | |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment