Подтвердить что ты не робот

Sqlalchemy добавить ребенка в отношения "один ко многим"

Это первый раз, когда я использовал ORM, поэтому я не уверен, что это лучший способ справиться с этим. У меня есть отношения "один ко многим", где у каждого родителя может быть много детей:

class Parent(Base):
    __tablename__ = 'Parent'

    name = Column(String(50))
    gid = Column(String(16), primary_key = True)
    lastUpdate = Column(DateTime)

    def __init__(self,name, gid):
        self.name = name
        self.gid = gid
        self.lastUpdate = datetime.datetime.now()


class Child(Base):
    __tablename__ = 'Child'

    id = Column(Integer, primary_key = True)
    loc = Column(String(50))
    status = Column(String(50))

    parent_gid = Column(String(16), ForeignKey('Parent.gid'))

    parent = relationship("Parent", backref=backref('children'))

Теперь обновления поступают по сети. Когда приходит обновление, я хочу ОБНОВИТЬ соответствующую родительскую строку (обновление столбца LastUpdate) и INSERT новых дочерних строк в базу данных. Я не знаю, как это сделать с ORM. Вот моя неудачная попытка:

engine = create_engine('sqlite+pysqlite:///file.db',
                       module=dbapi2)
Base.metadata.create_all(engine)
session = sessionmaker(bind=engine)()

def addChildren(parent):
    p = session.query(Parent).filter(Parent.gid == p1.gid).all()
    if len(p) == 0:
        session.add(p1)
        session.commit()
    else:
        updateChildren = parent.children[:]
        parent.chlidren = []
        for c in updateChildren:
            c.parent_gid = parent.gid

        session.add_all(updateChildren)
        session.commit()

if __name__ == '__main__':

    #first update from the 'network'
    p1 = Parent(name='team1', gid='t1')
    p1.children = [Child(loc='x', status='a'), Child(loc='y', status='b')]
    addChildren(p1)

    import time
    time.sleep(1)

    #here comes another network update
    p1 = Parent(name='team1', gid='t1')
    p1.children = [Child(loc='z', status='a'), Child(loc='k', status='b')]
    #this fails
    addChildren(p1)

Сначала я попытался выполнить слияние, но это привело к тому, что старые дети были отключены с родителем (для иностранных идентификаторов было установлено значение null). Каков наилучший способ приблизиться к ORM? Благодаря

ИЗМЕНИТЬ

Думаю, на самом деле не имеет смысла создавать совершенно новые объекты, когда обновления попадают по сети. Я должен просто запросить сеанс для соответствующего родителя, а затем создать новые дети, если необходимо, и слить? Например.

def addChildren(pname, pid, cloc, cstat):
    p = session.query(Parent).filter(Parent.gid == pid).all()
    if len(p) == 0:
        p = Parent(pname, pid)
        p.children = [Child(loc=cloc, status=cstat)]
        session.add(p)
        session.commit()
    else:
        p = p[0]
        p.children.append(Child(loc=cloc, status=cstat))
        session.merge(p)
        session.commit()
4b9b3361

Ответ 1

Вы правы - вы не должны создавать один и тот же родитель дважды. Что касается добавления детей,... ну, вам действительно нужно только добавить их, и вам все равно, что у них есть... Таким образом, ваш отредактированный код должен отлично выполнять эту работу. Вы можете сделать его более коротким и читаемым, хотя:

def addChildren(pname, pid, cloc, cstat):
    p = session.query(Parent).get(pid) # will give you either Parent or None
    if not(p):
        p = Parent(pname, pid)
        session.add(p)
    p.children.append(Child(loc=cloc, status=cstat))
    session.commit()

Недостатком этого способа является то, что для существующего родителя вся коллекция Children будет загружена в память до того, как новый ребенок будет добавлен и впоследствии сохранен в базе данных. Если это так (много и увеличивающееся число детей для каждого родителя), то может оказаться полезным lazy='noload':

parent = relationship("Parent", backref=backref('children', lazy='noload'))

Это может значительно повысить скорость вставки, но в этом случае доступ к p.children будет никогда загружать существующие объекты из базы данных. В таких сценариях достаточно определить другую взаимосвязь. В этих ситуациях я предпочитаю использовать Building Query-Enabled Properties, поэтому вы можете добавить только одно свойство для добавления объектов, а другой только для запроса сохраняемых результатов, которые часто используются разными частями системы.