DevGang
Авторизоваться

FlaskSQLAlchemy

Расширение для поддержки SQLAlchemy в Flask

Обычно Flask-SQLAlchemy ведет себя как правильно настроенная декларативная база из декларативного расширения. Поэтому мы рекомендуем прочитать документы по SQLAlchemy для полного ознакомления. Однако наиболее распространенные случаи использования также задокументированы здесь.

Что нужно иметь в виду:

  • Базовый класс для всех ваших моделей называется db.Model. Он хранится в экземпляре SQLAlchemy, который вы должны создать. Смотрите "Быстрый старт" для более подробной информации.
  • Некоторые части, которые требуются в SQLAlchemy, являются необязательными в Flask-SQLAlchemy. Например, имя таблицы устанавливается автоматически, если оно не переопределено. Оно будет взято из имени класса, преобразованного в строчные буквы, и из «CamelCase», получится следующее «camel_case». Чтобы переопределить имя таблицы, установите атрибут класса __tablename__.

Простой пример

class User(db.Model):
    id = db.Column(db.Integer, primary_key=True)
    username = db.Column(db.String(80), unique=True, nullable=False)
    email = db.Column(db.String(120), unique=True, nullable=False)

    def __repr__(self):
        return '<User %r>' % self.username

Используйте Column, чтобы определить столбец. Имя столбца - это имя, которому вы его назначаете. Если вы хотите использовать другое имя в таблице, вы можете указать необязательный первый аргумент, представляющий собой строку с нужным именем столбца. Первичные ключи помечены как primary_key = True. Несколько ключей могут быть помечены как первичные ключи, и в этом случае они становятся составными первичными ключами.

Типы столбца являются первым аргументом Column. Вы можете предоставить их напрямую или вызвыть, чтобы уточнить их настройки (например, указать длину). Следующие типы являются наиболее распространенными:

  • Integer целое число
  • String(size) строка с максимальной длиной (необязательно в некоторых базах данных, например PostgreSQL)
  • Text длинный текст в юникоде
  • DateTime дата и время, выраженные как объект datetime Python.
  • Float хранит значения с плавающей точкой
  • Boolean хранит логическое значение
  • PickleType содержит объекты Python, которые сериализуются с использованием pickle.
  • LargeBinary хранит большие произвольные двоичные данные

Отношения один ко многим

Наиболее распространенные отношения - это отношения one-to-many. Поскольку отношения объявляются до того, как они установлены, вы можете использовать строки для ссылки на классы, которые еще не созданы (например, если Person определяет отношение к Address, которое объявлено позже в файле).

Отношения выражаются с помощью функции relations(). Однако внешний ключ должен быть объявлен отдельно с классом ForeignKey:

class Person(db.Model):
    id = db.Column(db.Integer, primary_key=True)
    name = db.Column(db.String(50), nullable=False)
    addresses = db.relationship('Address', backref='person', lazy=True)

class Address(db.Model):
    id = db.Column(db.Integer, primary_key=True)
    email = db.Column(db.String(120), nullable=False)
    person_id = db.Column(db.Integer, db.ForeignKey('person.id'),
        nullable=False)

Что делает db.relationship()? Эта функция возвращает новое свойство, которое может делать несколько вещей. В этом случае мы сказали ему указать класс Address и загрузить несколько из них. Откуда он знает, что это вернет более одного адреса? Потому что SQLAlchemy угадывает полезное значение по умолчанию из вашего объявления. Если вы хотите иметь отношения один-к-одному, вы можете передать uselist=False в relationship().

Поскольку человек без имени или адреса электронной почты без связанного адреса не имеет смысла, nullable=False указывает SQLAlchemy создать столбец как NOT NULL. Это подразумевается для столбцов первичного ключа, но рекомендуется указать его для всех остальных столбцов, чтобы другим людям, работающим над вашим кодом, было понятно, что вы действительно хотите обнуляемый столбец, а не просто забыли добавить его.

Так что же значит backref и lazy? backref - это простой способ объявить новое свойство в классе Address. Затем вы также можете использовать my_address.person, чтобы получить всех пользователей связанных с этим адрессом. lazy определяет, когда SQLAlchemy будет загружать данные из базы данных:

  • select / True (по умолчанию, но явное лучше, чем неявное) означает, что SQLAlchemy будет загружать данные по мере необходимости за один раз, используя стандартную инструкцию select.
  • join / False указывает SQLAlchemy загружать отношения в том же запросе, что и родительский, с помощью оператора JOIN.
  • subquery работает как joined, но вместо этого SQLAlchemy будет использовать подзапрос.
  • dynamic является особенным и может быть полезным, если у вас много элементов и вы всегда хотите применить к ним дополнительные фильтры SQL. Вместо загрузки элементов SQLAlchemy будет возвращать другой объект запроса, который вы можете уточнить перед загрузкой элементов. Обратите внимание, что это нельзя превратить в другую стратегию загрузки при запросах, поэтому часто лучше избегать использования этого в пользу lazy=True. Объект запроса, эквивалентный динамическому отношению user.addresses, может быть создан с использованием Address.query.with_parent(user), но при этом он может использовать ленивую или нетерпеливую загрузку самого отношения по мере необходимости.

Как вы определяете ленивый статус для обратных ссылок? Используя функцию backref():

class User(db.Model):
    id = db.Column(db.Integer, primary_key=True)
    name = db.Column(db.String(50), nullable=False)
    addresses = db.relationship('Address', lazy='select',
        backref=db.backref('person', lazy='joined'))

Отношения многие ко многим

Если вы хотите использовать отношения «many-to-many», вам необходимо определить вспомогательную таблицу, которая будет использоваться для этих отношений. Для этой вспомогательной таблицы настоятельно рекомендуется использовать не модель, а реальную таблицу:

tags = db.Table('tags',
    db.Column('tag_id', db.Integer, db.ForeignKey('tag.id'), primary_key=True),
    db.Column('page_id', db.Integer, db.ForeignKey('page.id'), primary_key=True)
)

class Page(db.Model):
    id = db.Column(db.Integer, primary_key=True)
    tags = db.relationship('Tag', secondary=tags, lazy='subquery',
        backref=db.backref('pages', lazy=True))

class Tag(db.Model):
    id = db.Column(db.Integer, primary_key=True)

Здесь мы настроили Page.tags для загрузки сразу после загрузки страницы, но с использованием отдельного запроса. Это всегда приводит к двум запросам при получении страницы, но при запросе нескольких страниц вы не получите дополнительные запросы.

Список страниц для тега с другой стороны - это то, что редко требуется. Например, вам не понадобится этот список при получении тегов для определенной страницы. Таким образом, обратная ссылка настроена так, что она загружается лениво, так что при первом обращении к ней будет инициирован запрос на получение списка страниц для этого тега. Если вам нужно применить дополнительные параметры запроса к этому списку, вы можете либо перейти к «динамической» стратегии - с указанными выше недостатками - либо получить объект запроса, используя Page.query.with_parent(some_tag), а затем использовать его точно так же, как и с объектом запроса из динамических отношений.

Комментарии 1
Spirit412 03.09.2020 в 16:31

Что делать, если для конкретного поля нужно включить autoincrement, а для другого - выключить? autoincrement=False не срабатывает! zub_num = db.Column(db.Integer, primary_key=True, unique=True, nullable=False, autoincrement=False)

Чтобы оставить комментарий, необходимо авторизоваться