def test_cycle(session): from mixer.backend.sqlalchemy import Mixer mixer = Mixer(session=session, commit=True) profile1 = mixer.blend('tests.test_sqlalchemy.Profile', name='first') profile2 = mixer.blend('tests.test_sqlalchemy.Profile', name='second') users = mixer.cycle(2).blend(User, profile=(p for p in (profile1, profile2))) assert len(users) == 2 assert users[0].profile.name == 'first' assert users[1].profile.name == 'second'
def test_select(session): from mixer.backend.sqlalchemy import Mixer mixer = Mixer(session=session, commit=True) users = session.query(User).all() role = mixer.blend(Role, user=mixer.SELECT) assert role.user in users user = users.pop() role = mixer.blend(Role, user=mixer.SELECT(User.id == user.id)) assert user == role.user
def test_select(self): from mixer.backend.sqlalchemy import Mixer mixer = Mixer(session=self.session, commit=True) users = self.session.query(User).all() role = mixer.blend(Role, user=mixer.select) self.assertTrue(role.user in users) user = users.pop() role = mixer.blend(Role, user=mixer.select(User.id == user.id)) self.assertEqual(user, role.user)
def test_mixer(session): from mixer.backend.sqlalchemy import Mixer mixer = Mixer(session=session, commit=True) p = mixer.blend('tests.test_sqlalchemy.ProfileNonIncremental', id=5) role = mixer.blend('tests.test_sqlalchemy.Role', user__profile_id_nonincremental=p) assert role and role.user role = mixer.blend(Role, user__name='test2') assert role.user.name == 'test2' profile = mixer.blend('tests.test_sqlalchemy.Profile') user = mixer.blend(User, profile__name='test') assert user.profile.name == 'test' user = mixer.blend(User, profile=profile) assert user.profile == profile user = mixer.blend(User, score=mixer.RANDOM) assert user.score != 50 user = mixer.blend(User, username=lambda: 'callable_value') assert user.username == 'callable_value'
def test_mixer(self): from mixer.backend.sqlalchemy import Mixer mixer = Mixer(session=self.session, commit=True) role = mixer.blend('tests.test_sqlalchemy.Role') self.assertTrue(role) self.assertTrue(role.user) role = mixer.blend(Role, user__name='test2') self.assertEqual(role.user.name, 'test2') profile = mixer.blend('tests.test_sqlalchemy.Profile') user = mixer.blend(User, profile__name='test') self.assertEqual(user.profile.name, 'test') user = mixer.blend(User, profile=profile) self.assertEqual(user.profile, profile) user = mixer.blend(User, score=mixer.random) self.assertNotEqual(user.score, 50) user = mixer.blend(User, username=lambda: 'callable_value') self.assertEqual(user.username, 'callable_value')
class Faker: """ Class for automatic filling the database with test data """ def __init__(self, models: Models, verbose=False, fake=False, db=None): self._models = models self._config = Config() self._verbose = verbose self._mixer = None self._fake = fake self._silence_system_warn = False self._max_resolve = 20 if db: self._init_mixer(db) def _init_mixer(self, db): self._mixer = Mixer(session=db, commit=False, fake=self._fake) def fake_all(self, default_num=5): """Fill the database with fake data :param default_num: number of entries per table """ schemas = list(self._models.schemas.keys()) if 'system' in schemas: self.fake_schema('system', default_num=default_num) schemas.remove('system') self._silence_system_warn = True for schema in schemas: self.fake_schema(schema, default_num=default_num) self._silence_system_warn = False def fake_schema(self, schema, entries={}, default_num=5): """Fill the database schema tables with fake data raises ResolveError if could not resolve foreign keys :param schema: schema name :param entries: {model_name: number_of_entries} model_name is generated camelCase :param default_num: number of entries, if t_name is not in entries """ if 'system' in self._models.schemas and not self._silence_system_warn: logging.warning('If the model has references to system tables, ' 'it may be required to fake schema `system` first') generated = {name: 0 for name in self._models[schema].keys()} not_resolved = {name: 0 for name in self._models[schema].keys()} if schema in self._config.Faker['ignore']: for key in self._config.Faker['ignore'][schema]: del generated[key] del not_resolved[key] total = sum([ entries.get(name, default_num) for name in self._models[schema].keys() ]) try: if self._verbose: bar = tqdm.tqdm(total=total) with DBConn.get_session(autoflush=False) as db: self._init_mixer(db) while len(generated) > 0: finished = [] for name, i in generated.items(): if i >= entries.get(name, default_num): finished.append(name) continue model = self._models[schema][name] if self._fake_model(model, db): generated[name] += 1 if self._verbose: bar.update(1) not_resolved[name] = 0 else: not_resolved[name] += 1 if not_resolved[name] > self._max_resolve: raise ResolveError( f"Can't resolve foreign keys for {name}") for name in finished: del generated[name] self._flush_faked(db) finally: if self._verbose: bar.close() def fake_one(self, model, db): """Generate fake model :param model: SQLAlchemy model :param db: session, connection or engine :returns: model instance, if fks were resolved successfully """ attributes = {} resolved = True relationships = inspect(model).relationships.values() for name, attr in dict(model.__table__.columns).items(): if attr.foreign_keys: fk = next(iter(attr.foreign_keys)) else: continue t_name = '.'.join(fk._column_tokens[:-1]) # backref_name = fk._column_tokens[1] column = fk._column_tokens[-1] entry = list( db.execute( f"SELECT {column} FROM {t_name} ORDER BY RANDOM() LIMIT 1") ) if not entry: resolved = False break else: attributes[name] = entry[0][0] rel = self._get_relationship(relationships, name) if rel: name = NamesConverter.class_name(rel.target.schema, rel.target.name) target = self._models[rel.target.schema][name] search = {} search[column] = entry[0][0] obj = db.query(target).filter_by(**search).first() attributes[rel.class_attribute.key] = obj if resolved: faked = self._mixer.blend(model, **attributes) return faked return None def _flush_faked(self, db): try: db.commit() except sa.exc.IntegrityError: db.rollback() # TODO logging def _fake_model(self, model, db): faked = self.fake_one(model, db) if faked: db.add(faked) return True return False def _get_relationship(self, relationships, column_name): for r in relationships: if any([c.name == column_name for c in r.local_columns]): return r def faked_models(self): models = [] for schema in self._models.schemas.keys(): if schema not in self._config.Faker['ignore']: models.extend(self._models[schema].values()) else: for key, model in self._models[schema].items(): if key not in self._config.Faker['ignore'][schema]: models.append(model) return models