#python #postgresql #sqlalchemy
#python #postgresql #sqlalchemy
Вопрос:
Используя Python и SQLAlchemy, можно ли вставить значение None / NIL_UUID / NULL в столбец внешнего ключа Postgresql, который ссылается на первичный ключ, оба хранятся как UUID ?
None
ВОЗВРАТcolumn none does not exist
:
statement = "INSERT INTO tb_person (pk_person, first_name, last_name, fk_person_parent) VALUES ('9ce131...985
fea06', 'John', 'Doe', None)"
parameters = {}, context = <sqlalchemy.dialects.postgresql.psycopg2.PGExecutionContext_psycopg2 object at 0x7fbff5ea2730>
def do_execute(self, cursor, statement, parameters, context=None):
> cursor.execute(statement, parameters)
E psycopg2.errors.UndefinedColumn: column "none" does not exist
E LINE 1: '9ce131...985','John', 'Doe', None)
E ^
E HINT: Perhaps you meant to reference the column "tb_person.last_name".
../../.local/share/virtualenvs/project/lib/python3.8/site-packages/sqlalchemy/engine/default.py:593: UndefinedColumn
- a
NIL_UUID
(т.Е. Действительный UUID, сформированный с 0s) возвращаетpsycopg2.errors.ForeignKeyViolation
:
E psycopg2.errors.ForeignKeyViolation: insert or update on table "tb_person" violates foreign key constrain
t "tb_person_fk_person_parent_fkey"
E DETAIL: Key (fk_person_parent)=(00000000-0000-0000-0000-000000000000) is not present in table "tb_person
".
ПОДРОБНЕЕ
Я использую классическое сопоставление SQLAlchemy (ядро SQLAlchemy), моя таблица определяется следующим образом :
tb_person = Table(
"tb_person",
metadata,
Column(
"pk_person",
UUID(as_uuid=True),
default=uuid.uuid4,
unique=True,
nullable=False
),
Column("first_name", String(255)),
Column("last_name", String(255)),
Column(
"fk_person_parent", UUID(as_uuid=True),
ForeignKey("tb_person.pk_person"),
nullable=True
)
)
Сопоставитель определяется следующим образом :
client_mapper = mapper(
domain.model.Person,
tb_person,
properties={
"child": relationship(domain.model.Person),
},
)
Модульный тест хорошо работает при вставке UUID, который уже существует в базе данных, в поле pk_person.
Комментарии:
1. Возможно
INSERT
NULL
значение вFOREIGN KEY
поле. ВашеNIL_UUID
значение неNULL
соответствует, поэтому для него потребуется записьPRIMARY KEY
в таблице, на которую ссылается.2. @AdrianKlaver Спасибо, вы сформулировали мой вопрос в более короткие сроки, что я и сделал. Как я могу вставить NULL в поле UUID, когда ни None, ни NIL_UUID не работают?
3. На самом деле вы не передаете
None
, вместо этого вы передаете «None», который рассматривается как имя столбца. Еслиfk_person_parent
позволяетNULL
, то просто не включайте это поле вINSERT
иNULL
будет установлено для этого значения. Вы действительно должны использовать параметры для передачи значений, тогдаNone
они будут правильно адаптированы кNULL
onINSERT
.4. Из обратной трассировки psycopg2 инструкция гласит: «ВСТАВИТЬ В tb_person (pk_person, first_name, last_name, fk_person_parent) ЗНАЧЕНИЯ (‘9ce131 … 985fea06’, ‘John’, ‘Doe’, None)» — оно было вставлено как None (а не «None») в запросе, но, спасибо, я сделаюповторите попытку с использованием параметров.
5. Да, но, как показывает ошибка,
psycopg2.errors.UndefinedColumn: column "none" does not exist
, поскольку она была в строке, она была заключена в двойные кавычки и ошибочно принята за идентификатор (в данном случае столбец). Именно поэтому он был закрыт.
Ответ №1:
Я использовал необработанный SQL, и, как предложил @AdrianKlaver, гораздо предпочтительнее использовать параметры — это устраняет проблему.
# In the context of this unit test,
# we have to handle UUID generation here
uuid_1 = uuid.uuid4()
uuid_2 = uuid.uuid4()
uuid_3 = uuid.uuid4()
session.execute(
"""
INSERT INTO tb_person
(pk_person, first_name, last_name, fk_parent_person)
VALUES
(:uuid_1, 'John', 'Doe', :uuid_none),
(:uuid_2, 'Jean', 'Dupont', :uuid_none)
(:uuid_3, 'Baby', 'Doe', :uuid_1)
""",
{"uuid_1": uuid_1, "uuid_2", "uuid_3": uuid_3, ":uuid_none": None}
)
Он эффективно преобразуется в NULL в запросе.