Вопросы для собеседования SQLAlchemy junior

  1. Что такое SQLAlchemy?

SQLAlchemy — это Python-библиотека для работы с реляционными базами данных с применением технологии ORM. [1]

  1. В Python имеются встроенные инструменты для написания сырых SQL-запросов к БД. Для чего тогда использовать сторонние библиотеки?

  • сторонние библиотеки позволяют выполнять запросы к БД на языке Python, не обращая внимания на диалекты каждой СУБД

  • предотвращение SQL-инъекций

  • замена СУБД без переписывания большого объема кода. Достаточно изменить движок

  1. Что такое ORM и для чего эта технология нужна?

ORM (Object-Relational Mapping) — это технология, которая позволяет сопоставлять модели, типы которых несовместимы, в нашем случае это таблица базы данных и объект Python. То есть, можно обращаться к объектам классов для управления данными в таблицах БД. Также можно создавать, изменять, удалять, фильтровать и наследовать объекты классов, сопоставленные с таблицами БД. [2][3]

  1. Какие еще ORM вам известны и почему именно SQLAlchemy?

  • Peewee, Django ORM

  • Преимущества SQLAlchemy:

    • старая и знакомая многим библиотека с большим объемом написанного кода

    • поддерживает asyncio

5. В коде приложения вы наткнулись на такой класс. Что можете про него рассказать?

class AsyncDatabaseSession:
    def __init__(self):
        self.engine = create_async_engine(“postgres://user:pass@localhost/dbname”)
        self.session_factory = async_scoped_session(
                             async_sessionmaker(self.engine, expire_on_commit=False),
                             scopefunc=current_task
                            )
    
    @asynccontextmanager
    async def session(self):
        session = self.session_factory()
        try:
            yield session
        except Exception:
            await session.rollback()
        finally:
            await session.close()
  • это класс асинхронной сессии SQLAlchemy для подключения к БД.

  • @asynccontextmanager – декоратор, который возвращает объект асинхронного контекстного менеджера для генераторного объекта. В данном случае генерируются фабрики сессий для подключения к БД. При возникновении исключения все изменения откатываются. По окончании запросов соединение с БД закрывается.

  • async_sessionmaker – конфигуратор сессии.

  • async_scoped_session – это хранилище уже созданных сессий, каждая из которых привязана к своему потоку. Если вызвать сконфигурированный экземпляр scoped_session в новом потоке, он создаст новую сессию. А если потом из этого же потока вызвать scoped_session во второй раз, он вернёт ту же сессию, а не создаст новую.

  • self.session_factory – объект сессии (фабрика сессий(=транзакций) в рамках одного потока.

  • self.engine – интерфейс для работы с конкретной базой данных. Включает драйвер СУБД, точку доступа к БД, логин и пароль пользователя БД и другие настройки.

  • session — отдельная транзакция, которая может быть зафиксирована или отменена. Транзакция — это архив для запросов к базе. Она защищает данные по принципу «всё, или ничего». [4]

6. В коде приложения вы наткнулись на такой класс. Что можете про него рассказать?

class User(Base):
    __tablename__ = "user"
    username: Mapped[str] = mapped_column(String(length=50), unique=True, nullable=False, index=True)
    created_at: Mapped[datetime] = mapped_column(default=datetime.utcnow, server_default=func.now())
    permissions: Mapped[List["UserPermission"]] = relationship("UserPermission", lazy="selectin")
  • этот класс является отражением таблицы user в базе данных.

  • username: Mapped[str] = mapped_column – атрибут username соответствует столбцу с таким же именем.

  • String(length=50) – столбец может содержать только строковые значения, длина каждой записи не более 50 символов.

  • unique=True – повторение записей в одном столбце не допускается.

  • nullable=False – пустая запись не допускается.

  • index=True – сделать столбец индексируемым для ускорения поиска. Однако индексы замедляют операции добавления, обновления, удаления строк таблицы, поскольку при этом приходится обновлять сами индексы.

  • default – запись ячейки в столбце по умолчанию со стороны клиента.

  • server_default – запись ячейки в столбце по умолчанию со стороны сервера.

  • foreign_keys – ссылка на ячейку другой таблицы.

  • relationship("UserPermission", lazy="selectin") – связь с таблицей UserPermission.

  • lazy="selectin" – ленивая загрузка, загружает связанные объекты UserPermission в один запрос с User.

7. В коде приложения вы наткнулись на такую функцию. Что можете про неё рассказать?

async def get_users_with_posts_and_profiles(session: AsyncSession):
    stmt = (select(User).options(joinedload(User.profile), selectinload(User.posts)).order_by(User.id))
    users = await session.scalars(stmt)
    return list(users)
  • эта функция возвращает список пользователей и загружает связанные записи о профилях и постах, используя методы оптимизации запросов (Eager loading):

    • joinedload(User.profile) загружает связанные записи о профилях пользователей через оператор JOIN

    • selectinload(User.posts) генерирует второй оператор SELECT, который загружает связанные дочерние объекты Posts для каждого объекта User

Список литературы

  1. Официальная документация «SQLAlchemy 2.0 Documentation»: https://docs.sqlalchemy.org/en/20/intro.html

  2. Статья «Крадущийся тигр, затаившийся SQLAlchemy. Основы»: https://habr.com/ru/articles/470285/

  3. Статья «Python: Работа с базой данных, часть 2/2: Используем ORM»: https://habr.com/ru/articles/322086/

  4. Статья «Что такое транзакция»: https://habr.com/ru/articles/537594/

  5. Статья «Правильно работаем с сессиями БД в SQLAlchemy»: https://otus.ru/nest/post/250/

Last updated