sqlalchemy flush () và được chèn id?


114

Tôi muốn làm một cái gì đó như thế này:

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

Nhưng f.idNonekhi tôi thử nó. Làm thế nào tôi có thể làm cho nó hoạt động?


2
Bạn có thể khởi tạo công cụ SA bằng echo=True, và xem những gì SQL được thực thi tại thời điểm không? Những gì bạn mô tả sẽ hoạt động và cung cấp cho bạn id, nhưng có thể có một số vấn đề khác dẫn đến f.id là Không có.
Pavel Repin

Câu trả lời:


63

Mã mẫu của bạn lẽ ra phải hoạt động như bình thường. SQLAlchemy nên cung cấp một giá trị cho f.id, giả sử nó là cột khóa chính tự động tạo. Các thuộc tính khóa chính được điền ngay trong flush()quy trình khi chúng được tạo và không cần gọi tới commit(). Vì vậy, câu trả lời ở đây nằm trong một hoặc nhiều điều sau:

  1. Chi tiết về bản đồ của bạn
  2. Nếu có bất kỳ câu hỏi kỳ quặc nào của phần phụ trợ đang được sử dụng (chẳng hạn như SQLite không tạo giá trị số nguyên cho khóa chính tổng hợp)
  3. SQL phát ra nói gì khi bạn bật echo

1
Bạn nói đúng, kiểm tra nhanh trong shell cho thấy nó sẽ điền vào trường khóa chính một giá trị. Tôi sẽ phải điều tra lý do tại sao nó không hoạt động trong thực tế.
Eloff

3
"mã mẫu của bạn phải cung cấp một giá trị" nghe có vẻ như bạn đang nói "đáng lẽ bạn phải cung cấp một giá trị cho id ngay từ đầu", chứ không phải "mã bạn phải hoạt động như nó vốn có". Tôi đã trở nên rõ ràng rằng bạn muốn nói đến cái sau hơn là cái trước chỉ sau lần đọc thứ ba. Bạn có thể làm rõ?
stochastic

126

Tôi vừa gặp phải vấn đề tương tự và sau khi kiểm tra, tôi nhận thấy rằng KHÔNG CÓ câu trả lời nào trong số các câu trả lời này là đủ.

Hiện tại, hoặc kể từ sqlalchemy .6+, có một giải pháp rất đơn giản (tôi không biết liệu điều này có tồn tại trong phiên bản trước hay không, mặc dù tôi tưởng tượng là có):

session.refresh ()

Vì vậy, mã của bạn sẽ trông giống như sau:

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.

Đó là cách thực hiện.


8
Điều này đang tiến gần đến câu trả lời có thể phù hợp với tôi nhưng tôi nhận được lỗi sau: InvalidRequestError: Không thể làm mới phiên bản '<....>'. Nó xuất hiện sau khi tuôn ra rằng cá thể đơn giản là không còn tồn tại. Đánh giá cao bất kỳ cái nhìn sâu sắc nào.
PlaidFan

1
Bạn vừa cứu mông tôi. Tôi không nghĩ mình sẽ sử dụng lại ORM đến từ Django. Lệnh flush () KHÔNG hoạt động như IMHO được ghi lại.
Marc

2
Cập nhật, tôi phải sử dụng sessionmaker(autoflush=True), tổ hợp w / refresh () đó đã cung cấp cho tôi ID hàng. #grrr
Marc

5
Nếu bạn không hiểu sự khác biệt giữa flush()commit()dưới đây là một lời giải thích tốt: stackoverflow.com/a/4202016/1252290
Epoc

2
@PlaidFan Thay vì flush()sử dụng commit()và ngay sau đó - làm mới nó với session.refresh(f), làm việc cho tôi, và tôi sử dụng phiên bản SQLAlchemy0.6.7
Ricky Levi

20

Cảm ơn tất cả mọi người. Tôi đã giải quyết vấn đề của mình bằng cách sửa đổi ánh xạ cột. Đối với tôi, autoincrement=Truelà bắt buộc.

gốc:

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

sau khi sửa đổi:

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

sau đó

session.flush()  
print(f.id)

là ok!


6

không giống như câu trả lời được đưa ra bởi dpb, làm mới là không cần thiết. khi bạn xóa, bạn có thể truy cập trường id, sqlalchemy sẽ tự động làm mới id được tạo tự động tại phần phụ trợ

Tôi gặp phải sự cố này và đã tìm ra lý do chính xác sau một số cuộc điều tra, mô hình của tôi được tạo với id là trường số nguyên và ở dạng của tôi, id được đại diện bằng hiddenfield (vì tôi không muốn hiển thị id trong biểu mẫu của mình). Trường ẩn theo mặc định được biểu diễn dưới dạng văn bản. khi tôi thay đổi biểu mẫu thành trường số nguyên với widget = hiddenInput ()) thì vấn đề đã được giải quyết.


1
Như đã nói, chỉ refresh () làm việc cho tôi. Khi thực hiện di chuyển dữ liệu, tôi cần ID hàng trong một vòng lặp để điền FK. Tôi đã thử mọi kết hợp gồm commit, flush, session hacking và refresh () là thứ duy nhất có hiệu quả. Dữ liệu của tôi siêu sạch và tôi chỉ thấy rằng SQLA không thực sự tốt (có điểm kinh nghiệm đáng kể trong ít nhất 5 ORMS chính). Chỉ cần kéo dài hơn 5 giờ để cố gắng lấy id hàng cho add () -> commit () / flush ().
Marc

1

Tôi đã từng gặp sự cố với việc gán 0cho id trước khi gọi session.addphương thức. Id đã được chỉ định chính xác bởi cơ sở dữ liệu nhưng id chính xác không được truy xuất từ ​​phiên sau đó session.flush().


-7

Bạn nên thử sử dụng session.save_or_update(f)thay vì session.add(f).


1
save_or_updateđã không được chấp nhận kể từ 0,5 trở lên. session.add()Hãy làm nó.
Pavel Repin
Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.