gscraft
@gscraft
Программист, философ

Как правильнее запросить/ограничить несколько отношений в Peewee?

Допустим, есть следующая простая структура данных:
Company(BaseModel):
    id = AutoField()
    # ...

User(BaseModel):
    id = AutoField()
    # ...

Delivery(BaseModel):
    id = AutoField()
     # peewee игнорирует lazy_load=True ?
    manager = ForeignKeyField(User, backref='deliveries', lazy_load=True)
    sender = ForeignKeyField(Company, backref='sender_deliveries')
    receiver = ForeignKeyField(Company, backref='receiver_deliveries')


Когда необходимо запросить заказы с менеджерами, получателями и отправителями, предполагается следующий код:
select = Delivery.select().limit(3)
— Peewee в любом случае запросит все внешние связи, причем, экономными запросами по идентификаторам.

Но, допустим, нам требуется выбрать только отправителей.
select = Delivery.select(Delivery.id, ..., Delivery.sender).limit(3)

— приходится ограничивать поля Delivery. Этот способ теплее, но вместе с тем все связи Company будут загружены, включая, например, пользователя-автора записи с его паролем.

Более строгое ограничение не получится сделать (или я не нашел способ), если мы вручную не ограничим поля и не сделаем JOIN:
Sender = Company.alias()
Receiver = Company.alias()
select = Delivery.select(Delivery.id, Sender, Receiver, ...).limit(3)
select = select.join(Sender, JOIN.LEFT_OUTER, on=(Sender.id == Delivery.sender))
select = select.join(Receiver, JOIN.LEFT_OUTER, on=(Receiver.id == Delivery.receiver))

— во-первых, JOIN во многих случаях имеет больше расходов, во-вторых, в каком-то смысле ломается чистота кода, придется уделять много внимания менеджменту выборки полей и связей.

Документация хорошо рассматривает именно JOIN в разделе отношений, но как быть с "автоматической" выборкой по ForeignKeyField? Хотелось бы обнаружить привычное with('sender'), но не вышло.

Есть ли способ изменять выборку без select(Model), join(Model)?
  • Вопрос задан
  • 229 просмотров
Решения вопроса 1
gscraft
@gscraft Автор вопроса
Программист, философ
Собственно, других вариантов нет, есть лишь один более-менее эффективный способ ограничить выборку, чтобы не жертвовать выборкой id IN и не нагромождать колонки, реализовать метод, для получения колонок, например:
class BaseModel(Model):
    class Meta:
        database = database

    @classmethod
    def fields(cls):
        fields = list()
        for field in cls._meta.sorted_fields:
            if not isinstance(field, ForeignKeyField):
                fields.append(field)
        return fields

И впоследствии выбирать так (из примера выше):
select = Delivery.select(Delivery.sender, *Delivery.fields()).limit(3)

Впрочем, это не отменит погружения, если sender имеет несколько зависимостей, они все будут запрошенными. Плюс, если запрос подразумевает условие по связанной модели, придется делать JOIN и Delivery.sender будет "дублироваться" и другие мелкие неудобства.

Жаль, Peewee одна из самых элегантных библиотек в своем роде и позволяет очень быстро прототипировать приложение. Но решения по загрузке связей у разработчиков странные и удивительно, что не работает lazy_load (а в обратном случае, как выбирать записи с погружением и без JOIN?).
Ответ написан
Комментировать
Пригласить эксперта
Ваш ответ на вопрос

Войдите, чтобы написать ответ

Войти через центр авторизации
Похожие вопросы