Внешний ключ в SQLAlchemy (фляга) меняется перед вставкой в ​​БД?

Я пытаюсь использовать SQLAlchemy для вставки некоторых записей в базу данных с внешним ключом, но внешний ключ, похоже, не меняется, как я ожидал.

У меня есть простая связь 1-M между двумя объектами, Account & Transaction. Объекты учетной записи были вставлены в базу данных, но когда я пытаюсь выполнить итерацию этих учетных записей и добавлять к ним объекты транзакций, внешним ключом для всех транзакций является идентификатор последней учетной записи, выполняемой через.

Может ли кто-нибудь помочь мне разобраться, почему это так? это сводило меня с ума!

Объекты моей учетной записи и транзакции:

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, работает нормально. Как только я начинаю повторять несколько учетных записей, все транзакции получают один и тот же внешний ключ (ключ последней учетной записи – 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 вызывает проблему. Если я изменю приведенный выше код на следующий, проблема исчезнет, ​​но я до сих пор не понимаю, почему из-за моего отсутствия знаний о питоне я угадываю 🙂

 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)) 

Проблема в том, что вы добавляете все свои транзакции в последнюю учетную запись. Изменить:

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

в

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

Почему это происходит

Python только анализирует объявление для функции один раз, и все аргументы по умолчанию привязаны к их значениям на этом этапе компиляции. Это означает, что вместо каждого вызова ParseCsvFile предоставляется новый список, вместо этого каждый вызов ParseCsvFile использует тот же список.

Чтобы узнать, что происходит, попробуйте следующее в командной строке:

 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 переопределил x и y для каждого вызова, вы увидите разные идентификаторы памяти для каждого вызова, а x будет [1] , затем [2] , затем [3] . Поскольку Python оценивает только x и y когда он сначала компилирует test_mutable_defaults вы увидите одинаковый идентификатор памяти для каждого вызова (и аналогично для y ), а x будет [1] , затем [1, 2] а затем [1, 2, 3] .