Django Tastypie Override URL with slug

Question:

I have the following code:

def override_urls(self):
    return [
        url(r"^(?P<resource_name>%s)/(?P<slug>[wd_.-]+)/$" % self._meta.resource_name, self.wrap_view('dispatch_detail'), name="api_dispatch_detail"),
    ]

Which produces an URL like:

/api/v1/nodes/<slug>/

Everything is fine except that self.get_resource_uri(bundle) returns /api/v1/nodes/<id>/ and I cannot compare the current URL with the resource URI effectively.

What am I doing wrong?

Solution: working code

I implemented the proposed solution here:
https://github.com/ninuxorg/nodeshot/blob/refactoring/nodeshot/core/base/resources.py

Any additional feedback for improvement is welcome.

Asked By: nemesisdesign

||

Answers:

You could override get_resource_uri on your resource to return the correct uri. After all, the correct one is the one with the slug since that is the (first) one captured by your resource.

Update

The right way to do this is actually to skip override_urls and put this on the Resource’s Meta:

detail_uri_name = 'slug'

TLDR Background

I dug a bit deeper and it looks like there’s a much better place to implement this. The default implementation of get_resource_uri (indirectly) calls self.detail_uri_kwargs and then reverse.

The default implementation of detail_uri_kwargs in ModelResource just looks up self._meta.detail_uri_name (whose name is not intuitive), and grabs that key off the model. detail_uri_name defaults to pk.

If you just provide a different name here, you can skip the override_urls and the get_resource_uri!

Something like this (building on the code linked in comments by the OP):

from tastypie.resources import ModelResource
from tastypie.bundle import Bundle

class BaseSlugResource(ModelResource):
    """ Base Model Resource using slug urls """

    class Meta:
        abstract = True
        detail_uri_name = 'slug'

I’m not sure off the top of my head whether resource Metas are inherited (I’m going to guess they’re not), so this may not work as a base class. Luckily, the one line required is probably fine to paste into each Resource that needs it.

Answered By: dokkaebi

I’d like to clean it up with a working example even though the @dokkaebi’s answer is marked (and partially) correct. The only missing part is you still have to prepend url that will be resolved for listing and such.

from tastypie.resources import ModelResource
from myapp.models import SomeModel

class BaseSlugResource(ModelResource):
    """ Base Model Resource using slug urls """

    class Meta:
        queryset = SomeModel.objects.all()
        detail_uri_name = 'slug'

    def prepend_urls(self):
        return [
            url(r'^(?P<resource_name>%s)/(?P<slug>[w.-]+)/$' % self._meta.resource_name, self.wrap_view('dispatch_detail'), name='api_dispatch_detail'),
        ]

This will display the correct resource_uri’s for listing as well as resource get. However, you’ll most likely loose {schema} resource (i.e. /api/posts/schema/) as it’s treated as a slug too.

Answered By: kirpit
Categories: questions Tags: , ,
Answers are sorted by their score. The answer accepted by the question owner as the best is marked with
at the top-right corner.