Как вставить nil_uuid в Postgres, используя SQLAlchemy?

#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 on INSERT .

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 в запросе.