Code that worked in api.py With Python 2.6, Django 1.4.1, Tastypie 0.9.11 (Running on 2.6.32-48-generic-pae #110-Ubuntu SMP)
class DriveResource(CommonModelResource):
driver = fields.ForeignKey(DriverResource, 'driver')
bus = fields.ForeignKey(BusResource, 'bus')
route = fields.ForeignKey(RouteResource, 'route')
class Meta(CommonModelResource.Meta):
queryset = Drive.objects.all()
allowed_methods = ['get', 'put']
excludes = ('bus_location',)
filtering = {
'updated': ('gt', 'gte', 'lt', 'lte', 'exact'),
'id': ('exact',),
}
def get_object_list(self, request):
whole_list = super(DriveResource, self).get_object_list(request)
todays_day_name = datetime.now().strftime('%w')
filtered_list = whole_list.filter(driver__user=request.user).filter(day=todays_day_name)
return filtered_list
def dehydrate_driver(self, bundle):
return bundle.obj.driver.person_id
def dehydrate_bus(self, bundle):
return bundle.obj.bus.number
def dehydrate_route(self, bundle):
return bundle.obj.route.id
def hydrate_bus_location(self, bundle):
# Guard against the method being called twice
if not hasattr(bundle, 'geopoint_processed'):
lon = bundle.data['bus_location'][0] / 1e6
lat = bundle.data['bus_location'][1] / 1e6
bundle.data['bus_location'] = Point(lon, lat)
bundle.geopoint_processed = True
return bundle
class DriveFullResource(CommonModelResource):
driver = fields.ForeignKey(DriverResource, 'driver', full=True, blank=True, null=True)
bus = fields.ForeignKey(BusResource, 'bus', full=True, blank=True, null=True)
route = fields.ForeignKey(RouteResource, 'route', full=True)
class Meta(CommonModelResource.Meta):
queryset = Drive.objects.all()
resource_name = 'drive'
allowed_methods = ['get', 'put']
excludes = ('bus_location',)
def obj_get_list(self, request=None, **kwargs):
whole_list = super(DriveFullResource, self).obj_get_list(request, **kwargs)
todays_day_name = datetime.now().strftime('%w')
filtered_list = whole_list.filter(driver__user=request.user).filter(day=todays_day_name)
return filtered_list
def hydrate_bus_location(self, bundle):
# Guard against the method being called twice
if not hasattr(bundle, 'geopoint_processed'):
lon = bundle.data['bus_location'][0] / 1e6
lat = bundle.data['bus_location'][1] / 1e6
bundle.data['bus_location'] = Point(lon, lat)
bundle.geopoint_processed = True
return bundle
def put_detail(self, request, **kwargs):
deserialized = self.deserialize(request, request.raw_post_data, format=request.META.get('CONTENT_TYPE', 'application/json'))
deserialized = self.alter_deserialized_detail_data(request, deserialized)
bundle = self.build_bundle(data=dict_strip_unicode_keys(deserialized), request=request)
try:
drive = Drive.objects.get(pk=kwargs['pk'])
bundle = self.hydrate_bus_location(bundle)
drive.bus_location = bundle.data['bus_location']
drive.save()
return http.HttpNoContent()
except:
return http.HttpBadRequest()
Does not work with Python 2.6, Django 1.5, Tastypie 0.11 (Running on Centos 5)
Calling server with : https://api.server.com/api/full/drive/?format=json&limit=50&user=12345
I tried changing to:
def obj_get_list(self, bundle, **kwargs):
request = bundle.request
whole_list = super(DriveFullResource, self).get_object_list(request)
todays_day_name = datetime.now().strftime('%w')
filtered_list = whole_list.filter(driver__user=request.user).filter(day=todays_day_name)
return filtered_list
and
def obj_get_list(self, bundle, **kwargs):
request = bundle.request
whole_list = super(DriveFullResource, self).obj_get_list(bundle, **kwargs)
todays_day_name = datetime.now().strftime('%w')
filtered_list = whole_list.filter(driver__user=request.user).filter(day=todays_day_name)
return filtered_list
Still get error messages of the type below:
{"error_message": "obj_get_list() got multiple values for keyword
argument 'bundle'", "traceback": "Traceback (most recent call
last):\n\n File
\"/usr/lib/python2.6/site-packages/tastypie/resources.py\", line 195,
in wrapper\n\n File
\"/usr/lib/python2.6/site-packages/tastypie/resources.py\", line 426,
in dispatch_list\n\n File
\"/usr/lib/python2.6/site-packages/tastypie/resources.py\", line 458,
in dispatch\n\n File
\"/usr/lib/python2.6/site-packages/tastypie/resources.py\", line 1266,
in get_list\n\n File
\"/home/django_projects/project/src/routes/api.py\", line 110, in
obj_get_list\n request = bundle.request\n\nTypeError:
obj_get_list() got multiple values for keyword argument 'bundle'\n"}
{"error_message": "obj_get_list() got multiple values for keyword
argument 'bundle'", "traceback": "Traceback (most recent call
last):\n\n File
\"/usr/lib/python2.6/site-packages/tastypie/resources.py\", line 195,
in wrapper\n\n File
\"/usr/lib/python2.6/site-packages/tastypie/resources.py\", line 426,
in dispatch_list\n\n File
\"/usr/lib/python2.6/site-packages/tastypie/resources.py\", line 458,
in dispatch\n\n File
\"/usr/lib/python2.6/site-packages/tastypie/resources.py\", line 1266,
in get_list\n\n File
\"/home/django_projects/project/src/routes/api.py\", line 114, in
obj_get_list\n return filtered_list\n\nTypeError: obj_get_list()
got multiple values for keyword argument 'bundle'\n"}
I tested the method on Tastypie 0.11 and seems to work ok. But in my opinion you are taking wrong and error prone approach. I prepared solution that will eliminate them.
Problem definition
You didn't defined what is your code supposed to do. Reviewing code I assume you want to have resource that returns object for currently logged user and filtered by day name.
In other words you want to limit access to resource - And this is.. I think you know, authorization.
Why not obj_get_list?
You see that Tastypie obj_get_list
is quite Django ORM specific and not documented well. I don't really know how to use it. I wouldn't dare to deal with it.
Solution
Let's define Authorization class for your resource:
from datetime import datetime
from tastypie.authorization import Authorization
class DriverAuthorization(Authorization):
def read_list(self, object_list, bundle):
todays_day_name = datetime.now().strftime('%w')
return object_list.filter(driver__user=bundle.request.user,
day=todays_day_name)
def read_detail(self, object_list, bundle):
todays_day_name = datetime.now().strftime('%w')
return bundle.obj.day == todays_day_name and bundle.obj.driver.user == bundle.request.user
def create_list(self, object_list, bundle):
return []
def create_detail(self, object_list, bundle):
return False
def update_list(self, object_list, bundle):
return []
def update_detail(self, object_list, bundle):
object_before_update = object_list.get(pk=bundle.obj.pk)
return object_before_update.driver.user == bundle.request.user
def delete_list(self, object_list, bundle):
return []
def delete_detail(self, object_list, bundle):
return False
Now attach authorization to resource:
class DriveResource(CommonModelResource):
driver = fields.ForeignKey(DriverResource, 'driver')
bus = fields.ForeignKey(BusResource, 'bus')
route = fields.ForeignKey(RouteResource, 'route')
class Meta(CommonModelResource.Meta):
queryset = Drive.objects.all()
allowed_methods = ['get', 'put']
excludes = ('bus_location',)
authorization = DriverAuthorization()
filtering = {
'updated': ('gt', 'gte', 'lt', 'lte', 'exact'),
'id': ('exact',),
}
def dehydrate_driver(self, bundle):
return bundle.obj.driver.person_id
def dehydrate_bus(self, bundle):
return bundle.obj.bus.number
def dehydrate_route(self, bundle):
return bundle.obj.route.id
def hydrate_bus_location(self, bundle):
# Guard against the method being called twice
if not hasattr(bundle, 'geopoint_processed'):
lon = bundle.data['bus_location'][0] / 1e6
lat = bundle.data['bus_location'][1] / 1e6
bundle.data['bus_location'] = Point(lon, lat)
bundle.geopoint_processed = True
return bundle
Benefits
1. Code now is much more clearer to read
2. You have control on what user can do or cannot do in one place.
3. No obj_get_list
means no more problems.