فصل ۸: تنظیم، اتصال به برنامه و پروژه‌ی پایانی

اتصال از پایتون و جنگو: mysqlclient یا PyMySQL، و OPTIONS درست

دو درایور، یک رابط

هر دو درایور اصلی پایتون رابط استاندارد DB-API را پیاده می‌کنند، پس کد شما تقریباً یکسان است؛ تفاوت در نصب و سرعت است.

mysqlclientPyMySQL
پیاده‌سازیافزونه‌ی C روی libmysqlclientپایتون خالص
سرعتسریع‌ترکندتر در نتایج بزرگ
نصبدر لینوکس به کامپایلر و هدرها نیاز داردهمیشه بی‌دردسر
جنگودرایور توصیه‌شده‌ی رسمیبا install_as_MySQLdb
# اوبونتو و دبیان
sudo apt install python3-dev default-libmysqlclient-dev build-essential pkg-config
pip install mysqlclient

# جایی که کامپایل ممکن نیست (مثلاً هاست اشتراکی)
pip install PyMySQL
# config/__init__.py  (فقط اگر PyMySQL را انتخاب کرده‌اید)
import pymysql
pymysql.install_as_MySQLdb()

تنظیم DATABASES

DATABASES = {
    "default": {
        "ENGINE": "django.db.backends.mysql",
        "NAME": "carpet_factory",
        "USER": "web",
        "PASSWORD": os.environ["DB_PASSWORD"],
        "HOST": "127.0.0.1",
        "PORT": "3306",
        "CONN_MAX_AGE": 60,
        "CONN_HEALTH_CHECKS": True,
        "OPTIONS": {
            "charset": "utf8mb4",
            "init_command": (
                "SET sql_mode='STRICT_TRANS_TABLES,ONLY_FULL_GROUP_BY,"
                "NO_ZERO_IN_DATE,NO_ZERO_DATE,ERROR_FOR_DIVISION_BY_ZERO,"
                "NO_ENGINE_SUBSTITUTION'"
            ),
            "isolation_level": "read committed",
        },
        "TEST": {"CHARSET": "utf8mb4", "COLLATION": "utf8mb4_0900_ai_ci"},
    }
}

charset مجموعه‌کاراکتر اتصال را تعیین می‌کند؛ اگر جدول utf8mb4 باشد اما اتصال نباشد، ایموجی در نام محصول یا نظر مشتری خطا می‌دهد. STRICT_TRANS_TABLES باعث می‌شود رشته‌ی بلندتر از ستون یا عدد نامعتبر به‌جای بریده شدن بی‌صدا خطا بدهد؛ جنگو بدون آن هشدار mysql.W002 می‌دهد. isolation_level را جنگو به‌طور پیش‌فرض read committed می‌گذارد که با الگوی ORM سازگارتر است.

کوئری خام امن

from django.db import connection

with connection.cursor() as cur:
    cur.execute(
        "SELECT id, status FROM production_jobs WHERE loom_id = %s AND status = %s",
        [loom_id, "weaving"],
    )
    rows = cur.fetchall()

هرگز مقدار را با f-string داخل SQL نگذارید؛ placeholder ‏%s را درایور به‌درستی escape می‌کند.

نکته‌هایی که کمتر کسی می‌داند

  • HOST = "localhost" با سوکت یونیکس وصل می‌شود و "127.0.0.1" با TCP؛ این دو از دید MySQL حساب‌های متفاوت‌اند و منشأ رایج Access denied.
  • init_command کل sql_mode را جایگزین می‌کند؛ اگر فقط STRICT_TRANS_TABLES بنویسید، ONLY_FULL_GROUP_BY و بقیه‌ی حالت‌های پیش‌فرض خاموش می‌شوند.
  • با USE_TZ = True و TIME_ZONE = "Asia/Tehran"، lookupهایی مثل __date بدون بارگذاری جدول‌های منطقه‌ی زمانی نتیجه‌ی خالی می‌دهند: mysql_tzinfo_to_sql /usr/share/zoneinfo | sudo mysql mysql
  • ایندکس روی CharField(unique=True, max_length=1000) با utf8mb4 خطای Specified key was too long می‌دهد؛ سقف کلید ۳۰۷۲ بایت، یعنی ۷۶۸ کاراکتر است.
  • در ویندوز، mysqlclient چرخ (wheel) آماده دارد و کامپایلر نمی‌خواهد؛ اگر به‌خاطر تحریم دسترسی به PyPI کند است، با pip config set global.index-url از یک میرور داخلی استفاده کنید.

برای ذخیره‌ی پیشرفت و شرکت در آزمون، وارد شوید — رایگان است.