Использование SQLAlchemy для переноса баз данных (sqlite в Postgres CloudSQL) - PullRequest
1 голос
/ 19 апреля 2019

Попытка перенести базу данных для моего раннего проекта в облако.Несмотря на то, что код, который строил все, является излишним, структура базы данных и сами данные довольно надежны.Я мог бы, вероятно, найти метод дампа для переноса всего (pgdump и т. Д.), Но мне еще многое предстоит узнать об этом материале, поэтому я бы предпочел получить опыт, делая это шаг за шагом.

Источник: Файл базы данных ~ 1 ГБ sqlite

Назначение: Google CloudSQL под управлением Postgres v9.6

Уже созданы таблицы в облачной базе данных,используя ту же схему и имена таблиц, что и в sqlite db.Не беспокойтесь об ошибках принудительного применения схемы, потому что я еще не определил внешние ключи в облаке.

План: Создание отдельных одновременных соединений SQLAlchemy с каждой базой данных, а затем чтение sqlite ->написать в CloudSQL.Вернулись и определили структуры данных для каждой таблицы, используя SQLAlchemy.Фрагмент из models.py :

from sqlalchemy import Column, Integer, String
from sqlalchemy.ext.declarative import declarative_base, declared_attr

Base = declarative_base()

class PublicMixin(object):
    @declared_attr
    def __tablename__(cls):
        return cls.__name__.lower()

class Category(PublicMixin, Base):
    id = Column(Integer, primary_key=True)
    name = Column(String)

class Player(PublicMixin, Base):
    id = Column(Integer, primary_key=True)
    name = Column(String)
    username = Column(String)
    notes = Column(String)
[...]

Затем я продублировал этот файл как models_lite.py , чтобы можно было импортировать каждую модель без помех.Вот файл igration.py , который я пытался запустить в качестве подтверждения концепции:

from sqlalchemy import create_engine
from sqlalchemy.orm import sessionmaker
from models import Base, Category, Player
from models_sqlite import Base as Base_lite, Category as Category_lite, Player as Player_lite

# SQLite db
engine_lite = create_engine('sqlite:///mydb.sqlite')
Base_lite.metadata.bind = engine_lite
LiteSession = sessionmaker()
LiteSession.bind = engine_lite
session_lite = LiteSession()

# CloudSQL, via local proxy
engine_cloud = create_engine('postgresql+psycopg2://USER:PW@/DBNAME?host=/cloudsql/INSTANCE')
Base.metadata.bind = engine_cloud
CloudSession = sessionmaker()
CloudSession.bind = engine_cloud
session_cloud = CloudSession()

category_lite = session_lite.query(Category_lite).all()
category_cloud = Category()

for c in category_lite:
    category_cloud = c
    session_cloud.add(category_cloud)

session_cloud.commit()

Запуск этого файла приводит к следующей ошибке:

File "postgres migration.py", line 68, in <module>
    session_cloud.add(category_cloud)
[...]
sqlalchemy.exc.InvalidRequestError: Object '<Category at 0x11141b908>' is already attached to session '1' (this is '2')

Явно устанавливая каждыйстолбец внутри цикла for работает (то есть: category_cloud.id = c.id), но должен быть способ избежать необходимости делать это для каждого столбца в каждой таблице.Как мне подойти к этому?

1 Ответ

4 голосов
/ 20 апреля 2019

Этот тип операции передачи данных гораздо проще осуществить с помощью ядра sqlalchemy, чем с помощью orm.Здесь нет смысла отображать данные базы данных на объекты, если они только будут немедленно записаны в другую базу данных, это только добавляет сложности и замедляет процесс.Следующий код будет перебирать каждую таблицу в Base, выбирать все столбцы в базе данных sqlite и записывать их по одной таблице за раз в облачную базу данных.

from sqlalchemy import create_engine, select
from models import Base

engine_lite = create_engine('sqlite:///mydb.sqlite')
engine_cloud = create_engine('postgresql+psycopg2://USER:PW@/DBNAME?host=/cloudsql/INSTANCE')

with engine_lite.connect() as conn_lite:
    with engine_cloud.connect() as conn_cloud:
        for table in Base.metadata.sorted_tables:
            data = [dict(row) for row in conn_lite.execute(select(table.c))]
            conn_cloud.execute(table.insert().values(data))

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...