2012-02-02 15 views
5

Giả sử có một đối tượng có tên duy nhất. Bây giờ bạn muốn chuyển sang tên của hai đối tượng:Làm cách nào để chuyển đổi hai trường của một hàng duy nhất trong một lần commit bằng SQLAlchemy?

Dưới đây là cách bố trí:

import sqlalchemy as sa 
import sqlalchemy.orm as orm 
from sqlalchemy.ext.declarative import declarative_base 

Base = declarative_base() 

class MyObject(Base): 
    __tablename__ = 'my_objects' 
    id = sa.Column(sa.Integer, primary_key=True) 
    name = sa.Column(sa.Text, unique=True) 

if __name__ == "__main__": 
    engine = sa.create_engine('sqlite:///:memory:', echo=True) 
    Session = orm.sessionmaker(bind=engine) 
    Base.metadata.create_all(engine) 
    session = Session() 

Và tôi muốn làm điều này:

a = MyObject(name="Max") 
b = MyObject(name="Moritz") 
session.add_all([a, b]) 
session.commit() 

# Now: switch names! 
tmp = a.name 
a.name = b.name 
b.name = tmp 
session.commit() 

này ném một IntegrityError. Có cách nào để chuyển đổi các trường này trong một lần commit mà không có lỗi này không?

Trả lời

4

Bạn đã unique=True trong lĩnh vực tên vì vậy khi bạn đang cố gắng để thực hiện nó sẽ chạy các truy vấn cập nhật nó sẽ nâng cao lỗi.

Tình hình là khi bạn thay đổi tên nó sẽ thiết lập trong bộ nhớ. Nhưng khi nó sẽ cố gắng chạy truy vấn cập nhật, bản ghi cũ đã tồn tại với cùng một tên để nó sẽ cung cấp cho các IntegrityError.

Cách đổi tên là

a = MyObject(name="Max") 
b = MyObject(name="Moritz") 
session.add_all([a, b]) 
session.commit() 

# Now: switch names! 
atmp = a.name 
btemp = b.name 

a.name = a.name+btemp # Temp set the any random name 
session.commit() 

b.name = atemp 
a.name = btemp 
session.commit() # Run the update query for update the record. 
+1

Cảm ơn bạn. Thật vậy, nó có vẻ là một tài sản SQL cơ bản được truyền trực tiếp thông qua SQLAlchemy. Dưới đây là một liên kết đến một câu hỏi liên quan: http://stackoverflow.com/questions/644/swap-unique-indexed-column-values-in-database –

+0

Đối với một số lý do tôi cũng đã phải thiết lập 'b.name' đến một giá trị ngẫu nhiên (bên cạnh 'a.name') bởi vì, trong một số trường hợp,' session.commit() 'thứ hai đã tăng một IntegrityError. – wil93

+0

Đầu tiên cam kết có lẽ nên được một flush(), bởi vì nếu bạn vi phạm ACID ngữ nghĩa (thay đổi của bạn không phải là nguyên tử). –

1

Python cho phép cú pháp này (sử dụng các bộ):

a.name, b.name = b.name, a.name 

Nó hoàn toàn không quan trọng để chuyển hai đối bình thường theo cách này, nhưng chưa được thử nghiệm trong tình huống của bạn, có lẽ bạn có thể cung cấp cho nó một thử?

+0

Chừng nào tôi còn làm không cam kết với cơ sở dữ liệu hoạt động tốt. Vấn đề có vẻ là cơ sở dữ liệu. –

+0

Tôi dường như không tìm thấy một câu lệnh SQL tương đương để thực hiện điều này. Có lẽ đây là điều mà SQLAlchemy không tính đến. –

3

Một lựa chọn tinh khiết hơn sẽ xóa, đổi tên b, sau đó lại thêm một đổi tên:

session.delete(a) 
sqlalchemy.orm.session.make_transient(a) 
a.name, b.name = b.name, a.name 
session.flush() 
session.add(a) 
session.commit()