2012-12-25 33 views
8

Tôi có một lớp:SQLAlchemy - Cập nhật ForeignKey khi thiết lập các mối quan hệ

class ExampleClass(Base): 
    __tablename__ = 'chart' 
    id = Column(Integer, primary_key=True) 
    element_id = Column(Integer, ForeignKey('anotherTable.id')) 
    element = relationship(AnotherClass) 
    element2_id = Column(Integer, ForeignKey('anotherTable2.id')) 
    element2 = relationship(AnotherClass2) 

tôi muốn làm một tra cứu dựa trên element_idelement2_id:

class ExampleClass(Base): 
    ... 
    def get_with_element2(self, element2): 
     return session.query(ExampleClass).\ 
         filter_by(element_id = self.element_id, 
           element2_id = element2.id).first() 

Vấn đề tôi thấy là nếu Tôi tạo đối tượng ExampleClass mới và chỉ định đối tượng đó element, trường element_id không được đặt:

a = ExampleClass(element=element_obj) 
a.element_id => None 

Tôi làm cách nào để giải quyết vấn đề này? Cách tốt nhất để đối phó với tình huống này là gì?

Trả lời

9

Trước hết, tất cả các ví dụ dưới đây giả định rằng trường hợp ExampleClass của bạn ít nhất ở trạng thái pending nếu không phải trạng thái "liên tục" (nghĩa là, session.add(a)). Nói cách khác, nếu bạn chưa tương tác với một Session và chưa thêm đối tượng ExampleClass vào một đối tượng, thì bạn sẽ không nhận được bất kỳ hành vi cấp cơ sở dữ liệu nào của relationship(), trong đó duy trì các giá trị cột khóa ngoài là chính đặc tính. Bạn hoàn toàn có thể thực hiện nhiệm vụ này trực tiếp:

a = ExampleClass(element_id=element_obj.id) 

nhưng điều này rõ ràng là không sử dụng tự động được cung cấp bởi cấu trúc relationship().

Việc chuyển nhượng thuộc tính quan trọng nước ngoài bởi relationship() xảy ra trong một flush, mà là một quá trình mà chỉ xảy ra khi tương tác với cơ sở dữ liệu là cần thiết, chẳng hạn như trước khi bạn phát ra một câu lệnh SQL sử dụng session.query() hoặc trước khi bạn hoàn tất giao dịch của bạn sử dụng session.commit() .

Nói chung, triết lý của relationship() là bạn chỉ xử lý thuộc tính "phần tử" và "phần tử2" ở đây và để các thuộc tính khóa ngoài được xử lý sau hậu trường. Bạn có thể viết truy vấn của bạn như thế này:

session.query(ExampleClass).\ 
    filter_by(element=self.element).\ 
    filter_by(element2=element2) 

Các ORM sẽ mất một so sánh như SomeClass.somerelationship=someobject và chuyển đổi đó vào biểu foreign-key SomeClass.some_fk=some_id, nhưng sự khác biệt là, việc đánh giá giá trị cuối cùng của "some_id" được hoãn lại cho đến khi quyền truy vấn được thực thi ngay trước khi thực hiện. Trước khi truy vấn được thực thi, đối tượng Query() cho biết số Session để "tự động điền", sẽ có hiệu lực của hàng ExampleClass được chèn cùng với mã nhận dạng khóa chính của element_obj được gán cho thuộc tính element_id trên đối tượng ExampleClass.

bạn có thể có được một hiệu ứng tương tự trong khi vẫn sử dụng FK thuộc tính như thế này, đây là chủ yếu chỉ để hiểu làm thế nào nó hoạt động mặc dù:

session.query(ExampleClass).\ 
    filter_by(element_id=bindparam(callable_=lambda: self.element_id)).\ 
    filter_by(element2_id=element2.id) 

hoặc thậm chí rõ ràng hơn, làm tuôn ra đầu tiên:

session.flush() 
session.query(ExampleClass).\ 
    filter_by(element_id=self.element_id).\ 
    filter_by(element2_id=element2.id) 

Vì vậy, ở mức độ bạn muốn tham chiếu đến các thuộc tính khóa ngoài như element_id một cách rõ ràng, bạn cũng cần phải làm những điều relationship() cũng cho bạn một cách rõ ràng.Nếu bạn xử lý nghiêm ngặt với các cá thể đối tượng và thuộc tính ràng buộc relationship() và để mặc định thông thường như được kích hoạt autoflush, thông thường nó sẽ làm "điều đúng" và đảm bảo các thuộc tính sẵn sàng khi cần.

+0

Cảm ơn rất nhiều vì đã giải thích. Tôi không biết liệu tôi có thể lọc chính mối quan hệ đó và tôi không muốn làm mọi thứ rối tung lên. Vì vậy, chắc chắn đó là cách tôi sẽ làm điều đó. –

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