def map_column( self, mode: EditMode, request: Request, node: colander.SchemaNode, model: type, name: str, column: Column, column_type: TypeEngine) -> t.Tuple[colander.SchemaType, dict]: """Map non-relationship SQLAlchemy column to Colander SchemaNode. :return: Tuple(constructed colander.SchemaType, dict of addtional colander.SchemaNode construction arguments) """ logger.debug( "Mapping field %s, mode %s, node %s, column %s, column type %s", name, mode, node, column, column_type) validator = None # Check for autogenerated columns (updated_at) if column.onupdate: if mode in (EditMode.edit, EditMode.add): return TypeOverridesHandling.drop, {} # Don't fill default values when added, as they are automatically populated if column.default: if mode == EditMode.add: return TypeOverridesHandling.drop, {} # Set unique validator if column.unique and (mode in (EditMode.add, EditMode.edit)): validator = ValidateUnique(model, mode) # Never add primary keys # NOTE: TODO: We need to preserve ids because of nesting mechanism and groupedit widget wants it id if column.primary_key: # TODO: Looks like column.autoincrement is set True by default, so we cannot use it here if mode in (EditMode.edit, EditMode.add): return TypeOverridesHandling.drop, {} if column.foreign_keys: # Handled by relationship mapper return TypeOverridesHandling.drop, {} elif isinstance(column_type, (PostgreSQLUUID, columns.UUID)): # UUID's cannot be edited if mode in (EditMode.add, EditMode.edit): return TypeOverridesHandling.drop, {} # But let's show them return fields.UUID(), dict( missing=colander.drop, widget=FriendlyUUIDWidget(readonly=True)) elif isinstance(column_type, Text): return colander.String(), dict( widget=deform.widget.TextAreaWidget(), validator=validator) elif isinstance(column_type, JSONB): return JSONValue(), dict(widget=JSONWidget()) elif isinstance(column_type, (LargeBinary, Geometry)): # Can't edit binary and geometry return TypeOverridesHandling.drop, {} elif isinstance(column_type, (INET, columns.INET)): return colander.String(), dict(validator=validator) else: # Default mapping / unknown, let the parent handle return TypeOverridesHandling.unknown, dict(validator=validator)
class UserShow(admin_views.Show): """Show one user.""" resource_buttons = admin_views.Show.resource_buttons + [TraverseLinkButton(id="set-password", name="Set password", view_name="set-password")] includes = [ "id", "uuid", "enabled", "created_at", "updated_at", "username", colander.SchemaNode(colander.String(), name='full_name'), "email", "last_login_at", "last_login_ip", colander.SchemaNode(colander.String(), name="registration_source", missing=colander.drop), colander.SchemaNode(JSONValue(), name="user_data", widget=JSONWidget(), description="user_data JSON properties"), colander.SchemaNode(GroupSet(), name="groups", widget=defer_widget_values(deform.widget.CheckboxChoiceWidget, group_vocabulary, css_class="groups")) ] form_generator = SQLAlchemyFormGenerator(includes=includes) def get_title(self) -> str: """Title for the User object. :return: Title for the User object. """ user = self.get_object() return "{friendly_name} #{id}".format( friendly_name=user.friendly_name, id=self.get_object().id ) @view_config(context=UserAdmin.Resource, route_name="admin", name="show", renderer="crud/show.html", permission='view') def show(self): """User show view. :return: Context for template rendering. """ return super(UserShow, self).show()
class AssetSchema(colander.Schema): #: Human readable name name = colander.SchemaNode(colander.String()) #: The network this asset is in network = colander.SchemaNode( UUIDForeignKeyValue(model=AssetNetwork, match_column="id"), widget=defer_widget_values(deform.widget.SelectWidget, available_networks), ) #: Symbol how this asset is presented in tickers symbol = colander.SchemaNode(colander.String()) description = colander.SchemaNode(colander.String(), missing="") #: Markdown page telling about this asset long_description = colander.SchemaNode(colander.String(), description="Markdown formatted", missing="", widget=deform.widget.TextAreaWidget( rows=20, cols=80)) #: Ethereum address external_id = colander.SchemaNode(colander.String(), title="Address", validator=validate_ethereum_address, missing=None, description="0x hex string format") #: Number of units avaialble supply = colander.SchemaNode(colander.Decimal(), missing=None) #: What kind of asset is this asset_class = colander.SchemaNode( EnumValue(AssetClass), widget=deform.widget.SelectWidget(values=enum_values(AssetClass))) #: Workflow state of this asset state = colander.SchemaNode( EnumValue(AssetState), widget=deform.widget.SelectWidget(values=enum_values(AssetState))) other_data = colander.SchemaNode( JSONValue(), widget=JSONWidget(), description="JSON bag of attributes of the object", missing=dict) def dictify(self, obj: Asset) -> dict: """Serialize SQLAlchemy model instance to nested dictionary appstruct presentation.""" appstruct = dictify(self, obj, excludes=("long_description", "external_id")) # Convert between binary storage and human readable hex presentation appstruct["long_description"] = obj.other_data.pop( "long_description", "") if obj.external_id: appstruct["external_id"] = bin_to_eth_address(obj.external_id) else: appstruct["external_id"] = "" return appstruct def objectify(self, appstruct: dict, obj: Asset): """Store the dictionary data from the form submission on the object.""" objectify(self, appstruct, obj, excludes=("long_description", "external_id")) if not obj.other_data: # When creating the object JSON value may be None # instead of empty dict obj.other_data = {} # Special case of field stored inside JSON bag obj.other_data["long_description"] = appstruct["long_description"] # Convert between binary storage and human readable hex presentation if appstruct["external_id"]: obj.external_id = eth_address_to_bin(appstruct["external_id"])