2010-10-13 43 views
15

Tôi có một mô hình sqlalchemy, trong đó tất cả hầu hết tất cả các bảng/đối tượng đều có trường ghi chú. Vì vậy, để thử theo nguyên tắc DRY, tôi đã chuyển trường này sang một lớp mixin.sqlalchemy Di chuyển các cột kết hợp để kết thúc

class NotesMixin(object): 
    notes = sa.Column(sa.String(4000) , nullable=False, default='') 

class Service(Base, NotesMixin): 
    __tablename__ = "service" 
    service_id = sa.Column(sa.Integer, primary_key=True) 
    name = sa.Column(sa.String(255), nullable=False, index=True, unique=True) 


class Datacenter(Base, NotesMixin): 
    __tablename__ = "datacenter" 
    datacenter_id = sa.Column(sa.Integer, primary_key=True) 
    name = sa.Column(sa.String(255), nullable=False, index=True, unique=True) 


class Network(Base, NotesMixin, StatusMixin): 
    __tablename__ = "network" 
    network_id = sa.Column(sa.Integer, primary_key=True) 

etc... 

Bây giờ cột ghi chú là cột đầu tiên trong mô hình/db. Tôi biết nó không ảnh hưởng đến chức năng của ứng dụng của tôi, nhưng nó kích thích tôi một chút để xem ghi chú trước khi id, vv Bất kỳ cách nào để di chuyển nó đến cùng?

Trả lời

15

Tìm thấy một giải pháp sạch hơn:

Sử dụng sqlalchemy.ext.declarative.declared_attr trang trí trong SQLAlchemy 0.6.5 (sqlalchemy.util.classproperty trong SQLAlchemy < = 0.6.4)

class NotesMixin(object): 
    @declared_attr 
    def notes(cls): 
     return sa.Column(sa.String(4000) , nullable=False, default='') 

Theo các tài liệu, đây là "cho các cột có khóa ngoại, cũng như cho nhiều cấu trúc cấp bản đồ yêu cầu bối cảnh rõ ràng đích ". Trong khi điều này là nghiêm túc nói không phải là trường hợp ở đây, nó làm như vậy bằng cách gọi phương thức (và tạo cột) khi lớp con được xây dựng, do đó tránh sự cần thiết phải tạo một bản sao. Có nghĩa là cột mixin sẽ kết thúc. Có thể là một giải pháp tốt hơn so với hack _creation_order ...

+0

Tôi chắc chắn ưa thích giải pháp sạch hơn vì nó được xây dựng thành sqlalchemy rồi. Tôi nhận thấy rằng thứ tự mà bạn thừa hưởng các vấn đề mixin, nếu bạn có nhiều mixin cơ sở khai báo. Đó là điều tôi mong đợi. –

+0

Giải pháp tuyệt vời, cảm ơn! – sleepycal

+1

Trong khi điều này đòi hỏi ít mã, nhưng nó cảm thấy một vòng xoay nhỏ như nó sử dụng một cách "hack", đó là, một tác dụng phụ không có ý thức của ràng buộc khóa nước ngoài để đảm bảo đặt hàng. Tôi thích giải pháp '_creation_order' vì đó là cách dự định để ** KHUYẾN CÁO ** đặt thứ tự tạo cột, bất kể là FK hay không. – Devy

5

Câu trả lời dễ dàng: chỉ cần tạo bảng cơ sở dữ liệu cho mình, thay vì có sqlalchemy làm điều đó với metadata.create_all().

Nếu bạn không thấy điều đó có thể chấp nhận được, tôi sợ điều này sẽ yêu cầu thay đổi (nhỏ) trong số sqlalchemy.ext.declarative hoặc bạn phải tạo metaclass của riêng mình và chuyển nó tới declarative_base() với đối số từ khóa metaclass. Lớp đó sẽ được sử dụng thay vì mặc định là DeclarativeMeta.

Giải thích: sqlalchemy sử dụng thứ tự tạo của thuộc tính cột, nó lưu trữ trong thuộc tính "riêng tư" ._creation_order (được tạo khi Column() được gọi). Phần mở rộng khai báo không trộn lẫn các cột bằng cách tạo một bản sao của đối tượng cột từ lớp mixin của bạn và thêm vào lớp đó. ._creation_order của bản sao này được đặt thành cùng giá trị với thuộc tính ban đầu của lớp mixin. Vì lớp mixin dĩ nhiên được tạo đầu tiên, các thuộc tính cột của nó sẽ có thứ tự tạo thấp hơn so với lớp con.

Vì vậy, để thực hiện yêu cầu của bạn, cần phải gán đơn đặt hàng mới khi bản sao được tạo, thay vì lấy bản gốc. Bạn có thể thử và tạo metaclass của riêng bạn dựa trên lời giải thích này, và sử dụng nó. Nhưng bạn cũng có thể thử và hỏi các nhà phát triển sqlalchemy. Có lẽ họ sẵn lòng chấp nhận điều này như một yêu cầu về lỗi/tính năng? Ít nhất, nó dường như giống như thay đổi nhỏ (một dòng), sẽ không có bất kỳ tác động nào khác ngoài thay đổi mà bạn yêu cầu (điều này được cho là tốt hơn).

+4

Cảm ơn. Sử dụng thông tin đó, tôi đã làm: 'NotesMixin.notes._creation_order = 9999' –

+0

Mặc dù với ít phiếu bầu hơn, đây là cách" đúng "vì nó ** XIN LÃI ** đặt thứ tự tạo cột giống như OP được hỏi, bất kể cột nào là một FK hay không. – Devy

+0

Bạn đặt NotesMixin.notes._creation_order = 9999 ở đâu? Trong Mixin hoặc trong phân lớp? Hay ở đâu đó khác? –

2

Người ta cũng có thể thay đổi thứ tự các cột khi CREATE TABLE biên soạn (ở đây minh họa cho phương ngữ postgresql):

from sqlalchemy.schema import CreateTable 
from sqlalchemy.ext.compiler import compiles 


@compiles(CreateTable, 'postgresql') 
def _compile_create_table(element, compiler, **kwargs): 
    element.columns = element.columns[::-1] # reverse order of columns 
    return compiler.visit_create_table(element) 

này sau đó làm việc với metadata.create_all().

+0

Tôi đã sử dụng một biến thể của điều này: thêm một phím "đặt hàng" vào mỗi cột (ví dụ: 'Cột (..., thông tin = {'đặt hàng': 0})') và sắp xếp 'element.columns' bằng cách sử dụng khóa đó (ví dụ 'element.columns = được sắp xếp (element.columns, key = lambda c: c.element.info.get ('đặt hàng')') trong tạo bảng tổng hợp. – jadkik94

0

tôi thấy rằng tôi có thể thiết lập thứ tự cột (vào vị trí cuối cùng) trên Mixin sử dụng:

@declared_attr 
def notes(cls): 
    col = sa.Column(sa.String(4000) , nullable=False, default='') 
    # get highest column order of all Column objects of this class. 
    last_position = max([value._creation_order 
      for key, value in vars(cls).items() 
      if isinstance(value, Column)]) 
    col._creation_order = last_position + 0.5 
    return col 

class Service(Base, NotesMixin): 
    __tablename__ = "service" 
    service_id = sa.Column(sa.Integer, primary_key=True) 
    name = sa.Column(sa.String(255), nullable=False, index=True, unique=True) 

Để thiết lập thứ tự cột dựa trên vị trí của cột khác (tương tự như

alter table `some_table` modify `some_colum` `some_type` after `some_other_column;

thấy https://stackoverflow.com/a/3822219/488331)

Bạn có thể sử dụng:

@declared_attr 
def notes(cls): 
    col = sa.Column(sa.String(4000) , nullable=False, default='') 
    col._creation_order = cls.some_other_column._creation_order + 0.5 
    return col 

Chú ý: Nếu bạn sử dụng + 1 bạn kết thúc 2 cột trở lại. Tôi không thực sự hiểu tại sao bạn thậm chí có thể sử dụng một số thập phân.

Để thiết lập thứ tự cột dựa tắt của vị trí của cột đầu tiên (chắc lúc nào cũng là cột thứ 4 này), bạn có thể làm:

@declared_attr 
def notes(cls): 
    col = sa.Column(sa.String(4000) , nullable=False, default='') 
    # get lowest column order of all Column objects of this class. 
    start_position = min([value._creation_order 
      for key, value in vars(cls).items() 
      if isinstance(value, Column)]) 
    col._creation_order = start_position + 3.5 
    return col 
Các vấn đề liên quan