2010-04-04 44 views
10

Tôi có vấn đề sau đây:SQLAlchemy lập bản đồ năng động

Tôi có lớp:


class Word(object): 

    def __init__(self): 
     self.id = None 
     self.columns = {} 

    def __str__(self): 
     return "(%s, %s)" % (str(self.id), str(self.columns)) 

self.columns là một dict đó sẽ tổ chức: giá trị (ColumnName columnValue). Tên của các cột được biết trong thời gian chạy và họ được nạp trong một danh sách wordColumns, ví dụ


wordColumns = ['english', 'korean', 'romanian'] 

wordTable = Table('word', metadata, 
        Column('id', Integer, primary_key = True) 
       ) 
for columnName in wordColumns: 
    wordTable.append_column(Column(columnName, String(255), nullable = False)) 

Tôi thậm chí đã tạo ra một đặc tính mapper rõ ràng để "ép" các cột bảng để được ánh xạ vào từ. cột [columnName], thay vì word.columnName, tôi không nhận được bất kỳ lỗi nào khi ánh xạ, nhưng có vẻ như nó không hoạt động.


mapperProperties = {} 
for column in wordColumns: 
    mapperProperties['columns[\'%']' % column] = wordTable.columns[column] 

mapper(Word, wordTable, mapperProperties) 

Khi tôi tải một đối tượng văn bản, SQLAlchemy tạo ra một đối tượng trong đó có các word.columns [ 'tiếng Anh'], word.columns [ 'Hàn Quốc'], vv thuộc tính thay vì tải chúng vào word.columns dict . Vì vậy, đối với mỗi cột, nó tạo ra một thuộc tính mới. Hơn nữa từ điển word.columns thậm chí không tồn tại. Cũng giống như vậy, khi tôi cố gắng kiên trì một từ, SQLAlchemy hy vọng sẽ tìm thấy các giá trị cột trong các thuộc tính có tên như word.columns ['english'] (loại chuỗi) thay vì từ điển.columns.

Tôi phải nói rằng kinh nghiệm của tôi với Python và SQLAlchemy là khá hạn chế, có lẽ nó không thể làm những gì tôi đang cố gắng làm.

Bất kỳ trợ giúp nào được đánh giá cao,

Xin cảm ơn trước.

Trả lời

29

Có vẻ như bạn chỉ có thể sử dụng trực tiếp các thuộc tính thay vì sử dụng columnsdict.

Hãy xem xét các thiết lập sau:

from sqlalchemy import Table, Column, Integer, Unicode, MetaData, create_engine 
from sqlalchemy.orm import mapper, create_session 

class Word(object): 
    pass 

wordColumns = ['english', 'korean', 'romanian'] 
e = create_engine('sqlite://') 
metadata = MetaData(bind=e) 

t = Table('words', metadata, Column('id', Integer, primary_key=True), 
    *(Column(wordCol, Unicode(255)) for wordCol in wordColumns)) 
metadata.create_all() 
mapper(Word, t) 
session = create_session(bind=e, autocommit=False, autoflush=True) 

Với lớp trống bạn có thể làm:

w = Word() 
w.english = u'name' 
w.korean = u'이름' 
w.romanian = u'nume' 

session.add(w) 
session.commit() 

Và khi bạn muốn truy cập dữ liệu:

w = session.query(Word).filter_by(english=u'name').one() 
print w.romanian 

Đó là toàn bộ điểm ORM sqlalchemy của, thay vì sử dụng a tuple hoặc dict để truy cập dữ liệu, bạn sử dụng thuộc tính giống như truy cập thuộc lớp của riêng bạn.

Vì vậy, tôi đã tự hỏi lý do bạn muốn sử dụng một số dict. Có lẽ đó là bởi vì bạn có dây với tên ngôn ngữ. Vâng, cho rằng bạn có thể sử dụng python của getattrsetattr thay vào đó, như bạn trên bất kỳ đối tượng python:

language = 'korean' 
print getattr(w, language) 

Điều đó sẽ giải quyết tất cả các vấn đề của bạn.


Điều đó nói rằng, nếu bạn vẫn muốn sử dụng dict giống như quyền truy cập vào các cột, cũng có thể.Bạn chỉ cần thực hiện một đối tượng giống như dict. Bây giờ tôi sẽ cung cấp mã để làm điều này, mặc dù tôi nghĩ rằng nó hoàn toàn không cần thiết, vì truy cập thuộc tính rất sạch sẽ. Nếu sự cố của bạn đã được giải quyết bằng cách sử dụng phương pháp ở trên, không sử dụng mã bên dưới điểm này.

Bạn có thể làm điều đó trên lớp Word:

class Word(object): 
    def __getitem__(self, item): 
     return getattr(self, item) 
    def __setitem__(self, item, value): 
     return setattr(self, item, value) 

Phần còn lại của thiết lập hoạt động như trên. Sau đó, bạn có thể sử dụng nó như thế này:

w = Word() 
w['english'] = u'name' 

Nếu bạn muốn có một thuộc tính columns sau đó bạn cần một dict -like

class AttributeDict(DictMixin): 
    def __init__(self, obj): 
     self._obj = obj 
    def __getitem__(self, item): 
     return getattr(self._obj, item) 
    def __setitem__(self, item, value): 
     return setattr(self._obj, item, value) 

class Word(object): 
    def __init__(self): 
     self.columns = AttributeDict(self) 

Sau đó, bạn có thể sử dụng như bạn dự định:

w = Word() 
w.columns['english'] = u'name' 

Tôi nghĩ rằng bạn sẽ đồng ý rằng tất cả điều này là không cần thiết phức tạp mà không có lợi ích bổ sung.

+2

Cảm ơn bạn đã dành thời gian viết câu trả lời hoàn chỉnh như vậy. Tôi đồng ý rằng giải pháp đầu tiên sạch hơn và thanh lịch hơn lần thứ hai. Tôi không có lý do chính đáng nào để sử dụng dict ngoại trừ sự thật rằng đây là thứ "bình thường" xuất hiện trong đầu tôi. Như tôi đã nói trước đây, tôi là người mới trong Python và tôi chưa học được cách nào để tận dụng lợi ích của các ngôn ngữ động. Tôi sẽ sử dụng giải pháp đầu tiên của bạn. Cảm ơn một lần nữa :) –

+3

Tôi không thể có được bao nhiêu bài viết như thế này tồn tại ở đây, nhưng tôi vẫn còn awed khi tôi nhìn thấy một. Cảm ơn bạn đã đặt quá nhiều suy nghĩ vào điều này. Vẫn còn hữu ích, cho những người mới, hơn một năm sau đó. Chúc mừng bạn – Profane

+0

Bây giờ, nếu tôi muốn sử dụng kwarg để bắt đầu Word, như trong w = Word (tiếng Anh = 'tên', tiếng Hàn = 'Phoo', tiếng rumani = 'nume'), nó sẽ hoạt động như thế nào? Trong trường hợp của tôi, nó sẽ tiếp tục ném một lỗi __init__. Cảm ơn! – Julius

3

Tôi đã sử dụng giải pháp của nosklo (cảm ơn!) Nhưng tôi đã có khóa chính (được chuyển thành pk_col) trong dòng cột (dòng đầu tiên của csv). Vì vậy, tôi nghĩ rằng tôi muốn chia sẻ sửa đổi của tôi. Tôi đã sử dụng một ternary.

table = Table(tablename, metadata, 
    *((Column(pk_col, Integer, primary_key=True)) if rowname == pk_col else (Column(rowname, String())) for rowname in row.keys())) 
table.create() 
Các vấn đề liên quan