1
votes

I have a little app I'm using to scrape data from online articles.

Currently, I am trying to figure out how to edit the migration scripts from Flask-migrate so I don't have to delete all the migration data and the SQlite database then re-scrape the data every time I edit the schema!

Of course, when I edit the models, delete everything, re-init, and scrape the data again, the database adapts just fine. But when I try to edit it manually, it runs the upgrade but I see no change in the data.

Examples below...

Migration script:

def upgrade():
    # ### commands auto generated by Alembic - please adjust! ###
    op.add_column('article', sa.Column('example', sa.DateTime(), nullable=True))
    # ### end Alembic commands ###

I've also tried adding this to populate it with data, but of course it hasn't been successful since the column doesn't exist (correct me if I'm missing something):

from datetime import datetime

...
def upgrade():
...
     op.execute("UPDATE article SET example = datetime.utcnow()")  # (new scraped data would get a new
                                                                   # timestamp but this would be fine
                                                                   # for old data for my purposes)

I'm pretty new to SQL and its frameworks but I did both of these with and without indexing just in case that matters. The upgrade appears to run fine in either case:

(venv) Files\app> flask db upgrade
INFO  [alembic.runtime.migration] Context impl SQLiteImpl.
INFO  [alembic.runtime.migration] Will assume non-transactional DDL.
INFO  [alembic.runtime.migration] Running upgrade 2177b1c9ee45 -> 6c61158ea270, empty message
(venv) Files\app>

But when I query on the command line with Flask-SQLAlchemy:

>>> from app.models import Article
>>> arts = Article.query.all()
>>> arts[0]
<Example Article: "Title" (2018)>       ##(Valid __repr__ of the model)
>>> arts[0].time_added
datetime.datetime(2019, 12, 25, 9, 23, 43, 331296)    ##(achieved this by deleting and restarting db from scratch)
>>> arts[0].example
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
AttributeError: 'Article' object has no attribute 'example'
>>>

I don't know what is wrong with the upgrade, but something must be, because the downgrade gives me an error:

INFO  [alembic.runtime.migration] Running downgrade 6c61158ea270 -> 2177b1c9ee45, empty message
Traceback (most recent call last):
  File "c:\desktop\projects\site\app\venv\lib\site-packages\sqlalchemy\engine\base.py", line 1246, in _execute_context
    cursor, statement, parameters, context
...
  File "c:\desktop\projects\site\app\venv\lib\site-packages\sqlalchemy\engine\default.py", line 581, in do_execute
    cursor.execute(statement, parameters)
sqlalchemy.exc.OperationalError: (sqlite3.OperationalError) near "DROP": syntax error
[SQL: ALTER TABLE article DROP COLUMN example]

The downgrade function is pretty simple so the column must never have been created:

def downgrade():
    # ### commands auto generated by Alembic - please adjust! ###
    op.drop_column('article', 'example')
    # ### end Alembic commands ###

Can someone please help me understand how to use Alembic in Flask-migrate migration scripts to customize database upgrades?

Alternatively, is there something wrong with the way I'm using op.execute("UPDATE table SET column = values")?

Thanks!

Edit:

Forgot to mention, here is the line from the model:

    example = db.Column(db.DateTime, default=datetime.utcnow())

Also, changing the upgrade and downgrade manually wasn't effective:

def upgrade():
    # ### commands auto generated by Alembic - please adjust! ###
    # ### end Alembic commands ###
    op.execute('UPDATE article SET example = datetime.utcnow()')

def downgrade():
    # ### commands auto generated by Alembic - please adjust! ###
    op.execute('UPDATE article SET example=Null')
    # ### end Alembic commands ###

ADDITIONAL EDIT:

from app.models import set_time ### (set_time is just datetime.utcnow() but I
                                ### thought it might work to import it
                                ### from elsewhere -- it didn't

def upgrade():
    # ### commands auto generated by Alembic - please adjust! ###
    # ### end Alembic commands ###
    op.execute("UPDATE 'article' SET 'example' = VALUES (?)", (set_time))  #*

### (*Importing set_time from elsewhere 

def downgrade():
    # ### commands auto generated by Alembic - please adjust! ###
    op.execute("UPDATE 'article' SET 'example'=Null")
    # ### end Alembic commands ###

I keep getting this error:

TypeError: execution_options() argument after ** must be a mapping, not str

It appears there must be a format for passing a Python variable into an Alembic SQL query that I am unaware of. Searching now.

ANOTHER EDIT (Sorry for so many):

The following just tells me that article (the table name) is undefined. Help with syntax?

connection = op.get_bind()

def upgrade():
    # ### commands auto generated by Alembic - please adjust! ###
    # ### end Alembic commands ###
    connection.execute(
        article.update().values({"example": set_time})
    )

Any ideas are greatly appreciated!!

-Pete

1
Using just alembic and sqlalchemy (no flask stuff), you would add the column definition to the Python model definition, generate the upgrade script with alembic, and then run alembic upgrade head. Are you adding the column definition to the Python model?snakecharmerb
I am, but I have not heard of alembic upgrade head. Do I enter this into the command line?Peter Charland
@snakecharberb OK, I see the issue--I can't actually run alembic commands in Flask-migrate. Flask migrate would upgrade through alembic when I run flask db migrate (creates the alembic migration script) and flask db upgrade (uses the script to update the database). (Which I did, so it should be working... if you see my edits above, I believe at this point I just need to figure out the syntax to use in alembic for passing a datetime value into the SQL.)Peter Charland

1 Answers

1
votes

if migrations is already settled that you updated with no problem but the column has not been created i would have personally just manually added it to the sqlite database so it will run just fine with no issues, these errors sometimes happens with sqlite.