Вставка дочернего объекта с родителем, когда родитель уже существует, с помощью SQLAlchemy

#sqlalchemy

Вопрос:

Я хотел бы вставить дочерний объект (см. Определения классов ниже), который связан с родительским объектом, который может существовать или не существовать в базе данных, а затем получить сгенерированный первичный ключ для дочернего объекта. Я пробовал использовать Session.add и то, и Session.merge другое, но у меня возникли проблемы с обоими.

  1. Использование Session.add не работает, если родительский объект уже существует в таблице. Например, не удается выполнить следующее:
 # Create a parent with id 1
parent = Parent(id = 1)
with Session(engine) as session:
    session.add(parent)
    session.commit()

...

# Later, add a child whose parent is the one with id 1. 
# I know the parent id and don't need to fetch it from
# the database, thus I'm directly creating the parent object. 
parent = Parent(id = 1)
child = Child(parent = parent)
with Session(engine) as session:
    session.add(child)
    session.commit()
    print("child.id = "   str(child.id))
 

Он производит:

 IntegrityError: (psycopg2.errors.UniqueViolation) duplicate key value violates unique constraint "parent_pkey"
DETAIL:  Key (id)=(1) already exists.
 

SQLAlchemy пытается снова добавить родителя, но жалуется, что первичный ключ «1» уже существует.

  1. Использование Session.merge работает, но я не могу получить сгенерированный идентификатор для нового ребенка:
 # The Parent with id = 1 now exists in the parent table

# Add the child with the parent using merge
parent = Parent(id = 1)
child = Child(parent = parent)
with Session(engine) as session:
    session.merge(child)
    session.commit()
    print("child.id = "   str(child.id))
 

Это показывает child.id = None .

Я, вероятно, не подхожу к правильному пути, и я был бы очень признателен за некоторые указания.

Вот примеры определений классов:

     class Parent(Base):
        __tablename__ = 'parent'

        id = Column(Integer, primary_key = True)

        children = relationship("Child", back_populates = "parent")

    class Child(Base):
        __tablename__ = 'children'

        id = Column(Integer, primary_key = True)
        parent_id = Column(Integer, ForeignKey("parent.id"), nullable = False)

        parent = relationship("Parent", back_populates = "children")


 

Ответ №1:

Вместо произвольного создания parent = Parent(id = 1) вы должны проверить, существует ли оно уже:

 # retrieve parent from database …
parent1 = session.get(Parent, 1)
if not parent1:
    # … and create if not found
    session.add(parent1 := Parent(id=1, name="Homer"))
 

Комментарии:

1. Спасибо! Мне было интересно , будет ли способ избежать ручной проверки наличия объекта в базе get данных, например, если это будет частью магии, которую SQLAlchemy выполняет с объектами отношений. Но я думаю, что нет…?