Code Monkey home page Code Monkey logo

safrs's Issues

SAFRSRestRelationshipAPI returns all objects in db of the related type

Hi, I just noticed /<obj_type>/<obj_id>/<child_obj_type> returns all instances of child_obj_type in my database. It seems a correct subset of related objects are present on line 853 here, but then an unfiltered query of the related object is fetched from jsonapi_filter:

https://github.com/thomaxxl/safrs/blob/master/safrs/jsonapi.py#L853-L855

I'm guessing the intention was to have filtering support for the related objects. If that's the case then perhaps jsonapi_filter and SAFRSBase._s_filter could take an optional argument for a prepared query that is to be filtered?

automap example bug

Hi,

Great package.

Just using the automap feature as in the skype example I noticed that when using the type() call to create the models as a subclass of both SAFRSBase and table there is a small issue. The sclass.query.limit().all() returns instances that are not instances of SAFRSBase - this only bugs when using "include" as there is an assert in the code. I think this is because the "prepare" call on the base happened before the call to type? Anyway i got round it by adding automap_base(cls=SAFRSBase) so that by default the class is a subclass of the base.

Just thought i would let you know,

Simon

swagger issue

Hi,

I noticed that the swagger json created has issues in the relations example.
e.g.
$ref: '#/definitions/Book POST sample'

This has the following error (to see this use http://editor.swagger.io/):
$ref values must be RFC3986-compliant percent-encoded URIs

I will have a look at fixing and then open a pull request.

Simon

Data in resource linkage objects for relationships is null

Hi! I am loving your project here. I am using it extensively and find it massively useful, so kudos to you!

I am wondering if you have some guidance here. I have an ORM generated from flask-sqlacodegen, including relationships with backrefs.

When I am querying on the base object, I do see the relationships appear like so:

"relationships": {
                "case_extendeds": {
                    "data": null,
                    "links": {
                        "self": "http://127.0.0.1:5004/cases/3/case_extendeds"
                    }
                },

but the data is null, despite having a valid link that returns the data I need.

My understanding is, the data that I can get with a get request should be provided in the data object, no?

I found the json api specification indicating:

Resource linkage MUST be represented as one of the following:

null for empty to-one relationships.
an empty array ([]) for empty to-many relationships.
a single resource identifier object for non-empty to-one relationships.
an array of resource identifier objects for non-empty to-many relationships

So it looks like this one is saying its an empty to one relationship.

Is there something I'm supposed to provide to actually have it give me the data in the in the data object, or do you have a hunch about something that might be wrong in my setup?

Thanks!

simple flask endpoint

How can I use swagger_doc to document a simple flask app? What I am trying:

@jsonapi_rpc(http_methods=["GET"])
@app.route("/hello/<name>", methods=["GET"])
def hello(name):
	"""
        pageable: True
        description : Hello world
        args:
            name: greeters name
    """
	return make_response("Hello World from %s!"%name, 201)

api = SAFRSAPI(app, host="0.0.0.0", port=5000, prefix="/swagger")

The resulting json has the metadata, but no paths.

Add possibility to limit methods

Objects are currently accessable via all methods: ['GET', 'POST', 'PATCH', 'DELETE', 'OPTIONS']

It would be great to limit (some) objects to only GET (for example).

And if this is already possible, than i do miss the documentation. I could not easily find how to do this reading the source code.

Object of type 'time' is not JSON serializable

Hi, very nice app. My database makes use of the "Time" column. I get this error: Object of type 'time' is not JSON serializable. I tried to add a custom serialization like below without success. How should I handle this?

in base.py I see this comment
"""
Parse datetime and date values for some common representations
If another format is uses, the user should create a custom column type or custom serialization
"""

def to_dict(self):
    return {c.key: getattrOnType(self, c) for c in inspect(self).mapper.column_attrs}

def getattrOnType(self, c):
if type(getattr(self, c.key)) is datetime.datetime or
type(getattr(self, c.key)) is datetime.time or
type(getattr(self, c.key)) is datetime.date or
type(getattr(self, c.key)) is decimal.Decimal:
return str(getattr(self, c.key))
elif (getattr(self, c.key)):
return getattr(self, c.key)
else:
# allows you to handle null values differently
return getattr(self, c.key)

jsonapi_rpc parameters and swagger

I'm trying to expose a custom endpoint related to a model. It's an endpoint for generating a new object given a value, so I created something like this:

@classmethod
@jsonapi_rpc(http_methods=["GET"])
def get_by_name(cls, name):
    """
        description : Generate and return a Thing based on name
        args:
            name:
                type : string
                example : thingy
        pageable: false
    """
    thing = cls(name=name)
    thing.description = populate_based_on_name()
    db.session.add(thing)
    db.session.commit()
    return thing.to_dict()

An endpoint is created and it does appear in the swagger ui, but the swagger docs are rather confusing; containing references to "page[offset]", "include", "sort", "filter" etc. It doesn't seem to be picking up on my docstring here.

It also seems like only one parameter, called "varargs", is supported?

Is there any way I can better control the docs generated and get a properly named parameter working? I could probably get parameters from the request instead of method args, but I'd still need to get the docs under control.

cors_domain is not being read

Even if you set the "cors_domain" variable, it is not being read on the jsonapi.py, via globals(). I'm using python 2.7.14

expose_existing.py - line 90, NameError: name 'io' is not defined

File "...safrs-master\expose_existing\expose_existing.py", line 90, in codegen
outfile = io.open(args.outfile, 'w', encoding='utf-8') if args.outfile else capture # sys.stdout
NameError: name 'io' is not defined

Passing an --outfile parameter triggers this error.

Why not use Flask-Restless ?

As stated in their definition "Flask-Restless provides simple generation of ReSTful APIs for database models defined using SQLAlchemy (or Flask-SQLAlchemy)."

So it seems the obvious choice to start building on top of it to add swagger/jsonapi support.
Did you consider it ? If so what advantages did you see in Flask-Restful ?

I have no experience in neither of Flask-Restful or Flask-Restless, so it is not a judgement, I just want to know the motivations behind your choice.

Using With Flask-Jwt-Extended

Hello,

I find your framework very helpful and open to development. I wonder, if it works with flask-jwt-extended or not. Since aim of your framework is creating endpoints using sqla models and flask-jwt-extended works with decorators, do you think that entegration of these two framework is possible or not? If so, I'll give a try coz I really need a handy framework like safrs.

Thanks for your effort.

Subclassing db.Model

from Limitations & TODOs:

I am not a big fan of the multiple inheritance needed to declare SAFRSBase instances but I couldn't subclass sqla's db.Model and I think inheritance is more clear than class decorators.

This is actually pretty easy.
All you need to do is pass your custom class to the constructor of SQLAlchemy:

from flask_sqlalchemy import SQLAlchemy
from flask_sqlalchemy.model import Model
from sqlalchemy.ext.declarative import declarative_base
from safrs import SAFRSBase

MODEL_NAME='Model'

class SAFRSModel(SAFRSBase, Model):
  pass

db = SQLAlchemy(model_class=declarative_base(cls=SAFRSModel, name=MODEL_NAME))

class Users(db.Model):
  __tablename__ = 'users'

You could even go so far and automatically expose SAFRSBase models via a custom Metaclass:

from flask_sqlalchemy import SQLAlchemy
from flask_sqlalchemy.model import Model, DefaultMeta
from sqlalchemy.ext.declarative import declarative_base
from safrs import SAFRSBase, Api

MODEL_NAME='Model'

api = Api(...)

class SAFRSModel(SAFRSBase, Model):
  pass

class SAFRSMeta(DefaultMeta):
  def __init__(cls, name, bases, d):
    if name != MODEL_NAME and issubclass(cls, SAFRSModel):
      api.expose_object(cls)
    super(_DbMeta, cls).__init__(name, bases, d)

db = SQLAlchemy(model_class=declarative_base(cls=SAFRSModel, name=MODEL_NAME))

class Users(db.Model):
  __tablename__ = 'users'

In case you are interested, let me know and I'll put up a PR when I find some free time :-)

New versions of pip break setup.py

This is the behavior in pip==18.1 and is I believe what setup.py expects:

>>> from pip._internal.req import parse_requirements
>>> foo = listparse_requirements("requirements.txt", session=False))
>>> foo[0].req
<Requirement('Flask-Cors==3.0.7')>

This is the behavior in pip==20.1:

>>> from pip._internal.req import parse_requirements
>>> foo = list(parse_requirements("requirements.txt", session=False))
>>> foo[0].req
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
AttributeError: 'ParsedRequirement' object has no attribute 'req'
>>> foo[0].requirement
'Flask-Cors==3.0.7'

Can't use safrs with poetry

For some reason, poetry does not sees the correct dependencies of the safrs library.
"An error occurred when reading setup.py or setup.cfg: 'Attribute' object has no attribute 'id'" -> this message is shown when I do a "poetry install", having "safrs" as a dependency.

Apparently, poetry can't see what libraries safrs depends on.

pypi version safrs==1.0.10 does not support pip 10, master code does

Collecting safrs==1.0.10 (from x)
  Using cached https://files.pythonhosted.org/packages/7b/05/abc1846dcd495d08c734c61244077f596c39d9036af47304e3f24777f7dd/safrs-1.0.10.tar.gz
    Complete output from command python setup.py egg_info:
    Traceback (most recent call last):
      File "<string>", line 1, in <module>
      File "C:\Users\denny\AppData\Local\Temp\pip-install-u55tif5y\safrs\setup.py", line 4, in <module>
        from pip.req import parse_requirements
    ModuleNotFoundError: No module named 'pip.req'

    ----------------------------------------

Search now requires ID

Currently testing the latest release, 8afc35d but the search and re_search now requires an ID

image

Before extending on the SAFRSBase class I do the following;

from safrs import SAFRSBase
from safrs.api_methods import search, re_search

SAFRSBase.search = search
SAFRSBase.re_search = re_search

swagger.json not found

Hi,

great project. Letting the models get created from an existing database and accessing them through REST calls work, but the Swagger doc is not being created? swagger.json can't be found.

Best regards,
David

Related instaces on init

I'm trying out SAFRS by re-implementing an existing API based on flask-sqlalchemy. One issue I'm facing trying to re-use the migration files is passing in related instances on model init:

# Doesn't work but can be worked around using parent_id=some_obj.id
obj = MyModel(parent=some_obj)
# Doesn't work and the workaround is a bit messy
obj = MyModel(children=[c1, c2])
# In my migration I'm actually doing this which works natively with sqlalchemy
obj = MyModel(children=[Child(), Child()])

This continue looks to be the culprit?:
https://github.com/thomaxxl/safrs/blob/master/safrs/db.py#L144

What's the story there? Would it be possible to just un-comment that stuff (or what about calling super to let sqlalchemy deal with this natively..?)

Edit:
FYI my workaround for now is using this ABC:

class BaseModel(SAFRSBase, db.Model):
    __abstract__ = True
    db_commit = False

    def __init__(self, *args, **kwargs):
        super().__init__(*args, **kwargs)
        db.Model.__init__(self, *args, **kwargs)

jsonapi_rpc response structure

Hello again! Here's another request regarding jsonapi_rpc :)

So I've got a working endpoint now - beautifully documented - generating an object with relations and all. Great stuff!
The next step is of course returning this object to the client in a jsonapi compliant fashion. I was hoping to simply do return obj.to_dict(), but that gives me this response:

{
  "meta": {
    "result": {
      "name": "test",
      "description": "text etc"
    }
  }
}

I realise this structure probably originates from an idea of jsonapi_rpc-methods performing arbitrary tasks and returning some information related to that task. But in some cases (like mine :)) these endpoints could easily want to return an instance of the model they belong to.

What do you think? Would be possible to give more control over the type of response to the method declaration somehow? Or just leave it up to the method completely to form its response? If the latter is preferred - what relevant internals should I be looking at for building a jsonapi compliant resource object etc?

returning relations

Is it possible to return, for example, the authors of the book, out of the box?

Lets say I query the books and I would like to display the authors as well, currently this leaves me with 2 options, 1 query all authors or 2 query them individually.

class Book(Model):
    """Book"""
    id = db.Column(db.Integer, primary_key=True)
    title = db.Column(db.String(255))
    sortable_title = db.Column(db.String(255))
    language = db.Column(db.String(3), comment="ISO-639-2")
    editions = db.relationship("BookEdition", uselist=False, back_populates="book")
    authors = db.relationship(
        "Author",
        secondary=author_book_table,
        back_populates="books",
        lazy='dynamic'
    )
"relationships": {
        "authors": {
          "data": [
            {
              "id": 1,
              "type": "author"
            },
            {
              "id": 2,
              "type": "author"
            },
            {
              "id": 3,
              "type": "author"
            }
          ],
          "links": {
            "self": "http://localhost:5000/book/1/authors"
          },

Wrong .tar.gz file in setup.py on PyPI

`"""
python3 setup.py sdist
twine upload dist/*
"""

from distutils.core import setup

try: # for pip >= 10
from pip._internal.req import parse_requirements
except ImportError: # for pip <= 9.0.3
from pip.req import parse_requirements

install_requires = [
str(ir.req) for ir in parse_requirements("requirements.txt", session=False)
]

setup(
name="safrs",
packages=["safrs"],
version="2.6.2",
license="MIT",
description="safrs : SqlAlchemy Flask-Restful Swagger2",
long_description=open("README.rst").read(),
author="Thomas Pollet",
author_email="[email protected]",
url="https://github.com/thomaxxl/safrs",
download_url="https://github.com/thomaxxl/safrs/archive/2.5.5.tar.gz",
keywords=["SqlAlchemy", "Flask", "REST", "Swagger", "JsonAPI", "OpenAPI"],
python_requires=">=3.0, !=3.0., !=3.1., !=3.2.*, <4",
install_requires=install_requires,
classifiers=[
"Development Status :: 3 - Alpha",
"License :: OSI Approved :: MIT License",
"Intended Audience :: Developers",
"Framework :: Flask",
"Topic :: Software Development :: Libraries",
"Environment :: Web Environment",
"Programming Language :: Python :: 3",
"Programming Language :: Python :: 3.7",
"Programming Language :: Python :: 3.6",
"Programming Language :: Python :: 3.5",
"Programming Language :: Python :: 3.3",
"Programming Language :: Python :: 3.4",
],
)`

In the "download_url" item, it should point to the correct latest version (2.6.2).

demo_jwt.py -- TypeError: 'module' object is not callable

When I run examples\authentication\demo_jwt.py I get this error: TypeError: 'module' object is not callable How do I resolve this error?

Traceback (most recent call last):
  File "C:\Sites\safrs2\safrs\_api.py", line 468, in api_decorator
    decorated_method = swagger_decorator(decorated_method)
  File "C:\Sites\safrs2\safrs\swagger_doc.py", line 484, in swagger_doc_gen
    rel_post_schema = schema_from_object("{}_Relationship".format(class_name), {"data": data})
  File "C:\Sites\safrs2\safrs\swagger_doc.py", line 198, in schema_from_object
    properties = encode_schema(properties)
  File "C:\Sites\safrs2\safrs\swagger_doc.py", line 154, in encode_schema
    log.warning("Json encoding failed for {}, type {} ({})".format(obj, type(obj), exc))
NameError: name 'log' is not defined
[2020-04-27 12:22:37,736] _api:475 ERROR: Failed to generate documentation for <function SAFRSRestRelationshipAPI.post at 0x051731E0>
[2020-04-27 12:22:37,784] _api:474 ERROR: name 'log' is not defined
<function SAFRSRestRelationshipAPI.delete at 0x051738A0> delete
Traceback (most recent call last):
  File "C:\Sites\safrs2\safrs\swagger_doc.py", line 152, in encode_schema
    result = json.loads(json.dumps(obj, cls=flask.current_app.json_encoder))
  File "c:\users\chris\appdata\local\programs\python\python36-32\Lib\json\__init__.py", line 238, in dumps
    **kw).encode(obj)
TypeError: 'module' object is not callable

error on json encode for column with func as default

I made a pretty simple mixin as seen below but the code seems to die on the dt.uctnow

from datetime import datetime as dt


class CreatedMixin:
    created_at = db.Column(db.DateTime(timezone=True), default=dt.utcnow)
    updated_at = db.Column(db.DateTime(timezone=True), default=dt.utcnow, onupdate=dt.utcnow)

    @classmethod
    def newest(cls, amount=5):
        assert isinstance(cls, Model)
        return cls.query.order_by(cls.created_at.desc()).limit(amount)

    @classmethod
    def updated(cls, amount=5):
        assert isinstance(cls, Model)
        return cls.query.order_by(cls.updated_at.desc()).limit(amount)

image

stacktrace

127.0.0.1 - - [18/Mar/2019 14:00:46] "GET /swagger.json HTTP/1.1" 500 -
Traceback (most recent call last):
  File "/home/patrick/PycharmProjects/ebookhub-backend/venv/lib/python3.6/site-packages/flask/app.py", line 2309, in __call__
    return self.wsgi_app(environ, start_response)
  File "/home/patrick/PycharmProjects/ebookhub-backend/venv/lib/python3.6/site-packages/flask/app.py", line 2295, in wsgi_app
    response = self.handle_exception(e)
  File "/home/patrick/PycharmProjects/ebookhub-backend/venv/lib/python3.6/site-packages/flask_restful/__init__.py", line 273, in error_router
    return original_handler(e)
  File "/home/patrick/PycharmProjects/ebookhub-backend/venv/lib/python3.6/site-packages/flask_restful/__init__.py", line 273, in error_router
    return original_handler(e)
  File "/home/patrick/PycharmProjects/ebookhub-backend/venv/lib/python3.6/site-packages/flask_cors/extension.py", line 161, in wrapped_function
    return cors_after_request(app.make_response(f(*args, **kwargs)))
  File "/home/patrick/PycharmProjects/ebookhub-backend/venv/lib/python3.6/site-packages/flask/app.py", line 1741, in handle_exception
    reraise(exc_type, exc_value, tb)
  File "/home/patrick/PycharmProjects/ebookhub-backend/venv/lib/python3.6/site-packages/flask/_compat.py", line 34, in reraise
    raise value.with_traceback(tb)
  File "/home/patrick/PycharmProjects/ebookhub-backend/venv/lib/python3.6/site-packages/flask/app.py", line 2292, in wsgi_app
    response = self.full_dispatch_request()
  File "/home/patrick/PycharmProjects/ebookhub-backend/venv/lib/python3.6/site-packages/flask/app.py", line 1815, in full_dispatch_request
    rv = self.handle_user_exception(e)
  File "/home/patrick/PycharmProjects/ebookhub-backend/venv/lib/python3.6/site-packages/flask_restful/__init__.py", line 273, in error_router
    return original_handler(e)
  File "/home/patrick/PycharmProjects/ebookhub-backend/venv/lib/python3.6/site-packages/flask_restful/__init__.py", line 273, in error_router
    return original_handler(e)
  File "/home/patrick/PycharmProjects/ebookhub-backend/venv/lib/python3.6/site-packages/flask_cors/extension.py", line 161, in wrapped_function
    return cors_after_request(app.make_response(f(*args, **kwargs)))
  File "/home/patrick/PycharmProjects/ebookhub-backend/venv/lib/python3.6/site-packages/flask/app.py", line 1718, in handle_user_exception
    reraise(exc_type, exc_value, tb)
  File "/home/patrick/PycharmProjects/ebookhub-backend/venv/lib/python3.6/site-packages/flask/_compat.py", line 34, in reraise
    raise value.with_traceback(tb)
  File "/home/patrick/PycharmProjects/ebookhub-backend/venv/lib/python3.6/site-packages/flask/app.py", line 1813, in full_dispatch_request
    rv = self.dispatch_request()
  File "/home/patrick/PycharmProjects/ebookhub-backend/venv/lib/python3.6/site-packages/flask_debugtoolbar/__init__.py", line 125, in dispatch_request
    return view_func(**req.view_args)
  File "/usr/lib/python3.6/cProfile.py", line 109, in runcall
    return func(*args, **kw)
  File "/home/patrick/PycharmProjects/ebookhub-backend/venv/lib/python3.6/site-packages/flask_restful/__init__.py", line 484, in wrapper
    return self.make_response(data, code, headers=headers)
  File "/home/patrick/PycharmProjects/ebookhub-backend/venv/lib/python3.6/site-packages/flask_restful/__init__.py", line 513, in make_response
    resp = self.representations[mediatype](data, *args, **kwargs)
  File "/home/patrick/PycharmProjects/ebookhub-backend/venv/lib/python3.6/site-packages/flask_restful/representations/json.py", line 21, in output_json
    dumped = dumps(data, **settings) + "\n"
  File "/usr/lib/python3.6/json/__init__.py", line 238, in dumps
    **kw).encode(obj)
  File "/usr/lib/python3.6/json/encoder.py", line 201, in encode
    chunks = list(chunks)
  File "/usr/lib/python3.6/json/encoder.py", line 430, in _iterencode
    yield from _iterencode_dict(o, _current_indent_level)
  File "/usr/lib/python3.6/json/encoder.py", line 404, in _iterencode_dict
    yield from chunks
  File "/usr/lib/python3.6/json/encoder.py", line 404, in _iterencode_dict
    yield from chunks
  File "/usr/lib/python3.6/json/encoder.py", line 404, in _iterencode_dict
    yield from chunks
  File "/usr/lib/python3.6/json/encoder.py", line 404, in _iterencode_dict
    yield from chunks
  File "/usr/lib/python3.6/json/encoder.py", line 404, in _iterencode_dict
    yield from chunks
  File "/usr/lib/python3.6/json/encoder.py", line 404, in _iterencode_dict
    yield from chunks
  File "/usr/lib/python3.6/json/encoder.py", line 404, in _iterencode_dict
    yield from chunks
  File "/usr/lib/python3.6/json/encoder.py", line 437, in _iterencode
    o = _default(o)
  File "/usr/lib/python3.6/json/encoder.py", line 180, in default
    o.__class__.__name__)
TypeError: Object of type 'function' is not JSON serializable

How to not display relationships in the object

Love the project, it helped bootstrap very quickly!

I'd like to be able to an object and all its relationships in the same object.
Why expose the implementation detail to the API user by defining the relationship?

"Api object has no attribute _extract_schemas"

Hi there!

Excited about this project, and I appreciate your work.

Attempting to run this on pre-existing sqlalchemy models, I get the following error:

api_1       | Traceback (most recent call last):
api_1       |   File "./swagger.py", line 54, in <module>
api_1       |     create_api(app)
api_1       |   File "./swagger.py", line 26, in create_api
api_1       |     api.expose_object(Market)
api_1       |   File "/usr/local/lib/python3.6/site-packages/safrs/jsonapi.py", line 112, in expose_object
api_1       |     methods = ['GET','POST', 'PUT'])
api_1       |   File "/usr/local/lib/python3.6/site-packages/safrs/jsonapi.py", line 265, in add_resource
api_1       |     operation, definitions_ = self._extract_schemas(operation)
api_1       | AttributeError: 'Api' object has no attribute '_extract_schemas'
api_1       | unable to load app 0 (mountpoint='') (callable not found or import error)
api_1       | *** no app loaded. going in full dynamic mode ***
api_1       | *** uWSGI is running in multiple interpreter mode ***
api_1       | spawned uWSGI worker 1 (and the only) (pid: 13, cores: 1)

This is running against objects that inherit from SAFRSBase.

The code is the same as the example, butwith my own models, and exposing them via expose_object.

In addition, I have tried it with the exact test code, replaced sqlite with postgres, and get the same error.

Port not included in swagger interface

I'm not sure wetter or not I'm forgetting something or this is an actual bug... If I don't add the port to the host it "forgets" to add it in the URL.

host = "localhost"
port = 5000

api = SAFRSAPI(app, host=host, port=port, prefix=api_prefix)

image

api = SAFRSAPI(app, host=host+":"+str(port), port=port, prefix=api_prefix)

image

The http_method restriction does not apply to a model's relationships

Restrict methods on a model

class Item(...):
    http_methods = ["get"]

    instance = db.relationship("Instance")

class Instance(...):
    http_methods = ["get"]

Now only GET methods should be displayed for Item and Instance.

curl https://myhost.com/api/

Only GET methods should be displayed Item and Instance, but the API page shows item/<id>/instance shows [POST, GET, PATCH, DELETE].

about safrs/db.py when column type is datetime/date , format Error.

I use datetime format is '%Y-%m-%d %H:%M:%S' and when value is None , it can't work.

 # Parse datetime and date values
    try:
        if column.type.python_type == datetime.datetime:
            arg_value = datetime.datetime.strptime(str(arg_value), '%Y-%m-%d %H:%M:%S.%f')
        elif column.type.python_type == datetime.date:
            arg_value = datetime.datetime.strptime(str(arg_value), '%Y-%m-%d')
    except (NotImplementedError, ValueError) as exc:
        safrs.log.warning('datetime {} for value "{}"'.format(exc, arg_value))

    return arg_value

And i want to change the code like this:

 # Parse datetime and date values
     if arg_value is not None:
         try:
            if column.type.python_type == datetime.datetime:
                arg_value = datetime.datetime.strptime(str(arg_value), '%Y-%m-%d %H:%M:%S')
            elif column.type.python_type == datetime.date:
                arg_value = datetime.datetime.strptime(str(arg_value), '%Y-%m-%d')
        except (NotImplementedError, ValueError) as exc:
            safrs.log.warning('datetime {} for value "{}"'.format(exc, arg_value))

    return arg_value

disable `POST /model/{modelId}/`

Is there an easy way to disable the post with the model ID? i.e. POST /model/{modelId}/ since the ID is autoincrement I don't want to let the api client be able to post/create to a certain ID/number.

P.s. sorry for the amount of issues.

Custom documented classmethod to execute advanced GET

Hello,

we created a custom documented (class)method to do advanced search on a table. Our code:

@classmethod
@documented_api_method
def search(cls, pattern, page_offset, page_limit, sort):
    """
    description : Search a client by pattern in most important fields
    args:
        pattern:
            en
        page_offset:
            0
        page_limit:
            10
        sort:
            first_name,last_name
    """
    request.args = dict(request.args)
    request.args['page[offset]'] = page_offset
    request.args['page[limit]'] = page_limit
    meta = {}
    errors = None
    jsonapi = dict(version='1.0')
    limit = request.args.get('limit', UNLIMITED)
    meta['limit'] = limit
    # retrieve a collection
    pattern = f'%{pattern}%'
    instances = cls.query.filter(or_(
        Client.first_name.like(pattern),
        Client.middle_name.like(pattern),
        Client.last_name.like(pattern),
    ))
    instances = jsonapi_sort(instances, cls)
    links, instances = paginate(instances)
    data = [item for item in instances]
    included = get_included(data, limit)
    result = dict(data=data)
    if errors:
        result['errors'] = errors
    if meta:
        result['meta'] = meta
    if jsonapi:
        result['jsonapi'] = jsonapi
    if links:
        result['links'] = links
    if included:
        result['included'] = included
    return result

The result is a generated POST endpoint, but we would like to see a GET, since we are getting data.
The search items could be passed as URL arguments instead of in the body. Also, because we have a POST, we can't show an example value in the swagger UI.

Example curl:

curl -X POST --header 'Content-Type: application/json' --header 'Accept: application/json' -d '{ \ 
   "meta": { \ 
     "method": "search", \ 
     "args": { \ 
       "pattern": "en", \ 
       "page_offset": 1, \ 
       "page_limit": 1, \ 
       "sort": "first_name,last_name" \ 
     } \ 
   } \ 
 }' 'http://localhost:5000/client/search'

Linked issue: #6

Can you provide a better method? Are we doing it right?

Filter records by logged in user

Hi After a user logins and gets a token, how do I filter all Gets to only show records with a relationship to the User record? And Post to only post if the parent relationship key exist?

Thank you

Wrong creation of "empty" object on initalization

I've found out that this "try" block was causing a small problem: creating an empty object (just with an id).

After commenting it, the problem was gone. Is it really necessary?

Also:

  • the examples requires a logging import that is not explicit on the examples files. (look demo.py)
  • why do the primary keys have to be a uuid (string)? Can't it be an integer (what is very common on pre-existing databases)

lazy loading ?

I may be mistaken but I didn't see a way to lazy load the Api object so that it's easily usable with the app factory pattern, is this something achievable ?

demo.py: TypeError: unsupported operand type(s) for +: 'SQLAlchemy' and 'str'

safrs-1.0.22 / Python 3.7.0 / macOS Mojave 10.14

$ python3 -m venv env
$ source env/bin/activate
$ pip install safrs
$ curl -O https://raw.githubusercontent.com/thomaxxl/safrs/master/examples/demo.py
$ python demo.py

This fails with

Traceback (most recent call last):
  File "demo.py", line 64, in <module>
    SAFRS(app, db)
  File "/blah/blah/env/lib/python3.7/site-packages/safrs/__init__.py", line 37, in __new__
    app.register_blueprint(swaggerui_blueprint, url_prefix= prefix)
  File "/blah/blah/env/lib/python3.7/site-packages/flask/app.py", line 64, in wrapper_func
    return f(self, *args, **kwargs)
  File "/blah/blah/env/lib/python3.7/site-packages/flask/app.py", line 951, in register_blueprint
    blueprint.register(self, options, first_registration)
  File "/blah/blah/env/lib/python3.7/site-packages/flask/blueprints.py", line 151, in register
    endpoint='static')
  File "/blah/blah/env/lib/python3.7/site-packages/flask/blueprints.py", line 68, in add_url_rule
    rule = self.url_prefix + rule
TypeError: unsupported operand type(s) for +: 'SQLAlchemy' and 'str'

static method of user model requires user_id

I would expect this not to be the case since it is a staticmethod. I looked at the search option, see below which one I mean. This is build on a classmethod.

@classmethod
@jsonapi_rpc(http_methods=['POST'])
def search(cls, **kwargs):

my code

class User(Model):
    custom_decorators = [jwt_required, auth.login_required]
    id = db.Column(db.Integer, primary_key=True)
    name = db.Column(db.String(255))
    email = db.Column(db.String(80, collation='NOCASE'), unique=True, nullable=False)
    password_hash = db.Column(db.String(128))
    language = db.Column(db.String(5), nullable=True, default="en")
    timezone = db.Column(db.String(64), nullable=True, default="Europe/Amsterdam")

    @staticmethod
    @jsonapi_rpc(http_methods=['POST'])
    def login(email_or_token, password):
        user = User.verify_auth_token(email_or_token)
        if not user:
            user = User.query.filter_by(email=email_or_token).first()
            if not user or not user.verify_password(password):
                return None
        return user.create_access_token()

image

database is locked

when I change sqlite uri to the following

    app.config.update(SQLALCHEMY_DATABASE_URI="sqlite:///test.db", DEBUG=True)

start demo_relationshio.py, I got a error:

D:\Python36\python3.exe D:/PycharmProjects/safrs/examples/demo_relationship.py
D:\Python36\lib\site-packages\flask_sqlalchemy\__init__.py:794: 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 '
[2019-09-27 17:10:28,217] _api:156 INFO: Exposing method User.send_mail on /Users/send_mail, endpoint: api.Users.send_mail
[2019-09-27 17:10:28,228] swagger_doc:380 DEBUG: no documentation for "<function SAFRSRestAPI.delete at 0x0000024D214F36A8>" 
[2019-09-27 17:10:28,239] _api:103 INFO: Exposing User on /Users/, endpoint: api.User
[2019-09-27 17:10:28,240] _api:111 INFO: Exposing Users instances on /Users/<string:UserId>/, endpoint: api.UserId
[2019-09-27 17:10:28,266] _api:217 INFO: Exposing relationship books on /Users/<string:UserId>/books, endpoint: /Users/<string:UserId>/api.books
[2019-09-27 17:10:28,269] _api:241 INFO: Exposing User relationship books on /Users/<string:UserId>/books/<string:BookId>, endpoint: /Users/<string:UserId>/api.booksId
[2019-09-27 17:10:28,283] swagger_doc:380 DEBUG: no documentation for "<function SAFRSRestAPI.delete at 0x0000024D214F36A8>" 
[2019-09-27 17:10:28,296] _api:103 INFO: Exposing Book on /Books/, endpoint: api.Book
[2019-09-27 17:10:28,298] _api:111 INFO: Exposing Books instances on /Books/<string:BookId>/, endpoint: api.BookId
[2019-09-27 17:10:28,338] _api:217 INFO: Exposing relationship user on /Books/<string:BookId>/user, endpoint: /Books/<string:BookId>/api.user
[2019-09-27 17:10:28,340] _api:241 INFO: Exposing Book relationship user on /Books/<string:BookId>/user/<string:UserId>, endpoint: /Books/<string:BookId>/api.userId
Starting API: http://localhost:5000
 * Serving Flask app "SAFRS Demo Application" (lazy loading)
 * Environment: production
   WARNING: This is a development server. Do not use it in a production deployment.
   Use a production WSGI server instead.
 * Debug mode: on
 * Restarting with stat
D:\Python36\lib\site-packages\flask_sqlalchemy\__init__.py:794: 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 '
[2019-09-27 17:10:35,801] errors:69 ERROR: Generic Error: (sqlite3.OperationalError) database is locked [SQL: 'INSERT INTO "Users" (id, name, email) VALUES (?, ?, ?)'] [parameters: ('f3899d67-f1f5-4954-a7d8-31c8b131bfab', 'thomas', 'em@il')] (Background on this error at: http://sqlalche.me/e/e3q8)
Traceback (most recent call last):
  File "D:\Python36\lib\site-packages\sqlalchemy\engine\base.py", line 1193, in _execute_context
    context)
  File "D:\Python36\lib\site-packages\sqlalchemy\engine\default.py", line 509, in do_execute
    cursor.execute(statement, parameters)
sqlite3.OperationalError: database is locked

The above exception was the direct cause of the following exception:

Traceback (most recent call last):
  File "D:\PycharmProjects\safrs\safrs\db.py", line 152, in __init__
    safrs.DB.session.commit()
  File "D:\Python36\lib\site-packages\sqlalchemy\orm\scoping.py", line 153, in do
    return getattr(self.registry(), name)(*args, **kwargs)
  File "D:\Python36\lib\site-packages\sqlalchemy\orm\session.py", line 954, in commit
    self.transaction.commit()
  File "D:\Python36\lib\site-packages\sqlalchemy\orm\session.py", line 467, in commit
    self._prepare_impl()
  File "D:\Python36\lib\site-packages\sqlalchemy\orm\session.py", line 447, in _prepare_impl
    self.session.flush()
  File "D:\Python36\lib\site-packages\sqlalchemy\orm\session.py", line 2313, in flush
    self._flush(objects)
  File "D:\Python36\lib\site-packages\sqlalchemy\orm\session.py", line 2440, in _flush
    transaction.rollback(_capture_exception=True)
  File "D:\Python36\lib\site-packages\sqlalchemy\util\langhelpers.py", line 66, in __exit__
    compat.reraise(exc_type, exc_value, exc_tb)
  File "D:\Python36\lib\site-packages\sqlalchemy\util\compat.py", line 249, in reraise
    raise value
  File "D:\Python36\lib\site-packages\sqlalchemy\orm\session.py", line 2404, in _flush
    flush_context.execute()
  File "D:\Python36\lib\site-packages\sqlalchemy\orm\unitofwork.py", line 395, in execute
    rec.execute(self)
  File "D:\Python36\lib\site-packages\sqlalchemy\orm\unitofwork.py", line 560, in execute
    uow
  File "D:\Python36\lib\site-packages\sqlalchemy\orm\persistence.py", line 181, in save_obj
    mapper, table, insert)
  File "D:\Python36\lib\site-packages\sqlalchemy\orm\persistence.py", line 836, in _emit_insert_statements
    execute(statement, multiparams)
  File "D:\Python36\lib\site-packages\sqlalchemy\engine\base.py", line 948, in execute
    return meth(self, multiparams, params)
  File "D:\Python36\lib\site-packages\sqlalchemy\sql\elements.py", line 269, in _execute_on_connection
    return connection._execute_clauseelement(self, multiparams, params)
  File "D:\Python36\lib\site-packages\sqlalchemy\engine\base.py", line 1060, in _execute_clauseelement
    compiled_sql, distilled_params
  File "D:\Python36\lib\site-packages\sqlalchemy\engine\base.py", line 1200, in _execute_context
    context)
  File "D:\Python36\lib\site-packages\sqlalchemy\engine\base.py", line 1413, in _handle_dbapi_exception
    exc_info
  File "D:\Python36\lib\site-packages\sqlalchemy\util\compat.py", line 265, in raise_from_cause
    reraise(type(exception), exception, tb=exc_tb, cause=cause)
  File "D:\Python36\lib\site-packages\sqlalchemy\util\compat.py", line 248, in reraise
    raise value.with_traceback(tb)
  File "D:\Python36\lib\site-packages\sqlalchemy\engine\base.py", line 1193, in _execute_context
    context)
  File "D:\Python36\lib\site-packages\sqlalchemy\engine\default.py", line 509, in do_execute
    cursor.execute(statement, parameters)
sqlalchemy.exc.OperationalError: (sqlite3.OperationalError) database is locked [SQL: 'INSERT INTO "Users" (id, name, email) VALUES (?, ?, ?)'] [parameters: ('f3899d67-f1f5-4954-a7d8-31c8b131bfab', 'thomas', 'em@il')] (Background on this error at: http://sqlalche.me/e/e3q8)

During handling of the above exception, another exception occurred:

Traceback (most recent call last):
  File "D:/PycharmProjects/safrs/examples/demo_relationship.py", line 78, in <module>
    user = User(name="thomas", email="em@il")
  File "<string>", line 4, in __init__
  File "D:\Python36\lib\site-packages\sqlalchemy\orm\state.py", line 424, in _initialize_instance
    manager.dispatch.init_failure(self, args, kwargs)
  File "D:\Python36\lib\site-packages\sqlalchemy\util\langhelpers.py", line 66, in __exit__
    compat.reraise(exc_type, exc_value, exc_tb)
  File "D:\Python36\lib\site-packages\sqlalchemy\util\compat.py", line 249, in reraise
    raise value
  File "D:\Python36\lib\site-packages\sqlalchemy\orm\state.py", line 421, in _initialize_instance
    return manager.original_init(*mixed[1:], **kwargs)
  File "D:\PycharmProjects\safrs\safrs\db.py", line 155, in __init__
    raise GenericError(exc)
safrs.errors.GenericError

Hybrid attributes

Unable to use hybrid properties, regular properties do work

https://docs.sqlalchemy.org/en/latest/orm/extensions/hybrid.html

Traceback (most recent call last):
  File "manage.py", line 9, in <module>
    app = init(get_config())
  File "/home/patrick/PycharmProjects/ebookhub-backend/eBookHub/__init__.py", line 47, in init
    configure_blueprints(app)
  File "/home/patrick/PycharmProjects/ebookhub-backend/eBookHub/__init__.py", line 56, in configure_blueprints
    create_api(app)
  File "/home/patrick/PycharmProjects/ebookhub-backend/eBookHub/__init__.py", line 74, in create_api
    api.expose_object(BookEditionFile)
  File "/home/patrick/PycharmProjects/ebookhub-backend/venv/lib/python3.6/site-packages/safrs/_api.py", line 85, in expose_object
    self.expose_methods(url_prefix, tags=tags)
  File "/home/patrick/PycharmProjects/ebookhub-backend/venv/lib/python3.6/site-packages/safrs/_api.py", line 130, in expose_methods
    api_methods = safrs_object.get_jsonapi_rpc_methods()
  File "/home/patrick/PycharmProjects/ebookhub-backend/venv/lib/python3.6/site-packages/safrs/db.py", line 626, in get_documented_api_methods
    for name, method in inspect.getmembers(cls):
  File "/usr/lib/python3.6/inspect.py", line 342, in getmembers
    value = getattr(object, key)
  File "/home/patrick/PycharmProjects/ebookhub-backend/venv/lib/python3.6/site-packages/sqlalchemy/ext/hybrid.py", line 867, in __get__
    return self._expr_comparator(owner)
  File "/home/patrick/PycharmProjects/ebookhub-backend/venv/lib/python3.6/site-packages/sqlalchemy/ext/hybrid.py", line 1066, in expr_comparator
    owner, self.__name__, self, comparator(owner),
  File "/home/patrick/PycharmProjects/ebookhub-backend/venv/lib/python3.6/site-packages/sqlalchemy/ext/hybrid.py", line 1055, in _expr
    return ExprComparator(cls, expr(cls), self)
  File "/home/patrick/PycharmProjects/ebookhub-backend/eBookHub/models/core.py", line 148, in get_path
    return os.path.join(get_temp_path(), self.filename)
  File "/usr/lib/python3.6/posixpath.py", line 94, in join
    genericpath._check_arg_types('join', a, *p)
  File "/usr/lib/python3.6/genericpath.py", line 149, in _check_arg_types
    (funcname, s.__class__.__name__)) from None
TypeError: join() argument must be str or bytes, not 'InstrumentedAttribute'
class BookEditionFile(Model):

    ...

    _result = db.Column(db.Text)
    
    ...

    @hybrid_property
    def result(self):
        if self._result:
            return json.loads(self._result)
        return None

    @result.setter
    def result(self, value):
        self._result = json.dumps(value)

How to add sample value (column default= is not working)

When I try to add sample value through the default value of a field this sample value isn't shown in the example block POST body. See screen print.
no_sample_value_in_experiment_ name

My model Experiment looks like this:
`
class Experiment(SAFRSBase, db.Model):
"""
description: Experiment
"""

__tablename__ = "experiments"
exclude_rels = ["datafile"]
id = db.Column(db.Integer, primary_key=True)
name = db.Column(db.String, default="Experiment 1")
description = db.Column(db.String, default="")

`

May be here

try:
should be an else: ?

or it would even be nicer to have an extra Column attribute for example "sample_value"

pipenv install fails

Was reading #10 and tried multiple ways of installing with pipenv

pipenv install -e git+https://github.com/thomaxxl/safrs.git#egg=master
pipenv install safrs
pipenv install safrs==1.1.0

it always fails at the same point, I'm not sure it's a pipenv bug or the setup.py :

 pipenv --version            
pipenv, version 2018.11.26

my Pipfile

[[source]]
name = "pypi"
url = "https://pypi.org/simple"
verify_ssl = true

[dev-packages]
pytest = "*"

[packages]
flask = "*"
flask-sqlalchemy = "*"
psycopg2-binary = "*"
safrs = "==1.1.0"

[requires]
python_version = "3.7"

the error messages, strange as it seems like my pipenv venv doesn't catch the fact I'm using 3.7

pipenv install safrs==1.1.0 
Installing safrs==1.1.0...
✔ Installation Succeeded 
Pipfile.lock (3895a0) out of date, updating to (56688f)...
Locking [dev-packages] dependencies...
✔ Success! 
Locking [packages] dependencies...
✘ Locking Failed! 
Traceback (most recent call last):
  File "/usr/local/lib/python2.7/dist-packages/pipenv/resolver.py", line 126, in <module>
    main()
  File "/usr/local/lib/python2.7/dist-packages/pipenv/resolver.py", line 119, in main
    parsed.requirements_dir, parsed.packages)
  File "/usr/local/lib/python2.7/dist-packages/pipenv/resolver.py", line 85, in _main
    requirements_dir=requirements_dir,
  File "/usr/local/lib/python2.7/dist-packages/pipenv/resolver.py", line 69, in resolve
    req_dir=requirements_dir
  File "/usr/local/lib/python2.7/dist-packages/pipenv/utils.py", line 726, in resolve_deps
    req_dir=req_dir,
  File "/usr/local/lib/python2.7/dist-packages/pipenv/utils.py", line 480, in actually_resolve_deps
    resolved_tree = resolver.resolve()
  File "/usr/local/lib/python2.7/dist-packages/pipenv/utils.py", line 385, in resolve
    results = self.resolver.resolve(max_rounds=environments.PIPENV_MAX_ROUNDS)
  File "/usr/local/lib/python2.7/dist-packages/pipenv/patched/piptools/resolver.py", line 102, in resolve
    has_changed, best_matches = self._resolve_one_round()
  File "/usr/local/lib/python2.7/dist-packages/pipenv/patched/piptools/resolver.py", line 206, in _resolve_one_round
    for dep in self._iter_dependencies(best_match):
  File "/usr/local/lib/python2.7/dist-packages/pipenv/patched/piptools/resolver.py", line 301, in _iter_dependencies
    dependencies = self.repository.get_dependencies(ireq)
  File "/usr/local/lib/python2.7/dist-packages/pipenv/patched/piptools/repositories/pypi.py", line 234, in get_dependencies
    legacy_results = self.get_legacy_dependencies(ireq)
  File "/usr/local/lib/python2.7/dist-packages/pipenv/patched/piptools/repositories/pypi.py", line 426, in get_legacy_dependencies
    results, ireq = self.resolve_reqs(download_dir, ireq, wheel_cache)
  File "/usr/local/lib/python2.7/dist-packages/pipenv/patched/piptools/repositories/pypi.py", line 297, in resolve_reqs
    results = resolver._resolve_one(reqset, ireq)
  File "/usr/local/lib/python2.7/dist-packages/pipenv/patched/notpip/_internal/resolve.py", line 274, in _resolve_one
    self.requires_python = check_dist_requires_python(dist, absorb=False)
  File "/usr/local/lib/python2.7/dist-packages/pipenv/patched/notpip/_internal/utils/packaging.py", line 62, in check_dist_requires_python
    '.'.join(map(str, sys.version_info[:3])),)
pipenv.patched.notpip._internal.exceptions.UnsupportedPythonVersion: safrs requires Python '>=3.0, !=3.0.*, !=3.1.*, !=3.2.*, <4' but the running Python is 2.7.15
File "/usr/local/lib/python2.7/dist-packages/pipenv/resolver.py", line 126, in <module>
    main()
  File "/usr/local/lib/python2.7/dist-packages/pipenv/resolver.py", line 119, in main
    parsed.requirements_dir, parsed.packages)
  File "/usr/local/lib/python2.7/dist-packages/pipenv/resolver.py", line 85, in _main
    requirements_dir=requirements_dir,
  File "/usr/local/lib/python2.7/dist-packages/pipenv/resolver.py", line 69, in resolve
    req_dir=requirements_dir
  File "/usr/local/lib/python2.7/dist-packages/pipenv/utils.py", line 726, in resolve_deps
    req_dir=req_dir,
  File "/usr/local/lib/python2.7/dist-packages/pipenv/utils.py", line 480, in actually_resolve_deps
    resolved_tree = resolver.resolve()
  File "/usr/local/lib/python2.7/dist-packages/pipenv/utils.py", line 385, in resolve
    results = self.resolver.resolve(max_rounds=environments.PIPENV_MAX_ROUNDS)
  File "/usr/local/lib/python2.7/dist-packages/pipenv/patched/piptools/resolver.py", line 102, in resolve
    has_changed, best_matches = self._resolve_one_round()
  File "/usr/local/lib/python2.7/dist-packages/pipenv/patched/piptools/resolver.py", line 206, in _resolve_one_round
    for dep in self._iter_dependencies(best_match):
  File "/usr/local/lib/python2.7/dist-packages/pipenv/patched/piptools/resolver.py", line 301, in _iter_dependencies
    dependencies = self.repository.get_dependencies(ireq)
  File "/usr/local/lib/python2.7/dist-packages/pipenv/patched/piptools/repositories/pypi.py", line 234, in get_dependencies
    legacy_results = self.get_legacy_dependencies(ireq)
  File "/usr/local/lib/python2.7/dist-packages/pipenv/patched/piptools/repositories/pypi.py", line 426, in get_legacy_dependencies
    results, ireq = self.resolve_reqs(download_dir, ireq, wheel_cache)
  File "/usr/local/lib/python2.7/dist-packages/pipenv/patched/piptools/repositories/pypi.py", line 297, in resolve_reqs
    results = resolver._resolve_one(reqset, ireq)
  File "/usr/local/lib/python2.7/dist-packages/pipenv/patched/notpip/_internal/resolve.py", line 274, in _resolve_one
    self.requires_python = check_dist_requires_python(dist, absorb=False)
  File "/usr/local/lib/python2.7/dist-packages/pipenv/patched/notpip/_internal/utils/packaging.py", line 62, in check_dist_requires_python
    '.'.join(map(str, sys.version_info[:3])),)
pipenv.patched.notpip._internal.exceptions.UnsupportedPythonVersion: safrs requires Python '>=3.0, !=3.0.*, !=3.1.*, !=3.2.*, <4' but the running Python is 2.7.15

Bulk create to endpoints

Hi, this is a very good tool to build the API quickly and I am happy to use it.
Is there a way to bulk create rows?

Error when installing with pip and pipenv

Trying to install the package using pipenv with pipenv install safrs or the same with pip, this ertror message is displayed:

Collecting safrs
  Downloading https://files.pythonhosted.org/packages/00/21/d6b45459525d75a2a314c35ca01005f509a60ef0edf6124a72abca06cdca/safrs-1.0.15.tar.gz
    Complete output from command python setup.py egg_info:
    Traceback (most recent call last):
      File "<string>", line 1, in <module>
      File "/tmp/pip-install-egxf1ylx/safrs/setup.py", line 17, in <module>
        long_description=open('README.rst').read(),
    FileNotFoundError: [Errno 2] No such file or directory: 'README.rst'

Paging state variables always 0

The function safrs.jsonapi.paginate always returns

      "links": {
        "first": 0,
        "last": 0,
        "next": 0,
        "prev": 0
      },

(hardcoded)
so we don't know if we have other pages or if we reached the last one.

Is it possible to improve this paging function?

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. 📊📈🎉

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google ❤️ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.