¿Cómo puedo insertar algunos datos de semillas en mi primera migración? Si la migración no es el mejor lugar para esto, entonces, ¿cuál es la mejor práctica?

"""empty message

Revision ID: 384cfaaaa0be
Revises: None
Create Date: 2013-10-11 16:36:34.696069

"""

# revision identifiers, used by Alembic.
revision = '384cfaaaa0be'
down_revision = None

from alembic import op
import sqlalchemy as sa


def upgrade():
    ### commands auto generated by Alembic - please adjust! ###
    op.create_table('list_type',
    sa.Column('id', sa.Integer(), nullable=False),
    sa.Column('name', sa.String(length=80), nullable=False),
    sa.PrimaryKeyConstraint('id'),
    sa.UniqueConstraint('name')
    )
    op.create_table('job',
    sa.Column('id', sa.Integer(), nullable=False),
    sa.Column('list_type_id', sa.Integer(), nullable=False),
    sa.Column('record_count', sa.Integer(), nullable=False),
    sa.Column('status', sa.Integer(), nullable=False),
    sa.Column('sf_job_id', sa.Integer(), nullable=False),
    sa.Column('created_at', sa.DateTime(), nullable=False),
    sa.Column('compressed_csv', sa.LargeBinary(), nullable=True),
    sa.ForeignKeyConstraint(['list_type_id'], ['list_type.id'], ),
    sa.PrimaryKeyConstraint('id')
    )
    ### end Alembic commands ###

    # ==> INSERT SEED DATA HERE <==


def downgrade():
    ### commands auto generated by Alembic - please adjust! ###
    op.drop_table('job')
    op.drop_table('list_type')
    ### end Alembic commands ###
InformationsquelleAutor Mark Richman | 2013-10-12

4 Comentarios

  1. 66

    Alambique tiene, como uno de su funcionamiento, bulk_insert(). La documentación da el siguiente ejemplo (con algunas correcciones he incluido):

    from datetime import date
    from sqlalchemy.sql import table, column
    from sqlalchemy import String, Integer, Date
    from alembic import op
    
    # Create an ad-hoc table to use for the insert statement.
    accounts_table = table('account',
        column('id', Integer),
        column('name', String),
        column('create_date', Date)
    )
    
    op.bulk_insert(accounts_table,
        [
            {'id':1, 'name':'John Smith',
                    'create_date':date(2010, 10, 5)},
            {'id':2, 'name':'Ed Williams',
                    'create_date':date(2007, 5, 27)},
            {'id':3, 'name':'Wendy Jones',
                    'create_date':date(2008, 8, 15)},
        ]
    )

    Nota demasiado que el alambique tiene un execute() operación, que es igual a la normal execute() función en SQLAlchemy: puede ejecutar SQL que desea, como la documentación de ejemplo se muestra:

    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')})
            )

    Observe que la tabla que se utiliza para crear los metadatos que se utilizan en la update declaración se define directamente en el esquema. Esto puede parecer como que se rompe SECO (no es que la tabla ya definidos en la aplicación), pero en realidad es bastante necesario. Si usted fuera a tratar de utilizar la tabla o en la definición del modelo que es parte de su solicitud, usted podría romper esta migración cuando usted hace cambios a su mesa/modelo en su aplicación. Sus scripts de migración debe ser inamovible: un cambio a una versión futura de sus modelos no deben cambiar las migraciones de secuencias de comandos. Mediante la aplicación de los modelos se significa que las definiciones variará en función de la versión de los modelos de haber verificado (probablemente la última). Por lo tanto, usted necesita la definición de la tabla para ser auto-contenida en la secuencia de comandos de migración.

    Otra cosa es hablar de si usted debe poner su semilla de datos en una secuencia de comandos que se ejecuta como su propio comando (tales como el uso de un Frasco-comando de secuencia de comandos, como se muestra en la otra respuesta). Esto puede ser usado, pero usted debe tener cuidado acerca de esto. Si los datos de la carga de datos de prueba, entonces eso es una cosa. Pero he entendido «semilla de datos» para referirse a los datos que sean necesarios para que la aplicación funcione correctamente. Por ejemplo, si usted necesita para configurar los registros para «admin» y «usuario» en los «roles» de la tabla. Estos datos DEBEN ser insertados como parte de las migraciones. Recuerde que una secuencia de comandos solo funcionan con la última versión de su base de datos, mientras que la migración va a trabajar con la versión específica que se va a migrar a o de. Si quieres un script para cargar las funciones de información, usted podría necesitar un guión para cada versión de la base de datos con un esquema diferente de los «roles» de la tabla.

    También, apoyándose en un guión, que haría más difícil para usted para ejecutar la secuencia de comandos entre las migraciones (decir la migración 3->4 requiere que la semilla de datos en la migración inicial en la base de datos). Ahora se necesita modificar Alambique predeterminada del modo de funcionamiento para ejecutar estos scripts. Y eso aún no ignorar los problemas con el hecho de que estas secuencias de comandos se tendría que cambiar con el tiempo, y quién sabe qué versión de la aplicación se han extraído de control de código fuente.

    • Hay un reverso para bulk_insert()? Creo que no existe, lo que haría más difícil para escribir el downgrade. Incluso si no era un bulk_delete, ¿qué hacer si los datos se ha cambiado por la aplicación y se ve completamente diferente que cuando fue introducido por la bulk_insert? Sólo sería seguro para hacer downgrade si la tabla se ha añadido en la misma migración, como en el caso de que usted tenga que borrar la tabla de todos modos, pero en otros casos no son fáciles de abordar. Aún así, no me siento una necesidad de downvote su respuesta.
    • Si el bulk_insert se realiza cuando se crea una tabla (que es a menudo el caso), bajando en la tabla es suficiente. De lo contrario, puede utilizar execute eliminar. Esto no es un problema con el uso de alambique de esta manera, este es un problema con la base de datos de migraciones. Ellos no son fáciles, y no hay ninguna herramienta se va a hacer fácil (sólo los hará más fácil). También, he quitado mi downvote de su respuesta después de agregar mi comentario. Sin resentimientos 🙂
    • Me fui con su enfoque, ya que todo lo que estoy almacenando en la tabla en esta migración se requiere constantes, y esta tabla es de sólo lectura. Estoy de acuerdo en que el ad-hoc de la tabla es muy onu-SECO. Gracias!!!
    • Estoy tratando de poner esto en un manage.py secuencia de comandos, pero sigo recibiendo este críptico uno: NameError: Can't invoke function 'create_table', as the proxy object has not yet been established for the Alembic 'Operations' class. Try placing this code inside a callable. ¿tiene usted alguna idea de lo que esto significa?
    • Yo recomendaría la creación de una nueva pregunta en StackOverflow para esto.
    • usted necesita para ejecutar esta secuencia de comandos a través de otro script ‘manage.py» como este: python manage.py db upgrade ejemplo aquí: blog.miguelgrinberg.com/post/…

  2. 24

    De las migraciones debe ser limitado a cambios en el esquema sólo, y no sólo eso, es importante que cuando una migración hacia arriba o hacia abajo se aplica que los datos existentes en la base de datos antes de que se conserva tanto como sea posible. La inserción de datos de semillas como parte de una migración puede lío de datos pre-existentes.

    Como la mayoría de las cosas con el Frasco, se puede implementar de muchas maneras. Añadir un nuevo comando para Matraz de secuencia de Comandos es una buena manera de hacer esto, en mi opinión. Por ejemplo:

    @manager.command
    def seed():
        "Add seed data to the database."
        db.session.add(...)
        db.session.commit()

    Así que, a continuación, ejecutar:

    python manager.py seed
    • Por qué iba alguien a voto que?
    • Lo siento, un poco de gatillo fácil, pero estoy en desacuerdo con esto: «las Migraciones debe ser limitado a cambios en el esquema sólo». La respuesta es bien si se quiere hacer que la semilla de datos de un comando independiente. Pero, por ejemplo, si desea instalar cosas como «roles» (administrador, usuario, etc.), luego de que está perfectamente bien para hacer en la migración. De hecho, la adición de un comando en lugar de ponerlo en la migración significa que ahora deben, como parte de su instalación, hacer dos pasos (migración, la carga de datos) en lugar de uno. Dependiendo de su entorno, ir de cualquier manera. Pero por favor, no digas que las migraciones debe ser «limitado».
    • Aceptar Marca así que, ¿cómo iba a hacerlo como parte de la por encima de la migración?
    • mi razonamiento para mantener separados los datos de migraciones, es que los cambios de esquema tiene una muy bien definida de la historia independiente de la aplicación, pero los datos no, ya que la aplicación tiene acceso a ella y la puede cambiar. Supongo que para una tabla de solo lectura esto no sería un problema, pero esto es algo que yo no recomendaría como una práctica general.
    • Supongo que todo se reduce a qué tipo de datos estamos hablando. Sin embargo, como he dicho en mi respuesta, mi definición de «datos de semillas» son los datos necesarios para ejecutar la aplicación (constantes, administración de usuarios, etc). Por lo tanto, los datos necesarios se DEBE tener bien definido la historia que está en el paso con el esquema, como expliqué en mi respuesta.
    • Sí acordado, yo quiero agregar una lista de las monedas a una aplicación, que es la única manera de rellenar las monedas de la tabla. Parece totalmente natural para atar este con la creación de dicha mesa.
    • A pesar de que tengo un montón de upvotes en su respuesta, este consejo podría fácilmente ser líder de un montón de gente por el camino equivocado. Ciertos datos adecuadamente parte de una migración (el tipo de datos de Hildreth describe en su respuesta). Su argumento ignora la existencia de ese tipo de datos. Con respeto, por favor, edite su respuesta a dejar en claro qué tipo de datos se están hablando y la eliminación de su negro/blanco afirmación de que la semilla de datos no debe ser parte de las migraciones. (y mi profundo agradecimiento por su trabajo en el matraz de la migración en general)
    • de acuerdo.
    • Las migraciones deben no limitarse a cambios en el esquema sólo. Evolutiva Diseño de Base de datos.

  3. 5

    MarkHildreth ha proporcionado una excelente explicación de cómo alambique puede manejar esto. Sin embargo, el OP fue específicamente acerca de cómo modificar un frasco de migración de la secuencia de comandos de migración. Voy a publicar una respuesta a la de abajo para salvar a la gente el tiempo de tener que buscar en alambique en todo.

    Advertencia
    Miguel la respuesta es correcta con respecto a la normalidad de la base de datos de información. Es decir, uno debe seguir su consejo y absolutamente no utilizar este enfoque para rellenar una base de datos con «normal» de las filas. Este enfoque es específicamente para la base de datos de las filas que son necesarios para el funcionamiento de la aplicación, un tipo de datos que creo que como «semilla» de datos.

    OP script modificado para semilla de datos:

    """empty message
    
    Revision ID: 384cfaaaa0be
    Revises: None
    Create Date: 2013-10-11 16:36:34.696069
    
    """
    
    # revision identifiers, used by Alembic.
    revision = '384cfaaaa0be'
    down_revision = None
    
    from alembic import op
    import sqlalchemy as sa
    
    
    def upgrade():
        ### commands auto generated by Alembic - please adjust! ###
        list_type_table = op.create_table('list_type',
        sa.Column('id', sa.Integer(), nullable=False),
        sa.Column('name', sa.String(length=80), nullable=False),
        sa.PrimaryKeyConstraint('id'),
        sa.UniqueConstraint('name')
        )
        op.create_table('job',
        sa.Column('id', sa.Integer(), nullable=False),
        sa.Column('list_type_id', sa.Integer(), nullable=False),
        sa.Column('record_count', sa.Integer(), nullable=False),
        sa.Column('status', sa.Integer(), nullable=False),
        sa.Column('sf_job_id', sa.Integer(), nullable=False),
        sa.Column('created_at', sa.DateTime(), nullable=False),
        sa.Column('compressed_csv', sa.LargeBinary(), nullable=True),
        sa.ForeignKeyConstraint(['list_type_id'], ['list_type.id'], ),
        sa.PrimaryKeyConstraint('id')
        )
        ### end Alembic commands ###
    
    
        op.bulk_insert(
            list_type_table,
            [
                {'name':'best list'},
                {'name': 'bester list'}
            ]
        )
    
    
    def downgrade():
        ### commands auto generated by Alembic - please adjust! ###
        op.drop_table('job')
        op.drop_table('list_type')
        ### end Alembic commands ###

    Contexto para los nuevos en el flask_migrate

    Matraz de migrar genera scripts de migración en migrations/versions. Estas secuencias de comandos se ejecutan en el orden en una base de datos con el fin de llevarlo a la última versión. El OP incluye un ejemplo de uno de estos auto-generado scripts de migración. Con el fin de agregar semillas de datos, se debe modificar manualmente el correspondiente auto-generado el archivo de migración. El código que he publicado anteriormente es un ejemplo de eso.

    Lo que ha cambiado?

    Muy poco. Usted se dará cuenta de que en el nuevo archivo de yo soy el almacenamiento de la tabla que devuelve create_table para list_type en una variable llamada list_type_table. Luego nos operar sobre la mesa el uso de op.bulk_insert para crear un par de filas de ejemplo.

  4. 2

    También puede utilizar Python farsante biblioteca que puede ser un poco más rápido, ya que no es necesario venir con los datos usted mismo. Una forma de configurar sería poner un método en una clase que quería para generar los datos como se muestra a continuación.

    from extensions import bcrypt, db
    
    class User(db.Model):
        # this config is used by sqlalchemy to store model data in the database
        __tablename__ = 'users'
    
        id = db.Column(db.Integer, primary_key=True)
        name = db.Column(db.String(150))
        email = db.Column(db.String(100), unique=True)
        password = db.Column(db.String(100))
    
        def __init__(self, name, email, password, fav_movie):
            self.name = name
            self.email = email
            self.password = password
    
        @classmethod
        def seed(cls, fake):
            user = User(
                name = fake.name(),
                email = fake.email(),
                password = cls.encrypt_password(fake.password()),
            )
            user.save()
    
        @staticmethod
        def encrypt_password(password):
            return bcrypt.generate_password_hash(password).decode('utf-8')
    
        def save(self):
            db.session.add(self)
            db.session.commit()

    Y, a continuación, aplicar un método que llama a la semilla método que podría ser algo como esto:

    from faker import Faker
    from users.models import User
    
    fake = Faker()
        for _ in range(100):
            User.seed(fake)

Dejar respuesta

Please enter your comment!
Please enter your name here