from django.contrib.auth.hashers import make_password from django.contrib.auth.models import User from django.conf.urls import url from django.shortcuts import get_object_or_404 from tastypie import fields from dusken.authentication import ApiKeyAuthentication from tastypie.authorization import DjangoAuthorization from tastypie.exceptions import ImmediateHttpResponse from tastypie.http import HttpForbidden, HttpNoContent, HttpResponse, HttpAccepted from tastypie.resources import ModelResource, ALL from dusken.models import * from dusken.api.groupsbymember import GroupsByMemberResource class MemberResource(ModelResource): """ This class provides following endpoints: (1) /api/v1/member/ (2) /api/v1/member/{id}/ """ class Meta: queryset = Member.objects.all() resource_name = 'member' list_allowed_methods = [ 'get', 'post' ] detail_allowed_methods = [ 'get', 'patch', 'delete' ] authentication = ApiKeyAuthentication() authorization = DjangoAuthorization() excludes = [ 'date_joined', 'password', 'is_active', 'is_staff', 'is_superuser', 'last_login' ] filtering = { 'first_name' : [ 'exact' ], 'last_name' : [ 'exact' ], 'phone_number' : [ 'exact' ], } def prepend_urls(self): return [ url(r"^(?P%s)/(?P\d+)/groups/$" % (self._meta.resource_name), self.wrap_view("list_groups"), name="api_groups"), url(r"^(?P%s)/(?P\d+)/groups/(?P\d+)/$" % (self._meta.resource_name), self.wrap_view("detail_groups"), name="api_detail_groups"), ] def list_groups(self, request, **kwargs): resource = GroupsByMemberResource() return resource.dispatch_list(request, **kwargs) def detail_groups(self, request, **kwargs): resource = GroupsByMemberResource() return resource.dispatch_detail(request, **kwargs) def apply_filters(self, request, applicable_filters): # Extra filters are used for filtering by attributes in User class. Find out if there # are any, and if so, apply them. extra_filters = {} if 'username__exact' in applicable_filters: extra_filters['username'] = applicable_filters.pop('username__exact') if 'first_name__exact' in applicable_filters: extra_filters['first_name'] = applicable_filters.pop('first_name__exact') if 'last_name__exact' in applicable_filters: extra_filters['last_name'] = applicable_filters.pop('last_name__exact') if 'email__exact' in applicable_filters: extra_filters['email'] = applicable_filters.pop('email__exact') filtered = super(MemberResource, self).apply_filters(request, applicable_filters) for key,value in extra_filters.items(): filtered = filter(lambda m: getattr(m, key) == value, filtered) return filtered def hydrate(self, bundle): """ Catches POST and PATCH requests and intercepts data. """ update_existing_user = bundle.obj.user_ptr_id is not None if update_existing_user: # If the user already exists, return an error when attempting to change the username. if bundle.data['username'] != bundle.obj.username: raise ImmediateHttpResponse(HttpForbidden("You can't change your username.")) else: # If creating new user, check if it already exists: member_count = len(Member.objects.filter(username=bundle.data['username'])) \ + len(Member.objects.filter(email=bundle.data['email'])) \ + len(Member.objects.filter(phone_number=bundle.data['phone_number'])) if member_count > 0: raise ImmediateHttpResponse(HttpForbidden("User with given data already exists")) return bundle def obj_update(self, bundle, **kwargs): if bundle.data.get('password') is not None: bundle.obj.set_password(bundle.data['password']) bundle.obj.save() if 'address' in bundle.data and bundle.data['address'] is not None: new_address = bundle.data.get('address') address = bundle.obj.address self._update_address(address, new_address) return super(MemberResource, self).obj_update(bundle, **kwargs) def _update_address(self, address, new_address): if 'street_address' in new_address: address.street_address = new_address['street_address'] if 'city' in new_address: address.city = new_address['city'] if 'postal_code' in new_address: address.postal_code = new_address['postal_code'] if 'country' in new_address: try: country = Country.objects.get(name=new_address['country']) except Country.DoesNotExist, e: raise ImmediateHttpResponse(HttpForbidden( 'The country "{}" does not exist.'.format(new_address['country'])) ) else: address.country = country address.save() def dehydrate(self, bundle): """ Catches GET requests and adds more data. """ # Add address: address = bundle.obj.address if address is None: bundle.data['address'] = None else: # TODO: There has to be a better way... bundle.data['address'] = { 'city' : address.city, 'country' : address.country.name, 'postal_code' : address.postal_code, 'street_address' : address.street_address, } return bundle def post_detail(self, request, **kwargs): try: member = MemberResource.objects.get(kwargs['pk']) member.is_active = True member.save() return HttpAccepted() except MemberResource.DoesNotExist: pass return super(MemberResource, self).post_detail(request, **kwargs) def patch_list(self, request, **kwargs): return super(MemberResource, self).patch_list(request, **kwargs) def patch_detail(self, request, **kwargs): return super(MemberResource, self).patch_detail(request, **kwargs) def delete_list(self, request, **kwargs): return super(MemberResource, self).delete_list(request, **kwargs) def delete_detail(self, request, **kwargs): member = MemberResource.objects.get(kwargs['pk']) member.is_active = False member.save() return HttpNoContent()