def create_column( name: str, type_option: TypeOption, index: bool = False, nullable: bool = True, identifier: bool = False, foreign_key_relationship: Optional[str] = None, display_name: Optional[str] = None, ) -> Column: if identifier is True: constructor = IdentifierColumn elif foreign_key_relationship is not None: constructor = ForeignKey else: constructor = Column args = { "python_name": pythonize(name), "class_name": to_class_name(name), "json_property_name": to_json_case(name), "display_name": display_name if display_name else humanize(name), "type_option": type_option, "sqlalchemy_type": type_option_to_sqlalchemy_type(type_option), "python_type": type_option_to_python_type(type_option), "restplus_type": type_option_to_restplus_type(type_option), "default": type_option_to_default_value(type_option), "index": index, "nullable": nullable, } if foreign_key_relationship is not None: args["relationship"] = foreign_key_relationship return constructor(**args)
def create_api_path( joined_entities: List[str], route: str, endpoint: Optional[str]=None, class_name: Optional[str]=None, property_name: Optional[str]=None, ) -> APIPath: python_name = pythonize(joined_entities[-1]) property_name = property_name if property_name else to_json_case(python_name) return APIPath(joined_entities, route, endpoint if endpoint else '-'.join(joined_entities), class_name=class_name if class_name else to_class_name(python_name), python_name=python_name, property_name=property_name)
def create_column( name: str, type_option: TypeOption, index: bool = False, nullable: bool = True, identifier: bool = False, display_name: Optional[str] = None, alias: Optional[str] = None, foreign_key_relationship: Optional[ForeignKeyRelationship] = None, faker_method: Optional[str] = None, faker_options: Optional[str] = None, sqlalchemy_options: Optional[Dict[str, str]] = None, ) -> Union[Column, IdentifierColumn, ForeignKey]: """Return a column to be attached to an entity Args: name: The property name on the SQLAlchemy model type_option: The type of the column. index: Whether to create a database index for the column. nullable: Whether to allow the column to be nullable in the database. identifier: If set to True all other args will be ignored and this will created as an identifier column. See: create_identifier_column display_name: Human readable name intended for use in UIs. alias: Column name to be used the database. foreign_key_relationship: The entity this column relates. If this is not None the result will be a `ForeignKey`. faker_method: The method to pass to Faker to provide fixture data for this column. If this column is not nullable, defaults to the constructor for the type of this column. sqlalchemy_options: Pass additional keyword arguments to the SQLAlchemy column object. """ if identifier is True: constructor: Type[Column] = IdentifierColumn elif foreign_key_relationship is not None: constructor = ForeignKey else: constructor = Column if faker_method is None and nullable is False: faker_method = type_option_to_faker_method(type_option) if sqlalchemy_options is None: sqlalchemy_options = {} args = { "python_name": pythonize(name), "class_name": to_class_name(name), "json_property_name": to_json_case(name), "display_name": display_name if display_name else humanize(name), "type_option": type_option, "sqlalchemy_type": type_option_to_sqlalchemy_type(type_option), "python_type": type_option_to_python_type(type_option), "restplus_type": type_option_to_restplus_type(type_option), "default": type_option_to_default_value(type_option), "index": index, "nullable": nullable, "alias": alias, "faker_method": faker_method, "faker_options": faker_options, "sqlalchemy_options": list(sqlalchemy_options.items()), } if foreign_key_relationship is not None: args['relationship'] = '{}.{}'.format( pythonize(foreign_key_relationship.target_entity), 'id') args['target_restplus_type'] = type_option_to_restplus_type( foreign_key_relationship.target_entity_identifier_column_type) args['foreign_key_sqlalchemy_options'] = list( foreign_key_relationship.sqlalchemy_options.items()) return constructor(**args) # type: ignore
def create_relationship( target_entity_class_name: str, nullable: bool, lazy: bool, join: JoinOption, source_identifier_column_name: str, *, source_foreign_key_column_name: Optional[str] = None, key_alias_in_json: Optional[str] = None, join_table: Optional[str] = None, target_identifier_column_name: Optional[str] = None, target_foreign_key_column_name: Optional[str] = None, property_name: Optional[str] = None, secondary_join_name: Optional[str] = None, ) -> Relationship: """Return a relationship between two entities Args: target_entity_class_name: The entity this relationship is pointing to (ie. not the entity it is defined on). nullable: Early validation for whether the target column is nullable. lazy: If False the target entity is embedded in the JSON response. join: Whether the relationship is 1-to-1 or 1-to-many. If 1-to-1 the property will be scalar, if 1-to-many it will be a list. source_identifier_column_name: The identifier column for the entity this relationship starts from. This is *not* the join key. source_foreign_key_column_name: The foreign key property on the entity this relationship starts from. This will be None for 1-to-many relationship. key_alias_in_json: The name used for this relationship when it appears in JSON. This needs to be unique for a model. Has sensible default. join_table: The table name to join through to the target entity. This is usually only needed for many-to-many relationships. target_identifier_column_name: The identifier column of the target entity. (deprecated) target_foreign_key_column_name: The column name of the foreign key on the target model. This is usually only needed if there are multiple routes to the other entity. property_name: The property name used on the SQLAlchemy model. secondary_join_name: The column name of the secondary foreign key on the intermediary join table when doing a many-to-many join on a self-referential many-to-many relationship. See: https://docs.sqlalchemy.org/en/latest/orm/join_conditions.html#self-referential-many-to-many-relationship """ if source_foreign_key_column_name is not None: if target_foreign_key_column_name is not None: raise Exception( 'Cannot provide both source and target foreign key columns') if join != JoinOption.to_one: raise Exception( 'Can only provide source foreign key column on to-one relationships' ) target_entity_python_name = pythonize(target_entity_class_name) property_name = property_name if property_name is not None else target_entity_python_name relationship = Relationship( python_name=target_entity_python_name, target_entity_class_name=target_entity_class_name, target_entity_python_name=target_entity_python_name, target_foreign_key_column_name=target_foreign_key_column_name, source_identifier_column_name=source_identifier_column_name, source_foreign_key_column_name=source_foreign_key_column_name, property_name=property_name, json_property_name=to_json_case(property_name), key_alias_in_json=key_alias_in_json if key_alias_in_json is not None else target_identifier_column_name, nullable=nullable, lazy=lazy, join=join, secondary_join_name=secondary_join_name, ) if join_table is None: target_identifier_column_name = pythonize(target_identifier_column_name) \ if target_identifier_column_name else None return RelationshipWithoutJoinTable( **{ **{ 'target_identifier_column_name': target_identifier_column_name, **relationship.__dict__ } }) join_table = str(join_table) if join_table else None properties = relationship.__dict__ properties.update({ 'join_table': join_table, 'join_table_class_name': to_class_name(join_table), }) return RelationshipWithJoinTable(**properties)
def create_relationship( target_entity_class_name: str, nullable: bool, lazy: bool, join: JoinOption, source_identifier_column_name: str, *, source_foreign_key_column_name: Optional[str] = None, key_alias_in_json: Optional[str] = None, join_table: Optional[str] = None, target_identifier_column_name: Optional[str] = None, target_foreign_key_column_name: Optional[str] = None, property_name: Optional[str] = None, secondary_join_name: Optional[str] = None, passive_deletes: Optional[Union[bool, str]] = None, cascade: Optional[str] = None, post_update: bool = False, ) -> Relationship: """Return a relationship between two entities Args: target_entity_class_name: The entity this relationship is pointing to (ie. not the entity it is defined on). nullable: Early validation for whether the target column is nullable. lazy: If False the target entity is embedded in the JSON response. join: Whether the relationship is 1-to-1 or 1-to-many. If 1-to-1 the property will be scalar, if 1-to-many it will be a list. source_identifier_column_name: The identifier column for the entity this relationship starts from. This is *not* the join key. source_foreign_key_column_name: The foreign key property on the entity this relationship starts from. This will be None for 1-to-many relationship. key_alias_in_json: The name used for this relationship when it appears in JSON. This needs to be unique for a model. Has sensible default. join_table: The table name to join through to the target entity. This is usually only needed for many-to-many relationships. target_identifier_column_name: The identifier column of the target entity. (deprecated) target_foreign_key_column_name: The column name of the foreign key on the target model. This is usually only needed if there are multiple routes to the other entity. property_name: The property name used on the SQLAlchemy model. secondary_join_name: The column name of the secondary foreign key on the intermediary join table when doing a many-to-many join on a self-referential many-to-many relationship. See: https://docs.sqlalchemy.org/en/latest/orm/join_conditions.html#self-referential-many-to-many-relationship passive_deletes: Option to prevent SQLAlchemy from cascading to target objects on delete. Valid options are True|False|'all' cascade: SQLAlchemy ORM Cascading option. See: https://docs.sqlalchemy.org/en/14/orm/cascades.html post_update: Set this to have sqlalchemy set relationships after create or before delete. Useful for items with circular relationships. See: https://docs.sqlalchemy.org/en/14/orm/relationship_persistence.html """ if source_foreign_key_column_name is not None: if target_foreign_key_column_name is not None: raise GenyratorError( 'Cannot provide both source and target foreign key columns') if join != JoinOption.to_one: raise GenyratorError( 'Can only provide source foreign key column on to-one relationships' ) target_entity_python_name = pythonize(target_entity_class_name) property_name = property_name if property_name is not None else target_entity_python_name if key_alias_in_json is None: if target_identifier_column_name is None: raise GenyratorError( 'Must have a key_alias_in_json or target_idenfitier_column_name' ) key_alias_in_json = target_identifier_column_name if isinstance(passive_deletes, str) and passive_deletes != 'all': passive_deletes = None relationship = Relationship( python_name=target_entity_python_name, target_entity_class_name=target_entity_class_name, target_entity_python_name=target_entity_python_name, target_foreign_key_column_name=target_foreign_key_column_name, source_identifier_column_name=source_identifier_column_name, source_foreign_key_column_name=source_foreign_key_column_name, property_name=property_name, json_property_name=to_json_case(property_name), key_alias_in_json=key_alias_in_json, nullable=nullable, lazy=lazy, join=join, secondary_join_name=secondary_join_name, passive_deletes=passive_deletes, cascade=cascade, post_update=post_update, ) if join_table is None: properties = relationship.__dict__ properties.update({ 'target_identifier_column_name': pythonize(target_identifier_column_name) if target_identifier_column_name else None, }) return RelationshipWithoutJoinTable(**properties) else: properties = relationship.__dict__ properties.update({ 'join_table': join_table, 'join_table_class_name': to_class_name(join_table), }) return RelationshipWithJoinTable(**properties)