Question

I need a temporary table in my programme. I have seen that this can be achieved with the "mapper" syntax in this way:

t = Table(
    't', metadata,
    Column('id', Integer, primary_key=True),
    # ...
    prefixes=['TEMPORARY'],
)

Seen here

But, my whole code is using the declarative base, it is what I understand, and I would like to stick to it. There is the possibility of using a hybrid approach but if possible I'd avoid it.

This is a simplified version of how my declarative class looks like:

import SQLAlchemy as alc
class Tempo(Base):
    """
    Class for temporary table used to process data coming from xlsx
    @param Base Declarative Base
    """

    # TODO: make it completely temporary

    __tablename__ = 'tempo'

    drw = alc.Column(alc.String)
    date = alc.Column(alc.Date)
    check_number = alc.Column(alc.Integer)

Thanks in advance!

EDITED WITH THE NEW PROBLEMS:

Now the class looks like this:

import SQLAlchemy as alc

class Tempo(Base):
        """
        Class for temporary table used to process data coming from xlsx
        @param Base Declarative Base
        """

        # TODO: make it completely temporary

        __tablename__ = 'tempo'
        __table_args__ = {'prefixes': ['TEMPORARY']}

        drw = alc.Column(alc.String)
        date = alc.Column(alc.Date)
        check_number = alc.Column(alc.Integer)

And when I try to insert data in this table, I get the following error message:

sqlalchemy.exc.OperationalError: (OperationalError) no such table:
tempo u'INSERT INTO tempo (...) VALUES (?, ?, ?, ?, ?, ?, ?, ?)' (....)

It seems the table doesn't exist just by declaring it. I have seen something like create_all() that might be the solution for this (it's funny to see how new ideas come while explaining thoroughly)

Then again, thank you very much!

Was it helpful?

Solution

Is it possible to use __table_args__? See https://docs.sqlalchemy.org/en/14/orm/declarative_tables.html#orm-declarative-table-configuration

class Tempo(Base):
    """
    Class for temporary table used to process data coming from xlsx
    @param Base Declarative Base
    """

    # TODO: make it completely temporary

    __tablename__ = 'tempo'
    __table_args__ = {'prefixes': ['TEMPORARY']}

    drw = alc.Column(alc.String)
    date = alc.Column(alc.Date)
    check_number = alc.Column(alc.Integer)

OTHER TIPS

Old question, but if anyone out there wants to create a temp table from an existing declarative table model on the fly rather than having it always be a part of your model/code, you can try the following approach. Copying __table_args__ is a little tricky since it can have multiple formats and any Index objects need to be recreated so they aren't associated with the old table.

import time

from sqlalchemy.schema import CreateTable


def copy_table_args(model, **kwargs):
    """Try to copy existing __table_args__, override params with kwargs"""
    table_args = model.__table_args__

    if isinstance(table_args, tuple):
        new_args = []
        for arg in table_args:
            if isinstance(arg, dict):
                table_args_dict = arg.copy()
                table_args_dict.update(**kwargs)
                new_args.append(arg)
            elif isinstance(arg, sa.Index):
                index = sa.Index(
                    arg.name,
                    *[col for col in arg.columns.keys()],
                    unique=arg.unique,
                    **arg.kwargs,
                )
                new_args.append(index)
            else:
                # TODO: need to handle Constraints
                raise Exception(f"Unhandled table arg: {arg}")
        table_args = tuple(new_args)
    elif isinstance(table_args, dict):
        table_args = {
            k: (v.copy() if hasattr(v, "copy") else v) for k, v in table_args.items()
        }
        table_args.update(**kwargs)
    else:
        raise Exception(f"Unexpected __table_args__ type: {table_args}")

    return table_args


def copy_table_from_model(conn, model, **kwargs):
    model_name = model.__name__ + "Tmp"
    table_name = model.__table__.name + "_" + str(time.time()).replace(".", "_")
    table_args = copy_table_args(model, extend_existing=True)

    args = {c.name: c.copy() for c in model.__table__.c}
    args["__tablename__"] = table_name
    args["__table_args__"] = table_args

    copy_model = type(model_name, model.__bases__, args)
    print(str(CreateTable(copy_model.__table__)))
    copy_model.__table__.create(conn)
    return copy_model


def temp_table_from_model(conn, model, **kwargs):
    return copy_table_from_model(conn, model, prefixes=["TEMPORARY"])

Note: I haven't added logic to handle copying Constraints, and this is lightly tested against MySQL. Also note that if you do this with non-temporary tables and auto-named indexes (i.e. Column(..., index=True)) then this may not play nice with alembic.

Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top