├── .github ├── release-drafter.yml └── workflows │ ├── main.yml │ └── release-management.yml ├── .gitignore ├── LICENSE ├── MANIFEST.in ├── README.rst ├── requirements.txt ├── setup.cfg ├── setup.py ├── sqlalchemy_seed ├── __init__.py └── mixin.py ├── tests ├── __init__.py ├── fixtures │ ├── accounts.json │ ├── accounts.yaml │ ├── pictures.json │ └── pictures.yaml └── test_sqlalchey_seed.py └── tox.ini /.github/release-drafter.yml: -------------------------------------------------------------------------------- 1 | name-template: 'v$NEXT_PATCH_VERSION' 2 | tag-template: '$NEXT_PATCH_VERSION' 3 | change-template: '- $TITLE @$AUTHOR (#$NUMBER)' 4 | branches: 5 | - 'master' 6 | categories: 7 | - title: '🐛 Bug Fixes' 8 | labels: 9 | - 'bug' 10 | - title: '🧰 Maintenance' 11 | labels: 12 | - 'chore' 13 | - 'dependencies' 14 | 15 | template: | 16 | ## Changes 17 | 18 | $CHANGES 19 | 20 | ## Compare 21 | 22 | https://github.com/heavenshell/py-sqlalchemy_seed/compare/$PREVIOUS_TAG...$NEXT_PATCH_VERSION 23 | -------------------------------------------------------------------------------- /.github/workflows/main.yml: -------------------------------------------------------------------------------- 1 | name: build 2 | 3 | on: [push, pull_request] 4 | 5 | jobs: 6 | build: 7 | 8 | runs-on: ubuntu-latest 9 | strategy: 10 | matrix: 11 | python-version: [3.7, 3.8, 3.9, '3.10'] 12 | 13 | steps: 14 | - uses: actions/checkout@v2 15 | - name: Use Python ${{ matrix.python-version }} 16 | uses: actions/setup-python@v2 17 | with: 18 | python-version: ${{ matrix.python-version }} 19 | - name: Cache pip 20 | uses: actions/cache@v2 21 | with: 22 | path: ~/.cache/pip 23 | key: ${{ runner.os }}-pip-${{ hashFiles('requirements.txt') }} 24 | restore-keys: | 25 | ${{ runner.os }}-pip- 26 | ${{ runner.os }}- 27 | 28 | - name: Install dependencies 29 | run: | 30 | python -m pip install --upgrade pip 31 | pip install -r requirements.txt 32 | 33 | - name: Test 34 | run: | 35 | python setup.py test 36 | 37 | - name: Flake8 38 | run: | 39 | flake8 --verbose --jobs=8 40 | -------------------------------------------------------------------------------- /.github/workflows/release-management.yml: -------------------------------------------------------------------------------- 1 | name: Release Management 2 | 3 | on: 4 | push: 5 | branches: 6 | - master 7 | 8 | jobs: 9 | update_draft_release: 10 | runs-on: ubuntu-latest 11 | steps: 12 | - uses: toolmantim/release-drafter@v5 13 | env: 14 | GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} 15 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | *.pyc 2 | *.pyo 3 | *.egg-info 4 | bin 5 | build 6 | develop-eggs 7 | dist 8 | eggs 9 | parts 10 | .DS_Store 11 | .installed.cfg 12 | .tox 13 | .swp 14 | *~ 15 | __pycache__ 16 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | Copyright (c) 2017, Shinya Ohyanagi. 2 | All rights reserved. 3 | 4 | Redistribution and use in source and binary forms, with or without modification, 5 | are permitted provided that the following conditions are met: 6 | 7 | * Redistributions of source code must retain the above copyright notice, 8 | this list of conditions and the following disclaimer. 9 | 10 | * Redistributions in binary form must reproduce the above copyright notice, 11 | this list of conditions and the following disclaimer in the documentation 12 | and/or other materials provided with the distribution. 13 | 14 | * Neither the name of Shinya Ohyanagi, Inc. nor the names of its 15 | contributors may be used to endorse or promote products derived from this 16 | software without specific prior written permission. 17 | 18 | THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND 19 | ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED 20 | WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE 21 | DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR 22 | ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES 23 | (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; 24 | LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON 25 | ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 26 | (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS 27 | SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 28 | -------------------------------------------------------------------------------- /MANIFEST.in: -------------------------------------------------------------------------------- 1 | graft sqlalchemy_seed 2 | 3 | include LICENSE 4 | include README.rst 5 | include setup.cfg 6 | include setup.py 7 | 8 | exclude .pyup.yml 9 | exclude .travis.yml 10 | exclude requirements.txt 11 | exclude tox.ini 12 | 13 | global-exclude __pycache__ 14 | global-exclude *.py[co] 15 | 16 | prune tests 17 | -------------------------------------------------------------------------------- /README.rst: -------------------------------------------------------------------------------- 1 | sqlalchemy_seed 2 | ---------------- 3 | 4 | .. image:: https://github.com/heavenshell/py-sqlalchemy_seed/workflows/build/badge.svg 5 | :target: https://github.com/heavenshell/py-sqlalchemy_seed/actions?query=workflow%3Abuild 6 | .. image:: https://pyup.io/repos/github/heavenshell/py-sqlalchemy_seed/python-3-shield.svg 7 | :target: https://pyup.io/repos/github/heavenshell/py-sqlalchemy_seed/ 8 | :alt: Python 3 9 | .. image:: https://pyup.io/repos/github/heavenshell/py-sqlalchemy_seed/shield.svg 10 | :target: https://pyup.io/repos/github/heavenshell/py-sqlalchemy_seed/ 11 | :alt: Updates 12 | 13 | `sqlalchemy_seed` is a seed library which provides initial data to database using SQLAlchemy. 14 | 15 | `sqlalchemy_seed` is similar to `Django fixtures `_. 16 | 17 | Installation 18 | ============ 19 | 20 | .. code:: 21 | 22 | pip install sqlalchemy_seed 23 | 24 | Adding seed 25 | =========== 26 | 27 | .. code:: 28 | 29 | /myapp 30 | __init__.py 31 | models.py 32 | /fixtures 33 | accounts.yaml 34 | 35 | Model file. 36 | 37 | .. code:: python 38 | 39 | # -*- coding: utf-8 -*- 40 | 41 | from sqlalchemy import create_engine 42 | from sqlalchemy.exc import OperationalError 43 | from sqlalchemy.ext.declarative import declarative_base 44 | from sqlalchemy.orm import scoped_session, sessionmaker, relationship 45 | engine = create_engine('sqlite://', convert_unicode=True) 46 | 47 | Base = declarative_base() 48 | Base.metadata.bind = engine 49 | Session = sessionmaker(autocommit=False, autoflush=False, bind=engine) 50 | session = scoped_session(Session) 51 | 52 | class Account(Base): 53 | __tablename__ = 'accounts' 54 | 55 | id = Column(Integer, primary_key=True) 56 | first_name = Column(String(100), nullable=False) 57 | last_name = Column(String(100), nullable=False) 58 | age = Column(Integer(), nullable=True) 59 | 60 | 61 | Seed code. 62 | 63 | .. code:: python 64 | 65 | # -*- coding: utf-8 -*- 66 | 67 | from sqlalchemy_seed import ( 68 | create_table, 69 | drop_table, 70 | load_fixtures, 71 | load_fixture_files, 72 | ) 73 | from myapp.models import Base, session 74 | 75 | 76 | def main(): 77 | path = '/path/to/fixtures' 78 | fixtures = load_fixture_files(path, ['accounts.yaml']) 79 | load_fixtures(session, fixtures) 80 | 81 | 82 | if __name__ == '__main__': 83 | main() 84 | 85 | Seed file. 86 | 87 | .. code:: 88 | 89 | - model: myapp.models.Account 90 | id: 1 91 | fields: 92 | first_name: John 93 | last_name: Lennon 94 | age: 20 95 | 96 | - model: myapp.models.Account 97 | id: 2 98 | fields: 99 | first_name: Paul 100 | last_name: McCartney 101 | age: 21 102 | 103 | 104 | Update seed 105 | =========== 106 | 107 | If you want idempotent, you can describe seed like followings. 108 | 109 | Seed file. 110 | 111 | .. code:: 112 | 113 | - model: myapp.models.Account 114 | fields: 115 | id: 1 116 | first_name: John 117 | last_name: Lennon 118 | age: 20 119 | 120 | - model: myapp.models.Account 121 | fields: 122 | id: 2 123 | first_name: Paul 124 | last_name: McCartney 125 | age: 21 126 | 127 | LICENSE 128 | ======= 129 | NEW BSD LICENSE. 130 | -------------------------------------------------------------------------------- /requirements.txt: -------------------------------------------------------------------------------- 1 | SQLAlchemy==1.4.38 2 | pyyaml==5.4.1 3 | flake8==3.9.2 4 | flake8-coding==1.3.2 5 | flake8-commas==2.1.0 6 | flake8-comprehensions==3.10.0 7 | flake8-debugger==4.1.2 8 | flake8-docstrings==1.6.0 9 | flake8-import-order==0.18.1 10 | flake8-print==5.0.0 11 | flake8-string-format==0.3.0 12 | -------------------------------------------------------------------------------- /setup.cfg: -------------------------------------------------------------------------------- 1 | [flake8] 2 | max-line-length = 120 3 | exclude = 4 | .cache, 5 | .git, 6 | .tox, 7 | migrations, 8 | venv, 9 | __pycache__ 10 | ignore = D100,D101,D102,D103,D104,D106,D107,D205,D208,D400 11 | import-order-style = smarkets 12 | 13 | [wheel] 14 | universal = 1 15 | -------------------------------------------------------------------------------- /setup.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | """ 3 | sqlalchemy_seed 4 | ~~~~~~~~~~~~~~~ 5 | 6 | sqlalchemy_seed is a simple data seeder using SQLAlchemy. 7 | 8 | 9 | :copyright: (c) 2018 Shinya Ohyanagi, All rights reserved. 10 | :license: BSD, see LICENSE for more details. 11 | """ 12 | import os 13 | 14 | from setuptools import find_packages, setup 15 | 16 | requires = ['sqlalchemy', 'pyyaml', 'flake8'] 17 | 18 | rst_path = os.path.join(os.path.dirname(__file__), 'README.rst') 19 | description = '' 20 | with open(rst_path) as f: 21 | description = f.read() 22 | 23 | setup( 24 | name='sqlalchemy_seed', 25 | version='0.3.0', 26 | author='Shinya Ohyanagi', 27 | author_email='sohyanagi@gmail.com', 28 | url='http://github.com/heavenshell/py-sqlalchemy_seed', 29 | description='sqlalchemy_seed is simple data seeder using SQLAlchmy.', 30 | long_description=description, 31 | license='BSD', 32 | platforms='any', 33 | packages=find_packages(exclude=('tests',)), 34 | package_dir={'': '.'}, 35 | install_requires=requires, 36 | classifiers=[ 37 | 'Development Status :: 4 - Beta', 38 | 'Topic :: Database', 39 | 'Intended Audience :: Developers', 40 | 'License :: OSI Approved :: BSD License', 41 | 'Operating System :: OS Independent', 42 | 'Programming Language :: Python', 43 | ], 44 | tests_require=requires, 45 | test_suite='tests', 46 | ) 47 | -------------------------------------------------------------------------------- /sqlalchemy_seed/__init__.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | """ 3 | sqlalchemy_seed 4 | ~~~~~~~~~~~~~~~ 5 | 6 | `sqlalchemy_seed` is a seed library which provides initial data to 7 | database using SQLAlchemy. 8 | 9 | :copyright: (c) 2017 Shinya Ohyanagi, All rights reserved. 10 | :license: BSD, see LICENSE for more details. 11 | """ 12 | import importlib 13 | import json 14 | import os 15 | 16 | import yaml 17 | try: 18 | from yaml import CLoader as Loader 19 | except ImportError: 20 | from yaml import Loader 21 | 22 | __version__ = '0.3.0' 23 | 24 | 25 | def create_table(base, session=None): 26 | """Create table. 27 | 28 | :param base: `sqlalchemy.ext.declarative` 29 | :param session: `sqlalchemy.orm` 30 | """ 31 | if session: 32 | base.metadata.create_all(bind=session.bind) 33 | else: 34 | base.metadata.create_all() 35 | 36 | 37 | def drop_table(base, session): 38 | """Drop table. 39 | 40 | :param base: `sqlalchemy.ext.declarative` 41 | :param session: `sqlalchemy.orm` 42 | """ 43 | session.expunge_all() 44 | session.remove() 45 | base.metadata.drop_all() 46 | 47 | 48 | def load_fixture_files(paths, files): 49 | """Load fixture files. 50 | 51 | :param path: Path to fixtures 52 | :param files: Fixture file names 53 | """ 54 | fixtures = [] 55 | if not isinstance(paths, list): 56 | paths = [paths] 57 | 58 | for path in paths: 59 | for file in files: 60 | fixture_path = os.path.join(path, file) 61 | if not os.path.exists(fixture_path): 62 | continue 63 | 64 | with open(fixture_path, 'r') as f: 65 | if file.endswith('.yaml') or file.endswith('.yml'): 66 | data = yaml.load(f, Loader=Loader) 67 | elif file.endswith('.json'): 68 | data = json.loads(f.read()) 69 | else: 70 | continue 71 | fixtures.append(data) 72 | 73 | return fixtures 74 | 75 | 76 | def _create_model_instance(fixture): 77 | """Create model instance. 78 | 79 | :param fixture: Fixtures 80 | """ 81 | instances = [] 82 | for data in fixture: 83 | if 'model' in data: 84 | module_name, class_name = data['model'].rsplit('.', 1) 85 | module = importlib.import_module(module_name) 86 | model = getattr(module, class_name) 87 | instance = model(**data['fields']) 88 | instances.append(instance) 89 | 90 | return instances 91 | 92 | 93 | def load_fixtures(session, fixtures): 94 | """Load fixture. 95 | 96 | :param base: `sqlalchemy.ext.declarative` 97 | :param fixtures: Fixture files 98 | """ 99 | instances = [] 100 | for fixture in fixtures: 101 | _instances = _create_model_instance(fixture) 102 | for instance in _instances: 103 | instances.append(instance) 104 | 105 | try: 106 | for instance in instances: 107 | session.merge(instance) 108 | session.flush() 109 | session.commit() 110 | except Exception: 111 | session.rollback() 112 | raise 113 | -------------------------------------------------------------------------------- /sqlalchemy_seed/mixin.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | """ 3 | sqlalchemy_seed.seed_mixin 4 | ~~~~~~~~~~~~~~~~~~~~~~~~~~ 5 | 6 | Mixin class for unittest. 7 | 8 | 9 | :copyright: (c) 2017 Shinya Ohyanagi, All rights reserved. 10 | :license: BSD, see LICENSE for more details. 11 | """ 12 | from . import ( 13 | create_table, 14 | drop_table, 15 | load_fixture_files, 16 | load_fixtures, 17 | ) 18 | 19 | 20 | class SeedMixin(object): 21 | base = None 22 | session = None 23 | fixtures = [] 24 | fixtures_setup_class = False 25 | fixtures_paths = None 26 | 27 | def _create_fixtures(self): 28 | if self.base is None: 29 | return 30 | if self.session is None: 31 | return 32 | if self.fixtures_paths is None: 33 | return 34 | 35 | create_table(self.base, self.session) 36 | fixtures = load_fixture_files(self.fixtures_paths, self.fixtures) 37 | load_fixtures(self.session, fixtures) 38 | 39 | def _drop_fixtures(self): 40 | drop_table(self.base, self.session) 41 | 42 | @classmethod 43 | def setUpClass(cls): 44 | if cls.fixtures_setup_class is True: 45 | cls._create_fixtures(cls) 46 | 47 | @classmethod 48 | def tearDownClass(cls): 49 | if cls.fixtures_setup_class is True: 50 | cls._drop_fixtures(cls) 51 | 52 | def setUp(self): 53 | if self.fixtures_setup_class is True: 54 | return 55 | self._create_fixtures() 56 | 57 | def tearDown(self): 58 | if self.fixtures_setup_class is True: 59 | return 60 | self._drop_fixtures() 61 | -------------------------------------------------------------------------------- /tests/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/heavenshell/py-sqlalchemy_seed/95e80aa4323e7acddb81054f8ee84059f69cc22a/tests/__init__.py -------------------------------------------------------------------------------- /tests/fixtures/accounts.json: -------------------------------------------------------------------------------- 1 | [ 2 | { 3 | "model": "tests.test_sqlalchey_seed.Account", 4 | "id": 1, 5 | "fields": { 6 | "first_name": "John", 7 | "last_name": "Lennon", 8 | "age": 20 9 | } 10 | }, 11 | { 12 | "model": "tests.test_sqlalchey_seed.Account", 13 | "id": 2, 14 | "fields": { 15 | "first_name": "Paul", 16 | "last_name": "McCartney", 17 | "age": 21 18 | } 19 | } 20 | ] 21 | -------------------------------------------------------------------------------- /tests/fixtures/accounts.yaml: -------------------------------------------------------------------------------- 1 | - model: tests.test_sqlalchey_seed.Account 2 | id: 1 3 | fields: 4 | first_name: John 5 | last_name: Lennon 6 | age: 20 7 | - model: tests.test_sqlalchey_seed.Account 8 | id: 2 9 | fields: 10 | first_name: Paul 11 | last_name: McCartney 12 | age: 21 13 | -------------------------------------------------------------------------------- /tests/fixtures/pictures.json: -------------------------------------------------------------------------------- 1 | [ 2 | { 3 | "model": "tests.test_sqlalchey_seed.Picture", 4 | "id": 1, 5 | "fields": { 6 | "account_id": 1, 7 | "image": "cat.jpg" 8 | } 9 | }, 10 | { 11 | "model": "tests.test_sqlalchey_seed.Picture", 12 | "id": 2, 13 | "fields": { 14 | "account_id": 1, 15 | "image": "dog.jpg" 16 | } 17 | }, 18 | { 19 | "model": "tests.test_sqlalchey_seed.Picture", 20 | "id": 3, 21 | "fields": { 22 | "account_id": 2 23 | } 24 | }, 25 | { 26 | "model": "tests.test_sqlalchey_seed.Picture", 27 | "id": 4, 28 | "fields": { 29 | "account_id": 2, 30 | "image": "ham.jpg" 31 | } 32 | } 33 | ] 34 | -------------------------------------------------------------------------------- /tests/fixtures/pictures.yaml: -------------------------------------------------------------------------------- 1 | - model: tests.test_sqlalchey_seed.Picture 2 | id: 1 3 | fields: 4 | account_id: 1 5 | image: cat.jpg 6 | - model: tests.test_sqlalchey_seed.Picture 7 | id: 2 8 | fields: 9 | account_id: 1 10 | image: dog.jpg 11 | - model: tests.test_sqlalchey_seed.Picture 12 | id: 3 13 | fields: 14 | account_id: 2 15 | - model: tests.test_sqlalchey_seed.Picture 16 | id: 4 17 | fields: 18 | account_id: 2 19 | image: ham.jpg 20 | -------------------------------------------------------------------------------- /tests/test_sqlalchey_seed.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | """ 3 | sqlalchemy_seed.test_sqlalchmy_seed 4 | ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ 5 | 6 | Test for sqlalchemy_seed. 7 | 8 | 9 | :copyright: (c) 2017 Shinya Ohyanagi, All rights reserved. 10 | :license: BSD, see LICENSE for more details. 11 | """ 12 | 13 | import os 14 | from unittest import TestCase 15 | 16 | from sqlalchemy import Column, create_engine, ForeignKey, Integer, String 17 | from sqlalchemy.exc import OperationalError 18 | from sqlalchemy.ext.declarative import declarative_base 19 | from sqlalchemy.orm import relationship, scoped_session, sessionmaker 20 | from sqlalchemy_seed import ( 21 | create_table, 22 | drop_table, 23 | load_fixture_files, 24 | load_fixtures, 25 | ) 26 | from sqlalchemy_seed.mixin import SeedMixin 27 | 28 | 29 | engine = create_engine('sqlite://', convert_unicode=True) 30 | Base = declarative_base() 31 | Base.metadata.bind = engine 32 | Session = sessionmaker(autocommit=False, autoflush=False, bind=engine) 33 | session = scoped_session(Session) 34 | 35 | 36 | class Account(Base): 37 | __tablename__ = 'accounts' 38 | 39 | id = Column(Integer, primary_key=True) 40 | first_name = Column(String(100), nullable=False) 41 | last_name = Column(String(100), nullable=False) 42 | age = Column(Integer(), nullable=True) 43 | 44 | def __repr__(self): 45 | """Repr.""" 46 | return 'Account(id={0}, first_name={1}, last_name={2}, age={3})'.format( 47 | self.id, self.first_name, self.last_name, self.age, 48 | ) 49 | 50 | 51 | class Picture(Base): 52 | __tablename__ = 'pictures' 53 | 54 | id = Column(Integer, primary_key=True) 55 | account_id = Column(Integer, ForeignKey('accounts.id')) 56 | image = Column(String(120), unique=True) 57 | 58 | account = relationship( 59 | 'Account', 60 | backref='images', 61 | primaryjoin='Account.id==Picture.account_id', 62 | lazy='joined', 63 | ) 64 | 65 | def __init__(self, account_id=None, image=None): 66 | self.account_id = account_id 67 | self.image = image 68 | 69 | def __repr__(self): 70 | """Repr.""" 71 | return 'Picture(id={0}, account_id={1}, image={2})'.format( 72 | self.id, 73 | self.account, 74 | self.image, 75 | ) 76 | 77 | 78 | class TestFixtures(TestCase): 79 | path = os.path.join(os.path.dirname(os.path.abspath(__file__)), 'fixtures') 80 | 81 | def test_load_fixture_files(self): 82 | fixtures = load_fixture_files(self.path, ['accounts.yaml']) 83 | self.assertTrue(isinstance(fixtures[0], list)) 84 | 85 | def test_create_table(self): 86 | create_table(Base) 87 | accounts = session.query(Account).all() 88 | self.assertEqual(len(accounts), 0) 89 | 90 | def test_drop_table(self): 91 | create_table(Base) 92 | drop_table(Base, session) 93 | with self.assertRaises(OperationalError): 94 | session.query(Account).all() 95 | 96 | def test_load_fixture(self): 97 | create_table(Base) 98 | fixtures = load_fixture_files(self.path, ['accounts.yaml']) 99 | load_fixtures(session, fixtures) 100 | accounts = session.query(Account).all() 101 | self.assertEqual(len(accounts), 2) 102 | 103 | drop_table(Base, session) 104 | 105 | def test_load_json_fixture(self): 106 | create_table(Base) 107 | fixtures = load_fixture_files(self.path, ['accounts.json']) 108 | load_fixtures(session, fixtures) 109 | accounts = session.query(Account).all() 110 | self.assertEqual(len(accounts), 2) 111 | 112 | drop_table(Base, session) 113 | 114 | def test_load_fixtures(self): 115 | create_table(Base) 116 | fixtures = load_fixture_files( 117 | self.path, ['accounts.yaml', 'pictures.yaml'], 118 | ) 119 | load_fixtures(session, fixtures) 120 | accounts = session.query(Account).all() 121 | self.assertEqual(len(accounts), 2) 122 | pictures = session.query(Picture).all() 123 | self.assertEqual(len(pictures), 4) 124 | 125 | drop_table(Base, session) 126 | 127 | def test_load_json_fixtures(self): 128 | create_table(Base) 129 | fixtures = load_fixture_files( 130 | self.path, ['accounts.json', 'pictures.json'], 131 | ) 132 | load_fixtures(session, fixtures) 133 | accounts = session.query(Account).all() 134 | self.assertEqual(len(accounts), 2) 135 | pictures = session.query(Picture).all() 136 | self.assertEqual(len(pictures), 4) 137 | 138 | drop_table(Base, session) 139 | 140 | def test_load_fixture_by_wrong_order(self): 141 | # Picture has relationship to Account. 142 | # Model instances are added by `session.add_all()`. 143 | # So, it does not fail. 144 | create_table(Base) 145 | fixtures = load_fixture_files( 146 | self.path, ['pictures.yaml', 'accounts.yaml'], 147 | ) 148 | load_fixtures(session, fixtures) 149 | accounts = session.query(Account).all() 150 | self.assertEqual(len(accounts), 2) 151 | pictures = session.query(Picture).all() 152 | self.assertEqual(len(pictures), 4) 153 | 154 | drop_table(Base, session) 155 | 156 | 157 | class TestSeedMixin(SeedMixin, TestCase): 158 | base = Base 159 | session = session 160 | fixtures = ['accounts.yaml', 'pictures.yaml'] 161 | fixtures_paths = os.path.join( 162 | os.path.dirname(os.path.abspath(__file__)), 163 | 'fixtures', 164 | ) 165 | 166 | def test_create_seed_data(self): 167 | accounts = session.query(Account).all() 168 | self.assertEqual(len(accounts), 2) 169 | 170 | pictures = session.query(Picture).all() 171 | self.assertEqual(len(pictures), 4) 172 | 173 | 174 | class TestSeedMixiSetUp(SeedMixin, TestCase): 175 | base = Base 176 | session = session 177 | fixtures = ['accounts.yaml', 'pictures.yaml'] 178 | fixtures_paths = 'fixtures' 179 | fixtures_setup_class = False 180 | 181 | def setUp(self): 182 | pass 183 | 184 | def tearDown(self): 185 | pass 186 | 187 | def test_setup_and_teardown_not_called(self): 188 | with self.assertRaises(OperationalError): 189 | session.query(Account).all() 190 | 191 | 192 | class TestSeedMixinSetUpClass(SeedMixin, TestCase): 193 | base = Base 194 | session = session 195 | fixtures = ['accounts.yaml', 'pictures.yaml'] 196 | fixtures_paths = 'fixtures' 197 | fixtures_setup_class = True 198 | 199 | @classmethod 200 | def setUpClass(cls): 201 | pass 202 | 203 | @classmethod 204 | def tearDownClass(cls): 205 | pass 206 | 207 | def test_setup_class_and_teardown_class_not_called(self): 208 | with self.assertRaises(OperationalError): 209 | session.query(Account).all() 210 | -------------------------------------------------------------------------------- /tox.ini: -------------------------------------------------------------------------------- 1 | [tox] 2 | envlist = py37,py38,flake8 3 | 4 | [testenv] 5 | commands=python setup.py test 6 | deps= 7 | sqlalchemy 8 | 9 | [testenv:flake8] 10 | deps = flake8 11 | commands = flake8 sqlalchemy_seed tests setup.py 12 | --------------------------------------------------------------------------------