【问题标题】:Foreign key in SQLAlchemy (flask) changing before insert into DB?SQLAlchemy(烧瓶)中的外键在插入数据库之前发生变化?
【发布时间】:2014-02-08 14:34:37
【问题描述】:

我正在尝试使用 SQLAlchemy 将一些记录插入到具有外键的数据库中,但外键似乎没有像我预期的那样发生变化。

我在账户和交易这两个对象之间有一个简单的 1-M 关系。 Account 对象已很好地插入到数据库中,但是当我尝试迭代这些帐户并向它们添加事务对象时,所有事务的外键都是被迭代的最后一个帐户的 id。

谁能帮我弄清楚这是为什么?它一直让我发疯!

我的账户和交易对象:

class Account(db.Model):
    number = db.Column(db.String(80), primary_key=True)
    bsb = db.Column(db.String(80), primary_key=True)
    name = db.Column(db.String(80))
    description = db.Column(db.String(80))

class Transaction(db.Model):
    id = db.Column(db.Integer, primary_key=True)
    account_id = db.Column(db.String(80), db.ForeignKey('account.number'))
    amount = db.Column(db.Float(precprecision=2))

这是我在数据库中迭代帐户并尝试向它们添加事务对象的地方:

def Sync_Transactions():
    #The following definately returns two account objects from the DB
    accounts = [db.session.query(Account).filter(Account.number == '999999999').first(), db.session.query(Account).filter(Account.number == '222222222').first()]

    for acc in accounts:
        #The following parses a CSV file for the account and returns transaction objecs as a list
        transactions = myLib.ParseCsvFile('C:\\transactions_%s.csv' % (acc.number))
        acc.transactions = transactions
        db.session.merge(acc)
    db.session.commit()

以上,如果从 db 中只检索到 1 个帐户,则可以正常工作。一旦我开始迭代多个帐户,所有交易都会获得相同的外键(最后一个帐户的键 - 在上述情况下为 222222222)

这就是问题所在

def ParseCsvFile(self, fileLocation, existingTransactionList=[]):
    with open(fileLocation, 'rb') as f:
        reader = csv.DictReader(f,['Date','TransactionAmount','C','D','TransactionType','TransactionDescription','Balance'])
        for row in reader:
            if not row['TransactionDescription'] == '':
                existingTransactionList.append(Transaction(
                    float(row['TransactionAmount'])
                    )
                )
    return existingTransactionList

由于某种原因,参数 existingTransactionList 会导致问题。如果我将上面的代码更改为以下代码,问题就消失了,但我仍然不明白为什么由于我缺乏 python 知识我猜:)

def ParseCsvFile(self, fileLocation, existingTransactionList=[]):
    existingTransactionList=[]
    with open(fileLocation, 'rb') as f:
        reader = csv.DictReader(f,['Date','TransactionAmount','C','D','TransactionType','TransactionDescription','Balance'])
        for row in reader:
            if not row['TransactionDescription'] == '':
                existingTransactionList.append(Transaction(
                    float(row['TransactionAmount'])
                    )
                )
    return existingTransactionList

我将 existingTransactionList 变量作为参数的原因是,我最终会希望传入现有交易的列表,并且只有唯一的交易会通过以下方式返回:

transactionList = list(set(existingTransactionList))

【问题讨论】:

  • 看来你需要展示 ParseCsvFile 方法。
  • 谢谢!问题确实出在 ParseCsvFile 方法中,但我仍然不知道为什么会出现问题。我已将代码添加到帖子中。

标签: python mysql database sqlalchemy flask


【解决方案1】:

问题是您将所有交易添加到最后一个帐户。变化:

def ParseCsvFile(self, fileLocation, existingTransactionList=[]):

def ParseCsvFile(self, fileLocation, existingTransactionList=None):
    if existingTransactionList is None:
        existingTransactionList = []

为什么会这样

Python 仅解析函数的声明一次,并且在此编译阶段所有默认参数都绑定到它们的值。这意味着不是每次调用 ParseCsvFile 都会得到一个新列表,而是每次调用 ParseCsvFile 都使用 same 列表。

要查看发生了什么,请在命令行上尝试以下操作:

def test_mutable_defaults(value, x=[], y={}):
    print("x's memory id this run is", id(x))
    print("Contents of x", x)
    print("y's memory id this run is", id(y))
    print("Contents of y", y)

    x.append(value)
    y[value] = value

    return x, y

然后使用这个函数:

test_mutable_defaults(1)
test_mutable_defaults(2)
test_mutable_defaults(3)

如果 Python 在每次调用时重新评估 xy,您会看到每次调用的内存 ID 不同,x 将是 [1],然后是 [2],然后是 [3]。因为 Python 仅在第一次编译 test_mutable_defaults 时评估 xy,所以您将看到 x 每次调用(y 也是如此)和 @987654338 的 相同 内存 ID @ 将是 [1],然后是 [1, 2],然后是 [1, 2, 3]

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 2013-11-22
    • 2018-10-26
    • 2018-12-01
    • 2016-09-06
    • 1970-01-01
    • 1970-01-01
    • 2016-01-15
    • 2021-08-23
    相关资源
    最近更新 更多