{"id":12140,"date":"2020-02-13T06:31:15","date_gmt":"2020-02-13T11:31:15","guid":{"rendered":"https:\/\/qxf2.com\/blog\/?p=12140"},"modified":"2020-02-13T06:31:15","modified_gmt":"2020-02-13T11:31:15","slug":"database-migration-flask-migrate","status":"publish","type":"post","link":"https:\/\/qxf2.com\/blog\/database-migration-flask-migrate\/","title":{"rendered":"Getting started with Database Schema Migration (Flask-Migrate)"},"content":{"rendered":"<h3>Why this post?<\/h3>\n<p>This post helps you to quickly setup and get started with Database schema Migration using Flask-Migrate.<br \/>\nFor example if you already had created a database that contains some tables and now if you add a new column to one of the table and then if you want to revert it to the previous state, it\u2019s difficult to do so. This is when the database migration comes in handy, as it keeps a track of database schema changes for you, and one can easily traverse back to an older or newer version of the database.<\/p>\n<hr \/>\n<h3>What is Database Schema migration?<\/h3>\n<p><b>Database schema migration<\/b> refers to the managing of incremental, reversible changes and version control to the relational database schema. A schema migration is performed on a database whenever it is necessary to update or revert that database&#8217;s schema to some newer or older version.<\/p>\n<hr \/>\n<h3>What is Flask-Migrate?<\/h3>\n<p><a href=\"https:\/\/flask-migrate.readthedocs.io\/en\/latest\/\">Flask-Migrate<\/a> is a wrapper around Alembic, which handles SQLAlchemy database migrations for Flask applications. To be explicit, the use of the Flask-Migrate module is intended for Flask applications that are using SQLAlchemy.<\/p>\n<p><a class=\"reference external\" href=\"https:\/\/alembic.sqlalchemy.org\/\">Alembic<\/a> is a lightweight database migration tool for usage with the SQLAlchemy Database Toolkit for Python.<\/p>\n<p><a href=\"https:\/\/www.sqlalchemy.org\/\">SQLAlchemy<\/a> is the Python SQL toolkit and Object Relational Mapper that gives application developers the full power and flexibility of SQL.<\/p>\n<p>&nbsp;<\/p>\n<hr \/>\n<h3>Setup of Flask-Migrate<\/h3>\n<p>Install Flask with<code>pip<\/code>:<\/p>\n<pre lang=\"python\">pip install Flask\r\n<\/pre>\n<p>Install Flask-Migrate with <code>pip<\/code>:<\/p>\n<pre lang=\"python\">pip install Flask-Migrate\r\n<\/pre>\n<p>Install Flask-Script with<code>pip<\/code>:<\/p>\n<pre lang=\"python\">pip install Flask-Script\r\n<\/pre>\n<p>Install flask-sqlalchemy with<code>pip<\/code>:<\/p>\n<pre lang=\"python\">pip install flask-sqlalchemy\r\n<\/pre>\n<hr \/>\n<p>This is an example application that handles database migrations through Flask-Migrate:<\/p>\n<pre lang=\"python\">from flask import Flask\r\nfrom flask_sqlalchemy import SQLAlchemy\r\nfrom flask_script import Manager\r\nfrom flask_migrate import Migrate, MigrateCommand\r\n\r\napp = Flask(__name__)\r\napp.config[\"SQLALCHEMY_DATABASE_URI\"] = \"sqlite:\/\/\/data.db\"\r\ndb = SQLAlchemy(app)\r\nmigrate=Migrate(app,db) #Initializing migrate.\r\nmanager = Manager(app)\r\nmanager.add_command('db',MigrateCommand)\r\n\r\nclass Employee(db.Model):\r\n    id = db.Column(db.Integer, primary_key=True)\r\n    name = db.Column(db.String, unique=True, nullable=False)\r\n    email = db.Column(db.String, unique=True, nullable=False)\r\n\r\nclass Company(db.Model):\r\n    cid = db.Column(db.Integer, primary_key=True)\r\n    cname = db.Column(db.String, unique=True, nullable=False)\r\n    clocation = db.Column(db.String, unique=False, nullable=False)\r\n\r\nif __name__ == \"__main__\":\r\n     manager.run() \r\n<\/pre>\n<p><strong>Note:- Calling manager.run() prepares your Manager instance to receive input from the command line.<\/strong><\/p>\n<hr \/>\n<p>We need to run the code using python. The code has been saved as\u00a0<strong>migrate_example.py <\/strong>file.<\/p>\n<h3>We will use some basic commands:-<\/h3>\n<ul>\n<li>init<\/li>\n<li>migrate<\/li>\n<li>upgrade or downgrade<\/li>\n<\/ul>\n<hr \/>\n<p><strong>1. We need to use\u00a0<em>init\u00a0<\/em>command<\/strong><\/p>\n<pre lang=\"python\">python migrate_example.py db init<\/pre>\n<ul>\n<li>This creates a migration repository.<\/li>\n<li>This will add a\u00a0<cite>migrations<\/cite>\u00a0folder to your application. The contents of this folder need to be added to version control along with your other source files and see the Migrations folder in your project.<br \/>\nIn the\u00a0<em>migrations<\/em> folder, you will see\u00a0<em>versions<\/em> folder, <em>alembic.ini<\/em> (configuration file) and some other files.<\/li>\n<li>After running the command you should get something like this.<\/li>\n<\/ul>\n<p><a href=\"https:\/\/qxf2.com\/blog\/wp-content\/uploads\/2020\/01\/init_command.jpg\" data-rel=\"lightbox-image-0\" data-rl_title=\"\" data-rl_caption=\"\" title=\"\"><img loading=\"lazy\" decoding=\"async\" class=\"wp-image-12175 alignleft\" src=\"https:\/\/qxf2.com\/blog\/wp-content\/uploads\/2020\/01\/init_command-300x76.jpg\" alt=\"init_command\" width=\"869\" height=\"220\" srcset=\"https:\/\/qxf2.com\/blog\/wp-content\/uploads\/2020\/01\/init_command-300x76.jpg 300w, https:\/\/qxf2.com\/blog\/wp-content\/uploads\/2020\/01\/init_command-768x194.jpg 768w, https:\/\/qxf2.com\/blog\/wp-content\/uploads\/2020\/01\/init_command-1024x259.jpg 1024w, https:\/\/qxf2.com\/blog\/wp-content\/uploads\/2020\/01\/init_command.jpg 1647w\" sizes=\"auto, (max-width: 869px) 100vw, 869px\" \/><\/a><\/p>\n<hr \/>\n<p><strong>2. We need to use migrate command:<\/strong><\/p>\n<pre lang=\"python\">python migrate_example.py db migrate<\/pre>\n<ul>\n<li>This will create\u00a0<em>data.db<\/em> file. But the tables will not be added yet into the database.<\/li>\n<li>Alembic currently cannot detect table name changes, column name changes, or anonymously named constraints.<\/li>\n<li>Alembic can detect newly added tables and columns only for now.<\/li>\n<\/ul>\n<p><a href=\"https:\/\/qxf2.com\/blog\/wp-content\/uploads\/2020\/01\/migrate_command.jpg\" data-rel=\"lightbox-image-1\" data-rl_title=\"\" data-rl_caption=\"\" title=\"\"><img loading=\"lazy\" decoding=\"async\" class=\"alignnone wp-image-12191\" src=\"https:\/\/qxf2.com\/blog\/wp-content\/uploads\/2020\/01\/migrate_command-300x51.jpg\" alt=\"Table detection\" width=\"806\" height=\"137\" srcset=\"https:\/\/qxf2.com\/blog\/wp-content\/uploads\/2020\/01\/migrate_command-300x51.jpg 300w, https:\/\/qxf2.com\/blog\/wp-content\/uploads\/2020\/01\/migrate_command-768x131.jpg 768w, https:\/\/qxf2.com\/blog\/wp-content\/uploads\/2020\/01\/migrate_command-1024x175.jpg 1024w, https:\/\/qxf2.com\/blog\/wp-content\/uploads\/2020\/01\/migrate_command.jpg 1905w\" sizes=\"auto, (max-width: 806px) 100vw, 806px\" \/><\/a><\/p>\n<ul>\n<li style=\"text-align: left;\">After running the migrate command, it generates a version file, in <em>migrations\/versions\/&#8217;some version name.py&#8217; .\u00a0\u00a0<\/em>It should look something like this.<\/li>\n<li style=\"text-align: left;\">It&#8217;s good to check the changes match your needs.<br \/>\n<a href=\"https:\/\/qxf2.com\/blog\/wp-content\/uploads\/2020\/01\/version_code_1.jpg\" data-rel=\"lightbox-image-2\" data-rl_title=\"\" data-rl_caption=\"\" title=\"\"><img loading=\"lazy\" decoding=\"async\" class=\"wp-image-12192 alignleft\" src=\"https:\/\/qxf2.com\/blog\/wp-content\/uploads\/2020\/01\/version_code_1-274x300.jpg\" alt=\"alembic version\" width=\"849\" height=\"929\" srcset=\"https:\/\/qxf2.com\/blog\/wp-content\/uploads\/2020\/01\/version_code_1-274x300.jpg 274w, https:\/\/qxf2.com\/blog\/wp-content\/uploads\/2020\/01\/version_code_1-768x840.jpg 768w, https:\/\/qxf2.com\/blog\/wp-content\/uploads\/2020\/01\/version_code_1.jpg 853w\" sizes=\"auto, (max-width: 849px) 100vw, 849px\" \/><\/a><\/li>\n<li>In this, we can see that if we <em>upgrade<\/em> the script, it will add two tables(company and employee), and if we <em>downgrade<\/em> the script, it will drop the tables.<\/li>\n<\/ul>\n<hr \/>\n<p><strong>3. We need to use the\u00a0<em>upgrade<\/em> command.<\/strong><\/p>\n<pre lang=\"python\"> python migrate_example.py db upgrade<\/pre>\n<ul>\n<li>After using the\u00a0<em>upgrade<\/em> command we can\u00a0<span>apply the migration to the database. It should look something like this.<\/span><a href=\"https:\/\/qxf2.com\/blog\/wp-content\/uploads\/2020\/01\/upgrade_command.jpg\" data-rel=\"lightbox-image-3\" data-rl_title=\"\" data-rl_caption=\"\" title=\"\"><img loading=\"lazy\" decoding=\"async\" class=\"alignnone wp-image-12206\" src=\"https:\/\/qxf2.com\/blog\/wp-content\/uploads\/2020\/01\/upgrade_command-300x39.jpg\" alt=\"Add the two tables\" width=\"854\" height=\"111\" srcset=\"https:\/\/qxf2.com\/blog\/wp-content\/uploads\/2020\/01\/upgrade_command-300x39.jpg 300w, https:\/\/qxf2.com\/blog\/wp-content\/uploads\/2020\/01\/upgrade_command-768x99.jpg 768w, https:\/\/qxf2.com\/blog\/wp-content\/uploads\/2020\/01\/upgrade_command-1024x132.jpg 1024w, https:\/\/qxf2.com\/blog\/wp-content\/uploads\/2020\/01\/upgrade_command.jpg 1920w\" sizes=\"auto, (max-width: 854px) 100vw, 854px\" \/><\/a><\/li>\n<li>Then each time the database models change, repeat the<em> migrate<\/em> and <em>upgrade<\/em> commands.<\/li>\n<li>We can see that tables have been added into the Database.<a href=\"https:\/\/qxf2.com\/blog\/wp-content\/uploads\/2020\/01\/tables.jpg\" data-rel=\"lightbox-image-4\" data-rl_title=\"\" data-rl_caption=\"\" title=\"\"><img loading=\"lazy\" decoding=\"async\" class=\"alignnone wp-image-12208\" src=\"https:\/\/qxf2.com\/blog\/wp-content\/uploads\/2020\/01\/tables-300x82.jpg\" alt=\"DB \" width=\"863\" height=\"236\" srcset=\"https:\/\/qxf2.com\/blog\/wp-content\/uploads\/2020\/01\/tables-300x82.jpg 300w, https:\/\/qxf2.com\/blog\/wp-content\/uploads\/2020\/01\/tables.jpg 687w\" sizes=\"auto, (max-width: 863px) 100vw, 863px\" \/><\/a><\/li>\n<\/ul>\n<hr \/>\n<p><strong>NOTE:-<\/strong> To add or drop columns into a table after the first migration, there is a small workaround which one has to follow, since sqlite does not support DROP column.<\/p>\n<p><strong>1.To add a new column into a table.<\/strong><\/p>\n<ul>\n<li>In this example we will add a new column called age in the employee table.<\/li>\n<li style=\"list-style-type: none;\">\n<pre lang=\"python\">age= db.Column(db.Integer, unique=False, nullable=True)<\/pre>\n<\/li>\n<li>It should look something like this&#8230;<\/li>\n<li style=\"list-style-type: none;\"><a href=\"https:\/\/qxf2.com\/blog\/wp-content\/uploads\/2020\/01\/add_table.jpg\" data-rel=\"lightbox-image-5\" data-rl_title=\"\" data-rl_caption=\"\" title=\"\"><img loading=\"lazy\" decoding=\"async\" class=\"alignnone wp-image-12212\" src=\"https:\/\/qxf2.com\/blog\/wp-content\/uploads\/2020\/01\/add_table-300x44.jpg\" alt=\"Adding new column\" width=\"791\" height=\"116\" srcset=\"https:\/\/qxf2.com\/blog\/wp-content\/uploads\/2020\/01\/add_table-300x44.jpg 300w, https:\/\/qxf2.com\/blog\/wp-content\/uploads\/2020\/01\/add_table-768x114.jpg 768w, https:\/\/qxf2.com\/blog\/wp-content\/uploads\/2020\/01\/add_table.jpg 981w\" sizes=\"auto, (max-width: 791px) 100vw, 791px\" \/><\/a><\/li>\n<li>After adding the code run the migrate command once, which will detect the change .<a href=\"https:\/\/qxf2.com\/blog\/wp-content\/uploads\/2020\/01\/add_table1.jpg\" data-rel=\"lightbox-image-6\" data-rl_title=\"\" data-rl_caption=\"\" title=\"\"><img loading=\"lazy\" decoding=\"async\" class=\"wp-image-12213 alignnone\" src=\"https:\/\/qxf2.com\/blog\/wp-content\/uploads\/2020\/01\/add_table1-300x46.jpg\" alt=\"Change detected\" width=\"824\" height=\"126\" srcset=\"https:\/\/qxf2.com\/blog\/wp-content\/uploads\/2020\/01\/add_table1-300x46.jpg 300w, https:\/\/qxf2.com\/blog\/wp-content\/uploads\/2020\/01\/add_table1-768x117.jpg 768w, https:\/\/qxf2.com\/blog\/wp-content\/uploads\/2020\/01\/add_table1-1024x156.jpg 1024w, https:\/\/qxf2.com\/blog\/wp-content\/uploads\/2020\/01\/add_table1.jpg 1920w\" sizes=\"auto, (max-width: 824px) 100vw, 824px\" \/><\/a><\/li>\n<li>Now open the recent <em>version file\u00a0<\/em> from \/Migrations\/versions\/&#8217;your_file.py&#8217;.<\/li>\n<li style=\"list-style-type: none;\"><a href=\"https:\/\/qxf2.com\/blog\/wp-content\/uploads\/2020\/01\/add_table2.jpg\" data-rel=\"lightbox-image-7\" data-rl_title=\"\" data-rl_caption=\"\" title=\"\"><img loading=\"lazy\" decoding=\"async\" class=\"alignnone wp-image-12214\" src=\"https:\/\/qxf2.com\/blog\/wp-content\/uploads\/2020\/01\/add_table2-300x245.jpg\" alt=\"Version file\" width=\"558\" height=\"456\" srcset=\"https:\/\/qxf2.com\/blog\/wp-content\/uploads\/2020\/01\/add_table2-300x245.jpg 300w, https:\/\/qxf2.com\/blog\/wp-content\/uploads\/2020\/01\/add_table2.jpg 767w\" sizes=\"auto, (max-width: 558px) 100vw, 558px\" \/><\/a><\/li>\n<li>Insert this code in the &#8220;def upgrade()&#8221;<\/li>\n<\/ul>\n<pre lang=\"python\">with op.batch_alter_table('employee') as batch_op:\r\n    batch_op.add_column( sa.Column('age', sa.Integer(), nullable=True))\r\n<\/pre>\n<ul>\n<li>Which should look something like this.<br \/>\n<a href=\"https:\/\/qxf2.com\/blog\/wp-content\/uploads\/2020\/01\/add_table3jpg.jpg\" data-rel=\"lightbox-image-8\" data-rl_title=\"\" data-rl_caption=\"\" title=\"\"><img loading=\"lazy\" decoding=\"async\" class=\"alignnone wp-image-12217\" src=\"https:\/\/qxf2.com\/blog\/wp-content\/uploads\/2020\/01\/add_table3jpg-300x51.jpg\" alt=\"Adding changes in version control file\" width=\"665\" height=\"113\" srcset=\"https:\/\/qxf2.com\/blog\/wp-content\/uploads\/2020\/01\/add_table3jpg-300x51.jpg 300w, https:\/\/qxf2.com\/blog\/wp-content\/uploads\/2020\/01\/add_table3jpg-768x130.jpg 768w, https:\/\/qxf2.com\/blog\/wp-content\/uploads\/2020\/01\/add_table3jpg.jpg 891w\" sizes=\"auto, (max-width: 665px) 100vw, 665px\" \/><\/a><\/li>\n<li>Run the\u00a0<em>upgrade command,\u00a0<\/em>which should add the column (age) into the table (employee).<\/li>\n<\/ul>\n<p><a href=\"https:\/\/qxf2.com\/blog\/wp-content\/uploads\/2020\/01\/add_table4.jpg\" data-rel=\"lightbox-image-9\" data-rl_title=\"\" data-rl_caption=\"\" title=\"\"><img loading=\"lazy\" decoding=\"async\" class=\"alignnone wp-image-12218\" src=\"https:\/\/qxf2.com\/blog\/wp-content\/uploads\/2020\/01\/add_table4-300x39.jpg\" alt=\"changes detected\" width=\"738\" height=\"96\" srcset=\"https:\/\/qxf2.com\/blog\/wp-content\/uploads\/2020\/01\/add_table4-300x39.jpg 300w, https:\/\/qxf2.com\/blog\/wp-content\/uploads\/2020\/01\/add_table4-768x99.jpg 768w, https:\/\/qxf2.com\/blog\/wp-content\/uploads\/2020\/01\/add_table4-1024x132.jpg 1024w, https:\/\/qxf2.com\/blog\/wp-content\/uploads\/2020\/01\/add_table4.jpg 1905w\" sizes=\"auto, (max-width: 738px) 100vw, 738px\" \/><\/a><\/p>\n<ul>\n<li>If we look in the schema of the employee table, we can see that\u00a0 column (age) has been added.<\/li>\n<\/ul>\n<p><a href=\"https:\/\/qxf2.com\/blog\/wp-content\/uploads\/2020\/01\/add_table5.jpg\" data-rel=\"lightbox-image-10\" data-rl_title=\"\" data-rl_caption=\"\" title=\"\"><img loading=\"lazy\" decoding=\"async\" class=\"alignnone wp-image-12219\" src=\"https:\/\/qxf2.com\/blog\/wp-content\/uploads\/2020\/01\/add_table5-300x125.jpg\" alt=\"Database changes\" width=\"744\" height=\"310\" srcset=\"https:\/\/qxf2.com\/blog\/wp-content\/uploads\/2020\/01\/add_table5-300x125.jpg 300w, https:\/\/qxf2.com\/blog\/wp-content\/uploads\/2020\/01\/add_table5.jpg 710w\" sizes=\"auto, (max-width: 744px) 100vw, 744px\" \/><\/a><\/p>\n<p><strong>2. To drop column from a table.<\/strong><\/p>\n<ul>\n<li>In this example we will drop\u00a0 column (email) from the table (employee).<\/li>\n<li>Just delete this code\n<pre lang=\"python\">email\u00a0=\u00a0db.Column(db.String,\u00a0unique=False,\u00a0nullable=False)<\/pre>\n<\/li>\n<li>Save the file and run the\u00a0<em>migration<\/em> <em>command<\/em>, It<em>\u00a0<\/em>should detect the change.<a href=\"https:\/\/qxf2.com\/blog\/wp-content\/uploads\/2020\/01\/drop_table.jpg\" data-rel=\"lightbox-image-11\" data-rl_title=\"\" data-rl_caption=\"\" title=\"\"><img loading=\"lazy\" decoding=\"async\" class=\"alignnone wp-image-12224\" src=\"https:\/\/qxf2.com\/blog\/wp-content\/uploads\/2020\/01\/drop_table-300x43.jpg\" alt=\"dropping a column\" width=\"809\" height=\"116\" srcset=\"https:\/\/qxf2.com\/blog\/wp-content\/uploads\/2020\/01\/drop_table-300x43.jpg 300w, https:\/\/qxf2.com\/blog\/wp-content\/uploads\/2020\/01\/drop_table-768x110.jpg 768w, https:\/\/qxf2.com\/blog\/wp-content\/uploads\/2020\/01\/drop_table-1024x147.jpg 1024w, https:\/\/qxf2.com\/blog\/wp-content\/uploads\/2020\/01\/drop_table.jpg 1900w\" sizes=\"auto, (max-width: 809px) 100vw, 809px\" \/><\/a><\/li>\n<li>Now open the recent <em>version file\u00a0<\/em> from \/Migrations\/versions\/&#8217;your_file.py&#8217;.<\/li>\n<li>Insert this code in the &#8220;def upgrade()&#8221;<\/li>\n<\/ul>\n<pre lang=\"python\">with op.batch_alter_table('employee') as batch_op:   \r\n    batch_op.drop_column('email')\r\n<\/pre>\n<ul>\n<li>Save the file and run the\u00a0<em>upgrade command<\/em> , to DROP the column(email) in table(employee).<br \/>\n<a href=\"https:\/\/qxf2.com\/blog\/wp-content\/uploads\/2020\/01\/drop_table2.jpg\" data-rel=\"lightbox-image-12\" data-rl_title=\"\" data-rl_caption=\"\" title=\"\"><img loading=\"lazy\" decoding=\"async\" class=\"alignnone wp-image-12225\" src=\"https:\/\/qxf2.com\/blog\/wp-content\/uploads\/2020\/01\/drop_table2-300x38.jpg\" alt=\"DROP column email\" width=\"813\" height=\"103\" srcset=\"https:\/\/qxf2.com\/blog\/wp-content\/uploads\/2020\/01\/drop_table2-300x38.jpg 300w, https:\/\/qxf2.com\/blog\/wp-content\/uploads\/2020\/01\/drop_table2-768x97.jpg 768w, https:\/\/qxf2.com\/blog\/wp-content\/uploads\/2020\/01\/drop_table2-1024x130.jpg 1024w, https:\/\/qxf2.com\/blog\/wp-content\/uploads\/2020\/01\/drop_table2.jpg 1920w\" sizes=\"auto, (max-width: 813px) 100vw, 813px\" \/><\/a><\/li>\n<li>If we look in the schema of the employee table, we can see that\u00a0 column (email) has been dropped.<a href=\"https:\/\/qxf2.com\/blog\/wp-content\/uploads\/2020\/01\/drop_table3.jpg\" data-rel=\"lightbox-image-13\" data-rl_title=\"\" data-rl_caption=\"\" title=\"\"><img loading=\"lazy\" decoding=\"async\" class=\"alignnone wp-image-12226\" src=\"https:\/\/qxf2.com\/blog\/wp-content\/uploads\/2020\/01\/drop_table3-300x92.jpg\" alt=\"DB changes\" width=\"783\" height=\"240\" srcset=\"https:\/\/qxf2.com\/blog\/wp-content\/uploads\/2020\/01\/drop_table3-300x92.jpg 300w, https:\/\/qxf2.com\/blog\/wp-content\/uploads\/2020\/01\/drop_table3.jpg 523w\" sizes=\"auto, (max-width: 783px) 100vw, 783px\" \/><\/a><br \/>\n<hr \/>\n<p>I hope you have liked the blog. There are many more useful commands that can be used. This post captures the most commonly used commands in our experience.<\/p>\n<hr \/>\n<h4>Reference:-<\/h4>\n<p>Flask-Migrate:-<a href=\"https:\/\/flask-migrate.readthedocs.io\/en\/stable\/\">\u00a0https:\/\/flask-migrate.readthedocs.io\/en\/stable\/<\/a><br \/>\nAlembic Documentation:-\u00a0<a href=\"https:\/\/alembic.sqlalchemy.org\/en\/latest\/\">https:\/\/alembic.sqlalchemy.org\/en\/latest\/<\/a><\/p>\n<hr \/>\n<\/li>\n<\/ul>\n","protected":false},"excerpt":{"rendered":"<p>Why this post? This post helps you to quickly setup and get started with Database schema Migration using Flask-Migrate. For example if you already had created a database that contains some tables and now if you add a new column to one of the table and then if you want to revert it to the previous state, it\u2019s difficult to [&hellip;]<\/p>\n","protected":false},"author":30,"featured_media":0,"comment_status":"open","ping_status":"open","sticky":false,"template":"","format":"standard","meta":{"footnotes":""},"categories":[186,172,214],"tags":[219,217,215,216,218,220,221],"class_list":["post-12140","post","type-post","status-publish","format-standard","hentry","category-database","category-flask","category-sql","tag-alembic","tag-database-migration","tag-flask","tag-flask-migrate","tag-schema-migration","tag-sqlalchemy","tag-sqlite"],"_links":{"self":[{"href":"https:\/\/qxf2.com\/blog\/wp-json\/wp\/v2\/posts\/12140","targetHints":{"allow":["GET"]}}],"collection":[{"href":"https:\/\/qxf2.com\/blog\/wp-json\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/qxf2.com\/blog\/wp-json\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"https:\/\/qxf2.com\/blog\/wp-json\/wp\/v2\/users\/30"}],"replies":[{"embeddable":true,"href":"https:\/\/qxf2.com\/blog\/wp-json\/wp\/v2\/comments?post=12140"}],"version-history":[{"count":63,"href":"https:\/\/qxf2.com\/blog\/wp-json\/wp\/v2\/posts\/12140\/revisions"}],"predecessor-version":[{"id":12391,"href":"https:\/\/qxf2.com\/blog\/wp-json\/wp\/v2\/posts\/12140\/revisions\/12391"}],"wp:attachment":[{"href":"https:\/\/qxf2.com\/blog\/wp-json\/wp\/v2\/media?parent=12140"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/qxf2.com\/blog\/wp-json\/wp\/v2\/categories?post=12140"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/qxf2.com\/blog\/wp-json\/wp\/v2\/tags?post=12140"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}