The problem
You already import mysql.connector in your project and you want to use peewee, only problem is:
peewee.ImproperlyConfigured: MySQL driver not installed!
It's a known issue since 2018: Why does not peewee support mysql-connector-python? · Issue #1501 · coleifer/peewee.
The advice is to:
- either install
pymysql - or implement a proxy driver for the mysql.connector that you already have
Since mysql-connector-python (that provides us with mysql.connector) is compliant with DB-API 2.0 spec, the latter is actually an option.
The solution
Here's Database class that handles mysql.connector connection. It expects an environment variable named MYSQL_DSN that contains database credentials in DSN format. You can put it into db.py.
import os
import mysql.connector
import urllib.parse
class Database:
_conn = None
@classmethod
def credentials(cls):
mysql_dsn = os.getenv('MYSQL_DSN')
if not mysql_dsn:
raise RuntimeError('MYSQL_DSN environment variable not set')
mysql_url = urllib.parse.urlparse(mysql_dsn)
return {
'host': mysql_url.hostname,
'port': mysql_url.port or 3306,
'user': mysql_url.username,
'password': mysql_url.password,
'database': mysql_url.path.lstrip('/'),
}
@classmethod
def connection(cls):
if cls._conn is None or not cls._conn.is_connected():
credentials = cls.credentials()
try:
cls._conn = mysql.connector.connect(**credentials)
except mysql.connector.Error as err:
raise RuntimeError(f'Connection failed: {err}')
return cls._conn
@classmethod
def cursor(cls):
"""
For use with direct MySQL queries outside of peewee
"""
conn = cls.connection()
return conn.cursor()
Now base.py for peewee.
import peewee
from .db import Database
class MySQLConnectorDatabase(peewee.MySQLDatabase):
"""
Reuse existing DB-API 2.0 compliant mysql.connector instead of relying on PyMySQL or MySQLdb
- https://github.com/coleifer/peewee/issues/1501
- https://docs.peewee-orm.com/en/latest/peewee/database.html#adding-a-new-database-driver
"""
def __init__(self, database, **connect_params):
# Prevent peewee.InterfaceError: Error, database must be initialized before opening a connection
# Because parent constructor requires credentials, pass what we got at hand
super().__init__(**Database.credentials())
def _connect(self, **kwargs):
return Database.connection()
class BaseModel(peewee.Model):
class Meta:
database = MySQLConnectorDatabase(None) # Point to our wrapper proxying to Database.connection()
Now you can build your peewee model classes:
import peewee
from .base import BaseModel
class User(BaseModel):
id = peewee.AutoField()
name = peewee.CharField(max_length=255, null=True)
# ...
class Meta:
table_name = 'users'