def test_specify_order_fields(self): """ Test this issue: How to specify the order in which fields should be displayed ? #45 """ Base = declarative_base() # example taken from SQLAlchemy docs class MyClass(Base): __tablename__ = 'my_table' id = Column(Integer, primary_key=True) job_status = Column(String(50)) status = synonym("job_status") schema = SQLAlchemySchemaNode(MyClass, includes=['foo', 'job_status', 'id']) self.assertEqual(['job_status', 'id'], [x.name for x in schema]) self.assertNotIn('foo', schema) schema = SQLAlchemySchemaNode(MyClass, includes=['id', 'foo', 'job_status']) self.assertEqual(['id', 'job_status'], [x.name for x in schema]) self.assertNotIn('foo', schema)
def test_relationship_infinite_recursion(self): """Test to ensure infinite recursion does not occur when following backrefs """ # Unpatched, creating a bar or baz schema node causes infinite recursion schema = SQLAlchemySchemaNode(Bar) schema = SQLAlchemySchemaNode(Baz)
def test_imperative_relationships_overrides(self): overrides = {'person': {'name': 'Name'}} self.assertRaises(ValueError, SQLAlchemySchemaNode, Account, None, None, overrides) overrides = {'person': {'typ': colander.Integer}} self.assertRaises(ValueError, SQLAlchemySchemaNode, Account, None, None, overrides) overrides = { 'person': { 'children': [], 'includes': ['id'] }, } schema = SQLAlchemySchemaNode(Account, overrides=overrides) self.assertEqual(schema['person'].children, []) overrides = { 'person': { 'includes': ['id'] }, } schema = SQLAlchemySchemaNode(Account, overrides=overrides) self.assertIn('id', schema['person']) self.assertEqual(len(schema['person'].children), 1) overrides = { 'person': { 'excludes': ['id'] }, } schema = SQLAlchemySchemaNode(Account, overrides=overrides) self.assertNotIn('id', schema['person']) overrides = { 'addresses': { 'overrides': { 'id': { 'typ': colander.Float } } } } overrides = { 'addresses': { 'overrides': { 'id': { 'typ': colander.String } } } } schema = SQLAlchemySchemaNode(Person, overrides=overrides) self.assertTrue( isinstance(schema['addresses'].children[0]['id'].typ, colander.String))
def test_imperative_colums_overrides(self): overrides = {'email': {'typ': colander.Integer}} account_schema = SQLAlchemySchemaNode(Account, overrides=overrides) self.assertTrue( isinstance(account_schema['email'].typ, colander.Integer)) overrides = {'email': {'name': 'Name'}} self.assertRaises(ValueError, SQLAlchemySchemaNode, Account, None, None, overrides) overrides = {'email': {'children': []}} # following shouldn't raise an exception allowing the test to pass SQLAlchemySchemaNode(Account, None, None, overrides)
def get_add_edit_schema(edit=False): """ Add a form schema for login add/edit :returns: A colander form schema """ schema = SQLAlchemySchemaNode(Login) set_widgets(schema) schema.add( colander.SchemaNode( colander.String(), name="primary_group", validator=_deferred_primary_group_validator, widget=_deferred_primary_group_widget, title=u"Rôle de l'utilisateur", )) schema.add( colander.SchemaNode( colander.Set(), name="groups", validator=_deferred_group_validator, widget=_deferred_group_widget, title=u"Droits spécifiques", )) if edit: schema['login'].validator = _deferred_login_validator schema['pwd_hash'].missing = colander.drop schema['user_id'].validator = _deferred_user_id_validator else: schema['user_id'].validator = _get_unique_user_id_validator() schema['login'].validator = _get_unique_login_validator() return schema
class EstimationAdminView(BaseEditView): factory = Estimation schema = SQLAlchemySchemaNode( Estimation, title=u"Formulaire d'édition forcée de devis/factures/avoirs", help_msg=u"Les montants sont *10^5 10 000==1€", )
def group_schema(self, group, columns): columns = self.preprocessing(columns) includes = [x for x in columns] return SQLAlchemySchemaNode(self.table, name=group, title=group, includes=includes)
def schema(self): return SQLAlchemySchemaNode(CompetenceGridSubItem, includes=( 'evaluation', 'id', 'comments', ))
def test_doc_example_deform(self): """ Test 'using ColanderAlchemy with Deform' example found in docs/source/deform.rst """ Base = declarative_base() class Phone(Base): __tablename__ = 'phones' person_id = Column(Integer, ForeignKey('persons.id'), primary_key=True) number = Column(Unicode(128), primary_key=True) location = Column(Enum('home', 'work')) class Person(Base): __tablename__ = 'persons' id = Column(Integer, primary_key=True) name = Column(Unicode(128), nullable=False) surname = Column(Unicode(128), nullable=False) phones = relationship(Phone) schema = SQLAlchemySchemaNode(Person) # because of naming clashes, we need to do this in another function def generate_colander(): class Phone(colander.MappingSchema): person_id = colander.SchemaNode(colander.Int()) number = colander.SchemaNode( colander.String(), validator=colander.Length(0, 128) ) location = colander.SchemaNode( colander.String(), validator=colander.OneOf(['home', 'work']), missing=colander.null ) class Phones(colander.SequenceSchema): phones = Phone(missing=[]) class Person(colander.MappingSchema): id = colander.SchemaNode(colander.Int(), missing=colander.drop) name = colander.SchemaNode( colander.String(), validator=colander.Length(0, 128) ) surname = colander.SchemaNode( colander.String(), validator=colander.Length(0, 128) ) phones = Phones(missing=[]) return Person() schema2 = generate_colander() self.is_equal_schema(schema, schema2)
def test_null_issues(self): """ Make sure nullable values are set properly when null #42 introduced an issue where values may not have been properly set if the value is supposed to be set to null """ Base = declarative_base() class Person(Base): __tablename__ = 'person' id = Column(Integer, primary_key=True) fullname = Column(String, nullable=True) age = Column(Integer, nullable=True) schema = SQLAlchemySchemaNode(Person) person = Person( id=7, fullname="Joe Smith", age=35, ) # dict coming from a form submission update_cstruct = { 'id': '7', 'fullname': '', 'age': '', } update_appstruct = schema.deserialize(update_cstruct) schema.objectify(update_appstruct, context=person) self.assertEqual(person.id, 7) self.assertEqual(person.fullname, None) self.assertEqual(person.age, None)
def _prep_schema(self): overrides = { 'person': { 'includes': ['name', 'surname', 'gender', 'addresses'], 'overrides': { 'addresses': { 'includes': ['street', 'city'], 'overrides': { 'city': { 'exclude': False } } } } }, } includes = ['email', 'enabled', 'created', 'timeout', 'person'] schema = SQLAlchemySchemaNode(Account, includes=includes, overrides=overrides) # Add a non-SQLAlchemy field schema.add(colander.SchemaNode( colander.String(), name='non_sql', missing=colander.drop )) return schema
def test_validator_typedecorator_override(self): Base = declarative_base() def location_validator(value, node): """A dummy validator.""" pass class LocationEnum(TypeDecorator): impl = Enum __colanderalchemy_config__ = {'validator': location_validator} class LocationString(TypeDecorator): impl = String __colanderalchemy_config__ = {'validator': location_validator} class House(Base): __tablename__ = 'house' id = Column(Integer, primary_key=True) door = Column(LocationEnum(['back', 'front'])) window = Column(LocationString(128)) schema = SQLAlchemySchemaNode(House) self.assertEqual(schema['door'].validator, location_validator) self.assertEqual(schema['window'].validator, location_validator)
def test_typedecorator_overwrite(self): key = SQLAlchemySchemaNode.sqla_info_key Base = declarative_base() class MyIntType(TypeDecorator): impl = Integer __colanderalchemy_config__ = {'typ': colander.String()} def string_validator(node, value): """A dummy validator.""" pass class MyString(TypeDecorator): impl = Unicode __colanderalchemy_config__ = {'validator': string_validator} class World(Base): __tablename__ = 'world' id = Column(Integer, primary_key=True) not_a_number = Column(MyIntType) number = Column(MyIntType, info={key: {'typ': colander.Integer}}) name = Column(MyString(5)) world_schema = SQLAlchemySchemaNode(World) self.assertIsInstance( world_schema['not_a_number'].typ, colander.String) # should be overwritten by the key self.assertNotIsInstance(world_schema['number'].typ, colander.String) # test if validator is not overwritten by ColanderAlchemy self.assertEqual(world_schema['name'].validator, string_validator)
def get_password_schema(): """ Return the schema for user password change :returns: a colander Schema """ schema = SQLAlchemySchemaNode( Login, includes=('pwd_hash', ), title=u'', validator=deferred_password_validator, ) set_widgets(schema) schema.insert( 0, colander.SchemaNode( colander.String(), widget=deform.widget.PasswordWidget(), name='password', title=u'Mot de passe actuel', default=u'', )) schema['pwd_hash'].title = u"Nouveau mot de passe" return schema
def test_task_line(config, request_with_config, estimation, unity, tva, product, product_without_tva): from autonomie.models.task.task import TaskLine schema = SQLAlchemySchemaNode(TaskLine) request_with_config.context = estimation schema = schema.bind(request=request_with_config) value = { 'description': u'test', 'cost': 450, 'quantity': 1, 'unity': u'Mètre', 'tva': 20.00, 'product_id': product.id } assert schema.deserialize(value) == { 'description': u'test', 'cost': 45000000, 'quantity': 1.0, 'unity': u'Mètre', 'tva': 2000, 'product_id': product.id, 'order': 1 } value = { 'description': u'test', 'cost': 450, 'quantity': 1, 'unity': u'Mètre', 'tva': 20.00, 'product_id': product_without_tva.id } with pytest.raises(colander.Invalid): schema.deserialize(value)
def test_task_line_group_lines(tva, unity): from autonomie.models.task.task import TaskLineGroup schema = SQLAlchemySchemaNode(TaskLineGroup, includes=('lines', )) schema = schema.bind() value = {'lines': []} with pytest.raises(colander.Invalid): schema.deserialize(value) value = { 'lines': [{ 'cost': 15, 'tva': 20, 'description': u'description', 'unity': u"Mètre", "quantity": 5, }] } assert schema.deserialize(value) == { 'lines': [{ 'cost': 1500000, 'tva': 2000, 'description': u'description', 'unity': u"Mètre", "quantity": 5.0, 'order': 1 }] }
class CancelInvoiceAdminView(BaseEditView): factory = CancelInvoice schema = SQLAlchemySchemaNode( CancelInvoice, title=u"Formulaire d'édition forcée de devis/factures/avoirs", help_msg=u"Les montants sont *10^5 10 000==1€", )
def get_sequence_model_admin(model, title=u""): """ Return a schema for configuring sequence of models model The SQLAlchemy model to configure """ node_schema = SQLAlchemySchemaNode( model, widget=deform.widget.MappingWidget(template=TEMPLATES_URL + "clean_mapping.pt", )) node_schema.name = 'data' schema = colander.SchemaNode(colander.Mapping()) schema.add( colander.SchemaNode(colander.Sequence(), node_schema, widget=deform.widget.SequenceWidget( orderable=True, template=TEMPLATES_URL + "clean_sequence.pt", ), title=title, name='datas')) def dictify(models): return {'datas': [node_schema.dictify(model) for model in models]} def objectify(datas): return [node_schema.objectify(data) for data in datas] schema.dictify = dictify schema.objectify = objectify return schema
def test_example_typedecorator_overwrite(self): Base = declarative_base() class Email(TypeDecorator): impl = Unicode __colanderalchemy_config__ = {'validator': colander.Email} class User(Base): __tablename__ = 'user' id = Column(Integer, primary_key=True) email = Column(Email, nullable=False) second_email = Column(Email) schema = SQLAlchemySchemaNode(User) # because of naming clashes, we need to do this in another function def generate_colander(): class User(colander.MappingSchema): id = colander.SchemaNode(colander.Integer(), missing=colander.drop) email = colander.SchemaNode(colander.String(), validator=colander.Email()) second_email = colander.SchemaNode(colander.String(), validator=colander.Email(), missing=colander.null) return User() schema2 = generate_colander() self.is_equal_schema(schema, schema2)
class AdminInvoice(BaseEditView): """ Vue pour l'administration de factures ?token=admin Vue accessible aux utilisateurs admin """ schema = SQLAlchemySchemaNode(Invoice)
class BloodPressureCrudViews(IndividualRecordCRUDView, CRUDView): model = BloodPressure schema = SQLAlchemySchemaNode(BloodPressure, includes=[ 'time', 'systolic', 'diastolic', colander.SchemaNode(colander.Float(), name='heart_rate', title='Heart rate', missing=None) ]) title = 'blood pressure' url_path = '/blood_pressure' list_display = ('time', 'systolic', 'diastolic', heart_rate) def get_list_query(self): query = super(BloodPressureCrudViews, self).get_list_query() return query.order_by(BloodPressure.time.desc()) def dictify(self, obj): appstruct = super(BloodPressureCrudViews, self).dictify(obj) if obj.heart_rate is not None: appstruct['heart_rate'] = obj.heart_rate.rate return appstruct
def get_doctypes_schema(userdatas_model): """ Build a form schema for doctypes registration :param obj userdatas_model: An instance of userdatas we're building the form for """ registered = userdatas_model.doctypes_registrations node_schema = SQLAlchemySchemaNode(UserDatasSocialDocTypes) node_schema.widget = deform.widget.MappingWidget( template="clean_mapping.pt" ) node_schema['status'].widget = deform.widget.CheckboxWidget() form_schema = colander.Schema() for index, entry in enumerate(registered): node = node_schema.clone() name = 'node_%s' % index node.name = name node.title = u'' node['status'].title = u'' node['status'].label = entry.doctype.label form_schema.add(node) return form_schema
def test_dictify_with_null(self): """ Test SQLAlchemySchemaNode.dictify(obj) with null values and show that result is a valid appstruct for the given schema """ Base = declarative_base() class Sensor(Base): __tablename__ = 'sensor' sensor_id = Column(Integer, primary_key=True) institution_id = Column(Integer, nullable=True) sensor_label = Column(String, nullable=True) sensor = Sensor( sensor_id=3, institution_id=None, sensor_label=None, ) schema = SQLAlchemySchemaNode(Sensor) appstruct = schema.dictify(sensor) cstruct = schema.serialize(appstruct=appstruct) newappstruct = schema.deserialize(cstruct) newobj = schema.objectify(appstruct) self.assertEqual(appstruct, newappstruct) self.assertEqual(sensor.sensor_id, newobj.sensor_id) self.assertEqual(sensor.institution_id, newobj.institution_id) self.assertEqual(sensor.sensor_label, newobj.sensor_label)
def get_password_schema(): """ Return the schema for user password change """ schema = SQLAlchemySchemaNode( user.User, includes=( 'login', 'pwd', ), title=u'Modification de mot de passe', validator=auth, ) schema.insert( 1, colander.SchemaNode( colander.String(), widget=deform.widget.PasswordWidget(), name='password', title=u'Mot de passe actuel', default=u'', )) schema['login'].widget = deform.widget.HiddenWidget() # Remove login validation schema['login'].validator = None return schema
def test_aliased_view(self): """tests aliased view that was broken in 0.3.1 """ Base = declarative_base() class Person(Base): __tablename__ = 'person' id = Column(Integer, primary_key=True) parent_id = Column(Integer, ForeignKey('person.id')) class ParentView(Base): _parent_table = Person.__table__ _child_table = aliased(_parent_table) __mapper_args__ = { 'primary_key': [_child_table.c.id], 'include_properties': [_child_table.c.id], } __table__ = _parent_table.join( _child_table, _child_table.c.parent_id == _parent_table.c.id ) schema = SQLAlchemySchemaNode(ParentView) self.assertIn('id', schema)
def statistic_sheet_add_edit_view(context, request): """ View for adding editing statistics sheets """ post_datas = request.POST or request.json_body if 'title' in post_datas: schema = SQLAlchemySchemaNode(StatisticSheet, includes=('title',)) try: appstruct = schema.deserialize(request.POST) except colander.Invalid: logger.exception(u"Erreur à la création de la feuille de \ statistiques") else: if context.__name__ == 'statistic_sheet': sheet = schema.objectify(appstruct, context) sheet = request.dbsession.merge(sheet) else: sheet = schema.objectify(appstruct) request.dbsession.add(sheet) request.dbsession.flush() url = request.route_path('statistic', id=sheet.id) return dict(redirect=url) logger.debug(u"Invalid datas have been passed") raise HTTPClientError() logger.debug(u"Missing datas in the request") raise HTTPClientError()
def test_payment(mode, tva, bank): from autonomie.models.task.invoice import Payment schema = SQLAlchemySchemaNode(Payment) schema = schema.bind() value = { 'mode': mode.label, "amount": 12.5, "remittance_amount": u"Remittance", "date": NOW.isoformat(), "tva_id": tva.id, "bank_id": bank.id, "user_id": 5, "task_id": 5, } expected_value = { 'mode': mode.label, "amount": 1250000, "remittance_amount": u"Remittance", "date": NOW, "tva_id": tva.id, "bank_id": bank.id, "user_id": 5, "task_id": 5, } result = schema.deserialize(value) for key, value in expected_value.items(): assert result[key] == value
def get_add_edit_schema(edit=False): """ Return a user add schema """ schema = SQLAlchemySchemaNode( User, includes=( 'civilite', 'firstname', 'lastname', 'email', ), ) if not edit: schema.add( colander.SchemaNode( colander.Boolean(), name='add_login', title=u"Créer des identifiants pour ce compte ?", description=u"Les identifiants permettront au titulaire de ce " u"compte de se connecter", ) ) set_widgets(schema) return schema
def test_estimation_payment_lines(): from autonomie.models.task.estimation import Estimation schema = SQLAlchemySchemaNode(Estimation, includes=('payment_lines', )) schema = schema.bind() value = {'payment_lines': []} with pytest.raises(colander.Invalid): schema.deserialize(value) value = {} with pytest.raises(colander.Invalid): schema.deserialize(value) value = { 'payment_lines': [{ 'task_id': 5, 'date': datetime.date.today().isoformat(), 'amount': 12.5, 'description': u"Description" }] } expected_value = { 'payment_lines': [{ 'task_id': 5, 'date': datetime.date.today(), 'amount': 1250000, 'description': u"Description", 'order': 1 }] } assert schema.deserialize(value) == expected_value
class HeightWeightCrudViews(IndividualRecordCRUDView, CRUDView): model = HeightWeight schema = SQLAlchemySchemaNode( HeightWeight, includes=['time', 'weight', 'height', notes_schema]) url_path = '/height_weight' list_display = ('time', 'weight', 'height', bmi, notes) def get_list_query(self): query = super(HeightWeightCrudViews, self).get_list_query() query = query.outerjoin(HeightWeight.notes) query = query.with_entities(HeightWeight.id, HeightWeight.time, HeightWeight.weight, HeightWeight.height, HeightWeight.bmi, Note.text) return query.order_by(HeightWeight.time.desc()) def dictify(self, obj): """ Serialize a HeightWeightCrudViews object to a dict """ # Default serialization for built-in fields appstruct = super(HeightWeightCrudViews, self).dictify(obj) if obj.notes is not None: appstruct['notes'] = obj.notes.text return appstruct