2012-01-12 3 views
11

Это первый раз, когда я использовал ORM, поэтому я не уверен, что лучший способ справиться с этим. У меня есть отношения один-ко-многим, где каждый родитель может иметь много детей:sqlalchemy add child in one-to-many relationship

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) 

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

EDIT

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

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() 

ответ

24

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

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() 

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

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

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

+1

Чтобы добавить список детей, вы можете использовать 'p.children.extend ([list_of_children])', no? – aganders3

+1

Да, конечно. На самом деле, если мы уверены, что для данного 'pid' существует' Parent', а задача состоит только в том, чтобы хранить «Child (ren)», то что можно сделать, это не загружать отношения, а напрямую устанавливать Значение FK для 'parent_gid':' session.add (Child (loc = cloc, status = cstat, parent_gid = pid)) ', и в этом случае даже не нужно обманывать связь с' lazy = "noload" ', потому что код не будет использовать связь – van

+0

Не могли бы вы также показать, как обновить ребенка в том же сценарии? –