pythondjangovercellibmysqlclient

Error while installing `mysqlclient` on `vercel` for `django` app


I am trying to deploy a django app on vercel that requires a module mysqlclient but it seems to be failing because of some error for mysql_config. I am using PlanetScale and had added integration. The error is:

Error: Command failed: pip3.9 install --disable-pip-version-check --target . --upgrade -r /vercel/path0/requirements.txt
  error: subprocess-exited-with-error
  
  × python setup.py egg_info did not run successfully.
  │ exit code: 1
  ╰─> [16 lines of output]
      /bin/sh: mysql_config: command not found
      /bin/sh: mariadb_config: command not found
      /bin/sh: mysql_config: command not found
      Traceback (most recent call last):
        File "<string>", line 2, in <module>
        File "<pip-setuptools-caller>", line 34, in <module>
        File "/tmp/pip-install-xudcpdyi/mysqlclient_9d1c2ef21f0b41d6aa185af14610a5f3/setup.py", line 15, in <module>
          metadata, options = get_config()
        File "/tmp/pip-install-xudcpdyi/mysqlclient_9d1c2ef21f0b41d6aa185af14610a5f3/setup_posix.py", line 70, in get_config
          libs = mysql_config("libs")
        File "/tmp/pip-install-xudcpdyi/mysqlclient_9d1c2ef21f0b41d6aa185af14610a5f3/setup_posix.py", line 31, in mysql_config
          raise OSError("{} not found".format(_mysql_config_path))
      OSError: mysql_config not found
      mysql_config --version
      mariadb_config --version
      mysql_config --libs
      [end of output]
  
  note: This error originates from a subprocess, and is likely not a problem with pip.
error: metadata-generation-failed

And the final error is:

1: Command failed: pip3.9 install --disable-pip-version-check --target . --upgrade -r /vercel/path0/requirements.txt  error: subprocess-exited-with-error    × python setup.py egg_info did not run successfully.  │ exit code: 1  ╰─> [16 lines of output]      /bin/sh: mysql_config: command not found      /bin/sh: mariadb_config: command not found      /bin/sh: mysql_config: command not found      Traceback (most recent call last):        File "<string>", line 2, in <module>        File "<pip-setuptools-caller>", line 34, in <module>        File "/tmp/pip-install-xudcpdyi/mysqlclient_9d1c2ef21f0b41d6aa185af14610a5f3/setup.py", line 15, in <module>          metadata, options = get_config()        File "/tmp/pip-install-xudcpdyi/mysqlclient_9d1c2ef21f0b41d6aa185af14610a5f3/setup_posix.py", line 70, in get_config          libs = mysql_config("libs")        File "/tmp/pip-install-xudcpdyi/mysqlclient_9d1c2ef21f0b41d6aa185af14610a5f3/setup_posix.py", line 31, in mysql_config          raise OSError("{} no

Solution

  • This solution works for me. I used planetscale.com to host my MySQL Database, but I'm sure it works with any other provider like AWS RDS MySQL (just make sure to tweak your settings.py file to remove or add OPTIONS as necessary).

    first, install pyMySQL and dj_database_url packages

    pip install pymysql
    pip install dj_database_url
    

    or if you are using pipenv:

    pipenv install pymysql
    pipenv install dj_database_url
    

    Then, create a new file in your project folder, for example, mysql_setup.py.(e.g., this is the directory where you find the settings.py and wsgi.py files, your_project/your_project/mysql_setup.py) and add the following lines of code:

    import pymysql
    
    pymysql.version_info = (1, 4, 3, "final", 0)
    pymysql.install_as_MySQLdb()
    

    next, add the following lines of code at the beginning of your Django project's init.py file, which is located inside the same project folder (e.g., again, this is the directory where you find the settings.py and wsgi.py files, your_project/your_project/_init_.py):

    add the following code:

    from . import mysql_setup
    
    

    Next, in your settings.py file

    import dj_database_url
    
    ....
    
    DATABASES = {
        'default': {
            'ENGINE': 'django.db.backends.sqlite3',
            'NAME': BASE_DIR / 'db.sqlite3',
        }
    }
    
    
    DATABASES['default'] = dj_database_url.config(conn_max_age=600, ssl_require=True)
    DATABASES['default']['OPTIONS']['charset'] = 'utf8mb4'
    del DATABASES['default']['OPTIONS']['sslmode'] 
    DATABASES['default']['OPTIONS']['ssl'] =  {'ca': os.environ.get('MYSQL_ATTR_SSL_CA')}
    
    

    Next, remember to update your requirements.txt file to include the installed packages

    dj-database-url==2.0.0
    PyMySQL==1.0.3
    

    or recreate the entire requirements.txt file using the pip freeze command:

    pip freeze > requirements.txt
    

    NOTES: You will need to add the DATABASE_URL variable to the Vercel env variables for the project.

    It looks something like this:

    DATABASE_URL=mysql://user:password@host/dbname

    you can get your connection string by going inside your planetscale project, go to overview and hit the "connect" button.

    then, in the "Connect With" option select "Rust" and on the "Terminal" tab you will find your connection string in the format you need.

    I didn't have to add the MYSQL_ATTR_SSL_CA variable to Vercel env variables, but if complains of a missing variable try adding this environment variable: MYSQL_ATTR_SSL_CA=/etc/ssl/cert.pem

    Here are the rest of the files that I used to run my project.

    NOTE: Remember to change my_project_name with your actual project name.

    wsgy.py (just add app = application at the end of your file)

    import os
    
    from django.core.wsgi import get_wsgi_application
    
    os.environ.setdefault('DJANGO_SETTINGS_MODULE', 'my_project_name.settings')
    
    application = get_wsgi_application()
    
    app = application
    

    This is my build.sh file that I use to tell Vercel what commands to run on this project like installing dependencies and running the migrations:

    build.sh (this file should be at the root of the project)

    #!/bin/bash
    
    # Build the project
    echo "Building the project..."
    python3.9 -m pip install -r requirements.txt
    
    echo "Make Migration..."
    python3.9 manage.py makemigrations --noinput
    python3.9 manage.py migrate --noinput
    
    echo "Collect Static..."
    python3.9 manage.py collectstatic --noinput --clear
    

    vercel.json (this file should be at the root of the project)

    {
        "version": 2,
        "builds": [
          {
            "src": "your_project_name/wsgi.py",
            "use": "@vercel/python",
            "config": {
              "maxLambdaSize": "15mb",
              "runtime": "python3.9"
            }
          },
          {
            "src": "build.sh",
            "use": "@vercel/static-build",
            "config": {
              "distDir": "staticfiles_build"
            }
          }
        ],
        "routes": [
          {
            "src": "/static/(.*)",
            "dest": "/static/$1"
          },
          {
            "src": "/(.*)",
            "dest": "your_project_name/wsgi.py"
          }
        ]
      }
      
    

    Also, remember to make these changes to the settings.py file in order to make the collectstatic command works correctly inside the build.sh file.

    settings.py

    DEBUG = False
    
    .....
    
    STATIC_URL = '/static/'
    STATIC_ROOT = BASE_DIR / 'staticfiles_build' / 'static'