Расширение для поддержки SQLAlchemy в Flask
Flask-SQLAlchemy определяет разумные значения по умолчанию. Однако иногда требуется настройка. Существуют различные способы настройки определения и взаимодействия моделей.
Эти настройки применяются при создании объекта SQLAlchemy и распространяются на все модели, производные от его класса Model.
Все модели SQLAlchemy наследуются от декларативного базового класса. Это обозначается как db.Model
в Flask-SQLAlchemy,
который расширяет все модели. Это можно настроить путем создания подкласса по умолчанию и передачи пользовательского
класса в model_class
.
В следующем примере каждой модели присваивается целочисленный первичный ключ или внешний ключ для наследования объединенной таблицы.
Целочисленные первичные ключи для всего - не обязательно лучший дизайн базы данных (если только это не соответствует требованиям вашего проекта), это только пример
from flask_sqlalchemy import Model, SQLAlchemy
import sqlalchemy as sa
from sqlalchemy.ext.declarative import declared_attr, has_inherited_table
class IdModel(Model):
@declared_attr
def id(cls):
for base in cls.__mro__[1:-1]:
if getattr(base, '__table__', None) is not None:
type = sa.ForeignKey(base.id)
break
else:
type = sa.Integer
return sa.Column(type, primary_key=True)
db = SQLAlchemy(model_class=IdModel)
class User(db.Model):
name = db.Column(db.String)
class Employee(User):
title = db.Column(db.String)
Если поведение требуется только для некоторых моделей, а не для всех моделей, используйте смешанные классы для настройки только этих моделей. Например, если некоторые модели должны отслеживать, когда они создаются или обновляются:
from datetime import datetime
class TimestampMixin(object):
created = db.Column(
db.DateTime, nullable=False, default=datetime.utcnow)
updated = db.Column(db.DateTime, onupdate=datetime.utcnow)
class Author(db.Model):
...
class Post(TimestampMixin, db.Model):
...
Также возможно настроить то, что доступно для использования на специальном свойстве запроса моделей. Например, предоставив метод get_or:
from flask_sqlalchemy import BaseQuery, SQLAlchemy
class GetOrQuery(BaseQuery):
def get_or(self, ident, default=None):
return self.get(ident) or default
db = SQLAlchemy(query_class=GetOrQuery)
# получить пользователя по идентификатору или вернуть анонимный пользовательский экземпляр
user = User.query.get_or(user_id, anonymous_user)
И теперь все запросы, выполняемые из специального свойства запроса в моделях Flask-SQLAlchemy, могут использовать метод
get_or
как часть своих запросов. Все отношения, определенные с помощью db.relationship
(но не
sqlalchemy.orm.relationship()
), также будут обеспечены этой функцией.
Также можно определить пользовательский класс запросов для отдельных отношений, указав в определении ключевое слово
query_class
. Это работает как с db.relationship
, так и с sqlalchemy.relationship
:
class MyModel(db.Model):
cousin = db.relationship('OtherModel', query_class=GetOrQuery)
Если в отношении relationship определен класс запроса, он будет иметь приоритет над классом запроса, присоединенным к соответствующей модели.
Также можно определить конкретный класс запросов для отдельных моделей, переопределив атрибут класса query_class в модели:
class MyModel(db.Model):
query_class = GetOrQuery
В этом случае метод get_or будет доступен только для запросов, исходящих из MyModel.query.
Метаклассы - это сложная тема, и вам, вероятно, не нужно настраивать их, чтобы достичь того, чего вы хотите. Здесь в основном описано, как отключить генерацию имен таблиц.
Metaclass модели отвечает за настройку внутренних компонентов SQLAlchemy при определении подклассов модели. Flask-SQLAlchemy добавляет некоторое дополнительное поведение через миксины; его метакласс по умолчанию DefaultMeta наследует их все.
__bind_key__
извлекается из класса и применяется к таблице.
См. Несколько баз данных с привязками.__tablename__
, но указывает первичный ключ, имя генерируется
автоматическиВы можете добавить свое поведение, определив свой собственный метакласс и создав декларативную базу самостоятельно. Не забудьте по-прежнему наследовать нужные миксины (или просто наследовать от метакласса по умолчанию).
Передача декларативного базового класса вместо простого базового класса модели, как показано выше, в base_class заставит Flask-SQLAlchemy использовать эту базу вместо ее создания с метаклассом по умолчанию.
from flask_sqlalchemy import SQLAlchemy
from flask_sqlalchemy.model import DefaultMeta, Model
class CustomMeta(DefaultMeta):
def __init__(cls, name, bases, d):
# пользовательская настройка класса может пойти здесь
# обязательно вызовите super
super(CustomMeta, cls).__init__(name, bases, d)
# пользовательские методы только для классов могут быть здесь
db = SQLAlchemy(model_class=declarative_base(
cls=Model, metaclass=CustomMeta, name='Model'))
Вы также можете передать любые другие аргументы, которые вы хотите объявить, чтобы описать базовый класс по мере необходимости.
Некоторые проекты предпочитают устанавливать __tablename__
каждой модели вручную, а не полагаться на обнаружение и
генерацию Flask-SQLAlchemy. Генерацию имени таблицы можно отключить, определив пользовательский метакласс.
from flask_sqlalchemy.model import BindMetaMixin, Model
from sqlalchemy.ext.declarative import DeclarativeMeta, declarative_base
class NoNameMeta(BindMetaMixin, DeclarativeMeta):
pass
db = SQLAlchemy(model_class=declarative_base(
cls=Model, metaclass=NoNameMeta, name='Model'))
Это создает базу, которая все еще поддерживает функцию __bind_key__
, но не генерирует имена таблиц.