Forked from marteinn/external_tastypie_serialization.py
Last active
November 28, 2018 21:30
-
-
Save AndrewJHart/3daf2fdc3f092139e867 to your computer and use it in GitHub Desktop.
Different ways to use Tastypie's resource serialization and build a response outside of the API or a resource.. e.g. a separate view in views.py, building a custom view within the resource, etc..
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
""" | |
Credit to the parker library (https://github.com/coxmediagroup/parker/) and their TastyPieHandler. | |
""" | |
# Examples of building manual responses anywhere, w/i extra views, and overridden methods | |
############################################################################ | |
# example of building a serialized response from anywhere | |
req = HttpRequest() | |
resource = YourResource() | |
bundle = resource.build_bundle(obj=your_model) | |
bundle = resource.full_dehydrate(bundle) | |
bundle = resource.alter_detail_data_to_serialize(req, bundle) | |
json_string = resource.serialize(req, bundle, 'application/json') | |
# building a view & response for the API for our own view | |
# that provides login & logout functionality for a User Object | |
# represented as a JSON consumable API Resource `UserResource` | |
class UserResource(subclassedResource): | |
# must define urls for views within a resource | |
def prepend_urls(self): | |
return [ | |
url(r"^(?P<resource_name>%s)/login%s$" % | |
(self._meta.resource_name, trailing_slash()), | |
self.wrap_view('login'), name="api_login"), | |
url(r'^(?P<resource_name>%s)/logout%s$' % | |
(self._meta.resource_name, trailing_slash()), | |
self.wrap_view('logout'), name='api_logout'), | |
] | |
def login(self, request, **kwargs): | |
""" | |
Custom view, wrapped by the tastypie for handling auth over the API | |
uses method_check for post to allow only post requests to this endpoint; since this is a different | |
endpoint, we can use method_check to do per-view override on the allowed_methods or list_allowed_methods in meta :) | |
""" | |
self.method_check(request, allowed=['post']) | |
data = self.deserialize(request, request.body) | |
username = data.get('username', '') | |
password = data.get('password', '') | |
user = authenticate(username=username, password=password) | |
if user: | |
if user.is_active: | |
login(request, user) | |
key = user.api_key.key | |
# use create_response for simple responses or non-complex data, here we're | |
# returning the users api-key in the response to be stored in a device, etc.. | |
return self.create_response(request, { | |
'api_key': key | |
}) | |
else: | |
# simple response with status code for HTTP Forbbiden since acct is inactive | |
return self.create_response(request, { | |
'success': False, | |
'reason': 'disabled', | |
}, HttpForbidden) | |
else: | |
# simple response with proper status code HTTP Unathorized since credentials didnt match | |
return self.create_response(request, { | |
'success': False, | |
'reason': 'incorrect', | |
}, HttpUnauthorized) | |
def logout(self, request, **kwargs): | |
""" logout method - much simpler version of the login method """ | |
# check that only GET requests are allowed | |
self.method_check(request, allowed=['get']) | |
if request.user and request.user.is_authenticated(): | |
logout(request) | |
return self.create_response(request, {'success': True}) | |
else: | |
return self.create_response(request, {'success': False}, HttpUnauthorized) | |
# overriding tastypie built-in `detail` view & response for the purpose of | |
# altering this resources default detail (api/{resource_name}/{primary_key}/) | |
# response behavior to perform some special function that is not normally | |
# provided by a typical "GET" request for the default `detail` endpoint | |
# *all methods are prefixed with the corresponding HTTP verb* | |
class KeyResource(subclassedResource): | |
""" Represents an api key resource for the API """ | |
class Meta: | |
queryset = Key.objects.all() | |
resource_name = "api_key" | |
fields = ["key"] | |
authorization = DjangoAuthorization() | |
authentication = MultiAuthentication(SessionAuthentication(), BasicAuthentication()) | |
list_allowed_methods = [] | |
detail_allowed_methods = ["get"] # this is safe since we have overridden get_detail | |
def get_detail(self, request, **kwargs): | |
""" | |
modifies default response to allow only a logged in user/device to get their api key | |
by calling this resources detail view e.g. api/v1/resource_name/key/ | |
""" | |
if kwargs["pk"] != "key": | |
# override default behavior that looks for an ID and always expect resource/key/ | |
raise NotImplementedError("Resource not found") | |
# no need to check if user is authenticated since this class's authentication attr | |
# requires BasicHttpAuth or SessionAuth (Only use over SSL https) | |
obj = Key.objects.get(user=request.user) | |
# Manually build the serialized HTTP response to return | |
######################################################## | |
bundle = self.build_bundle(obj=obj, request=request) # take queryset obj & request | |
bundle = self.full_dehydrate(bundle) # serialize the object to default format (Xml / Json) | |
bundle = self.alter_detail_data_to_serialize(request, bundle) # call to ensure any special handling is in response | |
# again we use the tastypie create_response method with our bundle & current request & return it | |
return self.create_response(request, bundle) |
I tried using lines 10-17, but I get:
AttributeError: 'HttpRequest' object has no attribute 'user'
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Simple way to use built-in tastypie methods for serialization anywhere in your django code e.g. special views, etc..