2010-06-14 36 views
36

Tôi chỉ mới bắt đầu sử dụng SQLAlchemy và nhận được một DetachedInstanceError và không thể tìm thấy nhiều thông tin về điều này ở bất cứ đâu. Tôi đang sử dụng cá thể bên ngoài một phiên, vì vậy SQLAlchemy tự nhiên không thể tải bất kỳ quan hệ nào nếu chúng chưa được tải, tuy nhiên thuộc tính tôi truy cập không phải là quan hệ, trên thực tế đối tượng này không có quan hệ nào cả. Tôi tìm thấy các giải pháp như tải háo hức, nhưng tôi không thể áp dụng cho điều này bởi vì đây không phải là một mối quan hệ. Tôi thậm chí đã thử "chạm" thuộc tính này trước khi đóng phiên, nhưng nó vẫn không ngăn cản ngoại lệ. Điều gì có thể gây ra ngoại lệ này cho một thuộc tính phi quan hệ ngay cả sau khi nó đã được truy cập thành công một lần trước đó? Bất kỳ trợ giúp nào trong việc gỡ lỗi vấn đề này đều được đánh giá cao. Tôi sẽ cố gắng để có được một kịch bản độc lập có thể tái sản xuất và cập nhật tại đây.SQLAlchemy DetachedInstanceError với thuộc tính thông thường (không phải là một mối quan hệ)

Cập nhật: Đây là thông điệp ngoại lệ thực tế với một vài ngăn xếp:

File "/home/hari/bin/lib/python2.6/site-packages/SQLAlchemy-0.6.1-py2.6.egg/sqlalchemy/orm/attributes.py", line 159, in __get__ 
    return self.impl.get(instance_state(instance), instance_dict(instance)) 
    File "/home/hari/bin/lib/python2.6/site-packages/SQLAlchemy-0.6.1-py2.6.egg/sqlalchemy/orm/attributes.py", line 377, in get 
    value = callable_(passive=passive) 
    File "/home/hari/bin/lib/python2.6/site-packages/SQLAlchemy-0.6.1-py2.6.egg/sqlalchemy/orm/state.py", line 280, in __call__ 
    self.manager.deferred_scalar_loader(self, toload) 
    File "/home/hari/bin/lib/python2.6/site-packages/SQLAlchemy-0.6.1-py2.6.egg/sqlalchemy/orm/mapper.py", line 2323, in _load_scalar_attributes 
    (state_str(state))) 
DetachedInstanceError: Instance <ReportingJob at 0xa41cd8c> is not bound to a Session; attribute refresh operation cannot proceed 

Mô hình một phần trông như thế này:

metadata = MetaData() 
ModelBase = declarative_base(metadata=metadata) 

class ReportingJob(ModelBase): 
    __tablename__ = 'reporting_job' 

    job_id   = Column(BigInteger, Sequence('job_id_sequence'), primary_key=True) 
    client_id  = Column(BigInteger, nullable=True) 

Và client_id lĩnh vực là những gì đang gây ra ngoại lệ này với một cách sử dụng như bên dưới:

Truy vấn:

jobs = session \ 
      .query(ReportingJob) \ 
      .filter(ReportingJob.job_id == job_id) \ 
      .all() 
    if jobs: 
     # FIXME(Hari): Workaround for the attribute getting lazy-loaded. 
     jobs[0].client_id 
     return jobs[0] 

Đây là những gì gây nên các ngoại lệ sau ra khỏi phạm vi phiên:

 msg = msg + ", client_id: %s" % job.client_id 
+0

Điều này có thể trợ giúp: http://stackoverflow.com/a/25694346/134904 – kolypto

Trả lời

53

tôi thấy nguyên nhân gốc rễ trong khi cố gắng để thu hẹp mã gây ra ngoại lệ. Tôi đã đặt cùng một mã truy cập thuộc tính ở các vị trí khác nhau sau khi đóng phiên và thấy rằng nó chắc chắn không gây ra bất kỳ vấn đề nào ngay sau khi đóng phiên truy vấn. Nó chỉ ra vấn đề bắt đầu xuất hiện sau khi đóng một phiên mới được mở để cập nhật đối tượng. Một khi tôi hiểu rằng trạng thái của đối tượng là không thể sử dụng sau khi một phiên đóng, tôi đã có thể tìm thấy điều này thread mà thảo luận về cùng một vấn đề này. Hai giải pháp mà đi ra khỏi thread là:

  • Giữ một phiên mở (đó là hiển nhiên)
  • Chỉ định expire_on_commit=False-sessionmaker().

Các tùy chọn thứ 3 là để tự đặt expire_on_commit-False trên phiên khi nó được tạo ra, một cái gì đó như: session.expire_on_commit = False. Tôi đã xác minh rằng điều này giải quyết được vấn đề của tôi.

+4

Trợ giúp 'session.expunge_all()' có được trợ giúp không? – Sardathrion

+0

Điều đó giúp ích như thế nào? Dường như thực sự xóa phiên của tất cả các đối tượng được tải. – haridsv

+0

'session.expire_on_commit = False' là xấu, vì nếu cơ sở dữ liệu đã chuyển đổi các giá trị thành một cái gì đó, bạn sẽ không bao giờ biết nó – kolypto

8

Chúng tôi đã nhận được các lỗi tương tự, ngay cả với expire_on_commit được đặt thành False. Cuối cùng nó đã thực sự gây ra bởi có hai sessionmaker s đã được cả hai nhận được sử dụng để làm cho phiên trong các yêu cầu khác nhau. Tôi không thực sự hiểu những gì đang xảy ra, nhưng nếu bạn thấy ngoại lệ này với expire_on_commit=False, hãy đảm bảo bạn không có hai lần khởi tạo sessionmaker.

0

Đối với tôi (newbie), tôi đã phạm sai lầm trên thụt lề và đóng phiên trong vòng lặp của mình, trong đó tôi lặp mỗi hàng, thực hiện một số thao tác và cam kết mỗi lần.

Vì vậy, đối với những người mới như tôi, hãy kiểm tra mã của bạn trước khi thiết lập những thứ như expire_on_commit=False, nó có thể dẫn bạn đến một bẫy khác.

Các vấn đề liên quan