milibris / flask-rest-jsonapi Goto Github PK
View Code? Open in Web Editor NEWFlask extension to build REST APIs around JSONAPI 1.0 specification.
Home Page: http://flask-rest-jsonapi.readthedocs.io
License: MIT License
Flask extension to build REST APIs around JSONAPI 1.0 specification.
Home Page: http://flask-rest-jsonapi.readthedocs.io
License: MIT License
Hi there. I'm having trouble requesting multiple related resources and i can't tell if i'm misunderstanding ember-data or the API spec or if there's a problem with the backend library i'm using.
Example: Articles hasMany Comments, Comments belongsTo Author, Comments belongsTo Approver.
I want to load a specific Article along with all Comments and for each Comment, the Author and Approver.
If i do /articles/1?include=comments,comments.author,comments.approver
, i get the Article with the Comments and the Approvers, but no Authors.
If i do /articles/1?include=comments,comments.approver,comments.author
, i get the Article with the Comments and the Authors, but no Approvers.
I am very confident that i have the Schemas set up correctly, if i remove either of the 'comments.*' options from the query string, the data for the other one is loaded fine. The only issue occurs when i try to ask for both at the same time. I believe that JSONAPI should support this so i believe this is a bug?
Thanks for any help you can provide!
Currently used page[number]
/page[size]
pagination strategy is less flexible than offset
/limit
strategy. Getting page by number
and size
is easy and efficient with offset
/limit
, but getting data given offset and limit can be quite inefficient if we can only set page.
For example, the worst case. We want to get x
rows with offset x - 1
. We need to get 2x - 1
rows and cut requited locally. If we were using offset
/limit
pagination strategy we would have downloaded almost twice less data from our server.
It would be better if flask-rest-jsonapi
supported both strategies.
FSADeprecationWarning: SQLALCHEMY_TRACK_MODIFICATIONS adds significant overhead and will be disabled by default in the future. Set it to True or False to suppress this warning. 'SQLALCHEMY_TRACK_MODIFICATIONS adds significant overhead and '
app.config['SQLALCHEMY_TRACK_MODIFICATIONS'] = False
resolved this warning.
In 0.13.0+ when you query a collection with included relations, any collection items that do not have a relation item are omitted from the response data.
I took the example project and reproduced the bug here.
For example, in a GET request to /persons?include=events
, any person
resource that does not have an associated event
resource will be returned in 0.12.6 but not 0.13.0.
With 0.12.6:
{
data:[
{
type:"person",
relationships:{
events:{
links:{
self:"/persons/1/relationships/events",
related:"/events?id=1"
},
data:[ ]
}
},
attributes:{
name:"Bob"
},
id:"1",
links:{
self:"/persons/1"
}
}
],
links:{
self:"/persons?include=events"
},
meta:{
count:1
},
jsonapi:{
version:"1.0"
}
}
With 0.13.0:
{
data:[ ],
links:{
self:"/persons?include=events"
},
meta:{
count:1
},
jsonapi:{
version:"1.0"
}
}
Flask-Resltess in there new beta version has included simple filters. Like:
# instead of
filter=[{"name": "field", "op": "eq", "val": "field_value"}]
# one can use for equal operations
filter[field]=field_value
This approach makes urls look less ugly (as it removes most of the special characters thus encoding uri gives a cleaner result) and shorter urls to worry about.
Hi,
thanks for building this module. Have you considered how user authentification and limits to operation (e.g. only allow Read) could be handled? Something like the decorators from Flask-Security would be neat!
Cheers,
Sebastian
I'm trying to use flask-jwt-extended for authentication management. I can use jwt_required decorator for Resources and it works fine. However I'm having problems with using identity inside before_update_object method. I can load the current identity with get_jwt_identity() and it is dictionary. However if I try to access a key within the dictionary it throws an error. It works fine in before_create_object method though.
class StuffDetail(ResourceDetail):
def before_update_object(self, obj, data, view_kwargs):
current_user = get_jwt_identity()
if int(obj.user.id != current_user.id):
raise JsonApiException(detail="You are not authorized to edit this resource.",
status=403,
title="Authorization Error")
schema = StuffSchema
data_layer = {'session': db.session,
'model': Stuff,
'methods': {''before_update_object' : before_update_object}
}
decorators = (jwt_required,)
Traceback (most recent call last):
File "E:\WEB\Project1\flask\lib\site-packages\flask\app.py", line 1994, in __call__
return self.wsgi_app(environ, start_response)
File "E:\WEB\Project1\flask\lib\site-packages\flask\app.py", line 1985, in wsgi_app
response = self.handle_exception(e)
File "E:\WEB\Project1\flask\lib\site-packages\flask\app.py", line 1540, in handle_exception
reraise(exc_type, exc_value, tb)
File "E:\WEB\Project1\flask\lib\site-packages\flask\app.py", line 1982, in wsgi_app
response = self.full_dispatch_request()
File "E:\WEB\Project1\flask\lib\site-packages\flask\app.py", line 1614, in full_dispatch_request
rv = self.handle_user_exception(e)
File "E:\WEB\Project1\flask\lib\site-packages\flask\app.py", line 1517, in handle_user_exception
reraise(exc_type, exc_value, tb)
File "E:\WEB\Project1\flask\lib\site-packages\flask\app.py", line 1612, in full_dispatch_request
rv = self.dispatch_request()
File "E:\WEB\Project1\flask\lib\site-packages\flask\app.py", line 1598, in dispatch_request
return self.view_functions[rule.endpoint](**req.view_args)
File "E:\WEB\Project1\flask\lib\site-packages\flask_jwt_extended\view_decorators.py", line 35, in wrapper
return fn(*args, **kwargs)
File "E:\WEB\Project1\flask\lib\site-packages\flask_rest_jsonapi\decorators.py", line 32, in wrapper
return func(*args, **kwargs)
File "E:\WEB\Project1\flask\lib\site-packages\flask\views.py", line 84, in view
return self.dispatch_request(*args, **kwargs)
File "E:\WEB\Project1\flask\lib\site-packages\flask_rest_jsonapi\resource.py", line 72, in dispatch_request
raise e
TypeError: __init__() takes at least 3 arguments (4 given)
Every time new Resource class instantiates, methods (get, post, patch, put) are wrapped with check_headers decorator. Wrapping occures on class level. For example, after creating ten Resource classes, there are ten same decorators wrapping get method.
https://github.com/miLibris/flask-rest-jsonapi/blob/master/flask_rest_jsonapi/resource.py#L39
Since Flask creates new instance of View class (which is inherited by Resource) with every incoming request, in the end there are lots of same decorators wrapping (get, post, patch, put) methods. That causes RecursionError at some point.
The marshmallow verison is currently fixed to 2.13.1 which makes it hard if someone (like me) wants to integrate flask-rest-jsonapi into a existing application which uses another verion of marshmallow.
marshmallow_jsonapi requires 'marshmallow>=2.3.0'. does flask-rest-jsonapi depend on any specific features of marshmallow which where introduced after version 2.3.0? Basically between 2.3.0 and 2.13.1, no backward incompatible changes should have been made.
I sugest to change the requirement to marshmallow>=2.3.0
and maybe make it more future proof with marshmallow>=2.3.0, <3
because version 3 of marhsmallow might introduce breaking changes.
The JSONAPI spec specifically leaves latitude for the types of an object's attributes, including attributes of an object that are themselves an array or have some other join conditions but are not related objects (to ensure they cannot be individually addressed and/or are required).
That allows us to express one-to-many semantics even for required attributes, and to guarantee those attributes are present when an object is created or updated.
For example, we might want to require "tags" for people objects:
{
"data": {
"attributes": {
"name": "James",
"person_tags": [
{
"key": "k1",
"value": "v1"
},
{
"key": "k2",
"value": "v2"
}
]
},
"type": "person"
}
SQLAlchemy and Marshmallow support this feature via the relationship option and List and Nested field types, but the current create_object cannot handle this use case.
class Person_Tags(db.Model):
id = db.Column(db.Integer, db.ForeignKey('person.id'), primary_key=True, index=True)
key = db.Column(db.String, primary_key=True)
value = db.Column(db.String, primary_key=True)
class Person(db.Model):
id = db.Column(db.Integer, primary_key=True)
name = db.Column(db.String)
person_tags= db.relationship("Person_Tags")
## Note we inherit from Marshmallow schema instead of Marshmallow JSONAPI schema here
class PersonTagsSchema(mSchema):
class Meta:
type_ = 'person_tags'
id = fields.Str(dump_only=True)
key = fields.Str()
value = fields.Str()
class PersonSchema(Schema):
class Meta:
type_ = 'person'
self_view = 'person_detail'
self_view_kwargs = {'id': '<id>'}
self_view_many = 'person_list'
id = fields.Str(dump_only=True)
name = fields.Str()
person_tags = fields.List(fields.Nested(PersonTagsSchema))
I am working on a problem that needs filtering out related objects that doesn't belong to the currently logged in user, which means I can't implement the query filter in my model or database.
Here, I intend to override after_get_relationship
method and apply my filter there, but according to how it's called in get_relationship
, the filter won't be applied thus doesn't affect the final result. I also tried iterating through the list of related_objects
and call del
on objects that needs to be filtered, but that didn't help.
My question is what should I do to achieve what I intended?
The current calculation of last_page
value is:
last_page = int(ceil(object_count / page_size))
Which means when my object_count
is 43 and page_size
is 10, last_page
will be 4 while it should be 5 instead.
My current workaround is to set app.config['PAGE_SIZE'] = 10.0
instead of just 10 :) However, this won't work when we specify page size as an url parameter like this: ?page[size]=20
Refer: #64
def before_post(data=None, *args, **kwargs):
if not data:
raise ObjectNotFound({'parameter': 'data'},
"Not a POST request or No data supplied")
code = data['data']['attributes']['code']
publisher = self.session.query(Device).filter_by(code=code).first()
if not publisher:
raise ObjectNotFound({'parameter': 'code'},
"No publisher device found with provided code")
publisher_id = publisher.id
data['data']['attributes']['publisher_id'] = publisher_id
del data['data']['attributes']['code']
pass
This throws-
Traceback (most recent call last): File "/var/www/html/vrxis_rest_json/lib/python2.7/site-packages/flask/app.py", line 1997, in __call__ return self.wsgi_app(environ, start_response) File "/var/www/html/vrxis_rest_json/lib/python2.7/site-packages/flask/app.py", line 1985, in wsgi_app response = self.handle_exception(e) File "/var/www/html/vrxis_rest_json/lib/python2.7/site-packages/flask/app.py", line 1540, in handle_exception reraise(exc_type, exc_value, tb) File "/var/www/html/vrxis_rest_json/lib/python2.7/site-packages/flask/app.py", line 1982, in wsgi_app response = self.full_dispatch_request() File "/var/www/html/vrxis_rest_json/lib/python2.7/site-packages/flask/app.py", line 1614, in full_dispatch_request rv = self.handle_user_exception(e) File "/var/www/html/vrxis_rest_json/lib/python2.7/site-packages/flask/app.py", line 1517, in handle_user_exception reraise(exc_type, exc_value, tb) File "/var/www/html/vrxis_rest_json/lib/python2.7/site-packages/flask/app.py", line 1612, in full_dispatch_request rv = self.dispatch_request() File "/var/www/html/vrxis_rest_json/lib/python2.7/site-packages/flask/app.py", line 1598, in dispatch_request return self.view_functions[rule.endpoint](**req.view_args) File "/var/www/html/vrxis_rest_json/lib/python2.7/site-packages/flask_rest_jsonapi/decorators.py", line 33, in wrapper return func(*args, **kwargs) File "/var/www/html/vrxis_rest_json/lib/python2.7/site-packages/flask/views.py", line 84, in view return self.dispatch_request(*args, **kwargs) File "/var/www/html/vrxis_rest_json/lib/python2.7/site-packages/flask_rest_jsonapi/resource.py", line 77, in dispatch_request raise e TypeError: before_post() got multiple values for keyword argument 'data'
I have tried before_post(args, kwargs, data=None)
too. Same result. Please, let me know if I have missed something. Using 0.13.2 as of now.
Small correction.
https://github.com/miLibris/flask-rest-jsonapi/blob/master/flask_rest_jsonapi/exceptions.py#L4
It should be "Unknown error".
I don't know if this is an issue or just my ignorance but I am having difficulty adding my api routes with a blue print prefix:
app/api/views:
from app.api.resources import UserList, UserDetail, MaterialList, MaterialDetail
from flask import Blueprint
from flask_rest_jsonapi import Api
json_api = Blueprint('api', __name__, url_prefix='/api') # @todo figure out how to add blueprints
api = Api(blueprint=json_api)
# User routes
api.route(UserList, 'user_list', '/users')
api.route(UserDetail, 'user_detail', '/users/<string:id>')
# Material routes
api.route(MaterialList, 'material_list', '/materials')
api.route(MaterialDetail, 'material_detail', '/materials/<string:id>')
app/init.py
# -*- coding: utf-8 -*-
from flask import Flask
from app.api.views import api
from app.database import db
from app.cors import cors
from app.auth.views import auth
def create_app():
app = Flask(__name__)
app.config.from_object('config')
db.init_app(app)
cors.init_app(app)
api.init_app(app)
app.register_blueprint(auth)
return app
def setup_database(app):
with app.app_context():
db.create_all()
app = create_app()
setup_database(app)
Now if I visit http://localhost:5000/api/materials I receive the following error:
werkzeug.routing.BuildError: Could not build url for endpoint 'material_list'. Did you mean 'api.material_list' instead?
The obvious solution that springs to mind would be to change the endpoint name in routes and schemas to: api.material_list
however the error is now:
werkzeug.routing.BuildError: Could not build url for endpoint 'api.material_list'. Did you mean 'api.api.material_list' instead?
If I remove the blueprint:
api = Api()
and visit: http://localhost:5000/materials everything works perfectly!
I've tried defining the blueprint in every way conceivable but I'm still getting the same error. If this is not an issue and I'm just being a noob please forgive me.
I'm trying to move from flask-restless to rest-jsonapi, but i'm getting an attributeerror as soon as i include a resource.
e.g. in my model i have a user-class:
class User(Base):
uid = db.Column(db.String(100), unique = True)
password = db.Column(PasswordType(schemes=app.config['PASSWORD_SCHEMES']), nullable=False)
name = db.Column(db.String(100))
email = db.Column(EmailType)
with this schema:
class UserSchema(Schema):
class Meta:
type_ = 'user'
self_view = 'user_detail'
self_view_kwargs = {'id':'<id>'}
self_view_many = 'user_list'
uid = fields.Str(dump_only=True)
name = fields.Str(required=True,load_only=True)
email = fields.Email(load_only=True)
display_name = fields.Function(lambda obj: "{} <{}>".format(obj.name, obj.email))
projects=Relationship(self_view='user_projects',
self_view_kwargs={'id':'<id>'},
related_view='project_list',
related_view_kwargs={'id':'<id>'},
many=True,
schema='ProjectSchema',
type_='project')
but including
class UserList(ResourceList):
schema = UserSchema
data_layer = {'session': db.session,
'model ': User}
causes:
File "/home/tzimme/.local/lib/python3.5/site-packages/project/rest.py", line 265, in
class UserList(ResourceList):
File "/home/tzimme/.local/lib/python3.5/site-packages/flask_rest_jsonapi/resource.py", line 39, in new
rv._data_layer = data_layer_cls(data_layer_kwargs)
File "/home/tzimme/.local/lib/python3.5/site-packages/flask_rest_jsonapi/data_layers/alchemy.py", line 26, in init
.format(self.resource.name))
AttributeError: 'SqlalchemyDataLayer' object has no attribute 'resource'
Have you considered reducing the amount of configuration boilerplate required? Are you open to pull requests which help implement defaults? Prompted by reading the Logical data abstraction docs- the comment says we can see that Person has an attribute named โpasswordโ but it's not actually easy to see that due to the duplication of most of the fields and related configuration.
Suggestions:
I understand that convention over configuration is a bit more of a Ruby/Rails thing, but every extra bit of code introduces the possibility of error.
In comparison, I've used Python eve before which has seems to have defaults (see https://web.archive.org/web/20170809171457/http://python-eve.org/config.html#global-configuration) and Flask-Restless seems to have sane defaults as well.
How can I filter a relationship using a null value? Flask-Restless usesis_null
or is_not_null
, but there doesn't seem to be a similar operator in Flask-Rest-JSONAPI. Any recommendations on how to achieve the same result in Flask-Rest-JSONAPI?
hi, please how do i place this statement in resourcelist?
cat_ids = [cid for cid, in db.session.query(Catalog.category_id).filter(Catalog.category_id.isnot(None)).distinct(Catalog.category_id)]
listed = db.session.query(Category).join(Catalog).options(contains_eager(Category.products)).filter(Category.id.in_(cat_ids)).all()
newlist = [l.path_to_root()[-1] for l in listed]
new_list = list(set(newlist))
this query list output list of categories, but i m having hard time inserting this in flask-rest-jsonapi,
can anyone please guide?
here is my full code
import os
import random
from sqlalchemy.orm import relationship, backref, contains_eager
from core import db, api
from flask_rest_jsonapi import ResourceDetail, ResourceList, ResourceRelationship
from flask_rest_jsonapi.exceptions import ObjectNotFound
from sqlalchemy.orm.exc import NoResultFound
from marshmallow_jsonapi.flask import Schema, Relationship
from marshmallow_jsonapi import fields
from core.main import main
from flask import render_template, redirect, request, url_for, flash, make_response, abort, current_app, jsonify
from datetime import datetime
from core.main.model import Category, Catalog
class Category(db.Model, BaseNestedSets):
id = Column(Integer, primary_key=True)
name = Column(String(400), index=True, unique=False)
image = Column(String(400), index=True, unique=False)
# image = Column(ImageColumn(thumbnail_size=(30, 30, True), size=(300, 300, True)))
products = relationship("Catalog", backref='categs')
# TODO do a multi-joins and filter in order to fetch sizes and colors from categories.
@classmethod
def get_parents_list(cls, db, category_id):
beginning_getter = db.session.query(cls).filter(cls.id == category_id).cte(name='parent_for', recursive=True)
with_recursive = beginning_getter.union_all(db.session.query(cls).filter(cls.id == beginning_getter.c.parent_id))
return db.session.query(with_recursive)
@classmethod
def get_children_list(cls, db, catalog, category_id):
beginning_getter = db.session.query(cls).filter(cls.id == category_id).cte(name='children_for', recursive=True)
with_recursive = beginning_getter.union_all(db.session.query(cls).filter(cls.parent_id == beginning_getter.c.id))
return db.session.query(with_recursive).join(catalog).distinct(catalog.category_id)
def __repr__(self):
return self.name
def __str__(self):
return self.name
# Create logical data abstraction (same as data storage for this first example)
class CategorySchema(Schema):
class Meta:
type_ = 'category'
self_view = 'category_list'
self_view_kwargs = {'id': '<id>'}
self_view_many = 'category_list'
id = fields.Integer(as_string=True, dump_only=True)
name = fields.Str(requried=True, load_only=True)
products = Relationship(self_view='category_list',
self_view_kwargs={'id': '<id>'},
related_view='category_list',
related_view_kwargs={'id': '<id>'},
many=True,
schema='CategorySchema',
type_='categories')
class CatalogSchema(Schema):
class Meta:
type_ = 'catalog'
self_view = 'catalog_list'
self_view_kwargs = {'id': '<id>'}
self_view_many = 'catalog_list'
id = fields.Integer(as_string=True, dump_only=True)
name = fields.Str(requried=True, load_only=True)
products = Relationship(self_view='category_list',
self_view_kwargs={'id': '<id>'},
related_view='category_list',
related_view_kwargs={'id': '<id>'},
many=True,
schema='ComputerSchema',
type_='computer')
cat_ids = [cid for cid, in db.session.query(Catalog.category_id).filter(Catalog.category_id.isnot(None)).distinct(Catalog.category_id)]
listed = db.session.query(Category).join(Catalog).options(contains_eager(Category.products)).filter(Category.id.in_(cat_ids)).all()
newlist = [l.path_to_root()[-1] for l in listed]
new_list = list(set(newlist))
class CategoryList(ResourceList):
def lists(self):
cat_ids = [cid for cid, in
db.session.query(Catalog.category_id).filter(Catalog.category_id.isnot(None)).distinct(
Catalog.category_id)]
listed = db.session.query(Category).join(Catalog).options(contains_eager(Category.products)).filter(
Category.id.in_(cat_ids)).all()
newlist = [l.path_to_root()[-1] for l in listed]
new_list = list(set(newlist))
return new_list
schema = CategorySchema
data_layer = {'session': db.session,
'model': Category,
'methods': {'query': lists}}
api.route(CategoryList, 'category_list', '/categories')
# api.route(CategoryDetail, 'category_detail', '/categories/<int:id>', '/computers/<int:computer_id>/owner')
# api.route(PersonRelationship, 'person_computers', '/persons/<int:id>/relationships/computers')
# api.route(PersonList, 'person_list', '/persons')
# api.route(PersonDetail, 'person_detail', '/persons/<int:id>', '/computers/<int:computer_id>/owner')
# api.route(PersonRelationship, 'person_computers', '/persons/<int:id>/relationships/computers')
# api.route(ComputerList, 'computer_list', '/computers', '/persons/<int:id>/computers')
# api.route(ComputerDetail, 'computer_detail', '/computers/<int:id>')
# api.route(ComputerRelationship, 'computer_person', '/computers/<int:id>/relationships/owner')
@main.route('/')
def index():
return render_template('anchor/index.html')
MongoDB data layer is documented but not supported in the current version. Are there any plans to release the MongoDataLayer?
quick fix :)
Hello,
Thank you for your great work.
I am integrating flask-rest-jsonapi into my project and I have a question for you.
I use Flask-SqlAlchemy and have a model with a "unique" field.
When I try to create an object twice, this is the answer I get:
{ "errors": [ { "detail": "Object creation error: (psycopg2.IntegrityError) duplicate key value violates unique constraint "user_email_key" DETAIL: Key (email)=([email protected]) already exists. [SQL: 'INSERT INTO "user" ("firstName", "lastName", email, password, active, "createdAt", "group") VALUES (%(firstName)s, %(lastName)s, %(email)s, %(password)s, %(active)s, %(createdAt)s, %(group)s) RETURNING "user".id'] [parameters: {'firstName': 'toto', 'lastName': 'toto', 'email': '[email protected]', 'password': '$2b$12$XnZZlBozAcMP12ZnOjsP4.i3ftCOYTNLw9EnbsvZQtRSRYYXlldf6', 'active': False, 'createdAt': datetime.datetime(2017, 11, 8, 14, 39, 49, 244605), 'group': 'S'}]", "source": { "pointer": "/data" }, "status": 500, "title": "Unknown error" } ], "jsonapi": { "version": "1.0" } }
I get the error raised by SqlAlchemy as error description, and a 500 status.
How would it be possible to catch this exception and send a more human-friendly JsonApiException?
Step to reproduce:
When using a hybrid property as follows:
@hybrid_property
def email(self):
return self._email
@email.setter
def email(self, email):
self._email = email
In raw SQL query, you get order by table.email (the email column doesn't exist in the actual table) instead of order by table._email
A possible fix for this is here: shubham-padia@3c62271
If this seems okay, I can make a PR for it :) .
this issues is probably similar to #29 only the suggested answer doesn't solve it.
in a request like this:
api/author/123/?include=articles.comments,articles.ratings
only ratings are included(or only comments, depending on what comes last) never both.
Installing flask-rest-jsonapi will also install pymongo even if I don't use MongoDB. The problem is that pymongo conflicts with other bson implementations which hit me in one project.
Is pymongo a must have for flask-rest-jsonapi to work (without using mongo)?
If a user is using a dictionary and passes get_schema_kwargs
, the update is mutably resulting in unexpected behaviour because the user might want to user the same dictionary somewhere else.
Example:
My resource classes looks like the following:
class _BaseResource(object):
_schema_kwargs = {'exclude': ('password',)}
def _check_authorized():
...
def get_schema_kwargs(self):
return self._schema_kwargs if not self._check_authorized() else None
class SomeResourceDetail(ResourceDetail, _BaseResource):
...
class SomeResourceList(ResourceList, _BaseResource):
...
In this case, when the list is first fetched the _schema_kwargs will get updated to {'exclude': ('password',), 'many': True}
and when the detail resource is being fetched it will report an error saying that <model of SomeResource> is not iterable
.
Hi, I think that according to the jsonapi specification (http://jsonapi.org/format/#document-meta), DELETE should return a meta object, but this library is returning a string.
Incorrect:
"meta": {
"Object successful deleted"
},
"jsonapi": {
"version": "1.0"
}
}
Correct:
"meta": {
"result": "Object successful deleted"
},
"jsonapi": {
"version": "1.0"
}
}
I was having problems using this lib until I figured this out.
Cheers.
Hey. I've picked up an issue while trying to pass filter params while using flask-oauthlib. This is not an issue with flask-rest-jsonapi, but with the oauthlib 2.0.2 library. I am however giving you a heads up as it directly affects the functioning of this package. When trying to pass the following characters in "{}[]" in filter request you will recieve the following error:
127.0.0.1 - - [21/May/2017 07:44:58] "GET /api/products?filter=[{%22name%22:%22owner%22,%22op%22:%22has%22,%22val%22:%20{%22name%22:%22id%22,%22op%22:%22eq%22,%22val%22:1}}] HTTP/1.1
" 500 -
Traceback (most recent call last):
File "C:\projects\pythonapi\venv\lib\site-packages\flask\app.py", line 1997, in __call__
return self.wsgi_app(environ, start_response)
File "C:\projects\pythonapi\venv\lib\site-packages\flask\app.py", line 1985, in wsgi_app
response = self.handle_exception(e)
File "C:\projects\pythonapi\venv\lib\site-packages\flask_cors\extension.py", line 161, in wrapped_function
return cors_after_request(app.make_response(f(*args, **kwargs)))
File "C:\projects\pythonapi\venv\lib\site-packages\flask\app.py", line 1540, in handle_exception
reraise(exc_type, exc_value, tb)
File "C:\projects\pythonapi\venv\lib\site-packages\flask\_compat.py", line 33, in reraise
raise value
File "C:\projects\pythonapi\venv\lib\site-packages\flask\app.py", line 1982, in wsgi_app
response = self.full_dispatch_request()
File "C:\projects\pythonapi\venv\lib\site-packages\flask\app.py", line 1614, in full_dispatch_request
rv = self.handle_user_exception(e)
File "C:\projects\pythonapi\venv\lib\site-packages\flask_cors\extension.py", line 161, in wrapped_function
return cors_after_request(app.make_response(f(*args, **kwargs)))
File "C:\projects\pythonapi\venv\lib\site-packages\flask\app.py", line 1517, in handle_user_exception
reraise(exc_type, exc_value, tb)
File "C:\projects\pythonapi\venv\lib\site-packages\flask\_compat.py", line 33, in reraise
raise value
File "C:\projects\pythonapi\venv\lib\site-packages\flask\app.py", line 1612, in full_dispatch_request
rv = self.dispatch_request()
File "C:\projects\pythonapi\venv\lib\site-packages\flask\app.py", line 1598, in dispatch_request
return self.view_functions[rule.endpoint](**req.view_args)
File "C:\projects\pythonapi\venv\lib\site-packages\flask\views.py", line 84, in view
return self.dispatch_request(*args, **kwargs)
File "C:\projects\pythonapi\venv\lib\site-packages\flask_rest_jsonapi\resource.py", line 66, in dispatch_request
raise e
File "C:\projects\pythonapi\venv\lib\site-packages\flask_rest_jsonapi\resource.py", line 59, in dispatch_request
response = method(*args, **kwargs)
File "C:\projects\pythonapi\venv\lib\site-packages\flask_rest_jsonapi\decorators.py", line 32, in wrapped
return func(*args, **kwargs)
File "C:\projects\pythonapi\venv\lib\site-packages\flask_oauthlib\provider\oauth2.py", line 555, in decorated
valid, req = self.verify_request(scopes)
File "C:\projects\pythonapi\venv\lib\site-packages\flask_oauthlib\provider\oauth2.py", line 483, in verify_request
uri, http_method, body, headers, scopes
File "C:\projects\pythonapi\venv\lib\site-packages\oauthlib\oauth2\rfc6749\endpoints\base.py", line 64, in wrapper
return f(endpoint, uri, *args, **kwargs)
File "C:\projects\pythonapi\venv\lib\site-packages\oauthlib\oauth2\rfc6749\endpoints\resource.py", line 68, in verify_request
request = Request(uri, http_method, body, headers)
File "C:\projects\pythonapi\venv\lib\site-packages\oauthlib\common.py", line 417, in __init__
self._params.update(dict(urldecode(self.uri_query)))
File "C:\projects\pythonapi\venv\lib\site-packages\oauthlib\common.py", line 131, in urldecode
raise ValueError(error % (set(query) - urlencoded, query))
ValueError: Error trying to decode a non urlencoded string. Found invalid characters: {']', '[', '}', '{'} in the string: 'filter=[{%22name%22:%22owner%22,%22op%22:%22has%22,%22val%2
2:%20{%22name%22:%22id%22,%22op%22:%22eq%22,%22val%22:1}}]'. Please ensure the request/response body is x-www-form-urlencoded.
I managed to resolve the issue by modifying oauthlib and have created a pull request here: oauthlib/oauthlib#477 . Anyone wanting to continue development may implement this until it hopefully gets pulled into the master.
Package crashes during the import. MongoDataLayer is used here: https://github.com/miLibris/flask-rest-jsonapi/blob/master/flask_rest_jsonapi/__init__.py. One still needs to install pymongo to get the framework working.
Best Regards.
I have ran into some errors while registering multiple related schema.
Suppose we have 3 schema like below:
class A_Schema(Schema):
C = Relationship( .... )
class B_Schema(Schema):
C = Relationship( .... )
class C_Schema(Schema):
Now, Issue is how do I register related urls for A.C (/A//C) and B.C (/B//C) . While trying to create both urls, I have been running into over-write view method errors; created multiple resources, but got stuck at some:-
"BuildError: Could not build url for endpoint 'brand_api.company_related_brand'. Did you forget to specify values ['id']? // Werkzeug Debugger"
I've been running into some issues over the last few months setting multiple relationships in the same API call. Here's an example error message I received earlier today trying to set two relationships in the same call:
sqlalchemy.exc.IntegrityError: (raised as a result of Query-invoked autoflush; consider using a session.no_autoflush block if this flush is occurring prematurely) (psycopg2.IntegrityError) null value in column "conversation_id" violates not-null constraint
The problem seems to be related to the apply_relationships
method in the SqlalchemyDataLayer
. As it loops through the relationship fields it tries to fetch the related_object
and immediately sets the relationship with setattr(obj, key, related_obj)
. Although this works for the first relationship, it fails with the above error when it tries to fetch the second related_object
.
I am able to get around this problem with the following custom data layer that waits to set the relationships until all of them have been fetched. I'm not sure if this is the "right" way to solve it, but I wanted to post it here in case anyone else finds it helpful or can shed light on a better way.
class ImprovedDataLayer(SqlalchemyDataLayer):
def apply_relationships(self, data, obj):
"""Apply relationship provided by data to obj
:param dict data: data provided by the client
:param DeclarativeMeta obj: the sqlalchemy object to plug relationships to
:return boolean: True if relationship have changed else False
"""
relationships_to_apply = []
relationship_fields = get_relationships(self.resource.schema)
for key, value in data.items():
if key in relationship_fields:
related_model = getattr(obj.__class__, key).property.mapper.class_
related_id_field = self.resource.schema._declared_fields[
relationship_fields[key]].id_field
if isinstance(value, list):
related_objects = []
for identifier in value:
related_object = self.get_related_object(related_model,
related_id_field,
{'id': identifier})
related_objects.append(related_object)
# setattr(obj, key, related_objects)
relationships_to_apply.append({
'field': key,
'value': related_objects
})
else:
related_object = None
if value is not None:
related_object = self.get_related_object(related_model,
related_id_field,
{'id': value})
# setattr(obj, key, related_object)
relationships_to_apply.append({
'field': key,
'value': related_object
})
# Wait to set relationships until all have been fetched from the db
for relationship in relationships_to_apply:
setattr(obj, relationship['field'], relationship['value'])
Does not JSONAPI 1.0 says, GET requests be made with Accept header "application/vnd.api+json"; But, I am able to make GET requests without any headers.
Also, is response body in GET according to JSONAPI specs? Our app broke as it was not able to parse response body (It was working fine with restless before migrate).
Or, do I need to put in some options in API library to change mime type to application/vnd.api+json? I thought that to be default.
My include api request looks like this: http://127.0.0.1:5000/api/products?include=favorites
It's working correctly and includes favorites for products in the payload. However, I'd like to only include favorites for products that the current user has favorited. Where is the right place to set this up? The schema/view/data layer? I've looked through the source code quite a bit and can't seem to find the right way to filter the database query for the included favorites
relationship (using SqlAlchemy). Any recommendations on how to approach this?
The way flask-rest-jsonapi
accepts filter params doesn't match the JSONAPI
spec, and therefore makes it really ugly to filter with JSONAPI
clients that support it. For example in Ember I have to do:
filter: `[${JSON.stringify({
name: 'name',
op: 'eq',
val: name
})}]`
as opposed to what I can normally do with conforming server implementations:
filter: { name }
You can see why this might not be ideal.
The recent change to using a default PAGE_SIZE from app.config causes a KEY_ERROR if the PAGE_SIZE config option is not set, as it is not by default when initializing an API object from an existing Flask app (as is done in most of the documentation).
The simplest fix seems to be to ensure the default page size is set in init in addition to init_app() of the Api class, although the tests and documentation seem split about the preferred way to construct the Api object.
When instantiating a MongoDataLayer connection the following call in resource.py
object_count, objects = self.data_layer.get_collection(qs, **kwargs)
passes a querystring
this method has no params in mongo.py
{
"errors": [
{
"status": 500,
"source": "",
"detail": "get_collection() takes exactly 1 argument (2 given)",
"title": "Unknow error"
}
]
}
Are there any plans to implement Swagger integration so we can easily generate API documentation?
When i make an API call to my flask app running this library, i get the following error:
File "/home/nate/.virtualenvs/flask-insure/lib/python3.5/site-packages/flask_rest_jsonapi/pagination.py", line 53, in add_pagination_links
data['links'] = links
TypeError: list indices must be integers or slices, not str
When i debug my app, I see that data (passed from ResourceView.get())is a list of objects, but then the line in question is accessing the list as if it were a dictionary. I followed the documentation as closely as possible and my setup so far is extremely bare-bones (see here). Can you see any issues in my setup, and if not can you think of any other reason this is happening?
Thanks for any help you can provide, and thanks for creating such a cool library with such thorough and detailed documentation!
Currently, received data (json_data
from http://flask-restless.readthedocs.io/en/stable/customizing.html#request-preprocessors-and-postprocessors)
is not passed to before_patch and before_post, thus modifying the incoming data is not a possibility.
flask-restlless
currently supports this,
def post_preprocessor(data=None, **kw):
"""Accepts a single argument, `data`, which is the dictionary of
fields to set on the new instance of the model.
"""
pass
I am trying to write an API receiver, where I can not control the other end. The other end only allows headers 'Accept: application/json' and 'Content-Type: application/json'
What is the fastest way to accept and return application/json in the headers?
def resolve(self):
if 'or' not in self.filter_ and 'and' not in self.filter_:
if self.val is None and self.field is None:
raise InvalidFilters("Can't find value or field in a filter")
value = self.value
if isinstance(self.val, dict):
value = Node(self.related_model, self.val, self.resource, self.related_schema).resolve()
if '__' in self.filter_.get('name', ''):
value = {self.filter_['name'].split('__')[1]: value}
if isinstance(value, dict):
return getattr(self.column, self.operator)(**value)
else:
print(getattr(self.column, self.operator))
return getattr(self.column, self.operator)(value)
if 'or' in self.filter_:
return or_(Node(self.model, filt, self.resource, self.schema).resolve() for filt in self.filter_['or'])
if 'and' in self.filter_:
return and_(Node(self.model, filt, self.resource, self.schema).resolve() for filt in self.filter_['and'])
if 'not' in self.filter_:
return not_(Node(self.model, self.filter_['not'], self.resource, self.schema).resolve())
If if 'or' not in self.filter_ and 'and' not in self.filter_
is true, you never leave that block. If it's not, or_(Node...
or and_(Node...
gets called, but never the last not_(Node...
In order to include Flask-REST-JSONAPI in the Debian distribution, please publish a source tarball on PyPI. Please make sure it contains the license, all documentation and tests and ideally sign it with a verifiable GPG key.
I can provide the following steps to reproduce, but I have not been able to understand yet what's causing the issue, so I wanted to open this ticket to flag it.
{
"data": {
"type": "person",
"attributes": {
"name": "Person1"
}
}
}
{
"links": {
"self": "/persons/1/relationships/computers",
"related": "/persons/1/computers"
},
"data": [],
"jsonapi": {
"version": "1.0"
}
}
{
"data": {
"type": "person",
"attributes": {
"name": "Person2"
}
}
}
{
"data": {
"type": "person",
"id": "2",
"attributes": {
"display_name": "PERSON2 <None>",
"birth_date": null
},
"relationships": {
"computers": {
"links": {
"self": "/persons/2/relationships/computers",
"related": "/persons/1/computers"
}
}
},
"links": {
"self": "/persons/2"
}
},
"links": {
"self": "/persons/2"
},
"jsonapi": {
"version": "1.0"
}
}
I have not investigated deeply enough to understand why this is happening (or tested outside of the example sqlite), but wanted to open the issue in hopes the author can identify the problem since it definitely doesn't seem to match the JSONAPI spec or desired behavior.
I'm rewriting an old application where tables and columns names are in a foreign language. I would like to translate all to English as soon as possible in the code and limit use of non-english in the code. I also want to fix some inconsistencies in columns names.
First idea was to transform:
class User(db.Model):
__tablename__ = 'users'
userid = db.Column(db.Integer, primary_key=True)
user = db.Column(db.String)
to
class User(db.Model):
__tablename__ = 'users'
id = db.Column('userid', db.Integer, primary_key=True)
name = db.Column(db.String)
but it raise: Exception: User has no attribute userid
Adding key='id'
has no effect. (id = db.Column('userid', db.Integer, primary_key=True, key='id')
)
I'll propose a PR for that.
Hello,
I need a library like flask-rest-jsonapi, but using tornado instead. In your opinion, how hard would it be for me to port it to tornado? From the code it seems relatively straightforward.
Alternatively, are you aware of any package providing a similar API and technical level, but for tornado?
Thanks
Is it possible to add decorators to specific methods? For example I want to add a decorator for authorization on an endpoint's POST method. However I want GET method to be public.
I do not know if it is a bad configuration of mine or a Json specification. My problem is that I have a table of ads and would like to bring the photos and categories of the ads together in the ad endpoint. When placing the include parameter of photos and categories the result only brings those that have photos and categories and ignores the ones that do not have. Is it a JsonApi standard or can it be a bad setup in my API?
Sorry for grammatical errors, English is not my native language.
From my understanding of the code, POST on a relationship can return 204 if update is false, but this does not imply that there can't be any payload as a result of the following code.
Hi, can someone help me designing requests with bearer token which I got from authorization endpoint from my Auth provider to access flask-rest-jsonapi resources which I have protected with oauth. I have followed the specs to the document.
When I put a disable_oauth = True in resource class I get the access to resources as before; which makes me believe my oauth setup is correct for flask-rest-jsonapi. I am following RFC 6750 for building my requests. Any assistance on how to form requests would be great. Also, a demo/skeleton for putting the resources inside oauth decorators would be good, but I guess that has been covered in docs and I'm following them by word.
A declarative, efficient, and flexible JavaScript library for building user interfaces.
๐ Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.
TypeScript is a superset of JavaScript that compiles to clean JavaScript output.
An Open Source Machine Learning Framework for Everyone
The Web framework for perfectionists with deadlines.
A PHP framework for web artisans
Bring data to life with SVG, Canvas and HTML. ๐๐๐
JavaScript (JS) is a lightweight interpreted programming language with first-class functions.
Some thing interesting about web. New door for the world.
A server is a program made to process requests and deliver data to clients.
Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.
Some thing interesting about visualization, use data art
Some thing interesting about game, make everyone happy.
We are working to build community through open source technology. NB: members must have two-factor auth.
Open source projects and samples from Microsoft.
Google โค๏ธ Open Source for everyone.
Alibaba Open Source for everyone
Data-Driven Documents codes.
China tencent open source team.