Intro
SQLite is a great RDBMS that often comes “pre-installed” with many applications. It’s main selling point is that the entire database is stored in a single file on the disk, making SQLite very fast and efficient, without losing any core functionality that many people come to expect from mainstream database management systems. Unfortunately, while SQLite’s simple self-contained structure makes it great for the development and test phases of applications, it does not scale well when scaling to a multi-user application. There is no concept of user-management, where multiple clients need to access and use the same database. Changes are made through direct calls to the file holding the database.
This is where MySQL comes in–it is the most popular large-scale database management system on the web, with lots of third-party support. It comes as a standalone database server, so applications talk to the MySQL daemon process to access the database itself, unlike SQLite which has a separate file corresponding to a database in each application. MySQL makes up for the shortcomings in SQLite by being feature-rich, scalable, and extremely powerfull, being able to handle a lot of data at once.
This tutorial will explain how to migrate a Django application running SQLite to MySQL, from the ground up.
Prerequisites/Assumptions:
- You are using OS X
- Your webapp is using Django 1.10.x with Python 2.7
- You have an existing database in SQLite
Step 1 (OS X): Downloading and Setting Up MySQL
You can skip this step if you already have MySQL set up on your computer.
First, we will download and set up MySQL using instructions here. I used the native package installer rather than the TAR. Make sure to save the temporary root password they give you! Once MySQL is installed, we will set up a launch daemon that allows MySQL to start up on system/terminal launch.
After you finish installing, double check that your server is running. On Mac OS, you can do this my going to System Preferences > MySQL. If not, you can turn it on through that menu.
Step 1 (Ubuntu): Downloading and Setting Up MySQL
You can skip this step if you already have MySQL set up on your computer or server.
If you are using Ubuntu, you can install using
$ sudo apt-get update
$ sudo apt-get install mysql-server
You will be prompted to set up a root password. Don’t forget it, as you will use it later! Then, run the security script to configure MySQL.
$ sudo mysql_secure_installation
To test MySQL is working, enter
systemctl status mysql.service
And you should see an output like
● mysql.service - MySQL Community Server
Loaded: loaded (/lib/systemd/system/mysql.service; enabled; vendor preset: en
Active: active (running) since Wed 2016-11-23 21:21:25 UTC; 30min ago
Main PID: 3754 (mysqld)
Tasks: 28
Memory: 142.3M
CPU: 1.994s
CGroup: /system.slice/mysql.service
└─3754 /usr/sbin/mysqld
If MySQL isn’t running, start it using
sudo systemctl mysql start
Step 2: Finshing Up Installation
You can skip this step if you already have MySQL set up on your computer.
Try running mysql
in terminal. You might get an error like “command not found”. If so, in terminal, configure your computer’s $PATH
so it recognizes mysql
as an exectutable:
$ export PATH=$PATH:/path/to/your/mysql/bin
For me, the command was was
$ export PATH=$PATH:/usr/local/mysql/bin
Check and make sure that your terminal recognizes mysql
now. You might still get a MySQL specific error, but that’s okay–we will fix that in the next steps.
Unfortunately, this fix only works temporarily. If you open a new terminal tab, you will have to do export PATH...
again. To allow mysql
to be a recognizable command every time, we will edit your computer’s bash profile. Open the bash profile using your preferred text editor:
$ vim ~/.bash_profile
In the file, copy-paste this:
# Set architecture flags
export ARCHFLAGS="-arch x86_64"
# Ensure user-installed binaries take precedence
export PATH=/usr/local/mysql/bin:$PATH
# Load .bashrc if it exists
test -f ~/.bashrc && source ~/.bashrc
The above code allows mysql
to be recognized every time. Save the file, restart terminal, and it should work.
Step 3: Creating MySQL Users
NOTE: in this tutorial, we are altering the root user because it is assumed you do not have any other local users on your MySQL server. If you do, change the usernames accordingly
Now we are ready to use MySQL! Enter
$ mysql -u root -p
Log in using your root password. If this is your first time logging in, use the password that you saved in step 1. Now, if you want to change the root password to something of your own preference, in the MySQL shell, enter
mysql> ALTER USER 'root'@'localhost' IDENTIFIED BY 'your_new_password';
Now, you have your root user set up and can log into it using your own password.
If you want to create your own user, enter
mysql> CREATE USER 'your_new_username'@'localhost' IDENTIFIED BY 'new_password';
mysql> GRANT ALL PRIVILEGES ON * . * TO 'your_new_username'@'localhost';
mysql> FLUSH PRIVILEGES;
the * . *
allows the user to have all access to the databases and tables in the server. For more fine-toothed assignment of priviledges, check out the MySQL documentation.
Step 4: Creating the Project Database
Then, to create the database for your project:
mysql> CREATE DATABASE your_project_name CHARACTER SET UTF8;
mysql> GRANT ALL PRIVILEGES ON your_project_name.* TO your_username@localhost;
mysql> FLUSH PRIVILEGES;
mysql> QUIT
Step 5: Changing Django App Settings
Now, in your terminal, navigate to the root directory of your Django application. Run
$ python manage.py dumpdata --natural-foreign --natural-primary -e contenttypes -e auth.Permission --indent 4 > datadump.json
This will create a dumpfile of the data stored in your SQLite database. the --natural-foreign
argument serializes foreign keys, since you are transitioning to a new database. The -e contenttypes -e auth.Permission
arguments exclude tables that would cause Django to throw an IntegrityError
. Then, install the relevent dependencies in your Python environment using pip
or conda
:
$ pip install MySQL-python
If you are using a virtual environment for Python, make sure you have pip
installed in your virtual environment before using the pip
commands above, or else the dependencies will be installed globally.
MySQL-python
is a self-contained driver that enables Python programs to interface with MySQL databases. mysql-connector-python
is another database connector option, created and maintained by Oracle. The difference between the two is that mysql-connector-python
is written in Python while MySQL-python
is written in C. I suggest to use MySQL-python
because it is faster with almost all SQL commands. The downside is that it is not compliant with Python 3.
Finally, in your settings.py
file in your app, change the DATABASES
section to the following:
DATABASES = {
'default': {
'ENGINE': 'django.db.backends.mysql',
'NAME': 'your_project_name',
'USER': 'root',
'PASSWORD': 'your_root_password',
'HOST': 'your_host_address',
'PORT': 'your_port',
}
}
A reminder that your_project_name
should be the same name as the database you created in MySQL in Step 2. your_password
is the same password that you use to log into MySQL. The HOST
and PORT
values can be optional if you are simply hosting your webapp locally, but if you are running MySQL in a virtual server or a separate server then fill in your host address and port accordingly.
A Note
If you are using this MySQL server on your local machine, it is fine to store your MySQL root password in plaintext, but for a production-stage web app, we will need to add additional precautions to make sure your MySQL password is secure. In fact, even if you are using MySQL on your local machine, it doesn’t hurt to take these precautions. We will set your password as an environmental variable, and access your password through the os
module.
To allow your own user account to have access to the password, we will edit our .bashrc
file, which sets system-wide configurations.
$ vim ~/.bashrc
At the bottom of the file, add
export mysql_pass="your_root_password"
This will set mysql_pass
as an environment variable on your machine permanently.
If we want to let all users have access to the environmental variable, we will add a *.sh
file to the /etc/profile.d
folder of the production server. Alternatively, you can change /etc/environment
but that is not recommended because it can only be accessed and changed by root
, but /etc/profile.d/*.sh
is equivalent to the ~/.profile
for each user. First, we will create the file for edit:
$ cd /etc/profile.d
$ vim name_of_your_shell_script.sh
In the file, we will add the same line as before:
export mysql_pass="your_root_password"
Either way, you will have access to the environmental variable.
Then, at the top of settings.py
, write
import os
if it doesn’t already exist. We will then change our database configuration to be
DATABASES = {
'default': {
'ENGINE': 'django.db.backends.mysql',
'NAME': 'your_project_name',
'USER': 'root',
'PASSWORD': os.environ.get('mysql_pass'),
'HOST': 'your_host_address',
'PORT': 'your_port',
}
}
And you should be good!
**And as an aside, if you are working in production, you should apply the above process to your Django SECRET_KEY
as well to remove security vulnerabilities.
Step 6: Make Migrations
A Note
If your migration fails, you may need to do some debugging. Every case may be different, so I cannot give much advice here, unfortunately. However, if you want to retry a migration after making some changes, you will need to start with an empty database. To do that, we will DROP
the database and create a new one. To do so:
mysql> DROP DATABASE pcari;
mysql> CREATE DATABASE pcari CHARACTER SET UTF8;
mysql> GRANT ALL PRIVILEGES ON pcari.* TO root@localhost;
mysql> FLUSH PRIVILEGES;
mysql> QUIT
Then, you can try the migration again. Ok, let’s begin!
We are in the home stretch! Now, all we need to do is apply any migrations you made to the new MySQL database. The details are all abstracted away for you, so all you need to do is run:
$ python manage.py makemigrations
$ python manage.py migrate --run-syncdb
Finally, when your MySQL database is all set up, load all the data you saved in the dumpfile in step 3:
$ python manage.py loaddata datadump.json
Step 7: Wrap Up
And that’s it! Now you have transitioned your webapp from SQLite to MySQL. While SQLite is quick, reliable, and useful for most development purposes, when your app transitions into production you often need to transition your backend database to fill the needs. And with this tutorial, you now know how!
If you are having trouble, here are some pages that I found helpful when working on this writeup:
https://www.digitalocean.com/community/tutorials/how-to-install-mysql-on-ubuntu-16-04
http://stackoverflow.com/questions/3034910/whats-the-best-way-to-migrate-a-django-db-from-sqlite-to-mysql
http://stackoverflow.com/questions/19189813/setting-django-up-to-use-mysql
https://docs.djangoproject.com/en/1.10/ref/databases/#mysql-notes
http://stackoverflow.com/questions/11546151/how-to-make-db-dumpfile-in-django