Схема базы данных
В этой базе данных отслеживается, какие сотрудники какие эксперименты проводили ( «многие ко многим»), какие экспериментальные пластины в каких экспериментах участвуют (многие к одному) и какие из этих пластин признаны недействительными (необязательно «один к одному»).
Код: Выделить всё
-- staff members: staff_id is primary key
CREATE TABLE staff (
staff_id BIGINT,
personal TEXT,
family TEXT
);
-- experiments: sample_id is primary key
CREATE TABLE experiment (
sample_id BIGINT,
kind TEXT,
start TEXT,
"end" TEXT -- may be NULL if the experiment is ongoing
);
-- join table showing which staff were involved in which experiment
-- there is at least one staff member associated with every experiment
CREATE TABLE performed (
staff_id BIGINT,
sample_id BIGINT
);
-- plate_id is primary key; plate-to-experiment is many to one
- there is at least one plate associated with each experiment
CREATE TABLE plate (
plate_id BIGINT,
sample_id BIGINT,
date TEXT,
filename TEXT
);
-- invalidated plates (along with who invalidated the plate and when)
-- this table only contains records for plates that have been invalidated,
-- and contains at most one such record for each plate
CREATE TABLE invalidated (
plate_id BIGINT,
staff_id BIGINT,
date TEXT
);
Это просто, учитывая приведенные выше определения таблиц.
Код: Выделить всё
class Staff(DB.Entity):
staff_id = orm.PrimaryKey(int)
personal = orm.Required(str)
family = orm.Required(str)
performed = orm.Set("Performed")
invalidated = orm.Set("Invalidated")
class Experiment(DB.Entity):
sample_id = orm.PrimaryKey(int)
kind = orm.Required(str)
start = orm.Required(str)
end = orm.Optional(str)
performed = orm.Set("Performed")
plate = orm.Set("Plate")
class Performed(DB.Entity):
staff_id = orm.Required(Staff)
sample_id = orm.Required(Experiment)
orm.PrimaryKey(staff_id, sample_id)
class Plate(DB.Entity):
plate_id = orm.PrimaryKey(int)
sample_id = orm.Required(Experiment)
date = orm.Required(date)
filename = orm.Required(str)
invalidated = orm.Set("Invalidated")
class Invalidated(DB.Entity):
plate_id = orm.Required(Plate)
staff_id = orm.Required(Staff)
date = orm.Required(date)
orm.PrimaryKey(plate_id, staff_id)
Мне нужно следующее для каждого эксперимента:
- идентификатор эксперимента (чтобы я мог создать гиперссылку на страницу эксперимента)
- даты начала и окончания (опять же, дата окончания может быть нет, если эксперимент продолжается)
- есть ли в эксперименте какие-либо недействительные таблички
- идентификаторы и имена всех сотрудников, участвовавших в эксперименте
[*]Первые три поля (идентификатор, дата начала и дата окончания) легко получить, но мне нужно преобразовать два поля даты из текста в объекты даты Python после получения значений: у Pony есть функция str2date, но, похоже, нет способа использовать это внутри запроса. (Попытка сделать это приводит к появлению сообщений об ошибках.)
[*]Я ожидал, что функции Any() и all() будут работать вместе с функцией count() Pony, sum() и другие функции агрегирования, но они, похоже, не существуют, и Pony отказывается от использования встроенных функций. Поэтому я использую count() > 0, чтобы ответить на вопрос: «Были ли какие-либо пластины в этом эксперименте признаны недействительными?»
[*]Поскольку в работе могут участвовать несколько сотрудников В рамках одного эксперимента я создаю строку с идентификатором каждого сотрудника, личными именами и фамилиями, а затем использую Pony group_concat(), чтобы объединить их и получить одну строку в результате запроса. Затем я выполняю постобработку (удаляю, разделяю и преобразую идентификатор обратно в целое число). Я понимаю, что это действительно может быть необходимо, поскольку Pony не поддерживает вложенные поля (т. е., похоже, не может создавать результаты JSON), но я надеюсь, что есть более простой способ.
Код: Выделить всё
def experiment_details():
query = orm.select(
(
e.sample_id,
e.start,
e.end,
orm.count(e.plate.invalidated) != 0,
orm.group_concat(
f""
for s in Staff
if e.sample_id in s.performed.sample_id.sample_id
),
)
for e in Experiment
)
rows = list(query.order_by(lambda eid, start, end, inv, staff: eid))
reformatters = (_reformat_as_is, _reformat_date, _reformat_date, _reformat_as_is, _reformat_staff)
rows = [[f(r) for (f, r) in zip(reformatters, row)] for row in rows]
return rows
def _reformat_as_is(text):
"""Do not reformat (used for uniform zipping)."""
return text
def _reformat_date(text):
"""Reformat possibly-empty date."""
return None if text is None else str2date(text)
def _reformat_staff(text):
"""Convert concatenated staff information back to list of tuples."""
fields = text.lstrip("").split(">,
Подробнее здесь: [url]https://stackoverflow.com/questions/78829629/date-conversion-and-any-in-pony-orm-with-sqlite[/url]