Skip to main content
يُعدّ FastAPI إطار عمل ويب غير متزامن بلغة بايثون، في حين تُعدّ SQLAlchemy أكثر أدوات ربط الكائنات بقواعد البيانات انتشارًا في بايثون. ستُعرّف في هذا الدليل نموذج SQLAlchemy محدد الأنواع، ثم تُنشئ جدوله باستخدام عملية ترحيل عبر Alembic، وبعد ذلك تُوفّر عبر FastAPI نقاط نهاية لعمليات الإنشاء والقراءة والتحديث والحذف. يستخدم التطبيق المشغّل asyncpg، ويُشفَّر كل اتصال باستخدام TLS مع التحقق الكامل من الشهادة.

المتطلبات الأساسية

  • uv وبايثون 3.11 أو إصدار أحدث. اختُبر هذا الدليل باستخدام بايثون 3.13 وFastAPI 0.142 وSQLAlchemy 2.1 وasyncpg 0.31 وAlembic 1.20 وUvicorn 0.54.
  • حساب على ClickHouse Cloud
  • psql، لإنشاء قاعدة البيانات. ويمكنك بدلًا من ذلك تنفيذ عبارة CREATE DATABASE في SQL console.

إنشاء خدمة ClickHouse Managed Postgres

في وحدة تحكم ClickHouse Cloud، انقر على New service ثم اختر Postgres. سيكون المثيل جاهزًا في غضون بضع دقائق. للاطلاع على شرح مفصّل للخطوات، راجع دليل البدء السريع.

الحصول على بيانات الاتصال

انقر على Connect في الشريط الجانبي الأيسر لخدمتك. تعرض النافذة المنبثقة اسم المستخدم وكلمة المرور والخادم والمنفذ، وتتضمّن مفتاح تبديل بين Directly وvia PgBouncer. يستخدم هذا الدليل كلا الاتصالين:
  • PgBouncer (المنفذ 6432) للتطبيق. يحتفظ SQLAlchemy بمجمّع اتصالات في كل عامل من عمّال Uvicorn، ويتيح PgBouncer المضمَّن لعدد كبير من العمّال والنسخ المتماثلة مشاركة عدد محدود من اتصالات Postgres، وهو يعمل في وضع تجميع المعاملات.
  • الاتصال المباشر (المنفذ 5432) لـ Alembic. تنفّذ عمليات الترحيل أوامر DDL، وقد تستغرق وقتًا طويلًا، وقد تعتمد على حالة session، لذا ينبغي أن تتصل بـ Postgres مباشرةً.
فعّل Use SSL ثم انقر على Download CA certificate. ويمكنك أيضًا تنزيلها من Settings → CA Certificate. هذه الشهادة خاصة بمثيلك وحده، ولذلك يستطيع المشغّل الاعتماد عليها للتحقق من أنه يتصل بخادمك فعلًا. عند تفعيل Use SSL، ينتهي عنوان URL الظاهر في النافذة المنبثقة بـ sslmode=verify-full&sslrootcert=.... ونظرًا إلى أن asyncpg لا يقرأ هذه المعلمات، انسخ كلمة المرور والمضيف فقط من النافذة المنبثقة؛ إذ يمرّر التطبيق شهادة CA داخل الشيفرة بدلًا من ذلك (راجع تهيئة الاتصال).

إعداد المشروع

أنشئ مشروعًا وثبّت FastAPI وUvicorn وSQLAlchemy مع حزمة الاعتماديات الإضافية asyncio الخاصة بها، إلى جانب asyncpg وAlembic:
هل تُضيف ذلك إلى تطبيق قائم؟تخطَّ الأمرين uv init وmkdir. شغّل في مشروعك الأمر uv add "sqlalchemy[asyncio]" asyncpg alembic، ثم انتقل إلى تهيئة الاتصال. إذا كان تطبيقك يستخدم SQLite عبر SQLAlchemy غير المتزامن:
  • عدّل استدعاء create_async_engine ليطابق الاستدعاء الموجود في app/db.py. وفي migrations/env.py، استورد Base ونماذجك من الوحدات الخاصة بك.
  • أزِل Base.metadata.create_all من مرحلة بدء التشغيل. سيُنشئ Alembic جداولك الحالية في Postgres، لكنه لا ينسخ البيانات من SQLite.

تهيئة الاتصال

انقل شهادة CA التي نزّلتها إلى دليل المشروع، ثم أعد تسميتها إلى ca-certificate.pem. أنشئ قاعدة بيانات للتطبيق. استبدل <PASSWORD> والمضيف بالقيم الظاهرة في النافذة المنبثقة Connect:
أنشئ ملف .env في جذر المشروع، أو أضف هذه المتغيرات إلى ملفك الحالي. لا يختلف عنوانا URL إلا في المنفذ، ويستخدم كلاهما المخطط postgresql+asyncpg، ولا يحتويان على أي معلمات SSL:
.env
يُحدَّد مسار DATABASE_CA_CERT نسبةً إلى الدليل الذي تُشغِّل منه الأوامر. وإذا كانت كلمة المرور تحتوي على محارف خاصة مثل @ أو / أو #، فرمِّزها بترميز URL. واحرص على إبقاء .env خارج نظام التحكم في الإصدارات. أنشئ الملف app/db.py. يتولى هذا الملف تهيئة إعدادات TLS والمحرك، إضافةً إلى مصنع session يتشاركه سائر أجزاء التطبيق:
app/db.py
يتطلب كائن ssl.SSLContext المُنشأ باستخدام PROTOCOL_TLS_CLIENT شهادةً صالحة، ويتحقق من اسم المضيف، وهو ما يعادل sslmode=verify-full. ويمرّر SQLAlchemy هذا الكائن إلى asyncpg عبر connect_args.
مرّر شهادة CA في connect_args
  • إذا لم تُمرَّر الوسيطة ssl، يستخدم asyncpg بروتوكول TLS متى أتاحه الخادم، لكنه لا يتحقق من الشهادة.
  • لا تنسخ sslmode أو sslrootcert من نافذة Connect إلى عنوان URL، إذ يمرّر SQLAlchemy معلمات URL إلى asyncpg بوصفها وسيطات كلمات مفتاحية، فيفشل الاتصال مع الخطأ TypeError: connect() got an unexpected keyword argument 'sslmode'.
  • في بايثون 3.13 والإصدارات الأحدث، يفشل السياق المُنشأ باستخدام ssl.create_default_context مع الخطأ certificate verify failed: Missing Authority Key Identifier، لأن هذه الدالة تفعّل فحوصات X.509 الصارمة. لذا أنشئ السياق باستخدام ssl.SSLContext(ssl.PROTOCOL_TLS_CLIENT) كما هو موضّح.
  • إذا كانت شهادة CA غير صحيحة، يفشل الاتصال مع الخطأ certificate verify failed: unable to get local issuer certificate. وإذا لم يتطابق اسم المضيف، كما يحدث عند الاتصال عبر عنوان IP، يفشل مع الخطأ certificate verify failed: IP address mismatch.
إذا كنت تفضّل إبقاء إعدادات TLS في عنوان URL، أو كان تطبيقك يستخدم sessions SQLAlchemy متزامنة، فاستخدم المشغّل psycopg بدلًا من asyncpg. فهو مبني على libpq ويقبل المعلمات الواردة في نافذة Connect، مثل postgresql+psycopg://...:5432/guide_fastapi?sslmode=verify-full&sslrootcert=ca-certificate.pem، مع كلٍّ من create_engine وcreate_async_engine.

تعريف النموذج

أنشئ الملف app/models.py وضمّنه نموذج SQLAlchemy محدد الأنواع:
app/models.py

تشغيل عمليات الترحيل

أنشئ بيئة Alembic باستخدام القالب غير المتزامن:
استبدل محتوى الملف migrations/env.py الذي تم إنشاؤه. تحمّل النسخة الجديدة نماذجك لتستخدمها ميزة autogenerate، وتتصل مباشرةً بـ Postgres بإعدادات TLS نفسها التي يستخدمها التطبيق:
migrations/env.py
يتجاهل Alembic القيمة النائبة sqlalchemy.url في alembic.ini، لأن env.py يتولى إنشاء المحرك بنفسه. أنشئ ملف ترحيل انطلاقًا من النموذج، ثم طبّقه:
راجع الملف المُنشأ في migrations/versions ثم ثبّته (commit) مع الشيفرة الخاصة بك. يسجّل Alembic الـ revision المُطبَّق في الجدول alembic_version. وكلما غيّرت نموذجًا، شغّل revision --autogenerate وupgrade head مجددًا.

إضافة نقاط نهاية واجهة برمجة التطبيقات

أنشئ الملف app/main.py. يحصل كل طلب على AsyncSession خاصة به عبر تبعية FastAPI، ويُغلَق مجمّع الاتصالات الخاص بالمحرك عند إيقاف تشغيل التطبيق:
app/main.py
ينفّذ SQLAlchemy عبارات كل session ضمن معاملة واحدة تنتهي عند session.commit. وعند إغلاق الـ session، يُتراجَع عن أي عمل غير مُثبَّت. أما عند تنفيذ INSERT، فيستعيد SQLAlchemy قيمتَي id وcreated_at المُولَّدتين باستخدام RETURNING، ولذلك لا تحتاج create_todo إلى استعلام إضافي.

التشغيل والتحقق

شغّل التطبيق:
في طرفية ثانية، أنشئ مهمتين، ثم حدّد الأولى على أنها مكتملة، واحذف الثانية:
يُعيد طلب DELETE الرمز 204 No Content. أما طلب عنصر مهام غير موجود فيُعيد الرمز 404:
افتح http://localhost:3112/todos في متصفحك لعرض المهام المتبقية، أو افتح http://localhost:3112/docs لتجربة نقاط النهاية من خلال وثائق واجهة برمجة التطبيقات التفاعلية في FastAPI. للتحقق من أن الاتصال المباشر يستخدم TLS مع التحقق من الشهادات، نفّذ استعلامًا على pg_stat_ssl باستخدام إعدادات TLS نفسها التي يستخدمها التطبيق:
لعرض البيانات في وحدة التحكم، افتح SQL console من الشريط الجانبي الأيسر لخدمتك، ووسّع guide_fastapi ثم public، ثم انقر على جدول todos. ويحتفظ جدول alembic_version المجاور له بسجل عمليات الترحيل، بينما يحتوي جدول todos على الصف المتبقي: أوقف التطبيق بالضغط على Ctrl+C.

استخدام PgBouncer

يعمل التطبيق عبر PgBouncer باستخدام إعدادات asyncpg وSQLAlchemy الافتراضية:
  • ينفّذ asyncpg كل استعلام بوصفه عبارة مُحضَّرة مُسمّاة على مستوى البروتوكول، ويخزّن SQLAlchemy ما يصل إلى 100 منها مؤقتًا لكل اتصال. يدعم PgBouncer المرفق هذه العبارات في وضع تجميع المعاملات، ويحرّرها asyncpg عبر رسالة Close على مستوى البروتوكول بدلًا من أمر SQL DEALLOCATE. لذا لا حاجة إلى statement_cache_size=0 ولا إلى دالة prepared_statement_name_func مخصّصة. وإذا عطّلت مع ذلك ذاكرة التخزين المؤقت للعبارات في asyncpg، فأضف أيضًا prepared_statement_cache_size=0 إلى عنوان URL لتعطيل ذاكرة التخزين المؤقت في SQLAlchemy.
  • إعدادات session المضبوطة باستخدام SET، مثل search_path، لا تنتقل إلى المعاملة التالية، وقد تتسرّب إلى عملاء آخرين. لذا استخدم SET LOCAL داخل المعاملة بدلًا من ذلك.
  • يعرض pg_stat_ssl معلومات الاتصال بين PgBouncer وPostgres، لذا يُرجع فحص TLS المذكور أعلاه القيمة (False, None) عبر المنفذ 6432. ومع ذلك يظل اتصال التطبيق بـ PgBouncer خاضعًا للتحقق، إذ يفشل الاتصال بالطريقة نفسها عند استخدام شهادة CA خاطئة.
  • لا يستخدم Alembic الأقفال الاستشارية، وينفّذ كل عملية upgrade ضمن معاملة واحدة، لذا يعمل هو أيضًا عبر PgBouncer. ومع ذلك يبقى الاتصال المباشر الخيار الأفضل لعمليات الترحيل الطويلة.
تغيير نوع عموديحتفظ PgBouncer بالعبارات المُحضَّرة على اتصالاته بـ Postgres. فإذا غيّرت عملية ترحيل نوع عمود يُرجعه أحد الاستعلامات، كأن يتغيّر من integer إلى bigint، فسيفشل ذلك الاستعلام عبر PgBouncer مع الخطأ InvalidCachedStatementError: cached statement plan is invalid due to a database schema or configuration change، حتى من الاتصالات الجديدة، إلى أن يستبدل PgBouncer اتصالاته بـ Postgres. ولمعالجة ذلك، أغلق اتصالات PgBouncer بـ Postgres الخاصة بقاعدة البيانات عبر الاتصال المباشر، مستخدمًا المستخدم نفسه الذي يتصل به التطبيق. ونظرًا لأن PgBouncer يتصل بـ Postgres محليًا، فلا تحمل اتصالاته قيمة client_addr؛ وبذلك لا يمسّ الاستعلام الاتصالات المباشرة. بعدها يفتح PgBouncer اتصالات جديدة، وقد يفشل كل اتصال مُجمَّع للتطبيق مرة أخرى قبل أن يحدّث SQLAlchemy ذاكرة التخزين المؤقت الخاصة به:
أما إضافة عمود فلا تتسبّب في هذه المشكلة، لأن SQLAlchemy يحدّد بالاسم الأعمدة التي يستعلم عنها.

الخطوات التالية

آخر تعديل في ١٤ أكتوبر ٢٠٢٦