Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Append action on NestedMutableJson field (list) doesn't trigger @validates decorated function #29

Open
caffeinatedMike opened this issue Mar 19, 2021 · 0 comments

Comments

@caffeinatedMike
Copy link

caffeinatedMike commented Mar 19, 2021

This may be related to the questions posed in #21, but I figured it best to post a fresh topic to express how important validation functions are. Coincidentally, it's the main reason I am having to look into this package. I have a JSON field where I want to store a simple list of strings. Now that SQLite support JSON fields as of version 3.9 (thanks to JSON1) I attempted to setup a list field a few different ways, but unfortunately quickly realized the limitations with mutations on these fields.

I've searched for possible solutions to this issue, which led me to this article, which inevitably led me to this package.

models.py

from sqlalchemy_json import NestedMutableJson  # because I need to store a list, not a dict
from sqlalchemy.ext.mutable import MutableList
from sqlalchemy.orm import validates
from app import db  # SQLAlchemy instance via flask_sqlalchemy
# SQLALCHEMY_DATABASE_URI = 'sqlite:///:memory:'


class Portal(db.Model):
    __tablename__ = 'portal'

    id = db.Column(db.Integer, primary_key=True)
    name = db.Column(db.String(100), nullable=False)
    # ex: valid_kwargs will be json array of ['full_year']
    valid_kwargs = db.Column(MutableList.as_mutable(db.JSON), nullable=False, default=list())


class Account(db.Model):
    __tablename__ = 'account'

    id = db.Column(db.Integer, primary_key=True)
    portal_id = db.Column(db.Integer, db.ForeignKey('portal.id'))  # one-to-one relationship
    username = db.Column(db.String(100), nullable=False)
    password = db.Column(db.String(100), nullable=False)
    # portal = accessible thanks to portal_id/portal.id ForeignKey
    
    # for extra fields, specific to each portal
    # ATTEMPT 1 - using sqlalchemy.ext.mutable.MutableList, no extra packages
    kwargs = db.Column(MutableList.as_mutable(db.JSON), nullable=False, default=list())
    # ATTEMPT 2 - using NestedMutableJson from sqlalchemy_json
    kwargs = db.Column(NestedMutableJson, nullable=False, default=list())
    

    @validates('kwargs')
    def validates_kwargs(self, key, value):
        # make sure that only valid_kwargs are added to the account
        if value and not all([kw in self.portal.valid_kwargs for kw in value]):
            raise ValueError('Invalid value provided in kwargs')
        return value

unit_tests/test_models.py

def test_account(test_db, account):
    """
    GIVEN a database instance
    WHEN a new account is created
    THEN check that the username, password, kwargs, and reports fields are defined correctly
    """
    test_db.session.add(account)
    test_db.session.commit()
    assert Account.query.count() == 1
    assert account.username == 'mhill'
    assert account.password == 'password1'
    assert isinstance(account.kwargs, list) and len(account.kwargs) == 0  # check that kwargs defaults to an empty list
    assert account.reports.count() == 0  # check that reports relationship is present
    assert isinstance(account.portal, Portal)  # check that portal relationship is present
    account.portal.valid_kwargs.append('full_year')
    account.kwargs.append('full_year')
    test_db.session.add(account)
    test_db.session.commit()
    assert len(account.kwargs) == 1
    assert 'full_year' in account.kwargs

    """
    GIVEN an account whose associated portal has valid_kwargs
    WHEN kwargs are added to the account
    THEN check that only the kwargs in the portal's valid_kwargs are allowed to be added
    """
    # FIXME: Doesn't throw an error
    with pytest.raises(ValueError):
        account.kwargs.append('illegal_kwarg')
        test_db.session.add(account)
        test_db.session.commit()
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

1 participant