मैं एलेम्बिक अपग्रेड स्क्रिप्ट में आवेषण और अपडेट कैसे निष्पादित कर सकता हूं?


94

मुझे एलेम्बिक अपग्रेड के दौरान डेटा में बदलाव करना होगा।

वर्तमान में मेरे पास पहले संशोधन में एक 'खिलाड़ी' तालिका है:

def upgrade():
    op.create_table('player',
        sa.Column('id', sa.Integer(), nullable=False),
        sa.Column('name', sa.Unicode(length=200), nullable=False),
        sa.Column('position', sa.Unicode(length=200), nullable=True),
        sa.Column('team', sa.Unicode(length=100), nullable=True)
        sa.PrimaryKeyConstraint('id')
    )

मैं एक 'टीमों ’की तालिका शुरू करना चाहता हूं। मैंने दूसरा संशोधन बनाया है:

def upgrade():
    op.create_table('teams',
        sa.Column('id', sa.Integer(), nullable=False),
        sa.Column('name', sa.String(length=80), nullable=False)
    )
    op.add_column('players', sa.Column('team_id', sa.Integer(), nullable=False))

मैं निम्नलिखित डेटा को जोड़ने के लिए दूसरा माइग्रेशन चाहूंगा:

  1. टीमों टेबल आबाद:

    INSERT INTO teams (name) SELECT DISTINCT team FROM players;
  2. खिलाड़ियों को अद्यतन करें।

    UPDATE players AS p JOIN teams AS t SET p.team_id = t.id WHERE p.team = t.name;

मैं अपग्रेड स्क्रिप्ट के अंदर आवेषण और अपडेट कैसे निष्पादित करूं?

जवाबों:


146

आप जो पूछ रहे हैं वह डेटा माइग्रेशन है , जैसा कि एलेम्बिक डॉक्स में प्रचलित स्कीमा माइग्रेशन के विपरीत है ।

यह उत्तर मानता है कि आप अपने मॉडलों को परिभाषित करने के लिए घोषणात्मक (वर्ग-मैपर-टेबल या कोर के विपरीत) का उपयोग कर रहे हैं। इसे अन्य रूपों के अनुकूल बनाने के लिए अपेक्षाकृत सरल होना चाहिए।

नोट करें कि एलेम्बिक कुछ बुनियादी डेटा फ़ंक्शन प्रदान करता है: op.bulk_insert() और op.execute()। यदि ऑपरेशन काफी कम हैं, तो उन का उपयोग करें। यदि माइग्रेशन के लिए रिश्तों या अन्य जटिल इंटरैक्शन की आवश्यकता होती है, तो मैं नीचे वर्णित के रूप में मॉडल और सत्रों की पूरी शक्ति का उपयोग करना पसंद करता हूं।

निम्नलिखित एक उदाहरण माइग्रेशन स्क्रिप्ट है जो कुछ घोषणात्मक मॉडल सेट करती है जिनका उपयोग सत्र में डेटा में हेरफेर करने के लिए किया जाएगा। प्रमुख बिंदु हैं:

  1. उन बुनियादी मॉडलों को परिभाषित करें जिनकी आपको आवश्यकता है, उन कॉलमों के साथ जिनकी आपको आवश्यकता होगी। आपको प्रत्येक कॉलम की आवश्यकता नहीं है, बस प्राथमिक कुंजी और आपके द्वारा उपयोग किए जा रहे हैं।

  2. नवीनीकरण फ़ंक्शन के भीतर, op.get_bind()वर्तमान कनेक्शन प्राप्त करने के लिए उपयोग करें, और इसके साथ एक सत्र बनाएं।

    • या bind.execute()SQL क्वेरी को सीधे लिखने के लिए SQLAlchemy के निचले स्तर का उपयोग करने के लिए उपयोग करें। यह साधारण प्रवास के लिए उपयोगी है।
  3. मॉडल और सत्र का उपयोग करें जैसा कि आप आमतौर पर अपने आवेदन में करते हैं।

"""create teams table

Revision ID: 169ad57156f0
Revises: 29b4c2bfce6d
Create Date: 2014-06-25 09:00:06.784170
"""

revision = '169ad57156f0'
down_revision = '29b4c2bfce6d'

from alembic import op
import sqlalchemy as sa
from sqlalchemy import orm
from sqlalchemy.ext.declarative import declarative_base

Base = declarative_base()


class Player(Base):
    __tablename__ = 'players'

    id = sa.Column(sa.Integer, primary_key=True)
    name = sa.Column(sa.String, nullable=False)
    team_name = sa.Column('team', sa.String, nullable=False)
    team_id = sa.Column(sa.Integer, sa.ForeignKey('teams.id'), nullable=False)

    team = orm.relationship('Team', backref='players')


class Team(Base):
    __tablename__ = 'teams'

    id = sa.Column(sa.Integer, primary_key=True)
    name = sa.Column(sa.String, nullable=False, unique=True)


def upgrade():
    bind = op.get_bind()
    session = orm.Session(bind=bind)

    # create the teams table and the players.team_id column
    Team.__table__.create(bind)
    op.add_column('players', sa.Column('team_id', sa.ForeignKey('teams.id'), nullable=False)

    # create teams for each team name
    teams = {name: Team(name=name) for name in session.query(Player.team).distinct()}
    session.add_all(teams.values())

    # set player team based on team name
    for player in session.query(Player):
        player.team = teams[player.team_name]

    session.commit()

    # don't need team name now that team relationship is set
    op.drop_column('players', 'team')


def downgrade():
    bind = op.get_bind()
    session = orm.Session(bind=bind)

    # re-add the players.team column
    op.add_column('players', sa.Column('team', sa.String, nullable=False)

    # set players.team based on team relationship
    for player in session.query(Player):
        player.team_name = player.team.name

    session.commit()

    op.drop_column('players', 'team_id')
    op.drop_table('teams')

माइग्रेशन अलग-अलग मॉडल को परिभाषित करता है क्योंकि आपके कोड के मॉडल डेटाबेस की वर्तमान स्थिति का प्रतिनिधित्व करते हैं, जबकि माइग्रेशन रास्ते में चरणों का प्रतिनिधित्व करते हैं । आपका डेटाबेस उस पथ के साथ किसी भी स्थिति में हो सकता है, इसलिए मॉडल डेटाबेस के साथ अभी तक सिंक नहीं कर सकते हैं। जब तक आप बहुत सावधान न हों, वास्तविक मॉडल का सीधे उपयोग करने से अनुपलब्ध स्तंभों, अमान्य डेटा, आदि के साथ समस्याएँ उत्पन्न होंगी, यह स्पष्ट रूप से स्पष्ट है कि माइग्रेशन में आपके द्वारा उपयोग किए जाने वाले कॉलम और मॉडल क्या हैं।


11

आप निम्न उदाहरण के रूप में प्रत्यक्ष एसक्यूएल देखें ( एलेम्बिक ऑपरेशन संदर्भ ) का उपयोग कर सकते हैं :

from alembic import op

# revision identifiers, used by Alembic.
revision = '1ce7873ac4ced2'
down_revision = '1cea0ac4ced2'
branch_labels = None
depends_on = None


def upgrade():
    # ### commands made by andrew ###
    op.execute('UPDATE STOCK SET IN_STOCK = -1 WHERE IN_STOCK IS NULL')
    # ### end Alembic commands ###


def downgrade():
    # ### commands auto generated by Alembic - please adjust! ###
    pass
    # ### end Alembic commands ###

मामले में मैं हमेशा एक बाहरी फ़ाइल से एक एसक्यूएल स्टेटमेंट को पढ़ना चाहता था और फिर इसे पास करना चाहता op.executeथा upgrade(), क्या alembic revisionकमांड द्वारा उपयोग किए जाने के लिए एक डिफ़ॉल्ट टेम्पलेट प्रदान करने का एक तरीका है (उत्पन्न .pyफ़ाइल के लिए एक डिफ़ॉल्ट निकाय )?
क्वेंटिन

1
मैं @Quentin नहीं जानता। यह एक दिलचस्प विचार है।
मार्तुलक

6

मैं आधिकारिक दस्तावेज में विस्तृत रूप में SQLAlchemy कोर स्टेटमेंट्स का उपयोग करके एड-हॉक टेबल का उपयोग करने की सलाह देता हूं , क्योंकि यह अज्ञेय एसक्यूएल और पायथोनिक लेखन के उपयोग की अनुमति देता है और स्व-निहित भी है। SQLAlchemy Core, माइग्रेशन स्क्रिप्ट्स के लिए दोनों दुनिया का सर्वश्रेष्ठ है।

यहाँ अवधारणा का एक उदाहरण है:

from sqlalchemy.sql import table, column
from sqlalchemy import String
from alembic import op

account = table('account',
    column('name', String)
)
op.execute(
    account.update().\\
    where(account.c.name==op.inline_literal('account 1')).\\
        values({'name':op.inline_literal('account 2')})
        )

# If insert is required
from sqlalchemy.sql import insert
from sqlalchemy import orm

session = orm.Session(bind=bind)
bind = op.get_bind()

data = {
    "name": "John",
}
ret = session.execute(insert(account).values(data))
# for use in other insert calls
account_id = ret.lastrowid
हमारी साइट का प्रयोग करके, आप स्वीकार करते हैं कि आपने हमारी Cookie Policy और निजता नीति को पढ़ और समझा लिया है।
Licensed under cc by-sa 3.0 with attribution required.