Giter Site home page Giter Site logo

karec / cookiecutter-flask-restful Goto Github PK

View Code? Open in Web Editor NEW
786.0 17.0 182.0 155 KB

Flask cookiecutter template for builing APIs with flask-restful, including JWT auth, cli, tests, swagger, docker and more

License: MIT License

Python 87.89% Dockerfile 1.85% Makefile 3.61% Shell 0.77% Jinja 5.88%
flask cookiecutter marshmallow jwt flask-restful

cookiecutter-flask-restful's People

Contributors

caffeinatedmike avatar drnextgis avatar espoirmur avatar hprobotic avatar karec avatar lieutdan13 avatar rudaporto avatar slzdude avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

cookiecutter-flask-restful's Issues

Celery test failed

I am running python 3.7.4 on windows 10 with celery 4.4.7, and I am using rabbitmq 3.8.5 for broker and result backend.
I deploy example project by cookiecutter and install all requirements and cli, but when I tried to use tox to do unittest, all tests except celery's one passed. So am I doing wrong here? any help will be appreciated.

E:\Project\testrestful\restful_api>tox
GLOB sdist-make: E:\Project\testrestful\restful_api\setup.py
py38 create: E:\Project\testrestful\restful_api\.tox\py38
ERROR: InterpreterNotFound: python3.8
py37 inst-nodeps: E:\Project\testrestful\restful_api\.tox\.tmp\package\3\myapi-0.1.zip
py37 installed: alembic==1.4.2,amqp==2.6.1,aniso8601==8.0.0,apispec==3.3.1,apispec-webframeworks==0.5.2,appdirs==1.4.4,atomicwrites==1.4.0,attrs==19.3.0,billiard==3.6.3.0,black==19.10b0,celery==4.4.7,click==7.1.2,colorama==0.4.3,distlib==0.3.1,factory-boy==2.12.0,Faker==4.1.1,filelock==3.0.12,flake8==3.8.3,Flask==1.1.2,Flask-JWT-Extended==3.24.1,flask-marshmallow==0.13.0,Flask-Migrate==2.5.3,Flask-RESTful==0.3.8,Flask-SQLAlchemy==2.4.4,gunicorn==20.0.4,importlib-metadata==1.7.0,inflection==0.5.0,iniconfig==1.0.1,itsdangerous==1.1.0,Jinja2==2.11.2,kombu==4.6.11,Mako==1.1.3,MarkupSafe==1.1.1,marshmallow==3.7.1,marshmallow-sqlalchemy==0.23.1,mccabe==0.6.1,more-itertools==8.4.0,myapi @ file:///E:/Project/testrestful/restful_api/.tox/.tmp/package/3/myapi-0.1.zip,packaging==20.4,passlib==1.7.2,pathspec==0.8.0,pluggy==0.13.1,py==1.9.0,pycodestyle==2.6.0,pyflakes==2.2.0,PyJWT==1.7.1,pyparsing==2.4.7,pytest==6.0.1,pytest-factoryboy==2.0.3,pytest-flask==1.0.0,pytest-runner==5.2,python-dateutil==2.8.1,python-dotenv==0.14.0,python-editor==1.0.4,pytz==2020.1,PyYAML==5.3.1,redis==3.5.3,regex==2020.7.14,six==1.15.0,SQLAlchemy==1.3.18,text-unidecode==1.3,toml==0.10.1,tox==3.18.1,typed-ast==1.4.1,vine==1.3.0,virtualenv==20.0.30,Werkzeug==1.0.1,zipp==3.1.0
py37 run-test-pre: PYTHONHASHSEED='862'
py37 run-test: commands[0] | flake8 myapi
py37 run-test: commands[1] | black myapi --check
All done! ✨ 🍰 ✨
24 files would be left unchanged.
py37 run-test: commands[2] | pytest tests
================================================= test session starts =================================================
platform win32 -- Python 3.7.4, pytest-6.0.1, py-1.9.0, pluggy-0.13.1
cachedir: .tox\py37\.pytest_cache
rootdir: E:\Project\testrestful\restful_api
plugins: celery-4.4.7, Faker-4.1.1, factoryboy-2.0.3, flask-1.0.0
collected 8 items

tests\test_auth.py ..                                                                                            [ 25%]
tests\test_celery.py Message: 'Task handler raised error: %r'
Arguments: (Task of kind 'celery.ping' never registered, please make sure it's imported.,)
E                                                                                           [ 37%]
tests\test_user.py .....                                                                                         [100%]

======================================================= ERRORS ========================================================
___________________________________________ ERROR at setup of test_example ____________________________________________

request = <SubRequest 'celery_worker' for <Function test_example>>, celery_app = <Celery celery.tests at 0x1e4291ade08>
celery_includes = (), celery_worker_pool = 'prefork', celery_worker_parameters = {}

    @pytest.fixture()
    def celery_worker(request,
                      celery_app,
                      celery_includes,
                      celery_worker_pool,
                      celery_worker_parameters):
        # type: (Any, Celery, Sequence[str], str, Any) -> WorkController
        """Fixture: Start worker in a thread, stop it when the test returns."""
        if not NO_WORKER:
            for module in celery_includes:
                celery_app.loader.import_task_module(module)
            with worker.start_worker(celery_app,
                                     pool=celery_worker_pool,
>                                    **celery_worker_parameters) as w:

.tox\py37\lib\site-packages\celery\contrib\pytest.py:198:
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _
f:\anaconda\lib\contextlib.py:112: in __enter__
    return next(self.gen)
.tox\py37\lib\site-packages\celery\contrib\testing\worker.py:84: in start_worker
    assert ping.delay().get(timeout=ping_task_timeout) == 'pong'
.tox\py37\lib\site-packages\celery\result.py:237: in get
    on_message=on_message,
.tox\py37\lib\site-packages\celery\backends\base.py:668: in wait_for_pending
    return result.maybe_throw(propagate=propagate, callback=callback)
.tox\py37\lib\site-packages\celery\result.py:342: in maybe_throw
    self.throw(value, self._to_remote_traceback(tb))
.tox\py37\lib\site-packages\celery\result.py:335: in throw
    self.on_ready.throw(*args, **kwargs)
.tox\py37\lib\site-packages\vine\promises.py:244: in throw
    reraise(type(exc), exc, tb)
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _

tp = <class 'celery.exceptions.NotRegistered'>
value = Task of kind 'celery.ping' never registered, please make sure it's imported., tb = None

    def reraise(tp, value, tb=None):
        """Reraise exception."""
        if value.__traceback__ is not tb:
            raise value.with_traceback(tb)
>       raise value
E       celery.exceptions.NotRegistered: 'celery.ping'

.tox\py37\lib\site-packages\vine\five.py:195: NotRegistered
------------------------------------------------ Captured stderr setup ------------------------------------------------
--- Logging error ---
Traceback (most recent call last):
  File "f:\anaconda\lib\logging\__init__.py", line 1028, in emit
    stream.write(msg + self.terminator)
OSError: [WinError 6] ε₯ζŸ„ζ— ζ•ˆγ€‚
Call stack:
================================================== warnings summary ===================================================
tests/test_celery.py::test_example
  e:\project\testrestful\restful_api\.tox\py37\lib\site-packages\celery\backends\amqp.py:67: CPendingDeprecationWarning:
      The AMQP result backend is scheduled for deprecation in     version 4.0 and removal in version v5.0.     Please use RPC backend or a persistent backend.

    alternative='Please use RPC backend or a persistent backend.')

-- Docs: https://docs.pytest.org/en/stable/warnings.html
=============================================== short test summary info ===============================================
ERROR tests/test_celery.py::test_example - celery.exceptions.NotRegistered: 'celery.ping'
======================================== 7 passed, 1 warning, 1 error in 1.98s ========================================

Currently broken because incompatable with Celery 5

Installs latest Celery and then...

celery_1 | You are using -A as an option of the worker sub-command:
celery_1 | celery worker -A celeryapp <...>
celery_1 |
celery_1 | The support for this usage was removed in Celery 5.0. Instead you should use -A as a global option:
celery_1 | celery -A celeryapp worker <...>
celery_1 | Usage: celery worker [OPTIONS]
celery_1 | Try 'celery worker --help' for help.
celery_1 |

module 'myapi.manage' has no attribute 'cli'

Hi @karec !

It seems that the latest changes (regarding cli from flask) gives an error after the pip install -e ., I just run flask --help to check and I get this (I tried with python 3.8 and 3.9, same issue):

Traceback (most recent call last):
  File "/Users/johndoe/Software/python-virtualenvs/TestRestful/lib/python3.9/site-packages/pkg_resources/__init__.py", line 2455, in resolve
    return functools.reduce(getattr, self.attrs, module)
AttributeError: module 'myapi.manage' has no attribute 'cli'

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

Traceback (most recent call last):
  File "/Users/johndoe/Software/python-virtualenvs/TestRestful/bin/flask", line 8, in <module>
    sys.exit(main())
  File "/Users/johndoe/Software/python-virtualenvs/TestRestful/lib/python3.9/site-packages/flask/cli.py", line 994, in main
    cli.main(args=sys.argv[1:])
  File "/Users/johndoe/Software/python-virtualenvs/TestRestful/lib/python3.9/site-packages/flask/cli.py", line 600, in main
    return super().main(*args, **kwargs)
  File "/Users/johndoe/Software/python-virtualenvs/TestRestful/lib/python3.9/site-packages/click/core.py", line 1052, in main
    with self.make_context(prog_name, args, **extra) as ctx:
  File "/Users/johndoe/Software/python-virtualenvs/TestRestful/lib/python3.9/site-packages/click/core.py", line 914, in make_context
    self.parse_args(ctx, args)
  File "/Users/johndoe/Software/python-virtualenvs/TestRestful/lib/python3.9/site-packages/click/core.py", line 1615, in parse_args
    rest = super().parse_args(ctx, args)
  File "/Users/johndoe/Software/python-virtualenvs/TestRestful/lib/python3.9/site-packages/click/core.py", line 1370, in parse_args
    value, args = param.handle_parse_result(ctx, opts, args)
  File "/Users/johndoe/Software/python-virtualenvs/TestRestful/lib/python3.9/site-packages/click/core.py", line 2347, in handle_parse_result
    value = self.process_value(ctx, value)
  File "/Users/johndoe/Software/python-virtualenvs/TestRestful/lib/python3.9/site-packages/click/core.py", line 2309, in process_value
    value = self.callback(ctx, self, value)
  File "/Users/johndoe/Software/python-virtualenvs/TestRestful/lib/python3.9/site-packages/click/core.py", line 1270, in show_help
    echo(ctx.get_help(), color=ctx.color)
  File "/Users/johndoe/Software/python-virtualenvs/TestRestful/lib/python3.9/site-packages/click/core.py", line 693, in get_help
    return self.command.get_help(self)
  File "/Users/johndoe/Software/python-virtualenvs/TestRestful/lib/python3.9/site-packages/click/core.py", line 1295, in get_help
    self.format_help(ctx, formatter)
  File "/Users/johndoe/Software/python-virtualenvs/TestRestful/lib/python3.9/site-packages/click/core.py", line 1326, in format_help
    self.format_options(ctx, formatter)
  File "/Users/johndoe/Software/python-virtualenvs/TestRestful/lib/python3.9/site-packages/click/core.py", line 1524, in format_options
    self.format_commands(ctx, formatter)
  File "/Users/johndoe/Software/python-virtualenvs/TestRestful/lib/python3.9/site-packages/click/core.py", line 1587, in format_commands
    for subcommand in self.list_commands(ctx):
  File "/Users/johndoe/Software/python-virtualenvs/TestRestful/lib/python3.9/site-packages/flask/cli.py", line 561, in list_commands
    self._load_plugin_commands()
  File "/Users/johndoe/Software/python-virtualenvs/TestRestful/lib/python3.9/site-packages/flask/cli.py", line 539, in _load_plugin_commands
    self.add_command(ep.load(), ep.name)
  File "/Users/johndoe/Software/python-virtualenvs/TestRestful/lib/python3.9/site-packages/pkg_resources/__init__.py", line 2447, in load
    return self.resolve()
  File "/Users/johndoe/Software/python-virtualenvs/TestRestful/lib/python3.9/site-packages/pkg_resources/__init__.py", line 2457, in resolve
    raise ImportError(str(exc)) from exc
ImportError: module 'myapi.manage' has no attribute 'cli'

Any idea?
Thanks a lot!
Best regards,

Sylvain

tox and pytest

Hi Karec,

I'm currently trying to adapt your template for my uses, but I encounter an error when I want to run test.
Error on test_revoke_access_token(client, admin_headers)
This is using fixture admin_headers, and admin_headers is also using fixtures admin_user(...) (ok) and client(...) (ko)
where does client come from ? it seems it's not initialized yet when running test, and I cannot find occurence to it.

ty

method_decorator unused?

first, just wanna say thank you for making this project.

i noticed that method_decorator is assigned [jwt_required] but it isn't used? not sure if i'm reading it correctly, but maybe it belongs above the put and delete requests as @method_decorator by default? or is it up to the user to decide...

Socketio endpoints

Hi - wondered if you thought it would be useful to include socket endpoints?

The password inserted into the database when adding a user is a hash value, but it is plaintext when updating a user.

I ran into this problem when I created a demo for testing using this project.Maybe it's a bug, so submit this issues.Thanks for checking out.

The password inserted into the database when adding a user is a hash value, but it is plaintext when updating a user.

post -> /api/v1/users Add new user.The password inserted into the database when adding a user is a hash value.

PUT -> /api/v1/users/2 Update user info.The password for inserting the database is in plaintext

I analyzed it for perhaps the following reasons.

The encryption process is done here, and the __init__ method is executed when adding a user, but not called when updating. Maybe that's the reason.

Related Codes:

def post(self):
schema = UserSchema()
user = schema.load(request.json)
db.session.add(user)
db.session.commit()
return {"msg": "user created", "user": schema.dump(user)}, 201

def put(self, user_id):
schema = UserSchema(partial=True)
user = User.query.get_or_404(user_id)
user = schema.load(request.json, instance=user)
db.session.commit()
return {"msg": "user updated", "user": schema.dump(user)}

class User(db.Model):
"""Basic user model
"""
id = db.Column(db.Integer, primary_key=True)
username = db.Column(db.String(80), unique=True, nullable=False)
email = db.Column(db.String(80), unique=True, nullable=False)
password = db.Column(db.String(255), nullable=False)
active = db.Column(db.Boolean, default=True)
def __init__(self, **kwargs):
super(User, self).__init__(**kwargs)
self.password = pwd_context.hash(self.password)
def __repr__(self):
return "<User %s>" % self.username

Pagination fails by type error

Using 'next' URL on our tests we are getting the following error:

self = <flask_sqlalchemy.BaseQuery object at 0x7f192cf46d00>, page = '1', per_page = 50, error_out = True, max_per_page = None

    def paginate(self, page=None, per_page=None, error_out=True, max_per_page=None):
# ... about 40 lines ....
>       if page < 1:
E       TypeError: '<' not supported between instances of 'str' and 'int'

venv/lib/python3.8/site-packages/flask_sqlalchemy/__init__.py:484: TypeError

SQLAlchemy related libraries installed are these:

Flask-SQLAlchemy==2.4.4
PyMySQL==0.9.3
SQLAlchemy==1.3.23
SQLAlchemy-Utils==0.36.8

I humble propose PR #49 as solution

Docker support

Can we get a Dockerfile and possibly a docker compose file as well as part of this template?

add cli elements to flask terminal command

It would be useful to add the cli commands (manage.cli) to the flask terminal command. This is a change of just a few lines, and would provide easy access to cli actions. e.g.

flask app init

where app is declared as the top level group for commands related to the.... app.

Doing it this way also makes it easy to access current_app inside commands.

I imagine a blueprint that contains the app click.group which can be imported to all other modules with cli commands and used as the top level namespace for commands specific to the app.

I have a working general example at https://github.com/DonalChilde/flask_cli -> manual_cli branch

I would be happy to try to submit a pull request if you are interested in the idea, but I've never done that before so it would take some time to figure it out.

SQLAlchemy ignores existing database and creates new instead

Hi, I've been using your great template (slightly tweaked to meet my needs) in one of my flask apps and noticed odd problem.
After changing SQLALCHEMY_DATABASE_URI to sqlite:///myapi.db and initializing sqlite database using flask cli commands: init -> migrate -> upgrade. I get an error when querying User table in python shell:

$ flask shell
>>>  from myapi.models.user import User
>>> User.query.all()
Traceback (most recent call last):
  File "/home/linux-hp/git-clones/restful_api/.venv/lib/python3.5/site-packages/sqlalchemy/engine/base.py", line 1193, in _execute_context
    context)
  File "/home/linux-hp/git-clones/restful_api/.venv/lib/python3.5/site-packages/sqlalchemy/engine/default.py", line 509, in do_execute
    cursor.execute(statement, parameters)
sqlite3.OperationalError: no such table: user

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

Traceback (most recent call last):
  File "<console>", line 1, in <module>
  File "/home/linux-hp/git-clones/restful_api/.venv/lib/python3.5/site-packages/sqlalchemy/orm/query.py", line 2843, in all
    return list(self)
  File "/home/linux-hp/git-clones/restful_api/.venv/lib/python3.5/site-packages/sqlalchemy/orm/query.py", line 2995, in __iter__
    return self._execute_and_instances(context)
  File "/home/linux-hp/git-clones/restful_api/.venv/lib/python3.5/site-packages/sqlalchemy/orm/query.py", line 3018, in _execute_and_instances
    result = conn.execute(querycontext.statement, self._params)
  File "/home/linux-hp/git-clones/restful_api/.venv/lib/python3.5/site-packages/sqlalchemy/engine/base.py", line 948, in execute
    return meth(self, multiparams, params)
  File "/home/linux-hp/git-clones/restful_api/.venv/lib/python3.5/site-packages/sqlalchemy/sql/elements.py", line 269, in _execute_on_connection
    return connection._execute_clauseelement(self, multiparams, params)
  File "/home/linux-hp/git-clones/restful_api/.venv/lib/python3.5/site-packages/sqlalchemy/engine/base.py", line 1060, in _execute_clauseelement
    compiled_sql, distilled_params
  File "/home/linux-hp/git-clones/restful_api/.venv/lib/python3.5/site-packages/sqlalchemy/engine/base.py", line 1200, in _execute_context
    context)
  File "/home/linux-hp/git-clones/restful_api/.venv/lib/python3.5/site-packages/sqlalchemy/engine/base.py", line 1413, in _handle_dbapi_exception
    exc_info
  File "/home/linux-hp/git-clones/restful_api/.venv/lib/python3.5/site-packages/sqlalchemy/util/compat.py", line 265, in raise_from_cause
    reraise(type(exception), exception, tb=exc_tb, cause=cause)
  File "/home/linux-hp/git-clones/restful_api/.venv/lib/python3.5/site-packages/sqlalchemy/util/compat.py", line 248, in reraise
    raise value.with_traceback(tb)
  File "/home/linux-hp/git-clones/restful_api/.venv/lib/python3.5/site-packages/sqlalchemy/engine/base.py", line 1193, in _execute_context
    context)
  File "/home/linux-hp/git-clones/restful_api/.venv/lib/python3.5/site-packages/sqlalchemy/engine/default.py", line 509, in do_execute
    cursor.execute(statement, parameters)
sqlalchemy.exc.OperationalError: (sqlite3.OperationalError) no such table: user [SQL: 'SELECT user.id AS user_id, user.username AS user_username, user.email AS user_email, user.password AS user_password, user.active AS user_active \nFROM user'] (Background on this error at: http://sqlalche.me/e/e3q8)

As far as I understood from googling that this somewhat related to Python import system, but I can't currently come up with any solution except of workaround with storing sqlite database in /tmp/ directory. How this problem can be solved?

Tests fail

Ubuntu 20.04
Python 3.8

Running pytest after creating project gives:

____________________________________________________________________________ ERROR at setup of test_revoke_access_token _____________________________________________________________________________

app = <Flask 'flaskapi'>

    @pytest.fixture
    def db(app):
        _db.app = app
    
        with app.app_context():
>           _db.create_all()

tests/conftest.py:24: 
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _
venv/lib/python3.8/site-packages/flask_sqlalchemy/__init__.py:1039: in create_all
    self._execute_for_all_tables(app, bind, 'create_all')
venv/lib/python3.8/site-packages/flask_sqlalchemy/__init__.py:1031: in _execute_for_all_tables
    op(bind=self.get_engine(app, bind), **extra)
venv/lib/python3.8/site-packages/flask_sqlalchemy/__init__.py:962: in get_engine
    return connector.get_engine()
venv/lib/python3.8/site-packages/flask_sqlalchemy/__init__.py:555: in get_engine
    options = self.get_options(sa_url, echo)
venv/lib/python3.8/site-packages/flask_sqlalchemy/__init__.py:570: in get_options
    self._sa.apply_driver_hacks(self._app, sa_url, options)
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _

self = <[AttributeError("'NoneType' object has no attribute 'drivername'") raised in repr()] SQLAlchemy object at 0x7facf606deb0>, app = <Flask 'flaskapi'>, sa_url = None, options = {}

    def apply_driver_hacks(self, app, sa_url, options):
        """This method is called before engine creation and used to inject
        driver specific hacks into the options.  The `options` parameter is
        a dictionary of keyword arguments that will then be used to call
        the :func:`sqlalchemy.create_engine` function.
    
        The default implementation provides some saner defaults for things
        like pool sizes for MySQL and sqlite.  Also it injects the setting of
        `SQLALCHEMY_NATIVE_UNICODE`.
        """
>       if sa_url.drivername.startswith('mysql'):
E       AttributeError: 'NoneType' object has no attribute 'drivername'

venv/lib/python3.8/site-packages/flask_sqlalchemy/__init__.py:883: AttributeError
____________________________________________________________________________ ERROR at setup of test_revoke_refresh_token ____________________________________________________________________________

app = <Flask 'flaskapi'>

    @pytest.fixture
    def db(app):
        _db.app = app
    
        with app.app_context():
>           _db.create_all()

tests/conftest.py:24: 
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _
venv/lib/python3.8/site-packages/flask_sqlalchemy/__init__.py:1039: in create_all
    self._execute_for_all_tables(app, bind, 'create_all')
venv/lib/python3.8/site-packages/flask_sqlalchemy/__init__.py:1031: in _execute_for_all_tables
    op(bind=self.get_engine(app, bind), **extra)
venv/lib/python3.8/site-packages/flask_sqlalchemy/__init__.py:962: in get_engine
    return connector.get_engine()
venv/lib/python3.8/site-packages/flask_sqlalchemy/__init__.py:555: in get_engine
    options = self.get_options(sa_url, echo)
venv/lib/python3.8/site-packages/flask_sqlalchemy/__init__.py:570: in get_options
    self._sa.apply_driver_hacks(self._app, sa_url, options)
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _

self = <[AttributeError("'NoneType' object has no attribute 'drivername'") raised in repr()] SQLAlchemy object at 0x7facf606deb0>, app = <Flask 'flaskapi'>, sa_url = None, options = {}

    def apply_driver_hacks(self, app, sa_url, options):
        """This method is called before engine creation and used to inject
        driver specific hacks into the options.  The `options` parameter is
        a dictionary of keyword arguments that will then be used to call
        the :func:`sqlalchemy.create_engine` function.
    
        The default implementation provides some saner defaults for things
        like pool sizes for MySQL and sqlite.  Also it injects the setting of
        `SQLALCHEMY_NATIVE_UNICODE`.
        """
>       if sa_url.drivername.startswith('mysql'):
E       AttributeError: 'NoneType' object has no attribute 'drivername'

venv/lib/python3.8/site-packages/flask_sqlalchemy/__init__.py:883: AttributeError

ImportError: cannot import name 'jwt_refresh_token_required' from 'flask_jwt_extended'

Having this error as soon as I cloned the project and tried installation steps, can someone help me set this up?

here's my pip freeze :

alembic==1.5.4 aniso8601==8.1.1 apispec==4.3.0 apispec-webframeworks==0.5.2 appdirs==1.4.4 arrow==0.17.0 binaryornot==0.4.4 certifi==2020.12.5 chardet==4.0.0 click==7.1.2 cookiecutter==1.7.2 distlib==0.3.1 filelock==3.0.12 Flask==1.1.2 Flask-JWT-Extended==4.0.2 flask-marshmallow==0.14.0 Flask-Migrate==2.6.0 Flask-RESTful==0.3.8 Flask-SQLAlchemy==2.4.4 gunicorn==20.0.4 idna==2.10 itsdangerous==1.1.0 Jinja2==2.11.3 jinja2-time==0.2.0 Mako==1.1.4 MarkupSafe==1.1.1 marshmallow==3.10.0 marshmallow-sqlalchemy==0.24.2 packaging==20.9 passlib==1.7.4 pluggy==0.13.1 poyo==0.5.0 py==1.10.0 PyJWT==2.0.1 pyparsing==2.4.7 python-dateutil==2.8.1 python-dotenv==0.15.0 python-editor==1.0.4 python-slugify==4.0.1 pytz==2021.1 PyYAML==5.4.1 requests==2.25.1 six==1.15.0 SQLAlchemy==1.3.23 text-unidecode==1.3 toml==0.10.2 tox==3.22.0 urllib3==1.26.3 virtualenv==20.4.2 Werkzeug==1.0.1

Make config class instead of a plain strings

I have been working with this for a long time I think it should be okay to have the config as a different class one for development, another for production, and another one for testing...!

For more info check here

Celery

Celery tests not passing with tox or pytest. Probably because no broker/result backend running.
Needs a) update in readme and b) some sort of w/a. Either run tests eager, or spin up an instance to test end to end.

"myapi": executable file not found in $PATH: unknown when using docker

Seems to be failing when installing and using all the defaults and then using the make init command

Error

Recreating restful_api_web_1 ... error

ERROR: for restful_api_web_1  Cannot start service web: OCI runtime create failed: container_linux.go:370: starting container process caused: exec: "myapi": executable file not found in $PATH: unknown

ERROR: for web  Cannot start service web: OCI runtime create failed: container_linux.go:370: starting container process caused: exec: "myapi": executable file not found in $PATH: unknown

Full setup and init output below:

Setup

bbolt@bbolt playground % cookiecutter https://github.com/karec/cookiecutter-flask-restful
project_name [restful_api]: 
app_name [myapi]: 
Select python_version:
1 - 3.8
2 - 3.7
3 - 3.6
Choose from 1, 2, 3 [1]: 
tox_python_env [py38]: 
Select use_celery:
1 - no
2 - yes
Choose from 1, 2 [1]: 
admin_user_username [admin]: 
admin_user_email [[email protected]]: 
admin_user_password [admin]: 
Select wsgi_server:
1 - none
2 - uwsgi
3 - gunicorn
Choose from 1, 2, 3 [1]: 

Init

bbolt@bbolt playground % cd restful_api 
bbolt@bbolt restful_api % make init
docker-compose build
Building web
Step 1/10 : FROM python:3.8
 ---> b0358f6298cd
Step 2/10 : RUN mkdir /code
 ---> Using cache
 ---> 87c9c870b25e
Step 3/10 : WORKDIR /code
 ---> Using cache
 ---> f21a622260a4
Step 4/10 : COPY requirements.txt setup.py tox.ini ./
 ---> 1706cabbf3ef
Step 5/10 : RUN pip install -U pip
 ---> Running in abc8ae00d385
Requirement already satisfied: pip in /usr/local/lib/python3.8/site-packages (20.3.3)
Removing intermediate container abc8ae00d385
 ---> 6d7debcf7641
Step 6/10 : RUN pip install -r requirements.txt
 ---> Running in 45e00103d363
Collecting marshmallow>=3
  Downloading marshmallow-3.10.0-py2.py3-none-any.whl (46 kB)
Collecting apispec-webframeworks
  Downloading apispec_webframeworks-0.5.2-py2.py3-none-any.whl (12 kB)
Collecting apispec[yaml]
  Downloading apispec-4.0.0-py2.py3-none-any.whl (26 kB)
Collecting PyYAML>=3.10
  Downloading PyYAML-5.3.1.tar.gz (269 kB)
Collecting flask
  Downloading Flask-1.1.2-py2.py3-none-any.whl (94 kB)
Collecting click>=5.1
  Downloading click-7.1.2-py2.py3-none-any.whl (82 kB)
Collecting itsdangerous>=0.24
  Downloading itsdangerous-1.1.0-py2.py3-none-any.whl (16 kB)
Collecting Jinja2>=2.10.1
  Downloading Jinja2-2.11.2-py2.py3-none-any.whl (125 kB)
Collecting MarkupSafe>=0.23
  Downloading MarkupSafe-1.1.1-cp38-cp38-manylinux1_x86_64.whl (32 kB)
Collecting Werkzeug>=0.15
  Downloading Werkzeug-1.0.1-py2.py3-none-any.whl (298 kB)
Collecting flask-jwt-extended
  Downloading Flask-JWT-Extended-3.25.0.tar.gz (31 kB)
Collecting PyJWT<2.0,>=1.6.4
  Downloading PyJWT-1.7.1-py2.py3-none-any.whl (18 kB)
Collecting flask-marshmallow
  Downloading flask_marshmallow-0.14.0-py2.py3-none-any.whl (10 kB)
Collecting six
  Downloading six-1.15.0-py2.py3-none-any.whl (10 kB)
Collecting flask-migrate
  Downloading Flask_Migrate-2.5.3-py2.py3-none-any.whl (13 kB)
Collecting flask-sqlalchemy
  Downloading Flask_SQLAlchemy-2.4.4-py2.py3-none-any.whl (17 kB)
Collecting alembic>=0.7
  Downloading alembic-1.4.3-py2.py3-none-any.whl (159 kB)
Collecting python-editor>=0.3
  Downloading python_editor-1.0.4-py3-none-any.whl (4.9 kB)
Collecting SQLAlchemy>=0.8.0
  Downloading SQLAlchemy-1.3.22-cp38-cp38-manylinux2010_x86_64.whl (1.3 MB)
Collecting flask-restful
  Downloading Flask_RESTful-0.3.8-py2.py3-none-any.whl (25 kB)
Collecting aniso8601>=0.82
  Downloading aniso8601-8.1.0-py2.py3-none-any.whl (44 kB)
Collecting marshmallow-sqlalchemy
  Downloading marshmallow_sqlalchemy-0.24.1-py2.py3-none-any.whl (18 kB)
Collecting passlib
  Downloading passlib-1.7.4-py2.py3-none-any.whl (525 kB)
Collecting python-dotenv
  Downloading python_dotenv-0.15.0-py2.py3-none-any.whl (18 kB)
Collecting tox
  Downloading tox-3.21.0-py2.py3-none-any.whl (84 kB)
Collecting filelock>=3.0.0
  Downloading filelock-3.0.12-py3-none-any.whl (7.6 kB)
Collecting packaging>=14
  Downloading packaging-20.8-py2.py3-none-any.whl (39 kB)
Collecting pluggy>=0.12.0
  Downloading pluggy-0.13.1-py2.py3-none-any.whl (18 kB)
Collecting py>=1.4.17
  Downloading py-1.10.0-py2.py3-none-any.whl (97 kB)
Collecting pyparsing>=2.0.2
  Downloading pyparsing-2.4.7-py2.py3-none-any.whl (67 kB)
Collecting toml>=0.9.4
  Downloading toml-0.10.2-py2.py3-none-any.whl (16 kB)
Collecting virtualenv!=20.0.0,!=20.0.1,!=20.0.2,!=20.0.3,!=20.0.4,!=20.0.5,!=20.0.6,!=20.0.7,>=16.0.0
  Downloading virtualenv-20.3.0-py2.py3-none-any.whl (5.7 MB)
Collecting appdirs<2,>=1.4.3
  Downloading appdirs-1.4.4-py2.py3-none-any.whl (9.6 kB)
Collecting distlib<1,>=0.3.1
  Downloading distlib-0.3.1-py2.py3-none-any.whl (335 kB)
Collecting Mako
  Downloading Mako-1.1.3-py2.py3-none-any.whl (75 kB)
Collecting python-dateutil
  Downloading python_dateutil-2.8.1-py2.py3-none-any.whl (227 kB)
Collecting pytz
  Downloading pytz-2020.5-py2.py3-none-any.whl (510 kB)
Building wheels for collected packages: PyYAML, flask-jwt-extended
  Building wheel for PyYAML (setup.py): started
  Building wheel for PyYAML (setup.py): finished with status 'done'
  Created wheel for PyYAML: filename=PyYAML-5.3.1-cp38-cp38-linux_x86_64.whl size=572488 sha256=5757dd393e674951a1bc399b335ae17843b17d8745797072a231b2452a416863
  Stored in directory: /root/.cache/pip/wheels/13/90/db/290ab3a34f2ef0b5a0f89235dc2d40fea83e77de84ed2dc05c
  Building wheel for flask-jwt-extended (setup.py): started
  Building wheel for flask-jwt-extended (setup.py): finished with status 'done'
  Created wheel for flask-jwt-extended: filename=Flask_JWT_Extended-3.25.0-py2.py3-none-any.whl size=21569 sha256=27dd3a35f6c4e6767fa4a06541c8499490d1a8489c77746d85ab54bf2a2cc89e
  Stored in directory: /root/.cache/pip/wheels/f0/53/54/ec3db050e12900460634d2f7badb842fa29af7508a5b84557d
Successfully built PyYAML flask-jwt-extended
Installing collected packages: MarkupSafe, Werkzeug, six, Jinja2, itsdangerous, click, SQLAlchemy, PyYAML, python-editor, python-dateutil, pyparsing, Mako, flask, filelock, distlib, appdirs, apispec, virtualenv, toml, pytz, PyJWT, py, pluggy, packaging, marshmallow, flask-sqlalchemy, aniso8601, alembic, tox, python-dotenv, passlib, marshmallow-sqlalchemy, flask-restful, flask-migrate, flask-marshmallow, flask-jwt-extended, apispec-webframeworks
Successfully installed Jinja2-2.11.2 Mako-1.1.3 MarkupSafe-1.1.1 PyJWT-1.7.1 PyYAML-5.3.1 SQLAlchemy-1.3.22 Werkzeug-1.0.1 alembic-1.4.3 aniso8601-8.1.0 apispec-4.0.0 apispec-webframeworks-0.5.2 appdirs-1.4.4 click-7.1.2 distlib-0.3.1 filelock-3.0.12 flask-1.1.2 flask-jwt-extended-3.25.0 flask-marshmallow-0.14.0 flask-migrate-2.5.3 flask-restful-0.3.8 flask-sqlalchemy-2.4.4 itsdangerous-1.1.0 marshmallow-3.10.0 marshmallow-sqlalchemy-0.24.1 packaging-20.8 passlib-1.7.4 pluggy-0.13.1 py-1.10.0 pyparsing-2.4.7 python-dateutil-2.8.1 python-dotenv-0.15.0 python-editor-1.0.4 pytz-2020.5 six-1.15.0 toml-0.10.2 tox-3.21.0 virtualenv-20.3.0
Removing intermediate container 45e00103d363
 ---> 30025f46750e
Step 7/10 : RUN pip install -e .
 ---> Running in f0ab77a80cb4
Obtaining file:///code
Requirement already satisfied: flask in /usr/local/lib/python3.8/site-packages (from myapi==0.1) (1.1.2)
Requirement already satisfied: flask-sqlalchemy in /usr/local/lib/python3.8/site-packages (from myapi==0.1) (2.4.4)
Requirement already satisfied: flask-restful in /usr/local/lib/python3.8/site-packages (from myapi==0.1) (0.3.8)
Requirement already satisfied: flask-migrate in /usr/local/lib/python3.8/site-packages (from myapi==0.1) (2.5.3)
Requirement already satisfied: flask-jwt-extended in /usr/local/lib/python3.8/site-packages (from myapi==0.1) (3.25.0)
Requirement already satisfied: flask-marshmallow in /usr/local/lib/python3.8/site-packages (from myapi==0.1) (0.14.0)
Requirement already satisfied: marshmallow-sqlalchemy in /usr/local/lib/python3.8/site-packages (from myapi==0.1) (0.24.1)
Requirement already satisfied: python-dotenv in /usr/local/lib/python3.8/site-packages (from myapi==0.1) (0.15.0)
Requirement already satisfied: passlib in /usr/local/lib/python3.8/site-packages (from myapi==0.1) (1.7.4)
Requirement already satisfied: apispec[yaml] in /usr/local/lib/python3.8/site-packages (from myapi==0.1) (4.0.0)
Requirement already satisfied: apispec-webframeworks in /usr/local/lib/python3.8/site-packages (from myapi==0.1) (0.5.2)
Requirement already satisfied: PyYAML>=3.10 in /usr/local/lib/python3.8/site-packages (from apispec[yaml]->myapi==0.1) (5.3.1)
Requirement already satisfied: click>=5.1 in /usr/local/lib/python3.8/site-packages (from flask->myapi==0.1) (7.1.2)
Requirement already satisfied: itsdangerous>=0.24 in /usr/local/lib/python3.8/site-packages (from flask->myapi==0.1) (1.1.0)
Requirement already satisfied: Jinja2>=2.10.1 in /usr/local/lib/python3.8/site-packages (from flask->myapi==0.1) (2.11.2)
Requirement already satisfied: Werkzeug>=0.15 in /usr/local/lib/python3.8/site-packages (from flask->myapi==0.1) (1.0.1)
Requirement already satisfied: MarkupSafe>=0.23 in /usr/local/lib/python3.8/site-packages (from Jinja2>=2.10.1->flask->myapi==0.1) (1.1.1)
Requirement already satisfied: PyJWT<2.0,>=1.6.4 in /usr/local/lib/python3.8/site-packages (from flask-jwt-extended->myapi==0.1) (1.7.1)
Requirement already satisfied: six in /usr/local/lib/python3.8/site-packages (from flask-jwt-extended->myapi==0.1) (1.15.0)
Requirement already satisfied: marshmallow>=2.0.0 in /usr/local/lib/python3.8/site-packages (from flask-marshmallow->myapi==0.1) (3.10.0)
Requirement already satisfied: alembic>=0.7 in /usr/local/lib/python3.8/site-packages (from flask-migrate->myapi==0.1) (1.4.3)
Requirement already satisfied: python-dateutil in /usr/local/lib/python3.8/site-packages (from alembic>=0.7->flask-migrate->myapi==0.1) (2.8.1)
Requirement already satisfied: SQLAlchemy>=1.1.0 in /usr/local/lib/python3.8/site-packages (from alembic>=0.7->flask-migrate->myapi==0.1) (1.3.22)
Requirement already satisfied: python-editor>=0.3 in /usr/local/lib/python3.8/site-packages (from alembic>=0.7->flask-migrate->myapi==0.1) (1.0.4)
Requirement already satisfied: Mako in /usr/local/lib/python3.8/site-packages (from alembic>=0.7->flask-migrate->myapi==0.1) (1.1.3)
Requirement already satisfied: aniso8601>=0.82 in /usr/local/lib/python3.8/site-packages (from flask-restful->myapi==0.1) (8.1.0)
Requirement already satisfied: pytz in /usr/local/lib/python3.8/site-packages (from flask-restful->myapi==0.1) (2020.5)
Installing collected packages: myapi
  Running setup.py develop for myapi
Successfully installed myapi
Removing intermediate container f0ab77a80cb4
 ---> 7e90d7594f1f
Step 8/10 : COPY myapi myapi/
 ---> 60bef73528f1
Step 9/10 : COPY migrations migrations/
 ---> 2e2dbb23e90d
Step 10/10 : EXPOSE 5000
 ---> Running in e557f96dd4e0
Removing intermediate container e557f96dd4e0
 ---> 2fbe405e8216

Successfully built 2fbe405e8216
Successfully tagged myapi:latest
docker-compose up -d
Recreating restful_api_web_1 ... error

ERROR: for restful_api_web_1  Cannot start service web: OCI runtime create failed: container_linux.go:370: starting container process caused: exec: "myapi": executable file not found in $PATH: unknown

ERROR: for web  Cannot start service web: OCI runtime create failed: container_linux.go:370: starting container process caused: exec: "myapi": executable file not found in $PATH: unknown
ERROR: Encountered errors while bringing up the project.
make: *** [run] Error 1

How to debug with pycharm

hello,I'm a new python coder。

The methods I found through search are to click the debug button directly when there is an app.run() call. But the project generated by your command is run through the flask run command. Presumably, the flask command called the create_app method to obtain the instance and run it. What should we do now?

Separate Schema and Ressources in separates modules

Bonjour @karec !

What do you think about separating resources and schemas and have them in 2 different packages?

Why I have an impression that having this in one module violates the Single responsibility principle or separation of concern? But I am not sure, maybe you can give some light on this.

Plus it may increase readability for the cookiecutter.

What are your thoughts?

I can work on this if you agree.

Running tests failed

I created a project using coockiecutter from this template, then:

cd myproject
virtualenv --no-site-packages --pyhton=python3.6 venv
source venv/bin/activate
pip install -r requirements.txt
pip install -e .

myapi init

But when i running tests

tox

There were a lot of errors appeared, below is the test log:

tox
GLOB sdist-make: /mnt/f/workspace/git/FlaskProjectTemplate/ig-restful-api/setup.py
python create: /mnt/f/workspace/git/FlaskProjectTemplate/ig-restful-api/.tox/python
python installdeps: flake8, pytest, pytest-flask, pytest-runner, pytest-factoryboy, factory_boy, -rrequirements.txt
python inst: /mnt/f/workspace/git/FlaskProjectTemplate/ig-restful-api/.tox/.tmp/package/1/igapi-0.1.zip
python installed: alembic==1.0.11,aniso8601==7.0.0,atomicwrites==1.3.0,attrs==19.1.0,Click==7.0,entrypoints==0.3,factory-boy==2.12.0,Faker==2.0.0,filelock==3.0.12,flake8==3.7.8,Flask==1.1.1,Flask-JWT-Extended==3.21.0,flask-marshmallow==0.10.1,Flask-Migrate==2.5.2,Flask-RESTful==0.3.7,Flask-SQLAlchemy==2.4.0,igapi==0.1,importlib-metadata==0.19,inflection==0.3.1,itsdangerous==1.1.0,Jinja2==2.10.1,Mako==1.1.0,MarkupSafe==1.1.1,marshmallow==3.0.0,marshmallow-sqlalchemy==0.17.0,mccabe==0.6.1,more-itertools==7.2.0,packaging==19.1,passlib==1.7.1,pathlib2==2.3.4,pluggy==0.12.0,py==1.8.0,pycodestyle==2.5.0,pyflakes==2.1.1,PyJWT==1.7.1,pyparsing==2.4.2,pytest==5.1.0,pytest-factoryboy==2.0.3,pytest-flask==0.15.0,pytest-runner==5.1,python-dateutil==2.8.0,python-dotenv==0.10.3,python-editor==1.0.4,pytz==2019.2,six==1.12.0,SQLAlchemy==1.3.7,text-unidecode==1.2,toml==0.10.0,tox==3.13.2,virtualenv==16.7.3,wcwidth==0.1.7,Werkzeug==0.15.5,zipp==0.5.2
python run-test-pre: PYTHONHASHSEED='4188641553'
python run-test: commands[0] | flake8 igapi
python run-test: commands[1] | pytest tests
=============================================================================== test session starts ================================================================================
platform linux -- Python 3.5.2, pytest-5.1.0, py-1.8.0, pluggy-0.12.0
cachedir: .tox/python/.pytest_cache
rootdir: /mnt/f/workspace/git/FlaskProjectTemplate/ig-restful-api
plugins: factoryboy-2.0.3, flask-0.15.0
collected 7 items

tests/test_auth.py ..                                                                                                                                                        [ 28%]
tests/test_user.py FF.FF                                                                                                                                                     [100%]

===================================================================================== FAILURES =====================================================================================
__________________________________________________________________________________ test_get_user ___________________________________________________________________________________

client = <FlaskClient <Flask 'igapi'>>, db = <SQLAlchemy engine=sqlite:///:memory:>, user = <User user1>
admin_headers = {'authorization': 'Bearer eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJpZGVudGl0eSI6MSwiZXhwIjoxNTY2MTg0OTI0LCJpYXQiOjE1NjY...oxNTY2MTg0MDI0LCJ0eXBlIjoiYWNjZXNzIn0.qYzgV4glilctyzHvsBJSb6OnicTPJRskbOb3hJhRMds', 'content-type': 'application/json'}

>   ???

tests/test_user.py:283:
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _
.tox/python/lib/python3.5/site-packages/werkzeug/test.py:1029: in get
    return self.open(*args, **kw)
.tox/python/lib/python3.5/site-packages/flask/testing.py:227: in open
    follow_redirects=follow_redirects,
.tox/python/lib/python3.5/site-packages/werkzeug/test.py:993: in open
    response = self.run_wsgi_app(environ.copy(), buffered=buffered)
.tox/python/lib/python3.5/site-packages/werkzeug/test.py:884: in run_wsgi_app
    rv = run_wsgi_app(self.application, environ, buffered=buffered)
.tox/python/lib/python3.5/site-packages/werkzeug/test.py:1119: in run_wsgi_app
    app_rv = app(environ, start_response)
.tox/python/lib/python3.5/site-packages/flask/app.py:2463: in __call__
    return self.wsgi_app(environ, start_response)
.tox/python/lib/python3.5/site-packages/flask/app.py:2449: in wsgi_app
    response = self.handle_exception(e)
.tox/python/lib/python3.5/site-packages/flask_restful/__init__.py:269: in error_router
    return original_handler(e)
.tox/python/lib/python3.5/site-packages/flask/app.py:1866: in handle_exception
    reraise(exc_type, exc_value, tb)
.tox/python/lib/python3.5/site-packages/flask/_compat.py:38: in reraise
    raise value.with_traceback(tb)
.tox/python/lib/python3.5/site-packages/flask/app.py:2446: in wsgi_app
    response = self.full_dispatch_request()
.tox/python/lib/python3.5/site-packages/flask/app.py:1951: in full_dispatch_request
    rv = self.handle_user_exception(e)
.tox/python/lib/python3.5/site-packages/flask_restful/__init__.py:269: in error_router
    return original_handler(e)
.tox/python/lib/python3.5/site-packages/flask/app.py:1820: in handle_user_exception
    reraise(exc_type, exc_value, tb)
.tox/python/lib/python3.5/site-packages/flask/_compat.py:38: in reraise
    raise value.with_traceback(tb)
.tox/python/lib/python3.5/site-packages/flask/app.py:1949: in full_dispatch_request
    rv = self.dispatch_request()
.tox/python/lib/python3.5/site-packages/flask/app.py:1935: in dispatch_request
    return self.view_functions[rule.endpoint](**req.view_args)
.tox/python/lib/python3.5/site-packages/flask_restful/__init__.py:458: in wrapper
    resp = resource(*args, **kwargs)
.tox/python/lib/python3.5/site-packages/flask/views.py:89: in view
    return self.dispatch_request(*args, **kwargs)
.tox/python/lib/python3.5/site-packages/flask_restful/__init__.py:573: in dispatch_request
    resp = meth(*args, **kwargs)
.tox/python/lib/python3.5/site-packages/flask_jwt_extended/view_decorators.py:103: in wrapper
    return fn(*args, **kwargs)
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _

self = <igapi.api.resources.user.UserResource object at 0x7f87a3d18438>, user_id = 2

    def get(self, user_id):
        schema = UserSchema()
        user = User.query.get_or_404(user_id)
>       return {"user": schema.dump(user).data}
E       AttributeError: 'dict' object has no attribute 'data'

igapi/api/resources/user.py:27: AttributeError
__________________________________________________________________________________ test_put_user ___________________________________________________________________________________

client = <FlaskClient <Flask 'igapi'>>, db = <SQLAlchemy engine=sqlite:///:memory:>, user = <User updated>
admin_headers = {'authorization': 'Bearer eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJpZGVudGl0eSI6MSwiZXhwIjoxNTY2MTg0OTI0LCJpYXQiOjE1NjY...oxNTY2MTg0MDI0LCJ0eXBlIjoiYWNjZXNzIn0.mjjdw9SzzuvAvouJyuQi96GoQurv4WWKdOy8GMU93Uo', 'content-type': 'application/json'}

    def test_put_user(client, db, user, admin_headers):
        # test 404
        rep = client.put("/api/v1/users/100000", headers=admin_headers)
        assert rep.status_code == 404

        db.session.add(user)
        db.session.commit()

        data = {'username': 'updated'}

        # test update user
        rep = client.put(
            '/api/v1/users/%d' % user.id,
            json=data,
>           headers=admin_headers
        )

tests/test_user.py:50:
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _
.tox/python/lib/python3.5/site-packages/werkzeug/test.py:1049: in put
    return self.open(*args, **kw)
.tox/python/lib/python3.5/site-packages/flask/testing.py:227: in open
    follow_redirects=follow_redirects,
.tox/python/lib/python3.5/site-packages/werkzeug/test.py:993: in open
    response = self.run_wsgi_app(environ.copy(), buffered=buffered)
.tox/python/lib/python3.5/site-packages/werkzeug/test.py:884: in run_wsgi_app
    rv = run_wsgi_app(self.application, environ, buffered=buffered)
.tox/python/lib/python3.5/site-packages/werkzeug/test.py:1119: in run_wsgi_app
    app_rv = app(environ, start_response)
.tox/python/lib/python3.5/site-packages/flask/app.py:2463: in __call__
    return self.wsgi_app(environ, start_response)
.tox/python/lib/python3.5/site-packages/flask/app.py:2449: in wsgi_app
    response = self.handle_exception(e)
.tox/python/lib/python3.5/site-packages/flask_restful/__init__.py:269: in error_router
    return original_handler(e)
.tox/python/lib/python3.5/site-packages/flask/app.py:1866: in handle_exception
    reraise(exc_type, exc_value, tb)
.tox/python/lib/python3.5/site-packages/flask/_compat.py:38: in reraise
    raise value.with_traceback(tb)
.tox/python/lib/python3.5/site-packages/flask/app.py:2446: in wsgi_app
    response = self.full_dispatch_request()
.tox/python/lib/python3.5/site-packages/flask/app.py:1951: in full_dispatch_request
    rv = self.handle_user_exception(e)
.tox/python/lib/python3.5/site-packages/flask_restful/__init__.py:269: in error_router
    return original_handler(e)
.tox/python/lib/python3.5/site-packages/flask/app.py:1820: in handle_user_exception
    reraise(exc_type, exc_value, tb)
.tox/python/lib/python3.5/site-packages/flask/_compat.py:38: in reraise
    raise value.with_traceback(tb)
.tox/python/lib/python3.5/site-packages/flask/app.py:1949: in full_dispatch_request
    rv = self.dispatch_request()
.tox/python/lib/python3.5/site-packages/flask/app.py:1935: in dispatch_request
    return self.view_functions[rule.endpoint](**req.view_args)
.tox/python/lib/python3.5/site-packages/flask_restful/__init__.py:458: in wrapper
    resp = resource(*args, **kwargs)
.tox/python/lib/python3.5/site-packages/flask/views.py:89: in view
    return self.dispatch_request(*args, **kwargs)
.tox/python/lib/python3.5/site-packages/flask_restful/__init__.py:573: in dispatch_request
    resp = meth(*args, **kwargs)
.tox/python/lib/python3.5/site-packages/flask_jwt_extended/view_decorators.py:103: in wrapper
    return fn(*args, **kwargs)
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _

self = <igapi.api.resources.user.UserResource object at 0x7f87a3c7da20>, user_id = 2

    def put(self, user_id):
        schema = UserSchema(partial=True)
        user = User.query.get_or_404(user_id)
>       user, errors = schema.load(request.json, instance=user)
E       TypeError: 'User' object is not iterable

igapi/api/resources/user.py:32: TypeError
_________________________________________________________________________________ test_create_user _________________________________________________________________________________

client = <FlaskClient <Flask 'igapi'>>, db = <SQLAlchemy engine=sqlite:///:memory:>
admin_headers = {'authorization': 'Bearer eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJpZGVudGl0eSI6MSwiZXhwIjoxNTY2MTg0OTI0LCJpYXQiOjE1NjY...oxNTY2MTg0MDI0LCJ0eXBlIjoiYWNjZXNzIn0.0QCtn7uKt6eTjby9DA6ZvdjFn1Sach2uA-s5z8lUoy0', 'content-type': 'application/json'}

    def test_create_user(client, db, admin_headers):
        # test bad data
        data = {
            'username': 'created'
        }
        rep = client.post(
            '/api/v1/users',
            json=data,
>           headers=admin_headers
        )

tests/test_user.py:86:
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _
.tox/python/lib/python3.5/site-packages/werkzeug/test.py:1039: in post
    return self.open(*args, **kw)
.tox/python/lib/python3.5/site-packages/flask/testing.py:227: in open
    follow_redirects=follow_redirects,
.tox/python/lib/python3.5/site-packages/werkzeug/test.py:993: in open
    response = self.run_wsgi_app(environ.copy(), buffered=buffered)
.tox/python/lib/python3.5/site-packages/werkzeug/test.py:884: in run_wsgi_app
    rv = run_wsgi_app(self.application, environ, buffered=buffered)
.tox/python/lib/python3.5/site-packages/werkzeug/test.py:1119: in run_wsgi_app
    app_rv = app(environ, start_response)
.tox/python/lib/python3.5/site-packages/flask/app.py:2463: in __call__
    return self.wsgi_app(environ, start_response)
.tox/python/lib/python3.5/site-packages/flask/app.py:2449: in wsgi_app
    response = self.handle_exception(e)
.tox/python/lib/python3.5/site-packages/flask_restful/__init__.py:269: in error_router
    return original_handler(e)
.tox/python/lib/python3.5/site-packages/flask/app.py:1866: in handle_exception
    reraise(exc_type, exc_value, tb)
.tox/python/lib/python3.5/site-packages/flask/_compat.py:38: in reraise
    raise value.with_traceback(tb)
.tox/python/lib/python3.5/site-packages/flask/app.py:2446: in wsgi_app
    response = self.full_dispatch_request()
.tox/python/lib/python3.5/site-packages/flask/app.py:1951: in full_dispatch_request
    rv = self.handle_user_exception(e)
.tox/python/lib/python3.5/site-packages/flask_restful/__init__.py:269: in error_router
    return original_handler(e)
.tox/python/lib/python3.5/site-packages/flask/app.py:1820: in handle_user_exception
    reraise(exc_type, exc_value, tb)
.tox/python/lib/python3.5/site-packages/flask/_compat.py:38: in reraise
    raise value.with_traceback(tb)
.tox/python/lib/python3.5/site-packages/flask/app.py:1949: in full_dispatch_request
    rv = self.dispatch_request()
.tox/python/lib/python3.5/site-packages/flask/app.py:1935: in dispatch_request
    return self.view_functions[rule.endpoint](**req.view_args)
.tox/python/lib/python3.5/site-packages/flask_restful/__init__.py:458: in wrapper
    resp = resource(*args, **kwargs)
.tox/python/lib/python3.5/site-packages/flask/views.py:89: in view
    return self.dispatch_request(*args, **kwargs)
.tox/python/lib/python3.5/site-packages/flask_restful/__init__.py:573: in dispatch_request
    resp = meth(*args, **kwargs)
.tox/python/lib/python3.5/site-packages/flask_jwt_extended/view_decorators.py:103: in wrapper
    return fn(*args, **kwargs)
igapi/api/resources/user.py:60: in post
    user, errors = schema.load(request.json)
.tox/python/lib/python3.5/site-packages/marshmallow_sqlalchemy/schema.py:216: in load
    return super(ModelSchema, self).load(data, *args, **kwargs)
.tox/python/lib/python3.5/site-packages/marshmallow/schema.py:684: in load
    data, many=many, partial=partial, unknown=unknown, postprocess=True
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _

self = <UserSchema(many=False)>, data = {'username': 'created'}

    def _do_load(
        self, data, *, many=None, partial=None, unknown=None, postprocess=True
    ):
        """Deserialize `data`, returning the deserialized result.

        :param data: The data to deserialize.
        :param bool many: Whether to deserialize `data` as a collection. If `None`, the
            value for `self.many` is used.
        :param bool|tuple partial: Whether to validate required fields. If its
            value is an iterable, only fields listed in that iterable will be
            ignored will be allowed missing. If `True`, all fields will be allowed missing.
            If `None`, the value for `self.partial` is used.
        :param unknown: Whether to exclude, include, or raise an error for unknown
            fields in the data. Use `EXCLUDE`, `INCLUDE` or `RAISE`.
            If `None`, the value for `self.unknown` is used.
        :param bool postprocess: Whether to run post_load methods..
        :return: A dict of deserialized data
        :rtype: dict
        """
        error_store = ErrorStore()
        errors = {}
        many = self.many if many is None else bool(many)
        unknown = unknown or self.unknown
        if partial is None:
            partial = self.partial
        # Run preprocessors
        if self._has_processors(PRE_LOAD):
            try:
                processed_data = self._invoke_load_processors(
                    PRE_LOAD, data, many=many, original_data=data, partial=partial
                )
            except ValidationError as err:
                errors = err.normalized_messages()
                result = None
        else:
            processed_data = data
        if not errors:
            # Deserialize data
            result = self._deserialize(
                processed_data,
                error_store=error_store,
                many=many,
                partial=partial,
                unknown=unknown,
            )
            # Run field-level validation
            self._invoke_field_validators(
                error_store=error_store, data=result, many=many
            )
            # Run schema-level validation
            if self._has_processors(VALIDATES_SCHEMA):
                field_errors = bool(error_store.errors)
                self._invoke_schema_validators(
                    error_store=error_store,
                    pass_many=True,
                    data=result,
                    original_data=data,
                    many=many,
                    partial=partial,
                    field_errors=field_errors,
                )
                self._invoke_schema_validators(
                    error_store=error_store,
                    pass_many=False,
                    data=result,
                    original_data=data,
                    many=many,
                    partial=partial,
                    field_errors=field_errors,
                )
            errors = error_store.errors
            # Run post processors
            if not errors and postprocess and self._has_processors(POST_LOAD):
                try:
                    result = self._invoke_load_processors(
                        POST_LOAD,
                        result,
                        many=many,
                        original_data=data,
                        partial=partial,
                    )
                except ValidationError as err:
                    errors = err.normalized_messages()
        if errors:
            exc = ValidationError(errors, data=data, valid_data=result)
            self.handle_error(exc, data, many=many, partial=partial)
>           raise exc
E           marshmallow.exceptions.ValidationError: {'password': ['Missing data for required field.'], 'email': ['Missing data for required field.']}

.tox/python/lib/python3.5/site-packages/marshmallow/schema.py:842: ValidationError
________________________________________________________________________________ test_get_all_user _________________________________________________________________________________

client = <FlaskClient <Flask 'igapi'>>, db = <SQLAlchemy engine=sqlite:///:memory:>, user_factory = <class 'tests.test_user.UserFactory'>
admin_headers = {'authorization': 'Bearer eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJpZGVudGl0eSI6MSwiZXhwIjoxNTY2MTg0OTI0LCJpYXQiOjE1NjY...oxNTY2MTg0MDI0LCJ0eXBlIjoiYWNjZXNzIn0.F9D-sbfjg_jSmWcW0kkIfHBBgyfqHFXk9K1t347wWZs', 'content-type': 'application/json'}

    def test_get_all_user(client, db, user_factory, admin_headers):
        users = user_factory.create_batch(30)

        db.session.add_all(users)
        db.session.commit()

>       rep = client.get('/api/v1/users', headers=admin_headers)

tests/test_user.py:113:
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _
.tox/python/lib/python3.5/site-packages/werkzeug/test.py:1029: in get
    return self.open(*args, **kw)
.tox/python/lib/python3.5/site-packages/flask/testing.py:227: in open
    follow_redirects=follow_redirects,
.tox/python/lib/python3.5/site-packages/werkzeug/test.py:993: in open
    response = self.run_wsgi_app(environ.copy(), buffered=buffered)
.tox/python/lib/python3.5/site-packages/werkzeug/test.py:884: in run_wsgi_app
    rv = run_wsgi_app(self.application, environ, buffered=buffered)
.tox/python/lib/python3.5/site-packages/werkzeug/test.py:1119: in run_wsgi_app
    app_rv = app(environ, start_response)
.tox/python/lib/python3.5/site-packages/flask/app.py:2463: in __call__
    return self.wsgi_app(environ, start_response)
.tox/python/lib/python3.5/site-packages/flask/app.py:2449: in wsgi_app
    response = self.handle_exception(e)
.tox/python/lib/python3.5/site-packages/flask_restful/__init__.py:269: in error_router
    return original_handler(e)
.tox/python/lib/python3.5/site-packages/flask/app.py:1866: in handle_exception
    reraise(exc_type, exc_value, tb)
.tox/python/lib/python3.5/site-packages/flask/_compat.py:38: in reraise
    raise value.with_traceback(tb)
.tox/python/lib/python3.5/site-packages/flask/app.py:2446: in wsgi_app
    response = self.full_dispatch_request()
.tox/python/lib/python3.5/site-packages/flask/app.py:1951: in full_dispatch_request
    rv = self.handle_user_exception(e)
.tox/python/lib/python3.5/site-packages/flask_restful/__init__.py:269: in error_router
    return original_handler(e)
.tox/python/lib/python3.5/site-packages/flask/app.py:1820: in handle_user_exception
    reraise(exc_type, exc_value, tb)
.tox/python/lib/python3.5/site-packages/flask/_compat.py:38: in reraise
    raise value.with_traceback(tb)
.tox/python/lib/python3.5/site-packages/flask/app.py:1949: in full_dispatch_request
    rv = self.dispatch_request()
.tox/python/lib/python3.5/site-packages/flask/app.py:1935: in dispatch_request
    return self.view_functions[rule.endpoint](**req.view_args)
.tox/python/lib/python3.5/site-packages/flask_restful/__init__.py:458: in wrapper
    resp = resource(*args, **kwargs)
.tox/python/lib/python3.5/site-packages/flask/views.py:89: in view
    return self.dispatch_request(*args, **kwargs)
.tox/python/lib/python3.5/site-packages/flask_restful/__init__.py:573: in dispatch_request
    resp = meth(*args, **kwargs)
.tox/python/lib/python3.5/site-packages/flask_jwt_extended/view_decorators.py:103: in wrapper
    return fn(*args, **kwargs)
igapi/api/resources/user.py:56: in get
    return paginate(query, schema)
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _

query = <flask_sqlalchemy.BaseQuery object at 0x7f87a3addba8>, schema = <UserSchema(many=True)>

    def paginate(query, schema):
        page = request.args.get('page', DEFAULT_PAGE_NUMBER)
        per_page = request.args.get('page_size', DEFAULT_PAGE_SIZE)
        page_obj = query.paginate(page=page, per_page=per_page)
        next = url_for(
            request.endpoint,
            page=page_obj.next_num if page_obj.has_next else page_obj.page,
            per_page=per_page,
            **request.view_args
        )
        prev = url_for(
            request.endpoint,
            page=page_obj.prev_num if page_obj.has_prev else page_obj.page,
            per_page=per_page,
            **request.view_args
        )

        return {
            'total': page_obj.total,
            'pages': page_obj.pages,
            'next': next,
            'prev': prev,
>           'results': schema.dump(page_obj.items).data
        }
E       AttributeError: 'list' object has no attribute 'data'

igapi/commons/pagination.py:31: AttributeError
=========================================================================== 4 failed, 3 passed in 2.88s ============================================================================
ERROR: InvocationError for command /mnt/f/workspace/git/FlaskProjectTemplate/ig-restful-api/.tox/python/bin/pytest tests (exited with code 1)
_____________________________________________________________________________________ summary ______________________________________________________________________________________
ERROR:   python: commands failed

I noticed that the packages in requirements.txt have no fixed version, these may be the cause of the error.

The following packages is currently in use:

alembic==1.0.11
aniso8601==7.0.0
attrs==19.1.0
Click==7.0
filelock==3.0.12
Flask==1.1.1
Flask-JWT-Extended==3.21.0
flask-marshmallow==0.10.1
Flask-Migrate==2.5.2
Flask-RESTful==0.3.7
Flask-SQLAlchemy==2.4.0
# Editable install with no version control (igapi==0.1)
-e /mnt/f/workspace/git/FlaskProjectTemplate/ig-restful-api
importlib-metadata==0.19
itsdangerous==1.1.0
Jinja2==2.10.1
Mako==1.1.0
MarkupSafe==1.1.1
marshmallow==3.0.0
marshmallow-sqlalchemy==0.17.0
packaging==19.1
passlib==1.7.1
pluggy==0.12.0
py==1.8.0
PyJWT==1.7.1
pyparsing==2.4.2
python-dateutil==2.8.0
python-dotenv==0.10.3
python-editor==1.0.4
pytz==2019.2
six==1.12.0
SQLAlchemy==1.3.7
toml==0.10.0
tox==3.13.2
virtualenv==16.7.3
Werkzeug==0.15.

May i ask which packages should work correctly?

User Logout

Is there a reason this example doesn't have a user logout function that invalidates the jwt?

Adding a register endpoint

I found that we have a login endpoint but not a register endpoint.

Is a register endpoint within the scope of this cookie cutter?

Consider pyproject.toml as an option?

Really cool project, thank you very much for all your efforts! ❀️

Problem

Modern Python project uses the pyproject.toml file to configure a project. However, this can't be selected by these cookiecutter templates.

Suggestion

If it's feasible, could we offer an option to select between setup.py and pyproject.toml? We could use setup.py as default, but offer the new option if people want to go this way.

Thanks!

flask db upgrade sqlite fails with import error

flask db upgrade
Traceback (most recent call last):
File "/Users/tirghose/Work/src/py/.venv_upann/bin/flask", line 8, in
sys.exit(main())
File "/Users/tirghose/Work/src/py/.venv_upann/lib/python3.9/site-packages/flask/cli.py", line 994, in main
cli.main(args=sys.argv[1:])
File "/Users/tirghose/Work/src/py/.venv_upann/lib/python3.9/site-packages/flask/cli.py", line 600, in main
return super().main(*args, **kwargs)
File "/Users/tirghose/Work/src/py/.venv_upann/lib/python3.9/site-packages/click/core.py", line 782, in main
rv = self.invoke(ctx)
File "/Users/tirghose/Work/src/py/.venv_upann/lib/python3.9/site-packages/click/core.py", line 1259, in invoke
return _process_result(sub_ctx.command.invoke(sub_ctx))
File "/Users/tirghose/Work/src/py/.venv_upann/lib/python3.9/site-packages/click/core.py", line 1259, in invoke
return _process_result(sub_ctx.command.invoke(sub_ctx))
File "/Users/tirghose/Work/src/py/.venv_upann/lib/python3.9/site-packages/click/core.py", line 1066, in invoke
return ctx.invoke(self.callback, **ctx.params)
File "/Users/tirghose/Work/src/py/.venv_upann/lib/python3.9/site-packages/click/core.py", line 610, in invoke
return callback(*args, **kwargs)
File "/Users/tirghose/Work/src/py/.venv_upann/lib/python3.9/site-packages/click/decorators.py", line 21, in new_func
return f(get_current_context(), *args, **kwargs)
File "/Users/tirghose/Work/src/py/.venv_upann/lib/python3.9/site-packages/flask/cli.py", line 443, in decorator
with __ctx.ensure_object(ScriptInfo).load_app().app_context():
File "/Users/tirghose/Work/src/py/.venv_upann/lib/python3.9/site-packages/flask/cli.py", line 406, in load_app
app = locate_app(self, import_name, name)
File "/Users/tirghose/Work/src/py/.venv_upann/lib/python3.9/site-packages/flask/cli.py", line 278, in locate_app
return find_app_by_string(script_info, module, app_name)
File "/Users/tirghose/Work/src/py/.venv_upann/lib/python3.9/site-packages/flask/cli.py", line 205, in find_app_by_string
app = call_factory(script_info, attr, args, kwargs)
File "/Users/tirghose/Work/src/py/.venv_upann/lib/python3.9/site-packages/flask/cli.py", line 123, in call_factory
return app_factory(*args, **kwargs)
File "/Users/tirghose/Work/src/py/upann_api/upann/app.py", line 14, in create_app
app.config.from_object("upann.config")
File "/Users/tirghose/Work/src/py/.venv_upann/lib/python3.9/site-packages/flask/config.py", line 162, in from_object
obj = import_string(obj)
File "/Users/tirghose/Work/src/py/.venv_upann/lib/python3.9/site-packages/werkzeug/utils.py", line 887, in import_string
raise ImportStringError(import_name, e).with_traceback(
File "/Users/tirghose/Work/src/py/.venv_upann/lib/python3.9/site-packages/werkzeug/utils.py", line 883, in import_string
raise ImportError(e) from None
werkzeug.utils.ImportStringError: import_string() failed for 'upann.config'. Possible reasons are:

  • missing init.py in a package;
  • package or module path not included in sys.path;
  • duplicated package or module name taking precedence in sys.path;
  • missing module, class, function or variable;

Debugged import:

  • 'upann' found in '/Users/tirghose/Work/src/py/upann_api/upann/init.py'.
  • 'upann.config' not found.

Original exception:

ImportError: module 'upann' has no attribute 'config'

Use a declarative config?

Situation

The generated project contains a setup.py. Although this file is small and doesn't have much in it, it doesn't follow a more declarative way.

Suggestion

Although it's not mandatory of course, we should follow a more declarative approach. For example, we could add all the meta data into setup.cfg and have a very minimal setup.py. See the link below for a more exhaustive description.

I think there are some benefits for this approach. We encourage developers to add meta data into setup.cfg, keeping setup.py small. It's probably a bit easier to move to pyproject.toml if someone wants to go that way. It's also more in sync with the modern way to package Python project.

References

https://setuptools.rtfd.io/en/latest/userguide/declarative_config.html

Missing client pytest fixture

On a new cookiecutter from this repo, there is a missing client pytest fixture:

$ cookiecutter https://github.com/karec/cookiecutter-flask-restful
You've downloaded /Users/gburek/.cookiecutters/cookiecutter-flask-restful before. Is it okay to delete and re-download it? [yes]:
project_name [restful_api]:
app_name [myapi]:

$ cd restful_api

$ pipenv install
Creating a virtualenv for this project...
Pipfile: /Users/gburek/code/restful_api/Pipfile
Using /Users/gburek/.pyenv/versions/3.6.5/bin/python3.6 (3.6.5) to create virtualenv...
β ‹Already using interpreter /Users/gburek/.pyenv/versions/3.6.5/bin/python3.6
Using base prefix '/Users/gburek/.pyenv/versions/3.6.5'
New python executable in /Users/gburek/.local/share/virtualenvs/restful_api-w6vN7KAs/bin/python3.6
Also creating executable in /Users/gburek/.local/share/virtualenvs/restful_api-w6vN7KAs/bin/python
Installing setuptools, pip, wheel...done.
Setting project for restful_api-w6vN7KAs to /Users/gburek/code/restful_api

Virtualenv location: /Users/gburek/.local/share/virtualenvs/restful_api-w6vN7KAs
requirements.txt found, instead of Pipfile! Converting...
Warning: Your Pipfile now contains pinned versions, if your requirements.txt did.
We recommend updating your Pipfile to specify the "*" version, instead.
Pipfile.lock not found, creating...
Locking [dev-packages] dependencies...
Locking [packages] dependencies...
Updated Pipfile.lock (7950d7)!
Installing dependencies from Pipfile.lock (7950d7)...
  🐍   β–‰β–‰β–‰β–‰β–‰β–‰β–‰β–‰β–‰β–‰β–‰β–‰β–‰β–‰β–‰β–‰β–‰β–‰β–‰β–‰β–‰β–‰β–‰β–‰β–‰β–‰β–‰β–‰β–‰β–‰β–‰β–‰ 29/29 β€” 00:00:08
To activate this project's virtualenv, run pipenv shell.
Alternatively, run a command inside the virtualenv with pipenv run.

$ pipenv install --dev pytest pytest_factoryboy
Installing pytest...
Collecting pytest
  Using cached https://files.pythonhosted.org/packages/0c/9a/592314ceda78f3307afb6cf56d7fdbb92c5a5960a88a6d2fd25c11312ead/pytest-3.8.1-py2.py3-none-any.whl
Requirement already satisfied: setuptools in /Users/gburek/.local/share/virtualenvs/restful_api-w6vN7KAs/lib/python3.6/site-packages (from pytest) (40.4.3)
Requirement already satisfied: six>=1.10.0 in /Users/gburek/.local/share/virtualenvs/restful_api-w6vN7KAs/lib/python3.6/site-packages (from pytest) (1.11.0)
Collecting attrs>=17.4.0 (from pytest)
  Using cached https://files.pythonhosted.org/packages/3a/e1/5f9023cc983f1a628a8c2fd051ad19e76ff7b142a0faf329336f9a62a514/attrs-18.2.0-py2.py3-none-any.whl
Collecting atomicwrites>=1.0 (from pytest)
  Using cached https://files.pythonhosted.org/packages/3a/9a/9d878f8d885706e2530402de6417141129a943802c084238914fa6798d97/atomicwrites-1.2.1-py2.py3-none-any.whl
Requirement already satisfied: pluggy>=0.7 in /Users/gburek/.local/share/virtualenvs/restful_api-w6vN7KAs/lib/python3.6/site-packages (from pytest) (0.7.1)
Requirement already satisfied: py>=1.5.0 in /Users/gburek/.local/share/virtualenvs/restful_api-w6vN7KAs/lib/python3.6/site-packages (from pytest) (1.6.0)
Collecting more-itertools>=4.0.0 (from pytest)
  Using cached https://files.pythonhosted.org/packages/79/b1/eace304ef66bd7d3d8b2f78cc374b73ca03bc53664d78151e9df3b3996cc/more_itertools-4.3.0-py3-none-any.whl
Installing collected packages: attrs, atomicwrites, more-itertools, pytest
Successfully installed atomicwrites-1.2.1 attrs-18.2.0 more-itertools-4.3.0 pytest-3.8.1

Adding pytest to Pipfile's [dev-packages]...
Installing pytest_factoryboy...
Collecting pytest_factoryboy
Collecting inflection (from pytest_factoryboy)
Requirement already satisfied: pytest>=3.3.2 in /Users/gburek/.local/share/virtualenvs/restful_api-w6vN7KAs/lib/python3.6/site-packages (from pytest_factoryboy) (3.8.1)
Collecting factory-boy>=2.10.0 (from pytest_factoryboy)
  Using cached https://files.pythonhosted.org/packages/19/6c/b2ac85b3f0b48ac968af3741c4f020bf272ab9dabbd1643e9c719441099a/factory_boy-2.11.1-py2.py3-none-any.whl
Requirement already satisfied: more-itertools>=4.0.0 in /Users/gburek/.local/share/virtualenvs/restful_api-w6vN7KAs/lib/python3.6/site-packages (from pytest>=3.3.2->pytest_factoryboy) (4.3.0)
Requirement already satisfied: pluggy>=0.7 in /Users/gburek/.local/share/virtualenvs/restful_api-w6vN7KAs/lib/python3.6/site-packages (from pytest>=3.3.2->pytest_factoryboy) (0.7.1)
Requirement already satisfied: py>=1.5.0 in /Users/gburek/.local/share/virtualenvs/restful_api-w6vN7KAs/lib/python3.6/site-packages (from pytest>=3.3.2->pytest_factoryboy) (1.6.0)
Requirement already satisfied: atomicwrites>=1.0 in /Users/gburek/.local/share/virtualenvs/restful_api-w6vN7KAs/lib/python3.6/site-packages (from pytest>=3.3.2->pytest_factoryboy) (1.2.1)
Requirement already satisfied: six>=1.10.0 in /Users/gburek/.local/share/virtualenvs/restful_api-w6vN7KAs/lib/python3.6/site-packages (from pytest>=3.3.2->pytest_factoryboy) (1.11.0)
Requirement already satisfied: setuptools in /Users/gburek/.local/share/virtualenvs/restful_api-w6vN7KAs/lib/python3.6/site-packages (from pytest>=3.3.2->pytest_factoryboy) (40.4.3)
Requirement already satisfied: attrs>=17.4.0 in /Users/gburek/.local/share/virtualenvs/restful_api-w6vN7KAs/lib/python3.6/site-packages (from pytest>=3.3.2->pytest_factoryboy) (18.2.0)
Collecting Faker>=0.7.0 (from factory-boy>=2.10.0->pytest_factoryboy)
  Using cached https://files.pythonhosted.org/packages/6e/e6/78a4bcf0e59c31b0ed9f4b81ef62cf6b7f4978755b5a88e566dc0fec8e06/Faker-0.9.1-py2.py3-none-any.whl
Requirement already satisfied: python-dateutil>=2.4 in /Users/gburek/.local/share/virtualenvs/restful_api-w6vN7KAs/lib/python3.6/site-packages (from Faker>=0.7.0->factory-boy>=2.10.0->pytest_factoryboy) (2.7.3)
Collecting text-unidecode==1.2 (from Faker>=0.7.0->factory-boy>=2.10.0->pytest_factoryboy)
  Using cached https://files.pythonhosted.org/packages/79/42/d717cc2b4520fb09e45b344b1b0b4e81aa672001dd128c180fabc655c341/text_unidecode-1.2-py2.py3-none-any.whl
Installing collected packages: inflection, text-unidecode, Faker, factory-boy, pytest-factoryboy
Successfully installed Faker-0.9.1 factory-boy-2.11.1 inflection-0.3.1 pytest-factoryboy-2.0.1 text-unidecode-1.2

Adding pytest_factoryboy to Pipfile's [dev-packages]...
Pipfile.lock (7950d7) out of date, updating to (4b7861)...
Locking [dev-packages] dependencies...
Locking [packages] dependencies...
Updated Pipfile.lock (4b7861)!
Installing dependencies from Pipfile.lock (4b7861)...
  🐍   β–‰β–‰β–‰β–‰β–‰β–‰β–‰β–‰β–‰β–‰β–‰β–‰β–‰β–‰β–‰β–‰β–‰β–‰β–‰β–‰β–‰β–‰β–‰β–‰β–‰β–‰β–‰β–‰β–‰β–‰β–‰β–‰ 42/42 β€” 00:00:08
To activate this project's virtualenv, run pipenv shell.
Alternatively, run a command inside the virtualenv with pipenv run.

$ pipenv run py.test
============================================================================================================================== test session starts ==============================================================================================================================
platform darwin -- Python 3.6.5, pytest-3.8.1, py-1.6.0, pluggy-0.7.1
rootdir: /Users/gburek/code/restful_api, inifile:
plugins: factoryboy-2.0.1
collected 7 items

tests/test_auth.py EE                                                                                                                                                                                                                                                     [ 28%]
tests/test_user.py EEEEE                                                                                                                                                                                                                                                  [100%]

==================================================================================================================================== ERRORS =====================================================================================================================================
__________________________________________________________________________________________________________________ ERROR at setup of test_revoke_access_token ___________________________________________________________________________________________________________________
file /Users/gburek/code/restful_api/tests/test_auth.py, line 1
  def test_revoke_access_token(client, admin_headers):
E       fixture 'client' not found
>       available fixtures: admin_headers, admin_refresh_headers, admin_user, app, cache, capfd, capfdbinary, caplog, capsys, capsysbinary, db, doctest_namespace, factoryboy_request, monkeypatch, pytestconfig, record_property, record_xml_attribute, record_xml_property, recwarn, tmpdir, tmpdir_factory
>       use 'pytest --fixtures [testpath]' for help on them.

/Users/gburek/code/restful_api/tests/test_auth.py:1
__________________________________________________________________________________________________________________ ERROR at setup of test_revoke_refresh_token __________________________________________________________________________________________________________________
file /Users/gburek/code/restful_api/tests/test_auth.py, line 9
  def test_revoke_refresh_token(client, admin_refresh_headers):
E       fixture 'client' not found
>       available fixtures: admin_headers, admin_refresh_headers, admin_user, app, cache, capfd, capfdbinary, caplog, capsys, capsysbinary, db, doctest_namespace, factoryboy_request, monkeypatch, pytestconfig, record_property, record_xml_attribute, record_xml_property, recwarn, tmpdir, tmpdir_factory
>       use 'pytest --fixtures [testpath]' for help on them.

/Users/gburek/code/restful_api/tests/test_auth.py:9
________________________________________________________________________________________________________________________ ERROR at setup of test_get_user ________________________________________________________________________________________________________________________
file /Users/gburek/code/restful_api/tests/test_user.py, line 18
  def test_get_user(client, db, user, admin_headers):
E       fixture 'client' not found
>       available fixtures: admin_headers, admin_refresh_headers, admin_user, app, cache, capfd, capfdbinary, caplog, capsys, capsysbinary, db, doctest_namespace, factoryboy_request, monkeypatch, pytestconfig, record_property, record_xml_attribute, record_xml_property, recwarn, tmpdir, tmpdir_factory, user, user__email, user__password, user__username, user_factory
>       use 'pytest --fixtures [testpath]' for help on them.

/Users/gburek/code/restful_api/tests/test_user.py:18
________________________________________________________________________________________________________________________ ERROR at setup of test_put_user ________________________________________________________________________________________________________________________
file /Users/gburek/code/restful_api/tests/test_user.py, line 36
  def test_put_user(client, db, user, admin_headers):
E       fixture 'client' not found
>       available fixtures: admin_headers, admin_refresh_headers, admin_user, app, cache, capfd, capfdbinary, caplog, capsys, capsysbinary, db, doctest_namespace, factoryboy_request, monkeypatch, pytestconfig, record_property, record_xml_attribute, record_xml_property, recwarn, tmpdir, tmpdir_factory, user, user__email, user__password, user__username, user_factory
>       use 'pytest --fixtures [testpath]' for help on them.

/Users/gburek/code/restful_api/tests/test_user.py:36
______________________________________________________________________________________________________________________ ERROR at setup of test_delete_user _______________________________________________________________________________________________________________________
file /Users/gburek/code/restful_api/tests/test_user.py, line 60
  def test_delete_user(client, db, user, admin_headers):
E       fixture 'client' not found
>       available fixtures: admin_headers, admin_refresh_headers, admin_user, app, cache, capfd, capfdbinary, caplog, capsys, capsysbinary, db, doctest_namespace, factoryboy_request, monkeypatch, pytestconfig, record_property, record_xml_attribute, record_xml_property, recwarn, tmpdir, tmpdir_factory, user, user__email, user__password, user__username, user_factory
>       use 'pytest --fixtures [testpath]' for help on them.

/Users/gburek/code/restful_api/tests/test_user.py:60
______________________________________________________________________________________________________________________ ERROR at setup of test_create_user _______________________________________________________________________________________________________________________
file /Users/gburek/code/restful_api/tests/test_user.py, line 78
  def test_create_user(client, db, admin_headers):
E       fixture 'client' not found
>       available fixtures: admin_headers, admin_refresh_headers, admin_user, app, cache, capfd, capfdbinary, caplog, capsys, capsysbinary, db, doctest_namespace, factoryboy_request, monkeypatch, pytestconfig, record_property, record_xml_attribute, record_xml_property, recwarn, tmpdir, tmpdir_factory, user, user__email, user__password, user__username, user_factory
>       use 'pytest --fixtures [testpath]' for help on them.

/Users/gburek/code/restful_api/tests/test_user.py:78
______________________________________________________________________________________________________________________ ERROR at setup of test_get_all_user ______________________________________________________________________________________________________________________
file /Users/gburek/code/restful_api/tests/test_user.py, line 107
  def test_get_all_user(client, db, user_factory, admin_headers):
E       fixture 'client' not found
>       available fixtures: admin_headers, admin_refresh_headers, admin_user, app, cache, capfd, capfdbinary, caplog, capsys, capsysbinary, db, doctest_namespace, factoryboy_request, monkeypatch, pytestconfig, record_property, record_xml_attribute, record_xml_property, recwarn, tmpdir, tmpdir_factory, user, user__email, user__password, user__username, user_factory
>       use 'pytest --fixtures [testpath]' for help on them.

/Users/gburek/code/restful_api/tests/test_user.py:107
============================================================================================================================ 7 error in 0.26 seconds ============================================================================================================================

It appears to be wanting something like http://flask.pocoo.org/docs/1.0/testing/#the-testing-skeleton

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.