Esempio n. 1
0
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)
Esempio n. 2
0
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)
Esempio n. 3
0
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
Esempio n. 4
0
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)
Esempio n. 5
0
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)