Initial commit: Flask battery tracker app
- Flask + SQLAlchemy (MariaDB-compatible schema) battery tracking web app - 40 pre-seeded batteries (Eneloop, BONAI, Energizer NiMH) across 5 devices - Business rules: block retired assignment, brand-mix warnings, capacity checks - Mobile-friendly Jinja2 templates with inline CSS - waitress WSGI server via systemd user service (sbin/install-service.sh) - SQLite → MariaDB migration script (migrate_to_mariadb.py) - 26 passing acceptance tests (pytest + Flask test client) Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
+164
@@ -0,0 +1,164 @@
|
||||
# Migrating from SQLite to MariaDB
|
||||
|
||||
This guide covers moving the Battery Tracker database from SQLite to MariaDB/MySQL
|
||||
without changing any application code — only the connection string changes.
|
||||
|
||||
---
|
||||
|
||||
## 1. Install PyMySQL
|
||||
|
||||
```bash
|
||||
source .venv/bin/activate
|
||||
pip install pymysql
|
||||
pip freeze > requirements.txt
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 2. Create the MariaDB database and user
|
||||
|
||||
```sql
|
||||
CREATE DATABASE batteries CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci;
|
||||
CREATE USER 'battuser'@'localhost' IDENTIFIED BY 'strongpassword';
|
||||
GRANT ALL PRIVILEGES ON batteries.* TO 'battuser'@'localhost';
|
||||
FLUSH PRIVILEGES;
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 3. Update the connection string
|
||||
|
||||
Set the `DATABASE_URL` environment variable before starting the app:
|
||||
|
||||
```bash
|
||||
export DATABASE_URL='mysql+pymysql://battuser:strongpassword@localhost/batteries?charset=utf8mb4'
|
||||
```
|
||||
|
||||
Or add it to a `.env` file and load it with your process manager / systemd service:
|
||||
|
||||
```ini
|
||||
# In ~/.config/systemd/user/battery-tracker.service, add under [Service]:
|
||||
Environment=DATABASE_URL=mysql+pymysql://battuser:strongpassword@localhost/batteries?charset=utf8mb4
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 4. (Optional) Use Flask-Migrate for schema management
|
||||
|
||||
Flask-Migrate lets you apply schema changes incrementally via `flask db upgrade`
|
||||
rather than dropping and recreating tables.
|
||||
|
||||
```bash
|
||||
pip install Flask-Migrate
|
||||
```
|
||||
|
||||
In `app.py`, after the `db` setup, add:
|
||||
|
||||
```python
|
||||
from flask_migrate import Migrate
|
||||
migrate = Migrate(app, db) # pass your SQLAlchemy db object
|
||||
```
|
||||
|
||||
Then:
|
||||
|
||||
```bash
|
||||
flask db init # first time only — creates migrations/ directory
|
||||
flask db migrate -m "initial schema"
|
||||
flask db upgrade
|
||||
```
|
||||
|
||||
> **Note:** The app uses raw SQLAlchemy (not Flask-SQLAlchemy), so you will need
|
||||
> to adapt the `Migrate(app, db)` call to wrap your engine/session. Alternatively,
|
||||
> run `Base.metadata.create_all(engine)` directly for the initial schema — the app
|
||||
> already does this on startup.
|
||||
|
||||
---
|
||||
|
||||
## 5. Run the migration script
|
||||
|
||||
The `migrate_to_mariadb.py` script reads every record from SQLite and inserts
|
||||
it into MariaDB using the SQLAlchemy ORM — no raw SQL, no CSV exports.
|
||||
|
||||
```bash
|
||||
MARIADB_URL='mysql+pymysql://battuser:strongpassword@localhost/batteries?charset=utf8mb4' \
|
||||
python migrate_to_mariadb.py
|
||||
```
|
||||
|
||||
Or pass the URL as a positional argument:
|
||||
|
||||
```bash
|
||||
python migrate_to_mariadb.py 'mysql+pymysql://battuser:strongpassword@localhost/batteries?charset=utf8mb4'
|
||||
```
|
||||
|
||||
The script:
|
||||
1. Creates all tables on MariaDB if they don't exist
|
||||
2. Inserts all Device records (preserving primary keys)
|
||||
3. Inserts all Battery records (preserving primary keys and foreign keys)
|
||||
4. Resets `AUTO_INCREMENT` counters past the highest migrated ID
|
||||
5. Prints a verification table comparing row counts
|
||||
|
||||
---
|
||||
|
||||
## 6. Verify row counts
|
||||
|
||||
The migration script prints a summary:
|
||||
|
||||
```
|
||||
=== Verification ===
|
||||
Table SQLite MariaDB OK?
|
||||
--------------------------------------
|
||||
device 5 5 OK
|
||||
battery 40 40 OK
|
||||
|
||||
Migration complete. All row counts match.
|
||||
```
|
||||
|
||||
If you see `MISMATCH`, **do not** decommission SQLite. Re-run the script after
|
||||
investigating the error output.
|
||||
|
||||
You can also verify manually in the MariaDB shell:
|
||||
|
||||
```sql
|
||||
SELECT COUNT(*) FROM device;
|
||||
SELECT COUNT(*) FROM battery;
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 7. MariaDB configuration notes
|
||||
|
||||
| Setting | Recommended value |
|
||||
|---|---|
|
||||
| Character set | `utf8mb4` |
|
||||
| Collation | `utf8mb4_unicode_ci` |
|
||||
| `innodb_strict_mode` | `ON` (default in MariaDB 10.2+) |
|
||||
| `sql_mode` | Include `STRICT_TRANS_TABLES` |
|
||||
|
||||
Set these in `/etc/mysql/mariadb.conf.d/50-server.cnf` (or equivalent):
|
||||
|
||||
```ini
|
||||
[mysqld]
|
||||
character-set-server = utf8mb4
|
||||
collation-server = utf8mb4_unicode_ci
|
||||
```
|
||||
|
||||
The `?charset=utf8mb4` query parameter in the connection string ensures the
|
||||
client connection also uses utf8mb4, regardless of the server default.
|
||||
|
||||
---
|
||||
|
||||
## 8. Decommission SQLite
|
||||
|
||||
Once you have confirmed the row counts match and the app is running correctly
|
||||
against MariaDB:
|
||||
|
||||
```bash
|
||||
# Optional: keep a backup
|
||||
cp batteries.db batteries.db.bak
|
||||
|
||||
# Remove the SQLite file
|
||||
rm batteries.db
|
||||
```
|
||||
|
||||
The `DATABASE_URL` environment variable now fully controls which database is used —
|
||||
no code changes were required.
|
||||
Reference in New Issue
Block a user