I am building a REST API for my application that uses a NoSQL db (Neo4j) using Tastypie.
So I overrode some main methods of the class tastypie.resources.Resource to do so, and currently struggling to implement def obj_get_list(self, request=None, **kwargs): which is supposed to return a list of objects.
Actually, I want to pass a parameter to this method through the url (something like http://127.0.0.1:8000/api/airport/?query='aQuery' ) and then perform a query based on this parameter.
The problem is that the request is None so I can't get its parameter !
When printing the kwargs variable, I see this :
{'bundle': <Bundle for obj: '<testNeo4Django.testapp.api.Airport object at 0x9d829ac>' and with data: '{}'>}
Thanks for your help
Currently positional argument request is not passed toobj_get_list.
So you should:
def obj_get_list(self, bundle, **kwargs):
param = bundle.request.GET['param']
#fetch objects based on param
return objects
Related
I want to have a RESTful api which looks like this:
example.com/teams/
example.com/teams/<team_id>
example.com/teams/<team_id>/players
example.com/teams/<team_id>/players/<player_id>
...
example.com/teams/<team_id>/players/<player_id>/seasons/<season_id>/etc
Where each URI can appropriately handle GETs and possibly POSTs.
I would like to be able to do something like:
class Team(Resource):
def post(self):
#Handler for /teams/
def post(self, team_id):
#Handler for /teams/team_id
def post(self, team_id, player_id):
#Handler for /teams/team_id/players/player_id
and using:
api.add_resource(Team, '/teams/', 'teams/<team_id>/players/<player_id>')
Which won't work because the subsequent POST handlers overwrite the previous.
What is the right way with Flask-RESTful to handle an API where there may be a variable number of variables (variable depth of hierarchy) in the URL?
Python does not support method overloading in that particular way. In your code you are not overloading the post() function, you are redefining it
Basically the last definition of post() is what counts, which takes 3 parameters as you can see:
class Team(Resource):
def post(self, team_id, player_id):
# This is the final definition of post()
# The definitions above this one do not take effect
Otherwise it's pretty easy to get the behavior with a single method that has default values for the parameters:
class Team(Resource):
def post(self, team_id=None, player_id=None):
if team_id is None and player_id is None:
# first version
if team_id is not None and player_id is None:
# second version
if team_id is not None and player_id is not None:
# third version
For your URL, Flask will pass in None for the parameter that isn't defined in the URL.
How would correctly should look API that returns only objects belonging to the user who asks for them?
api/version/items/<items_id>
or
api/version/user/<user_id>/items/<items_id>
In the first case, the server queried the database with a user id, which it obtains from its authentication.
I don't know how to create both cases in Flask-restless. I think a preprocessor will be useful, where I could get user_id from authorization (JWT token), but I can't find a way to use it as search parameters for DB.
from flask_jwt import JWT, jwt_required, current_user
...
manager.create_api(Item,
methods=['GET'],
collection_name='items',
url_prefix='/api',
preprocessors=dict(GET_SINGLE=[api_auth],GET_MANY=[api_auth]))
#jwt_required()
def api_auth(*args, **kwargs):
user_id = current_user.id
# some code with user id addition.
pass
Preprocessor would be the place where you build a query object. I think the endpoint for items should look simply like:
api/version/items
but whithin the preprocessor you would build a query object that would be passed with the request:
GET api/version/items?q={"filters":[{"name":"userid","op":"eq","val":10}]}
You should use an endpoint that refers directly to the resources that you are trying to query. For example:
api/version/items
You should define a get_single and get_many preprocessor separately. The instance_id for the single (integer) and the result argument (dictionary) for the multiple pre-processor should be used to define what is returned to the user.
From the Flask-restless docs:
Those preprocessors and postprocessors that accept dictionaries as
parameters can (and should) modify their arguments in-place. That
means the changes made to, for example, the result dictionary will be
seen by the Flask-Restless view functions and ultimately returned to
the client.
Therefore in your pre-processors, you could do something like the following to retrieve the items (defined in a relationship to the user) in your database:
#jwt_required()
def api_auth_get_many(instance_id=None, *args, **kwargs):
user_id = current_user.id
if instance_id in User.query.get(user_id).items:
pass
else:
instance_id = None # Do not return the value if not permitted
#jwt_required()
def api_auth_get_many(result=None, *args, **kwargs):
user_id = current_user.id
result = User.query.get(user_id).items # Return all items allowed for user
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...:
The fields I return for this URL will be custom created for this Resource - there is no model that contains this information.
I want a single URL that will return the data, e.g. a GET request on /api/v1/settings.
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.
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).
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?
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
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.
I have a model with a version number in it. I want it to self-increment when new data is posted with an existing id via TastyPie. I'm currently doing this via the hydrate method, which works as long as two users don't try to update at once:
class MyResource(ModelResource):
...
def hydrate_version(self, bundle):
if 'id' in bundle.data:
target = self._meta.queryset.get(id=int(bundle.data['id']))
bundle.data['version'] = target.version+1
return bundle
I'd like to do this more robustly by using Django's F() expressions, e.g.:
def hydrate_version(self, bundle):
if 'id' in bundle.data:
from django.db.models import F
target = self._meta.queryset.get(id=int(bundle.data['id']))
bundle.data['version'] = F('version')+1
return bundle
However, this gives me an error:
TypeError: int() argument must be a string or a number, not 'ExpressionNode'
Is there a way to more robustly increment the version number with TastyPie?
thanks!
I would override the save() method for your Django Model instead, and perform the update there. That has the added advantage of ensuring the same behavior regardless of an update from tastypie or from the django/python shell.
def save(self, *args, **kwargs):
self.version = F('version') + 1
super(MyModel, self).save(*args, **kwargs)
This has been answered at github here, though I haven't tried this myself yet. To quote from that link:
You're setting bundle.data inside a hydrate method. Usually you modify bundle.obj in hydrate methods and bundle.data in dehydrate methods.
Also, those F objects are meant to be applied to Django model fields.
I think what you want is:
def hydrate_version(self, bundle):
if bundle.obj.id is not None:
from django.db.models import F
bundle.obj.version = F('version')+1
return bundle
In a Django application, I have more than a handful of views which return JSON, with an invocation similar to:
return HttpResponse(json.dumps(content), mimetype="application/json")
I want to start creating views that return either HTML or JSON depending on the Accept headers from the request. Possibly other types, too, but those are the main ones. I also want to get multiple URLs routed to this view; the file extensions ".html" and ".json" help tell clients which types they should Accept when making their request, and I want to avoid the "?format=json" antipattern.
What's the correct, blessed way to do this in Django with a minimum of boilerplate or repeated code?
(Edit: Rephrase in order to better follow SO's community guidelines.)
I think a class-based view mixin (django 1.3+) is the easiest way to do this. All your views would inherit from a base class that contains logic to respond with the appropriate content.
I think I may not be seeing your big picture here but this is what I would do:
Have a html template that you render when html is requested and keep your json.dumps(content) for when json is requested. Seems to be obvious but I thought i should mention it anyway.
Set your URLs to send you "json" or 'html'. :
(r'^some/path/(?P<url_path>.*)\.(?P<extension>html|json)$', 'some.redirect.view'),
(r'^/(?P<extension>html|json)/AppName', include(MyApp)),
# etc etc
and your view:
def myRedirectView(request, url_path, extension):
view, args, kwargs = resolve("/" + extension + "/" + urlPath)
kwargs['request'] = request
return view(*args, **kwargs)
I know this is a bit vague because I haven't fully thought it through but its where I would start.
I have addressed this by creating a generic view class, based on Django's own generic.View class, that defines a decorator 'accept_types'. This modifies the view to which it is applied so that it returns None if the indicated content-type is not in the Accept header. Then, the get() method (which is called by the generic.View dispatcher) looks like this:
def get(self, request):
self.request = request # For clarity: generic.View does this anyway
resultdata = { 'result': data, etc. }
return (
self.render_uri_list(resultdata) or
self.render_html(resultdata) or
self.error(self.error406values())
)
The actual view renderers are decorated thus:
#ContentNegotiationView.accept_types(["text/uri-list"])
def render_uri_list(self, resultdata):
resp = HttpResponse(status=200, content_type="text/uri-list")
# use resp.write(...) to assemble rendered response body
return resp
#ContentNegotiationView.accept_types(["text/html", "application/html", "default_type"])
def render_html(self, resultdata):
template = loader.get_template('rovserver_home.html')
context = RequestContext(self.request, resultdata)
return HttpResponse(template.render(context))
The (one-off) generic view class that declares the decorator looks like this:
class ContentNegotiationView(generic.View):
"""
Generic view class with content negotiation decorators and generic error value methods
Note: generic.View dispatcher assigns HTTPRequest object to self.request.
"""
#staticmethod
def accept_types(types):
"""
Decorator to use associated function to render the indicated content types
"""
def decorator(func):
def guard(self, values):
accept_header = self.request.META.get('HTTP_ACCEPT',"default_type")
accept_types = [ a.split(';')[0].strip().lower()
for a in accept_header.split(',') ]
for t in types:
if t in accept_types:
return func(self, values)
return None
return guard
return decorator
(The parameter handling in the decorator should be generalized - this code works, but is still in development as I write this. The actual code is in GitHub at https://github.com/wf4ever/ro-manager/tree/develop/src/roverlay/rovweb/rovserver, but in due course should be separated to a separate package. HTH.)