sqlalchemy flush () i wstawiono identyfikator?


114

Chcę zrobić coś takiego:

f = Foo(bar='x')
session.add(f)
session.flush()

# do additional queries using f.id before commit()
print f.id # should be not None

session.commit()

Ale f.idjest None, gdy próbuję go. Jak to działa?


2
Czy możesz zainicjować silnik SA za pomocą echo=Truei zobaczyć, jaki kod SQL jest wykonywany w czasie usuwania? To, co opisujesz, powinno zadziałać i podać identyfikator, ale może jest jakiś inny problem, który powoduje, że f.id ma wartość Brak.
Pavel Repin

Odpowiedzi:


63

Twój przykładowy kod powinien działać tak, jak jest. SQLAlchemy powinien podać wartość dla f.id, zakładając, że jest to automatycznie generująca się kolumna klucza podstawowego. Atrybuty klucza podstawowego są wypełniane natychmiast w flush()procesie w trakcie ich generowania i nie commit()powinno być wymagane żadne wywołanie . Tak więc odpowiedź tutaj leży w jednym lub kilku z poniższych:

  1. Szczegóły twojego mapowania
  2. Jeśli są jakieś dziwne dziwactwa używanego zaplecza (na przykład SQLite nie generuje wartości całkowitych dla złożonego klucza podstawowego)
  3. Co mówi emitowany SQL po włączeniu echa

1
Masz rację, szybkie sprawdzenie w powłoce pokazuje, że wypełnia ona pole klucza podstawowego wartością. Będę musiał zbadać, dlaczego to nie działa w praktyce.
Eloff

3
„Twój przykładowy kod powinien podawać wartość” brzmi tak, jakbyś mówił „powinieneś był najpierw podać wartość identyfikatora”, a nie „kod, który miałeś działać tak, jak jest”. Dopiero po trzecim czytaniu stało się dla mnie jasne, że miałeś na myśli raczej to drugie niż pierwsze. Czy mógłbyś to wyjaśnić?
stochastyczny

126

Właśnie natknąłem się na ten sam problem i po przetestowaniu stwierdziłem, że ŻADNA z tych odpowiedzi nie jest wystarczająca.

Obecnie, lub od sqlalchemy .6+, istnieje bardzo proste rozwiązanie (nie wiem, czy istnieje w poprzedniej wersji, chociaż wyobrażam sobie, że tak jest):

session.refresh ()

Twój kod wyglądałby więc mniej więcej tak:

f = Foo(bar=x)
session.add(f)
session.flush()
# At this point, the object f has been pushed to the DB, 
# and has been automatically assigned a unique primary key id

f.id
# is None

session.refresh(f)
# refresh updates given object in the session with its state in the DB
# (and can also only refresh certain attributes - search for documentation)

f.id
# is the automatically assigned primary key ID given in the database.

Tak to się robi.


8
To jest coraz bliżej odpowiedzi, która może mi zadziałać, ale pojawia się następujący błąd: InvalidRequestError: Nie można odświeżyć instancji „<....>”. Po opróżnieniu okazuje się, że instancja po prostu już nie istnieje. Doceń każdy wgląd.
PlaidFan

1
Właśnie uratowałeś mi tyłek. Nie sądzę, żebym kiedykolwiek użył ORM pochodzącego z Django. Polecenie flush () NIE działa zgodnie z dokumentacją IMHO.
Marc

2
Aktualizacja, musiałem użyć sessionmaker(autoflush=True), że combo w / refresh () dostarczyło mi identyfikator wiersza. #grrr
Marc

5
Jeśli nie rozumiesz różnicy między flush()i commit(), oto dobre wyjaśnienie: stackoverflow.com/a/4202016/1252290
Epoc

2
@PlaidFan Zamiast flush()używać commit()i zaraz potem - odśwież go session.refresh(f), działa dla mnie i używam wersji SQLAlchemy0.6.7
Ricky Levi

20

Dziękuję wszystkim. Rozwiązałem swój problem, modyfikując mapowanie kolumn. Dla mnie,autoincrement=True jest to wymagane.

pochodzenie:

id = Column('ID', Integer, primary_key=True, nullable=False)

po modyfikacji:

id = Column('ID', Integer, primary_key=True, autoincrement=True, nullable=True)

następnie

session.flush()  
print(f.id)

jest ok!


6

w przeciwieństwie do odpowiedzi udzielonej przez dpb, odświeżanie nie jest konieczne. po opróżnieniu możesz uzyskać dostęp do pola id, sqlalchemy automatycznie odświeża identyfikator, który jest automatycznie generowany na zapleczu

Napotkałem ten problem i po pewnym dochodzeniu ustaliłem dokładną przyczynę, mój model został utworzony z id jako całkowitym polem, aw mojej postaci identyfikator był reprezentowany przez ukryte pole (ponieważ nie chciałem pokazywać id w moim formularzu). Ukryte pole jest domyślnie reprezentowane jako tekst. gdy zmieniłem formularz na integerfield z widget = hiddenInput ()) problem został rozwiązany.


1
Jak stwierdzono, działało tylko odświeżanie (). Wykonując migrację danych, potrzebowałem identyfikatora wiersza w pętli, aby wypełnić FK. Próbowałem wszystkich kombinacji commit, flush, session hacking i refresh (), które działały. Moje dane są super czyste i właśnie stwierdzam, że SQLA nie jest tak dobry (mam znaczące doświadczenie w co najmniej 5 głównych ORMS). Po prostu zawijanie przez ponad 5 godzin próbując uzyskać identyfikator wiersza dla add () -> commit () / flush ().
Marc

1

Kiedyś miałem problem z przypisaniem 0do id przed wywołaniem session.addmetody. Identyfikator został poprawnie przypisany przez bazę danych, ale poprawny identyfikator nie został pobrany z sesji po session.flush().


-7

Powinieneś spróbować użyć session.save_or_update(f)zamiast session.add(f).


1
save_or_updatejest przestarzały od około 0,5 roku. session.add()powinien to zrobić.
Pavel Repin
Korzystając z naszej strony potwierdzasz, że przeczytałeś(-aś) i rozumiesz nasze zasady używania plików cookie i zasady ochrony prywatności.
Licensed under cc by-sa 3.0 with attribution required.