Skip to content

Instantly share code, notes, and snippets.

@svvitale
Created March 31, 2015 05:04
Show Gist options
  • Save svvitale/d3879f37e55b4e0d5246 to your computer and use it in GitHub Desktop.
Save svvitale/d3879f37e55b4e0d5246 to your computer and use it in GitHub Desktop.
Comparison of Traditional Django Views to Decorated Views for APIs
from django.views.generic import View
from django.shortcuts import get_object_or_404
from .decorators import json
from .models import RoomModel
from django.http import HttpResponse
class RoomView(View):
"""Room view class that implements the primary HTTP verbs (POST, GET, PUT, DELETE).
All verb implementations expect to receive and return dictionary objects. All serialization/deserialization
is handled outside of the view itself.
"""
@json
def post(self, json_data, *args, **kwargs):
""" POST verb handler (database create). Only supports single item creation at this time.
:param json_data: Model attributes to use in the creation of a new database entry.
:return: Public representation of the created model. This may not include all model fields, depending
on how the model defines its to_data() member.
"""
room = RoomModel(**json_data)
try:
room.save()
except django.db.IntegrityError as ex:
# Foreign or unique key error
if "violates not-null" in str(ex):
return HttpResponse(str(ex), status=400)
else:
return HttpResponse(str(ex), status=409)
return room.to_data()
@json
def get(self, json_data, item_id=None, *args, **kwargs):
""" GET verb handler (database read)
:param json_data: Not used.
:param item_id: (optional) Unique ID of the object to retrieve. If not specified, all objects will be returned.
:return: Public representation of the requested object (if item_id specified) or a dictionary containing a
single key referencing a list of public representations of all available objects in this collection.
"""
if item_id:
return get_object_or_404(RoomModel, id=item_id).to_data()
else:
return {
"rooms": [item_obj.to_data() for item_obj in RoomModel.objects.all()]
}
@json
def put(self, json_data, item_id, *args, **kwargs):
""" PUT verb handler (database update). Only supports single item updates at this time.
:param json_data: Dictionary of key/value pairs that should be updated on the specified object
:param item_id: Unique ID of the object to update.
:return: Public representation of the updated object
"""
existing_item = get_object_or_404(RoomModel, id=item_id)
for key, value in json_data.items():
setattr(existing_item, key, value)
try:
existing_item.save()
except django.db.IntegrityError as ex:
# Foreign or unique key error
if "violates not-null" in str(ex):
return HttpResponse(str(ex), status=400)
else:
return HttpResponse(str(ex), status=409)
return existing_item.to_data()
@json
def delete(self, json_data, item_id, *args, **kwargs):
""" DELETE verb handler (database delete). Only supports single item deletions at this time.
:param json_data: Not used.
:param item_id: Unique ID of the object to delete.
:return: Status 204 on success, error message on failure
"""
existing_item = get_object_or_404(RoomModel, id=item_id)
try:
existing_item.delete()
except django.db.Error as ex:
# General database error
return HttpResponse(str(ex), status=400)
except AssertionError as ex:
# Django error such as "object can't be deleted because its id attribute is set to None"
return HttpResponse(str(ex), status=400)
return existing_item.to_data()
from django.http import JsonResponse, HttpResponse
from json import loads
def json(view_func):
def wrapper(self, request, *args, **kwargs):
# Deserialize the request body into a native dictionary
if request.method == "GET":
json_data = {k: v for k, v in request.GET.items()}
elif request.body:
json_data = loads(request.body.decode('utf-8'))
else:
json_data = {}
# Call the view handler
response = view_func(self, json_data, *args, **kwargs)
# Translate the return into a JsonResponse if necessary. This allows us to return native Python
# data structures without having to remember to jsonify it.
if isinstance(response, HttpResponse):
# Most likely an error response. Just pass it through
return response
else:
# Return the appropriate HTTP status
if request.method == "POST":
status = 201 # CREATED
elif request.method == "DELETE":
status = 204 # NO CONTENT
else:
status = 200 # OK
# Serialize
return JsonResponse(response, status=status)
return wrapper
from django.db import models
from django.forms import model_to_dict
import django.db
from django.http import HttpResponse
from datetime import datetime
class Room(models.Model):
""" A chat room. """
name = models.CharField("room name", max_length=100, unique=True, default=None)
def to_data(self):
""" Iterate over all attributes and return a native Python dictionary containing all attributes. """
data = {}
# Iterate over each of our fields and return a dictionary that contains only whitelisted fields.
for attr_name, attr_val in model_to_dict(self).items():
data[attr_name] = attr_val
return data
from django.views.generic import View
from django.shortcuts import get_object_or_404
from .models import RoomModel
from json import loads
from django.http import JsonResponse, HttpResponse
class RoomView(View):
"""Room view class that implements the primary HTTP verbs (POST, GET, PUT, DELETE).
All verb implementations expect to receive and return dictionary objects. All serialization/deserialization
is handled outside of the view itself.
"""
def post(self, request, *args, **kwargs):
""" POST verb handler (database create). Only supports single item creation at this time.
:param json_data: Model attributes to use in the creation of a new database entry.
:return: Public representation of the created model. This may not include all model fields, depending
on how the model defines its to_data() member.
"""
json_data = loads(request.body.decode('utf-8'))
room = RoomModel(**json_data)
try:
room.save()
except django.db.IntegrityError as ex:
# Foreign or unique key error
if "violates not-null" in str(ex):
return HttpResponse(str(ex), status=400)
else:
return HttpResponse(str(ex), status=409)
return JsonResponse(room.to_data())
def get(self, request, item_id=None, *args, **kwargs):
""" GET verb handler (database read)
:param json_data: Not used.
:param item_id: (optional) Unique ID of the object to retrieve. If not specified, all objects will be returned.
:return: Public representation of the requested object (if item_id specified) or a dictionary containing a
single key referencing a list of public representations of all available objects in this collection.
"""
if item_id:
return JsonResponse(get_object_or_404(RoomModel, id=item_id).to_data())
else:
return JsonResponse({
"rooms": [item_obj.to_data() for item_obj in RoomModel.objects.all()]
})
def put(self, request, item_id, *args, **kwargs):
""" PUT verb handler (database update). Only supports single item updates at this time.
:param json_data: Dictionary of key/value pairs that should be updated on the specified object
:param item_id: Unique ID of the object to update.
:return: Public representation of the updated object
"""
existing_item = get_object_or_404(RoomModel, id=item_id)
json_data = loads(request.body.decode('utf-8'))
for key, value in json_data.items():
setattr(existing_item, key, value)
try:
existing_item.save()
except django.db.IntegrityError as ex:
# Foreign or unique key error
if "violates not-null" in str(ex):
return HttpResponse(str(ex), status=400)
else:
return HttpResponse(str(ex), status=409)
return JsonResponse(existing_item.to_data())
def delete(self, json_data, item_id, *args, **kwargs):
""" DELETE verb handler (database delete). Only supports single item deletions at this time.
:param json_data: Not used.
:param item_id: Unique ID of the object to delete.
:return: Status 204 on success, error message on failure
"""
existing_item = get_object_or_404(RoomModel, id=item_id)
try:
existing_item.delete()
except django.db.Error as ex:
# General database error
return HttpResponse(str(ex), status=400)
except AssertionError as ex:
# Django error such as "object can't be deleted because its id attribute is set to None"
return HttpResponse(str(ex), status=400)
return JsonResponse(existing_item.to_data())
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment