flask-pw icon indicating copy to clipboard operation
flask-pw copied to clipboard

Does Not Play Nicely with Application Factories.

Open hjc opened this issue 6 years ago • 0 comments

So, I am trying to use this library, mostly so I can use the wonderful peewee_migrate and get auto-migrations. However, this library does not play well with Flask Application factories. Whenever I try to simply import my models before my app is created, I get this error:

===================================================== ERRORS =====================================================
___________________________________ ERROR collecting tests/test_user_model.py ____________________________________
tests/test_user_model.py:5: in <module>
    from transfers.models.user import User
transfers/models/user.py:10: in <module>
    from transfers.models.base import BaseModel
transfers/models/base.py:12: in <module>
    class BaseModel(db.Model):
.pyenv/lib/python3.6/site-packages/cached_property.py:26: in __get__
    value = obj.__dict__[self.func.__name__] = self.func(obj)
.pyenv/lib/python3.6/site-packages/flask_pw/__init__.py:78: in Model
    Model_ = self.app.config['PEEWEE_MODELS_CLASS']
E   AttributeError: 'NoneType' object has no attribute 'config'

Because the library is trying to access the config before the app is created.

Here is my general app structure:

# transfers/app.py
from flask import Flask

from transfers.models import db

def create_app(settings=None):
    app = Flask('transfers')

    db.init_app(app)
    app.db = db

    return app
# transfers/models/__init__.py
from flask_pw import Peewee


db = Peewee()


def load_models():
    # has to be inner so we don't hit this error
    from .transfer import Transfer  # pylint: disable=wrong-import-position, unused-variable
    from .user import User  # pylint: disable=wrong-import-position, unused-variable
# transfers/models/user.py
import peewee

from transfers.models import db


class User(db.Model):
    email = peewee.CharField(max_length=255)
    first_name = peewee.CharField(max_length=128, db_column='firstName')
    last_name = peewee.CharField(max_length=128, db_column='lastName')
    points = peewee.DecimalField(max_digits=6, decimal_places=2, default=0)

Then, simply writing this test will produce the above error:

# tests/test_user_model.py
"""Tests for the user model."""

from transfers.models.user import User

This happens because there is no application properly initialized when pytest loads the file, so everything breaks. The only fix for this is doing an inner import on your models in every single test to make sure the app and proxy are properly created. This creates a bad developer experience.

In the above examples, I have omitted some code, but none of it is very important. For example, the BaseModel simply extends db.Model and adds some timestamp related fields.

hjc avatar Sep 13 '17 15:09 hjc