Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Creating a tastypie resource for a "singleton" non-model object

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?

like image 492
Andy Thomas Avatar asked Jan 05 '14 11:01

Andy Thomas


2 Answers

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
like image 176
miraculixx Avatar answered Oct 12 '22 11:10

miraculixx


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.

like image 38
Craig Labenz Avatar answered Oct 12 '22 11:10

Craig Labenz