Creating a tastypie resource for a "singleton" non-model object
Asked Answered
G

2

8

I'm using tastypie and I want to create a Resource for a "singleton" non-model object.

For the purposes of this question, let's assume what I want the URL to represent is some system settings that exist in an ini file. What this means is that...:

  1. The fields I return for this URL will be custom created for this Resource - there is no model that contains this information.
  2. I want a single URL that will return the data, e.g. a GET request on /api/v1/settings.
  3. The returned data should return in a format that is similar to a details URL - i.e., it should not have meta and objects parts. It should just contain the fields from the settings.
  4. It should not be possible to GET a list of such object nor is it possible to perform POST, DELETE or PUT (this part I know how to do, but I'm adding this here for completeness).
  5. Optional: it should play well with tastypie-swagger for API exploration purposes.

I got this to work, but I think my method is kind of ass-backwards, so I want to know what is the common wisdom here. What I tried so far is to override dehydrate and do all the work there. This requires me to override obj_get but leave it empty (which is kind of ugly) and also to remove the need for id in the details url by overriding override_urls.

Is there a better way of doing this?

Gametocyte answered 5/1, 2014 at 11:51 Comment(3)
Can you show what you've done so far?Shillelagh
Why singleton, why non-model?Simmon
@RaydelMiranda I don't understand your questions; I supplied an example.Gametocyte
R
8

You should be able to achieve this with the following. Note I haven't actually tested this, so some tweaking may be required. A more rich example can be found in the Tastypie Docs

class SettingsResource(Resource):
    value = fields.CharField(attribute='value', help_text='setting value')

    class Meta:
        resource_name = 'setting'
        fields = ['value']
        allowed_methods = ['get']

    def detail_uri_kwargs(self, bundle_or_obj):
        kwargs = {}
        return kwargs

    def get_object_list(self, request):
        return [self.obj_get()]

    def obj_get_list(self, request=None, **kwargs):
        return [self.obj_get()]

    def obj_get(self, request=None, key=None, **kwargs):
        setting = SettingObject()
        setting.value = 'whatever value'
        return setting

The SettingObject must support the getattr and setattr methods. You can use this as a template:

class SettingObject(object):
    def __init__(self, initial=None):
        self.__dict__['_data'] = {}
        if initial:
            self.update(initial)

    def __getattr__(self, name):
        return self._data.get(name, None)

    def __setattr__(self, name, value):
        self.__dict__['_data'][name] = value

    def update(self, other):
        for k in other:
            self.__setattr__(k, other[k])

    def to_dict(self):
        return self._data
Resupinate answered 16/1, 2014 at 17:47 Comment(0)
S
1

This sounds like something completely outside of TastyPie's wheelhouse. Why not have a single view somewhere decorated with @require_GET, if you want to control headers, and return an HttpResponse object with the desired payload as application/json?

The fact that your object is a singleton and all other RESTful interactions with it are prohibited suggests that a REST library is the wrong tool for this job.

Sportswoman answered 8/1, 2014 at 19:55 Comment(2)
One problem with your suggestion is that it won't display the resource in tastypie-swagger, which is essential for API exploration.Gametocyte
Mostly I'm suggesting that this isn't a problem to solve with an API, restful or not.Sportswoman

© 2022 - 2024 — McMap. All rights reserved.