从CSV导入在Python SQLAlchemy中映射类列标题

时间:2018-08-10 20:49:08

标签: python sqlite csv sqlalchemy

我在类中设置列名称,如下所示:

class Stat1(Base):
__tablename__ = 'stat1'
__table_args__ = {'sqlite_autoincrement': True}

id = Column(VARCHAR, primary_key=True, nullable=False)
Date_and_Time = Column(VARCHAR)
IP_Address = Column(VARCHAR)
Visitor_Label = Column(VARCHAR)
Browser = Column(VARCHAR)
Version = Column(VARCHAR)

csv文件在列名称中不使用UNDERSCORE。这是从互联网下载的csv文件。例如,当我导入列名时,诸如“ Date_and_Time”之类的标题将被导入为“ Date and Time”。

我曾经假设(是对的,对吗?),CSV的列名将映射到我设置的类列标题,但这没有发生,因此查询无法正常运行。我收到这样的消息:

  

sqlalchemy.exc.OperationalError:(sqlite3.OperationalError)否这样   列:stat1.Date_and_Time [SQL:'SELECT stat1.id AS stat1_id,   stat1。“ Date_and_Time” AS“ stat1_Date_and_Time”,stat1。“ IP_Address” AS   “ stat1_IP_Address” ...等。

有没有一种方法可以自动映射这些内容以使查询成功?还是一种自动更改CSV列标题的方法,以便在列标题中插入UNDERSCORE以与“类”中定义的列匹配?

1 个答案:

答案 0 :(得分:1)

您可以通过几种不同的方法来解决此问题:

实施您自己的反序列化逻辑

这意味着读取CSV文件并将其列映射到Base模型类的属性的过程是手动完成的(如您的问题所示),然后您使用自己的自定义来读取/映射CSV码。

我认为,在这种情况下,模型类属性(Stat1.Date_and_Time)中有下划线,而CSV标头(...,"Date and Time",...)中有 not 会使代码复杂化。但是,根据您实施映射代码的方式,您可以 Column设置为使用一个模型属性名称(Stat1.Date_and_Time) 和不同数据库列名称(例如Stat1.Date_and_Time映射到您的数据库列"Date and Time")。为此,您需要按如下所示传递name参数:

    class Stat1(Base):

        __tablename__ = 'stat1'
        __table_args__ = { 'sqlite_autoincrement': True }

        id = Column(name = 'id', type_ = VARCHAR, primary_key = True, nullable = False)
        Date_and_Time = Column(name = 'Date and Time', type_ = VARCHAR)
        IP_Address = Column(name = 'IP Address', type_ = VARCHAR)
        # etc.

现在,当您从CSV文件中读取记录时,需要将它们加载到Stat1类中的相应模型属性中。伪代码示例为:

    id, date_and_time, ip_address = read_csv_record(csv_record)
    # Let's assume the "read_csv_record()" function reads your CSV record and returns
    # the appropriate value for `id`, `Date_And_Time`, and `IP_Address`

    my_record = Stat1(id = id,
                      Date_And_Time = date_and_time,
                      ip_address
                      # etc.)

这里,诀窍是实现read_csv_record()函数,以便它读取并返回模型属性的列值,以便随后将它们适当地传递给Stat1()构造函数。

使用SQLAthanor

实施我自己的反序列化解决方案的一种(我认为更容易)的替代方法是使用类似 SQLAthanor 的库(完整说明:我是该库的作者,所以我我有点偏见)。使用SQLAthanor,您可以:

  1. 以编程方式创建Stat模型类:

    from sqlathanor import generate_model_from_csv
    
    Stat1 = generate_model_from_csv('your_csv_file.csv',
                                    'stat1',
                                    primary_key = 'id')
    

    但是,请注意,如果列标题名称不是ANSI SQL标准列名称(例如,如果包含空格),则可能会产生错误。

  2. 定义模型,然后从CSV创建实例。

    要执行此操作,您将与上述操作非常类似地定义模型:

    from sqlathanor import BaseModel
    
    class Stat1(BaseModel):
    
        __tablename__ = 'stat1'
        __table_args__ = { 'sqlite_autoincrement': True }
    
        id = Column(name = 'id', type_ = VARCHAR, primary_key = True, nullable = False, supports_csv = True, csv_sequence = 1)
        Date_and_Time = Column(name = 'Date and Time', type_ = VARCHAR, supports_csv = True, csv_sequence = 2)
        IP_Address = Column(name = 'IP Address', type_ = VARCHAR, supports_csv = True, csv_sequence = 3)
        # etc.
    

    supports_csv自变量告诉您​​的Stat1类,可以从CSV反序列化模型属性Stat1.id(并序列化为CSV),而csv_sequence自变量指示它将始终是CSV记录的第一列。

    现在,您可以通过将CSV记录传递到Stat1来创建新的Stat1.new_from_csv()实例(数据库中的记录):

    # let's assume you have loaded a single CSV record into a variable "csv_record"
    my_record = Stat1.new_from_csv(csv_record)
    

    就是这样!现在,您的my_record变量将包含CSV记录的对象表示形式,然后您可以在选择时将其提交到数据库。由于可以使用多种方式构造CSV文件(使用不同的定界符,包装策略等),因此可以为.new_from_csv()提供大量配置参数,但是您可以找到所有它们记录在这里:https://sqlathanor.readthedocs.io/en/latest/using.html#new-from-csv

    SQLAthanor是一个非常健壮的库,用于将数据移入和移出CSV和SQLAlchemy,因此,我强烈建议您阅读文档。这是重要的链接:

希望这会有所帮助!