2014-05-16 7 views
13

Mam następujący model definicjeDependency zasada próbował pusty z klucza podstawowego w SQLAlchemy, gdy ograniczenie klucza obcego jest częścią kompozytowych klucz podstawowy

class Foo(Base): 
    __tablename__ = 'foo' 

    id = Column(Integer, primary_key=True) 
    name = Column(String(200)) 


class FooCycle(Base): 
    __tablename__ = 'foocycle' 

    foo_id = Column(
     String(50), 
     ForeignKey('foo.id'), 
     primary_key=True 
    ) 
    some_number = Column(
     Integer, 
     primary_key=True, 
    ) 

    foo = relationship("Foo", backref="cycles") 

i następujący przypadek testowy

class HierarchicModelTest(unittest.TestCase): 
    def test_create_data_via_orm_save_twice(self): 
     # get_session is a convenience wrapper to access a scoped session object 
     s = get_session() 

     def create_foo(): 
      foo = Foo(id="12345", name="fancy foo") 
      foo.cycles = [FooCycle(some_number=1)] 

      return foo 

     # initially create foo 
     foo = create_foo() 
     s.add(foo) 
     s.flush() 

     # recreating foo, using merge to update into database 
     foo = create_foo() 
     s.merge(foo) 

     # raises Exception: Dependency rule tried to blank-out primary key 
     # column 'foocycle.foo_id' on instance '<FooCycle at 0x32e6b10>' 
     s.flush() 

test kończy się niepowodzeniem z dokładnym niewielkim śladem stosu i ostatecznym błędem asercji, informującym mnie, że "reguła zależności próbowała wyzerować kluczową kolumnę klucza" foocycle.foo_id ". Zakładam, że SQLAlchemy nie może lub nie chce obliczyć wartości dla foo_id w samym FooCycle. Mogę jawnie ustawić tę wartość się w create_foo:

def create_foo(): 
    foo = Foo(id="12345", name="fancy foo") 
    foo.cycles = [FooCycle(some_number=1, foo_id="12345")] 

    return foo 

Jednak, ze względu na zwięzłość, względów architektonicznych i wprawdzie osobistej dumy nie chce. Czy istnieje prosty sposób, aby SQLAlchemy rozwiązać ten problem. Nie całkiem zrozumiałem cel reguły zależności. Jakieś wskazówki/informacje na ten temat?

stosu Ślad:

# Test 1 of 7: 
# test_core.HierarchicModelTest.test_create_data_via_orm_save_twice 
=============== 
HierarchicModelTest: test_create_data_via_orm_save_twice (tests.test_core.HierarchicModelTest) 
Failed test "test_create_data_via_orm_save_twice (tests.test_core.HierarchicModelTest)"! Reason: Dependency rule tried to blank-out primary key column 'foocycle.foo_id' on instance '<FooCycle at 0x39cda10>' 
Traceback (most recent call last): 
    File "/home/xxx/xxx/xxx/backend/tests/test_core.py", line 115, in test_create_data_via_orm_save_twice 
    s.flush() 
    File "/home/xxx/xxx/xxx/.env/lib/python2.7/site-packages/sqlalchemy/orm/scoping.py", line 149, in do 
    return getattr(self.registry(), name)(*args, **kwargs) 
    File "/home/xxx/xxx/xxx/.env/lib/python2.7/site-packages/sqlalchemy/orm/session.py", line 1879, in flush 
    self._flush(objects) 
    File "/home/xxx/xxx/xxx/.env/lib/python2.7/site-packages/sqlalchemy/orm/session.py", line 1997, in _flush 
    transaction.rollback(_capture_exception=True) 
    File "/home/xxx/xxx/xxx/.env/lib/python2.7/site-packages/sqlalchemy/util/langhelpers.py", line 57, in __exit__ 
    compat.reraise(exc_type, exc_value, exc_tb) 
    File "/home/xxx/xxx/xxx/.env/lib/python2.7/site-packages/sqlalchemy/orm/session.py", line 1961, in _flush 
    flush_context.execute() 
    File "/home/xxx/xxx/xxx/.env/lib/python2.7/site-packages/sqlalchemy/orm/unitofwork.py", line 370, in execute 
    rec.execute(self) 
    File "/home/xxx/xxx/xxx/.env/lib/python2.7/site-packages/sqlalchemy/orm/unitofwork.py", line 479, in execute 
    self.dependency_processor.process_saves(uow, states) 
    File "/home/xxx/xxx/xxx/.env/lib/python2.7/site-packages/sqlalchemy/orm/dependency.py", line 552, in process_saves 
    uowcommit, False) 
    File "/home/xxx/xxx/xxx/.env/lib/python2.7/site-packages/sqlalchemy/orm/dependency.py", line 569, in _synchronize 
    sync.clear(dest, self.mapper, self.prop.synchronize_pairs) 
    File "/home/xxx/xxx/xxx/.env/lib/python2.7/site-packages/sqlalchemy/orm/sync.py", line 53, in clear 
    (r, orm_util.state_str(dest)) 
AssertionError: Dependency rule tried to blank-out primary key column 'foocycle.foo_id' on instance '<FooCycle at 0x39cda10>' 
+1

Nie sądzę, jest to problem z SA nie jest w stanie obsłużyć 'foo_id'. W rzeczywistości jest to jeden z najlepszych punktów, w jaki SA jest w stanie to zrobić. Uważam, że problem polega na tym, że podczas "scalania" SA usuwam pierwszą instancję 'FooCycle' i wstawiam nową. Usunięcie pierwszego problemu jest problemem, ponieważ nie usuwa jego wiersza, a jedynie próbuje usunąć relację. Musisz skonfigurować odpowiednią regułę ['cascade'] (http://docs.sqlalchemy.org/en/rel_0_9/orm/relationships.html#sqlalchemy.orm.relationship.params.cascade) dla swojego związku. – van

+0

Dzięki za komentarz, który skierował mnie we właściwym kierunku. – room2web

+0

@van Czy mógłbyś nam powiedzieć, jaka jest właściwa * zasada kaskadowa w tym przypadku (najlepiej jako odpowiedź, jak sądzę, może to być zbyt wiele, aby zmieścić się w komentarzu)? Mówisz, że * nie usuwa swojego wiersza, ale próbuje jedynie usunąć relację *; ale jeśli wiersz zostanie usunięty, za każdym razem otrzymamy nowe wystąpienie 'FooCycle'. Czy istnieje sposób, aby zaktualizować SA już istniejące wiersze/wystąpienia "FooCycle" zamiast? –

Odpowiedz

19

podstawie komentarza Van udało mi się wypracować rozwiązanie. Domyślna kaskada relacji to "save-update, merge". Musiałem ustawić to na "save-update, merge, delete, delete-orphan".

Samo samodzielne dodanie delete nie było konieczne, konieczne było wykonanie delete-orphan.

Dodanie tylko delete-orphan popełnił usunięcie testcase nie, ze "zależność zasada" błąd twierdzenia wymienione w pytaniu:

class HierarchicModelTest(unittest.TestCase): 
    def test_delete_parent_object(self): 
     foo = Foo(**foo_data).save() 
     self.assertEqual(Foo.query.count(), 1) 
     self.assertEqual(FooCycle.query.count(), 1) 

     s = get_session() 
     s.delete(foo) 
     s.flush() 

     self.assertEqual(Foo.query.count(), 0) 
     self.assertEqual(FooCycle.query.count(), 0) 

-

File "/home/xxx/xxx/xxx/backend/tests/test_core.py", line 128, in test_delete_parent_object 
    s.flush() 
    [...] 
    AssertionError: Dependency rule tried to blank-out primary key column 'foocycle.foo_id' on instance '<FooCycle at 0x37a1710>' 

Od docs sqlalchemy:

delete-orphan kaskada dodaje zachowanie do kaskady delete, suc h, że obiekt podrzędny zostanie oznaczony do usunięcia, gdy zostanie odłączony od obiektu nadrzędnego, a nie tylko wtedy, gdy rodzic jest oznaczony do usunięcia.

więc poprawna definicja FooCycle model jest

class FooCycle(Base): 
    __tablename__ = 'foocycle' 

    foo_id = Column(
     String(50), 
     ForeignKey('foo.id'), 
     primary_key=True 
    ) 
    some_number = Column(
     Integer, 
     primary_key=True, 
    ) 

    foo = relationship("Foo", 
         backref=backref("cycles", 
             cascade="save-update, merge, " 
               "delete, delete-orphan")) 
+0

Jednak użycie 'delete-orphan' powoduje usunięcie już istniejących instancji' FooCycle' zamiast aktualizacji. Jeśli tożsamość 'FooCycle' jest ważna (na przykład, gdy chcemy ją przetworzyć zachowując historię wszystkich wartości historycznych), to rozwiązanie to nie może być użyte. –

Powiązane problemy