动态数据集和SQLAlchemy [英] Dynamic Datasets and SQLAlchemy

查看:63
本文介绍了动态数据集和SQLAlchemy的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我正在将Python中的一些旧的SQLite3 SQL语句重构为SQLAlchemy.在我们的框架中,我们具有以下SQL语句,这些语句采用具有某些已知键以及可能包含任意数量的意外键和值(取决于所提供的信息)的字典.

I am refactoring some old SQLite3 SQL statements in Python into SQLAlchemy. In our framework, we have the following SQL statements that takes in a dict with certain known keys and potentially any number of unexpected keys and values (depending what information was provided).

import sqlite3
import sys

def dict_factory(cursor, row):
    d = {}
    for idx, col in enumerate(cursor.description):
        d[col[0]] = row[idx]
    return d


def Create_DB(db):
    #    Delete the database
    from os import remove
    remove(db)

#   Recreate it and format it as needed
    with sqlite3.connect(db) as conn:
        conn.row_factory = dict_factory
        conn.text_factory = str

        cursor = conn.cursor()

        cursor.execute("CREATE TABLE [Listings] ([ID] INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL UNIQUE, [timestamp] REAL NOT NULL DEFAULT(( datetime ( 'now' , 'localtime' ) )), [make] VARCHAR, [model] VARCHAR, [year] INTEGER);")


def Add_Record(db, data):
    with sqlite3.connect(db) as conn:
        conn.row_factory = dict_factory
        conn.text_factory = str

        cursor = conn.cursor()

        #get column names already in table
        cursor.execute("SELECT * FROM 'Listings'")
        col_names = list(map(lambda x: x[0], cursor.description))

        #check if column doesn't exist in table, then add it
        for i in data.keys():
            if i not in col_names:
                cursor.execute("ALTER TABLE 'Listings' ADD COLUMN '{col}' {type}".format(col=i, type='INT' if type(data[i]) is int else 'VARCHAR'))

        #Insert record into table
        cursor.execute("INSERT INTO Listings({cols}) VALUES({vals});".format(cols = str(data.keys()).strip('[]'), 
                    vals=str([data[i] for i in data]).strip('[]')
                    ))

#Database filename
db = 'test.db'

Create_DB(db)

data = {'make': 'Chevy',
    'model' : 'Corvette',
    'year' : 1964,
    'price' : 50000,
    'color' : 'blue',
    'doors' : 2}
Add_Record(db, data)

data = {'make': 'Chevy',
    'model' : 'Camaro',
    'year' : 1967,
    'price' : 62500,
    'condition' : 'excellent'}
Add_Record(db, data)

这种高度的动态性是必需的,因为我们无法知道将提供哪些其他信息,但是无论如何,存储提供给我们的所有信息非常重要.这从来都不是问题,因为在我们的框架中,因为我们从未期望表中的列数过多.

This level of dynamicism is necessary because there's no way we can know what additional information will be provided, but, regardless, it's important that we store all information provided to us. This has never been a problem because in our framework, as we've never expected an unwieldy number of columns in our tables.

虽然上面的代码有效,但显然这不是一个干净的实现,因此为什么我试图将其重构为SQLAlchemy的更简洁,更可靠的ORM范例.我开始阅读SQLAlchemy的官方教程和各种示例,并获得了以下代码:

While the above code works, it's obvious that it's not a clean implementation and thus why I'm trying to refactor it into SQLAlchemy's cleaner, more robust ORM paradigm. I started going through SQLAlchemy's official tutorials and various examples and have arrived at the following code:

from sqlalchemy import Column, String, Integer
from sqlalchemy.ext.declarative import declarative_base
from sqlalchemy import create_engine
from sqlalchemy.orm import sessionmaker

Base = declarative_base()

class Listing(Base):
    __tablename__ = 'Listings'
    id = Column(Integer, primary_key=True)
    make = Column(String)
    model = Column(String)
    year = Column(Integer)

engine = create_engine('sqlite:///')

session = sessionmaker()
session.configure(bind=engine)
Base.metadata.create_all(engine)

data = {'make':'Chevy',
    'model' : 'Corvette',
    'year' : 1964}

record = Listing(**data)

s = session()
s.add(record)
s.commit()
s.close()

,并且可以很好地与该数据字典配合使用.现在,当我添加新的关键字时,例如

and it works beautifully with that data dict. Now, when I add a new keyword, such as

data = {'make':'Chevy',
'model' : 'Corvette',
'year' : 1964,
'price' : 50000}

我收到一个TypeError: 'price' is an invalid keyword argument for Listing错误.为了解决该问题,我也将该类修改为动态的:

I get a TypeError: 'price' is an invalid keyword argument for Listing error. To try and solve the issue, I modified the class to be dynamic, too:

class Listing(Base):
    __tablename__ = 'Listings'
    id = Column(Integer, primary_key=True)
    make = Column(String)
    model = Column(String)
    year = Column(Integer)

    def __checker__(self, data):
        for i in data.keys():
            if i not in [a for a in dir(self) if not a.startswith('__')]:
                if type(i) is int:
                    setattr(self, i, Column(Integer))
                else:
                    setattr(self, i, Column(String))
            else:
                self[i] = data[i]

但是我很快意识到,由于多种原因,这根本不起作用,例如该类已被初始化,如果没有将其重新初始化,数据字典将无法被馈送到该类中,这比其他任何事情都更重要.我想得越多,对我来说,使用SQLAlchemy的解决方案似乎就越不明显.因此,我的主要问题是,如何使用SQLAlchemy来实现这一级别的动态性?

But I quickly realized this would not work at all for several reasons, e.g. the class was already initialized, the data dict cannot be fed into the class without reinitializing it, it's a hack more than anything, et al.). The more I think about it, the less obvious the solution using SQLAlchemy seems to me. So, my main question is, how do I implement this level of dynamicism using SQLAlchemy?

我研究了一下,看看是否有人有类似的问题.我找到的最接近的是 SQLAlchemy中的动态类创建,但是它只讨论了常量属性("表名"等).我相信未回答的 https://stackoverflow.com/questions/29105206/sqlalchemy-dynamic-attribute-change 可能会问同样的问题.尽管Python不是我的强项,但我认为自己是上下文科学/工程应用程序中熟练的程序员(C ++和JavaScript是我最强的语言),因此我可能在搜索中未找到正确的Python特定关键字.

I've researched a bit to see if anyone has a similar issue. The closest I've found was Dynamic Class Creation in SQLAlchemy but it only talks about the constant attributes ("tablename" et al.). I believe the unanswered https://stackoverflow.com/questions/29105206/sqlalchemy-dynamic-attribute-change may be asking the same question. While Python is not my forte, I consider myself a highly skilled programmer (C++ and JavaScript are my strongest languages) in the context scientific/engineering applications, so I may not hitting the correct Python-specific keywords in my searches.

我欢迎任何帮助.

推荐答案

class Listing(Base):
    __tablename__ = 'Listings'
    id = Column(Integer, primary_key=True)
    make = Column(String)
    model = Column(String)
    year = Column(Integer)
    def __init__(self,**kwargs):
       for k,v in kwargs.items():
           if hasattr(self,k):
              setattr(self,k,v)
           else:
              engine.execute("ALTER TABLE %s AD COLUMN %s"%(self.__tablename__,k)
              setattr(self.__class__,Column(k, String))
              setattr(self,k,v)

可能会工作...也许...我不确定我没有测试

might work ... maybe ... I am not entirely sure I did not test it

更好的解决方案是使用关系表

a better solution would be to use a relational table

class Attribs(Base):
    listing_id = Column(Integer,ForeignKey("Listing"))
    name = Column(String)
    val = Column(String)

class Listing(Base):
    id = Column(Integer,primary_key = True)
    attributes = relationship("Attribs",backref="listing")
    def __init__(self,**kwargs):
        for k,v in kwargs.items():
            Attribs(listing_id=self.id,name=k,value=v)
    def __str__(self):
        return "\n".join(["A LISTING",] + ["%s:%s"%(a.name,a.val) for a in self.attribs])

另一种解决方案是存储json

another solution would be to store json

class Listing(Base):
    __tablename__ = 'Listings'
    id = Column(Integer, primary_key=True)
    data = Column(String)
    def __init__(self,**kwargs):
       self.data = json.dumps(kwargs)
       self.data_dict = kwargs

最好的解决方案是使用无SQL密钥,值存储(我什至可能只是一个简单的json文件?还是可能搁置?甚至是泡菜)

the best solution would be to use a no-sql key,value store (maybe even just a simple json file? or perhaps shelve? or even pickle I guess)

这篇关于动态数据集和SQLAlchemy的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆