如何使用 Flask-SQLAlchemy 使用 csv 填充具有外键的模型?

Posted

技术标签:

【中文标题】如何使用 Flask-SQLAlchemy 使用 csv 填充具有外键的模型?【英文标题】:How to populate a model having foreign key using csv using Flask-SQLAlchemy? 【发布时间】:2013-05-04 18:04:47 【问题描述】:

我有 2 个模型类如下:

class Domain(db.Model):
    __tablename__ = 'domain'
    id = db.Column(db.Integer, primary_key=True)
    domain_name = db.Column(db.String(30), unique=True)
    mailboxes = db.Column(db.Integer, default=0)

    def __init__(self, **kwargs):
        self.__dict__.update(kwargs)

    def __repr__(self):
        return '%s'  % self.domain_name


class EmailAccount(db.Model):
    __tablename__ = 'email_account'
    __table_args__ = (
        db.UniqueConstraint('username', 'domain_id', 
                            name='_uq_username_domain'),
    )
    id = db.Column(db.Integer, primary_key=True)
    username = db.Column(db.String(30))
    domain_id = db.Column(db.Integer, db.ForeignKey('domain.id'))
    domain = db.relationship('Domain', backref=db.backref('emailaccounts',
                            lazy='dynamic'))
    def __init__(self,**kwargs):
        self.__dict__.update(kwargs)

    def __repr__(self):
         return  '%s@%s ' % (self.username, self.domain)

我仅在示例中添加了此处所需的相关属性。我希望通过读取数据的 csv 文件来使用脚本填充模型。域表脚本使用 Flask-SQLAlchemy 运行良好,但 emailaccount 表脚本抛出异常。脚本如下:

#Populate domains from csv
domain_file = "domain.csv"
csv_file = csv.DictReader(open(domain_file, 'rb'), delimiter=',')
for row in csv_file:
    #data type conversion from (csv)string before inserting to table
    for key, value in row.items():
          #some code omitted
        print key, value    
    domain = Domain(**row)
    db.session.add(domain)
    db.session.commit()

#Populate accounts from csv
accounts_file = "accounts.csv"
csv_file = csv.DictReader(open(accounts_file, 'rb'), delimiter=',')

for row in csv_file:
    mdomain_name = ''
    #data type conversion from (csv)string before inserting to table
    for key, value in row.items():
        print key, value
        if key == 'domain':
            mdomain = Domain.query.filter_by(domain_name = value).first()
            mdomain_name = mdomain.domain_name
            mdomain_id = mdomain.id
        if key == 'domain_id':
            value = mdomain_id
    account = EmailAccount(**row)
    db.session.add(account)
    db.session.commit()

抛出的异常是:

文件“data.py”,第 55 行,在 db.session.add(account) 文件“.../local/lib/python2.7/site-packages/sqlalchemy/orm/scoping.py”,第 149 行,返回 getattr(self.registry(), name)(*args, **kwargs ) 文件“.../local/lib/python2.7/site-packages/sqlalchemy/orm/session.py”,第 1397 行,添加 self._save_or_update_state(state) 文件“.../local/lib/python2.7/site-packages/sqlalchemy/orm/session.py”,第 1415 行,在 _save_or_update_state halt_on=self._contains_state): 文件“.../local/lib/python2.7/site-packages/sqlalchemy/orm/mapper.py”,第 1986 行,在 cascade_iterator parent_dict、visited_states、halt_on)) 文件“.../local/lib/python2.7/site-packages/sqlalchemy/orm/properties.py”,第 930 行,在 cascade_iterator get_all_pending(state, dict_) 文件“.../local/lib/python2.7/site-packages/sqlalchemy/orm/attributes.py”,第 761 行,在 get_all_pending ret = [(instance_state(current), current)] AttributeError: 'str' object没有属性“_sa_instance_state”

Pl。恢复 data.py 脚本中代码的更改,即用于为具有域类外键的 EmailAccount 模型上传数据的脚本。我希望只使用 Flask-SQLAlchemy。

accounts.csv 文件的提取:

Email Account,legacy_username,password,full_name,quota,is_domain_admin,is_catch_all,disabled_login,disabled_delivery
info@abc.com,,,,104857600,,,,
internal@abc.com,,,Internal,102400000,,,,
kiran.rs@abc.com,,,,102400000,,,, kishorepr,xyz.com,,,,209715200,,,,

【问题讨论】:

【参考方案1】:

当一行包含 domain 键时,您检索域以获取其键,但您不会使用域 ID 更新您的 row

那么当你这样做时:

account = EmailAccount(**row)

row 对象仍然具有与域名关联的键 domain。由于您的 EmailAccount 类使用名称 domain 来表示关系,因此数据库认为它将获得一个 Domain 对象,而实际上它正在获得一个 string (名称)。这就是为什么您会收到错误 AttributeError: 'str' object has no attribute '_sa_instance_state'

更新:应该可以了

for row in csv_file:
    account_values = 
    for key, value in row.items():
        if key == 'domain':
            mdomain = Domain.query.filter_by(domain_name = value).first()
            account_values['domain'] = mdomain
        else:
            account_values[key] = value
    account = EmailAccount(account_values)
    db.session.add(account)
    db.session.commit()

【讨论】:

@***.com/users/1193366/morphyn 感谢您的快速回复。你能告诉我修改代码来解决这个问题吗?提前致谢 您可以发布您的帐户 csv 文件的摘录吗? ***.com/users/1193366/morphyn 粘贴了上面 csv 的摘录。 @***.com/users/1193366/morphyn 我无法在电子邮件帐户模型中保存域。它保存为无。注意:在将 Domain 的代码更改为 row['domain'] = Domain.query.filter_by[domain_name=domain_str].first() 之后,我使用 account = EmailAccount(**row) 填充电子邮件帐户模型。 domain_str 是从另一个 csv 中获取的拆分值,其键为“电子邮件帐户”,它是 username@domain_name 的组合 注意到我根本没有将外键 id 值作为关键字的 1 传递。现在添加后,效果很好!再次感谢。

以上是关于如何使用 Flask-SQLAlchemy 使用 csv 填充具有外键的模型?的主要内容,如果未能解决你的问题,请参考以下文章

如何使用 Flask-SQLAlchemy 使用 csv 填充具有外键的模型?

如何在一段时间后更改Flask-SQLAlchemy URI?

Flask-SQLAlchemy - 会话如何与多个数据库一起工作?

如何返回 Flask-SqlAlchemy 错误详细信息

Flask-SQLAlchemy:如何有条件地插入或更新一行

Flask-SQLAlchemy 的隔离级别