Non-PK Identifiers for Tastypie URLs
I've been using Tastypie at work for a few weeks now, and while it's been generally great, I've had my share of frustrations with it. The most recent one involved a cookbook recipe that didn't quite work. Following the recipe, I got Tastypie to resolve my URLs, but I couldn't for the life of me get it to return a non-blank resource_uri. Because I am using Tastypie with Backbone, the frontend code relies on these URIs.
If you are using Tastypie 0.9.11, you will have to override get_resource_uri. The trick is to make sure you plug in all of the following into Django's urlresolver resolve:
- api_name - The name you used for Tastypie's Api class. Put it in Meta and grab it as self._meta.api_name. This couples the API with the Resource. Oh well.
- resource_name - Grab it as self._meta.resource_name
- The new key that you defined as a replacement for pk. In the cookbook example it is called username.
ModelResource (Tastypie v0.9.11):
class CompanyResource(ModelResource):
class Meta:
resource_name = 'companies'
queryset = Company.objects.all()
def override_urls(self):
return [
url(r"^(?P<resource_name>%s)/(?P<domain>[\w\d_.-]+)/$" %
self._meta.resource_name,
self.wrap_view('dispatch_detail'),
name="api_dispatch_detail"),
]
def get_resource_uri(self, bundle_or_obj):
kwargs = {
'resource_name': self._meta.resource_name,
'api_name': self._meta.api_name
}
if isinstance(bundle_or_obj, Bundle):
kwargs['domain'] = bundle_or_obj.obj.domain
else:
kwargs['domain'] = bundle_or_obj.domain
return self._build_reverse_url("api_dispatch_detail", kwargs=kwargs)
If you are using a later version of Tastypie, you can simplify it further. I started off using Tastypie master, but had to go back to v0.9.11 because Validation was broken.
ModelResource (Tastypie master):
class CompanyResource(ModelResource):
class Meta:
resource_name = 'companies'
api_name = 'v1'
detail_uri_name = 'domain'
queryset = Company.objects.all()
def prepend_urls(self):
return [
url(r"^(?P<resource_name>%s)/(?P<domain>[\w\d_.-]+)/$" %
self._meta.resource_name,
self.wrap_view('dispatch_detail'),
name="api_dispatch_detail"),
]
Resource (Tastypie master):
class CompanyResource(Resource):
class Meta:
resource_name = 'companies'
api_name = 'v1'
def obj_get(self, request=None, **kwargs):
return Company.objects.get(domain=kwargs['domain'])
def prepend_urls(self):
return [
url(r"^(?P<resource_name>%s)/(?P<domain>[\w\d_.-]+)/$" %
self._meta.resource_name,
self.wrap_view('dispatch_detail'),
name="api_dispatch_detail"),
]
# This (along with _meta.api_name and _meta.resource_name) will
# need to be plugged into the above URL
def detail_uri_kwargs(self, bundle_or_obj):
if isinstance(bundle_or_obj, Bundle):
return { 'domain': bundle_or_obj.obj.domain }
else:
return { 'domain': bundle_or_obj.domain }
I created a Tastypie issue. Hope this saved you some frustration.
Cheers!