Dalam artikel ini, saya akan membincangkan secara ringkas beberapa amalan terbaik yang membantu memastikan projek teratur, memudahkan penyelenggaraan pangkalan data dan mengelakkan perangkap biasa apabila bekerja dengan Alembic dan SQLAlchemy. Teknik-teknik ini telah menyelamatkan saya daripada masalah lebih daripada sekali. Inilah perkara yang akan kami bincangkan:
SQLAlchemy membolehkan anda menyediakan konvensyen penamaan yang digunakan secara automatik pada semua jadual dan kekangan apabila menjana migrasi. Ini menjimatkan anda daripada menamakan indeks secara manual, kunci asing dan kekangan lain, yang menjadikan struktur pangkalan data boleh diramal dan konsisten.
Untuk menyediakan ini dalam projek baharu, tambahkan konvensyen pada kelas asas supaya Alembic akan menggunakan format penamaan yang diingini secara automatik. Berikut ialah contoh konvensyen yang berfungsi dengan baik dalam kebanyakan kes:
from sqlalchemy import MetaData from sqlalchemy.orm import DeclarativeBase convention = { 'all_column_names': lambda constraint, table: '_'.join( [column.name for column in constraint.columns.values()] ), 'ix': 'ix__%(table_name)s__%(all_column_names)s', 'uq': 'uq__%(table_name)s__%(all_column_names)s', 'ck': 'ck__%(table_name)s__%(constraint_name)s', 'fk': 'fk__%(table_name)s__%(all_column_names)s__%(referred_table_name)s', 'pk': 'pk__%(table_name)s', } class BaseModel(DeclarativeBase): metadata = MetaData(naming_convention=convention)
Nama fail migrasi alembic biasanya bermula dengan teg semakan, yang boleh menjadikan susunan migrasi dalam direktori kelihatan rawak. Kadangkala adalah berguna untuk memastikannya disusun mengikut kronologi.
Alembic membenarkan menyesuaikan templat nama fail migrasi dalam fail alembic.ini dengan tetapan file_template. Berikut ialah dua format penamaan yang mudah untuk memastikan migrasi teratur:
file_template = %%(year)d-%%(month).2d-%%(day).2d_%%(rev)s_%%(slug)s
file_template = %%(epoch)d_%%(rev)s_%%(slug)s
Menggunakan cap masa tarikh atau Unix dalam nama fail memastikan migrasi teratur, menjadikan navigasi lebih mudah. Saya lebih suka menggunakan cap waktu Unix dan contoh akan diberikan dalam bahagian seterusnya.
Bagi mereka yang bekerja dalam pasukan, mengulas atribut ialah amalan yang baik. Dengan model SQLAlchemy, pertimbangkan untuk menambah ulasan terus pada lajur dan jadual dan bukannya bergantung pada docstrings. Dengan cara ini, ulasan tersedia dalam kod dan pangkalan data, menjadikannya lebih mudah bagi DBA atau penganalisis memahami tujuan jadual dan medan.
class Event(BaseModel): __table_args__ = {'comment': 'System (service) event'} id: Mapped[uuid.UUID] = mapped_column( UUID(as_uuid=True), primary_key=True, comment='Event ID - PK', ) service_id: Mapped[int] = mapped_column( sa.Integer, sa.ForeignKey( f'{IntegrationServiceModel.__tablename__}.id', ondelete='CASCADE', ), nullable=False, comment='FK to integration service that owns the event', ) name: Mapped[str] = mapped_column( sa.String(256), nullable=False, comment='Event name' )
Menambah ulasan pada migrasi juga berguna untuk menjadikannya lebih mudah ditemui dalam sistem fail. Komen boleh ditambah dengan -m
from sqlalchemy import MetaData from sqlalchemy.orm import DeclarativeBase convention = { 'all_column_names': lambda constraint, table: '_'.join( [column.name for column in constraint.columns.values()] ), 'ix': 'ix__%(table_name)s__%(all_column_names)s', 'uq': 'uq__%(table_name)s__%(all_column_names)s', 'ck': 'ck__%(table_name)s__%(constraint_name)s', 'fk': 'fk__%(table_name)s__%(all_column_names)s__%(referred_table_name)s', 'pk': 'pk__%(table_name)s', } class BaseModel(DeclarativeBase): metadata = MetaData(naming_convention=convention)
Model sering digunakan untuk manipulasi data, seperti memindahkan data dari satu jadual ke jadual lain atau mengubah suai nilai lajur. Walau bagaimanapun, menggunakan model ORM dalam migrasi boleh membawa kepada isu jika model berubah selepas migrasi dibuat. Dalam kes sedemikian, migrasi berdasarkan model lama akan terputus apabila dilaksanakan, kerana skema pangkalan data mungkin tidak lagi sepadan dengan model semasa.
Migrasi hendaklah statik dan bebas daripada keadaan semasa model untuk memastikan pelaksanaan yang betul tanpa mengira perubahan kod. Di bawah ialah dua cara untuk mengelak daripada menggunakan model untuk manipulasi data.
file_template = %%(year)d-%%(month).2d-%%(day).2d_%%(rev)s_%%(slug)s
file_template = %%(epoch)d_%%(rev)s_%%(slug)s
Ujian Stairway melibatkan menguji secara progresif peningkatan/turun taraf migrasi langkah demi langkah untuk memastikan keseluruhan rantaian migrasi berfungsi dengan betul. Ini memastikan setiap migrasi boleh berjaya mencipta pangkalan data baharu dari awal dan menurunkan taraf tanpa masalah. Menambah ujian ini pada CI sangat berharga untuk pasukan, menjimatkan masa dan kekecewaan.
Mengintegrasikan ujian ke dalam projek anda boleh dilakukan dengan mudah dan cepat. Anda boleh mencari contoh kod dalam repositori ini. Ia juga termasuk ujian migrasi berharga lain yang mungkin membantu.
Perkhidmatan berasingan untuk melakukan migrasi. Ini hanyalah satu cara untuk melaksanakan migrasi. Apabila membangun secara tempatan atau dalam persekitaran yang serupa dengan pembangunan, kaedah ini sesuai dengan baik. Saya ingin mengingatkan anda tentang ciri depende_on bersyarat, yang berkaitan di sini. Kami mengambil imej aplikasi dengan Alembic dan menjalankannya dalam bekas yang berasingan. Kami menambah pergantungan pada pangkalan data dengan syarat bahawa migrasi bermula hanya apabila pangkalan data bersedia untuk mengendalikan permintaan (service_healthy). Selain itu, bergantung_pada bersyarat (service_completed_successfully) boleh ditambahkan untuk aplikasi, memastikan ia bermula hanya selepas migrasi berjaya diselesaikan.
class Event(BaseModel): __table_args__ = {'comment': 'System (service) event'} id: Mapped[uuid.UUID] = mapped_column( UUID(as_uuid=True), primary_key=True, comment='Event ID - PK', ) service_id: Mapped[int] = mapped_column( sa.Integer, sa.ForeignKey( f'{IntegrationServiceModel.__tablename__}.id', ondelete='CASCADE', ), nullable=False, comment='FK to integration service that owns the event', ) name: Mapped[str] = mapped_column( sa.String(256), nullable=False, comment='Event name' )
Syarat depende_on memastikan migrasi dijalankan hanya selepas pangkalan data sedia sepenuhnya dan aplikasi bermula selepas migrasi selesai.
Walaupun ini mungkin perkara yang jelas, adalah penting untuk tidak mengabaikannya. Menggunakan mixin ialah cara mudah untuk mengelakkan pertindihan kod. Mixin ialah kelas yang mengandungi medan dan kaedah yang kerap digunakan, yang boleh disepadukan ke dalam mana-mana model yang diperlukan. Sebagai contoh, kita sering memerlukan medan create_at dan updated_at untuk menjejak masa penciptaan dan kemas kini rekod. Ia juga boleh berguna untuk menggunakan id berdasarkan UUID untuk menyeragamkan kunci utama. Semua ini boleh dikapsulkan dalam campuran.
from sqlalchemy import MetaData from sqlalchemy.orm import DeclarativeBase convention = { 'all_column_names': lambda constraint, table: '_'.join( [column.name for column in constraint.columns.values()] ), 'ix': 'ix__%(table_name)s__%(all_column_names)s', 'uq': 'uq__%(table_name)s__%(all_column_names)s', 'ck': 'ck__%(table_name)s__%(constraint_name)s', 'fk': 'fk__%(table_name)s__%(all_column_names)s__%(referred_table_name)s', 'pk': 'pk__%(table_name)s', } class BaseModel(DeclarativeBase): metadata = MetaData(naming_convention=convention)
Dengan menambahkan campuran ini, kami boleh memasukkan id UUID dan cap masa dalam mana-mana model jika diperlukan:
file_template = %%(year)d-%%(month).2d-%%(day).2d_%%(rev)s_%%(slug)s
Mengendalikan migrasi boleh menjadi mencabar, tetapi mengikuti amalan mudah ini membantu memastikan projek teratur dan terurus. Menamakan konvensyen, pengisihan tarikh, ulasan dan ujian telah menyelamatkan saya daripada huru-hara dan membantu mengelakkan kesilapan. Saya harap artikel ini terbukti membantu — jangan ragu untuk berkongsi petua penghijrahan anda sendiri dalam ulasan!
Atas ialah kandungan terperinci Amalan Terbaik untuk Alembic dan SQLAlchemy. Untuk maklumat lanjut, sila ikut artikel berkaitan lain di laman web China PHP!