Open Debianov opened 1 year ago
Обязательно ли использование адаптеров Loader и Dumper? В БД всё равно будут хранится только id целей: при загрузке отдаём id объектов, при выгрузке получаем id целей и при надобности образуем в Member или Channel. И делать это не в адаптерах, а отдельными функциями до и после взаимодействия с БД. Чтобы этим не занимался psycopg.
Почитал документацию psycopg и я могу вынести предположение, как сделать это с помощью адаптеров:
В теме про конфигурации адаптации данных есть абзац:
By default, connections obtain an adapters map from the global map exposed as
psycopg.adapters
: changing the content of this object will affect every connection created afterwards. You may specify a different template adapters map using the context parameter onconnect()
.
Говорят, мол, по умолчанию каждое соединение к БД берёт адаптеры из глобальной карты адаптеров в psycopg.adapters
. Если хочется добавить свои глобально, то добавляйте адаптеры в psycopg.adapters
: при каждом соединении будут ваши адаптеры. Если хотите адаптеры, зависящие от контекста, указывайте context
при получении соединения БД connect()
.
Тип context
при получении соединения это psycopg.adapt.AdaptersMap
, который хранит в себе адаптеры для соединения. Именно его вы используете, чтобы добавить свои адаптеры.
Получается, при новом соединении к БД можно указать в качестве context
эту карту адаптеров, которую мы создадим перед указанием context
, и в неё будут добавлены адаптеры, преобразующие данные в зависимости от контекста. Но как сделать сами адаптеры преобразовывающими от контекста?
register_dumper()
и register_loader()
принимают только классы соответсвующих адаптеров: не получится сделать объект адаптера. И в этом адаптере должен быть ctx
от бота, чтобы обрабатывать данные в зависимости от контекста.
Далее, моё предположение реализации:
Для динамического создания классов в Python есть функция type()
. Её и буду использовать.
При вызове нового соединения в качестве context
будем указывать вызов функции, который вернёт psycopg.adapt.AdaptersMap
, в котором будут наши контекстные адаптеры. Для создания контекстных адаптеров буду преобразовывать текущие DiscordObjectsDumper
и DiscordObjectsLoader
в новый класс со своим атрибутом ctx
.
В итоге получится что-то вроде такого:
class DiscordObjectsLoader:
ctx = None
def load(self, to_load):
print("With context:", self.ctx)
print("Loading:", to_load)
class DiscordObjectsDumper:
ctx = None
def dump(self, to_dump):
print("With context:", self.ctx)
print("Dumping:", to_dump)
def context_factory(ctx):
return (
type(
'CtxDiscordObjectsLoader',
(DiscordObjectsLoader,),
{'ctx': ctx}
),
type(
'CtxDiscordObjectsDumper',
(DiscordObjectsDumper,),
{'ctx': ctx}
)
)
def context_adapters(ctx):
adapters = []
Loader, Dumper = context_factory(ctx)
adapters.append(Loader)
adapters.append(Dumper)
return adapters
ctx = "my_ctx"
# with psycopg.connect(connection_string=..., context=context_adapters(ctx)):
# Load something
# Dump sometning
# hided
adapters = context_adapters(ctx)
Loader, Dumper = adapters
a = Loader()
a.load("foo")
b = Dumper()
b.dump("bar")
# hided
В зависимости от ctx
будем получать разные сообщения:
With context: my_ctx
Loading: foo
With context: my_ctx
Dumping: bar
Так как вы используете только одно соединение, то можно адаптировать вариант под cursor
: после создания курсора добавить в него адаптеры, так как он тоже поддерживает psycopg.adapt.AdaptersMap
(почему).
Возможно я что-то упустил в документации, но мне больше нечего сказать.
Довольно накладно выходит делать на каждый ctx по своему объекту. К сожалению, передать второй аргумент никак нельзя, передавать как-то кортежем через data вместе с основными данными к преобразованию не является нормой, поэтому решено убрать эти два класса из регистрации и вызывать их методы вручную.
Убирать два этих класса тоже не вариант. Они нужны, иначе придётся прописывать обработку вручную во многих местах кода.
То что вы реализовали - это глобальная переменная, ссылку на которую вы дали в классе адаптера Loader. Так как бот асинхронный, то в момент выгрузки данных с ctx первого соединения может вмешаться второе, которое вставит своё ctx в Provider, и первое соединение теперь будет пользоваться ctx от второго соединения, что может послужить многим проблемам.
Убирать два этих класса тоже не вариант. Они нужны, иначе придётся прописывать обработку вручную во многих местах кода.
Можете рассказать, как вы взаимодействуете с полученными id объектов из БД? Зачем их превращать в Discord объекты? Я не понимаю всей ситуации и предлагаю решения основываясь только на тексте из этого issue, из-за чего решения могут не подойти к настоящей обстановке.
Так как бот асинхронный, то в момент выгрузки данных с ctx первого соединения может вмешаться второе, которое вставит своё ctx в Provider, и первое соединение теперь будет пользоваться ctx от второго соединения, что может послужить многим проблемам.
Да, я собираюсь делать словарь с извлечением контекста по объекту сообщения.
Можете рассказать, как вы взаимодействуете с полученными id объектов из БД? Зачем их превращать в Discord объекты?
Ну хотя бы взять извлечение объектов из БД по схожим критериям для поиска совпадений.
Да, я собираюсь делать словарь с извлечением контекста по объекту сообщения.
Тогда можете, пожалуйста, это сделать? Или как-то покажите, что данная проблема ещё не решена, например оставить этот issue открытым. Ведь если всё так и оставить будут соответствующие проблемы.
Ну хотя бы взять извлечение объектов из БД по схожим критериям для поиска совпадений.
Классно придумано.
оставить этот issue открытым
Понял
Проблема. Сейчас передача осуществляется через атрибут
aconn.adapters.discord_context
. Жизненно необходим, поскольку используется для преобразования id в объект. В дальнейшем также будет записываться в БД.