I have a function that checks the table for duplicate rows and overwrites the previous ones.
It has worked for me in the past when moving the id to another row but now this error occurs:
raise orm_exc.FlushError(
sqlalchemy.orm.exc.FlushError: New instance <Usage at 0x7fb3f2a035e0>
with identity key (<class 'blueprints.site.usage.Usage'>, (859,), None)
conflicts with persistent instance <Usage at 0x7fb3d896ee50>
And when I assign a new id to the updated row there's no error. How can I reassign the old id to the new row?
def save_as_single_row(self):
"""
Save usage as a single row in usage table.
"""
query = Usage.query.filter_by(site_id=self.site_id, invoice_num=self.invoice_num,
supply_charge=self.supply_charge)
this_usage = query.all()
if not this_usage or (len(this_usage) == 1 and this_usage[0] == self):
print(self.start_date, 'self')
self.save()
print(self.start_date)
return self
print('Usage already exists', this_usage[-1].__dict__)
self.id = this_usage[-1].id
self.credit = this_usage[-1].credit
self.awaiting_bill_combine = this_usage[-1].awaiting_bill_combine
self.awaiting_site_combine = this_usage[-1].awaiting_site_combine
query.delete()
db.session.add(self)
db.session.commit()
return self
Looks like the answer was much simpler than I thought, I removed db.session.add(self) and added:
new_obj = db.session.merge(this_usage[-1])
db.session.commit()
It seems like this merges the values of the most recent row with the id of the original row.
For anyone else wondering - here is some more info.
EDIT:
session.merge() stopped playing nicely for multiple rows so I stopped using db.session.add(self) and the changes committed correctly.
Related
I want to update the "on_hand_qty" field of existing record
_inherit="product.product"
on_hand_qty=fields.Float('onhand', compute='copy_quant' ,store=True)
#api.constrains('qty_available')
def copy_quant(self):
for rec in self:
rec.on_hand_qty = rec.qty_available
I want this field with store =True
but this field is not updated in old records with store= True. please suggest how to achieve this.
user10810227
Make the related field of qty_available
on_hand_qty=fields.Float(related='qty_available', string="Your Field")
I am wondering form the following functions written in different ways in python.
The first function is working fine and success. Return a correct data and successfully delete the record from the database.
In the second one, return correct data, but in the database doesn't delete anything!!!. The record which should deleted still exit.
What do you thing the reason that prevent the second function from successfully deleting the record from the database.
Note: I use FLASK SQLAchemy connection to postgress database. The class name is (Movie) and the table name is (movies)
first success function is:
# Creating endpoint to delete a movie by providing movie_id
#app.route('/movies/<int:movie_id>', methods = ['DELETE'])
def delete_movie(movie_id):
movie = Movie.query.filter(Movie.id == movie_id).one_or_none()
if movie is None:
abort(404) # abort if id is not found
else:
try:
movie.delete()
# return movie id that was deleted
return jsonify({
'success': True,
'deleted': movie_id
})
except Exception:
abort(422)
The second fail function is:
#app.route('/movies/<int:movie_id>', methods = ['DELETE'])
def delete_movie(movie_id):
movie = Movie.query.filter(Movie.id == movie_id)
movie_availability = movie.one_or_none()
if movie_availability is None:
abort(404) # abort if id is not found
else:
try:
movie.delete()
# return movie id that was deleted
return jsonify({
'success': True,
'deleted': movie_id
})
except Exception:
abort(422)
You need to delete movie_availability in your second example not movie.
I.e.
try:
movie_availability.delete()
In your second example movie_availability becomes the record retrieved from the database, whereas in your first example that's condensed into one line and assigned to movie.
So I'm a flask/sqlalchemy newbie but this seems like it should be a pretty simple. Yet for the life of me I can't get it to work and I can't find any documentation for this anywhere online. I have a somewhat complex query I run that returns me a list of database objects.
items = db.session.query(X, func.count(Y.x_id).label('total')).filter(X.size >= size).outerjoin(Y, X.x_id == Y.x_id).group_by(X.x_id).order_by('total ASC')\
.limit(20).all()
after I get this list of items I want to loop through the list and for each item update some property on it.
for it in items:
it.some_property = 'xyz'
db.session.commit()
However what's happening is that I'm getting an error
it.some_property = 'xyz'
AttributeError: 'result' object has no attribute 'some_property'
I'm not crazy. I'm positive that the property does exist on model X which is subclassed from db.Model. Something about the query is preventing me from accessing the attributes even though I can clearly see they exist in the debugger. Any help would be appreciated.
class X(db.Model):
x_id = db.Column(db.Integer, primary_key=True)
size = db.Column(db.Integer, nullable=False)
oords = db.relationship('Oords', lazy=True, backref=db.backref('x', lazy='joined'))
def __init__(self, capacity):
self.size = size
Given your example your result objects do not have the attribute some_property, just like the exception says. (Neither do model X objects, but I hope that's just an error in the example.)
They have the explicitly labeled total as second column and the model X instance as the first column. If you mean to access a property of the X instance, access that first from the result row, either using index, or the implicit label X:
items = db.session.query(X, func.count(Y.x_id).label('total')).\
filter(X.size >= size).\
outerjoin(Y, X.x_id == Y.x_id).\
group_by(X.x_id).\
order_by('total ASC').\
limit(20).\
all()
# Unpack a result object
for x, total in items:
x.some_property = 'xyz'
# Please commit after *all* the changes.
db.session.commit()
As noted in the other answer you could use bulk operations as well, though your limit(20) will make that a lot more challenging.
You should use the update function.
Like that:
from sqlalchemy import update
stmt = update(users).where(users.c.id==5).\
values(name='user #5')
Or :
session = self.db.get_session()
session.query(Organisation).filter_by(id_organisation = organisation.id_organisation).\
update(
{
"name" : organisation.name,
"type" : organisation.type,
}, synchronize_session = False)
session.commit();
session.close()
The sqlAlchemy doc : http://docs.sqlalchemy.org/en/latest/core/dml.html
I am using the following passage of code:
#app.route('/budget_item/<int:budget_id>/edit', methods=['GET', 'POST'])
def budget_item_edit(budget_id):
budget_item = session.query(Budget).filter_by(id=budget_id).one()
print "Start EDIT sequence"
# Return form data from HTML initial load form
elif request.method == 'POST':
budget_amount_reallocated_total = budget_item.budget_amount_reallocated_total
#ORIGINAL BUDGET
if request.form['transaction_type'] == 'Original Budget':
#amount
if request.form['amount'] == "":
amount = 0
else:
amount = float(str(request.form['amount']))
budget_item = Budget(
#created_date = "",
budget_transaction_type = request.form['transaction_type'],
budget_line = request.form['budget_line'],
amount = amount,
description = request.form['description']
#date_received = request.form['date_received']
)
try:
count = 1
while count < 10000:
count += 1
#budget_line
setattr(budget_item,'budget_line'+str(count),request.form['budget_line'+str(count)])
#amount
setattr(budget_item,'amount'+str(count),float(request.form['amount'+str(count)]))
budget_amount_reallocated_total += float(request.form['amount'+str(count)])
setattr(budget_item, 'budget_amount_reallocated_total', budget_amount_reallocated_total)
#description
setattr(budget_item,'description'+str(count), request.form['description'+str(count)])
#date_received
setattr(budget_item,'date_received'+str(count),request.form['date_received'+str(count)])
session.commit()
except:
session.commit()
return redirect(url_for('budget_master'))
else:
print "I'm done! This is not a post request"
This block of code is setup to pass data from an HTML via a POST request an then update a corresponding object in the Postgres DB. I can confirm that the object queried from the DB "budget_item" is being updated by settattr. At the end of the passage, I use commit() to update the object; however, the database doesn't reflect the changes. Just to test to make sure things are flowing, I've tried session.add(budget_item) followed by session.commit() to make sure the connect to the DB is OK. That works. How do i update this budget_item object into the database? Any help is much appreciated.
i think that a simple
budget_item.budget_amount_reallocated_total = budget_amount_reallocated_total
session.add(budget_item)
session.commit()
is the right way to do it
To answer your question, to update the budget_item that already exists in the database you need to update the Budget instance that you retrieved from the database, i.e.
budget_item = session.query(Budget).filter_by(id=budget_id).one()
not the one that you have newly created with:
budget_item = Budget(...)
Here the first budget_item represents the row in the database, so this is the one to update. To that end you can replace the code that creates the second Budget instance with this:
budget_item.budget_transaction_type = request.form['transaction_type']
budget_item.budget_line = request.form['budget_line']
budget_item.amount = amount
budget_item.description = request.form['description']
Once you have finished updating the Budget instance you can call session.commit() to flush it to the database.
As mentioned in my comment to your question, it appears that you are trying to add a large number of additional attributes to budget_item all of which will be ignored by sqlalchemy unless they are defined in the mapping between the Budget instance and the Budget table.
I am trying to use SQLAlchemy to insert some records into a database with a foreign key but the foreign key does not seem to be changing as I would expect.
I have a simple 1-M relationship between two objects, Account & Transaction. The Account objects have been inserted into the database fine but when I try to iterate through these accounts and add transaction objects to them, the foreign key for all of the transactions is the id of the last account being iterated through.
Can anyone help me figure out why this is? it has been driving me crazy!
My account and transaction objects:
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))
Here is where I am iterating through the accounts in the DB and trying to add transaction objects to them:
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()
The above, if only 1 account is retrieved from db, works fine. As soon as I start iterating over multiple accounts all of the transactions get given the same foreign key (the key of the last account - 222222222 in the above case)
Here is where the issue is
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
For some reason having the parameter existingTransactionList causes the issue. If I change the above code to the following the problem goes away but I still don't understand why due to my lack of python knowledge I am guessing :)
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
The reason I have the existingTransactionList variable as a parameter is because I will eventually want to pass in a list of existing transactions and only the unique ones will get returned by using something like the following:
transactionList = list(set(existingTransactionList))
The issue is that you are adding all of your transactions to the last account. Change:
def ParseCsvFile(self, fileLocation, existingTransactionList=[]):
to
def ParseCsvFile(self, fileLocation, existingTransactionList=None):
if existingTransactionList is None:
existingTransactionList = []
Why does this happen
Python only parses the declaration for a function once and all of the default arguments are bound to their values during this compilation stage. That means that instead of every invocation of ParseCsvFile being given a new list instead every call to ParseCsvFile uses the same list.
To see what is going on, try the following on the command line:
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
then use this function:
test_mutable_defaults(1)
test_mutable_defaults(2)
test_mutable_defaults(3)
If Python re-evaluated x and y on each call you would see different memory IDs for each call and x would be [1], then [2], then [3]. Because Python only evaluates x and y when it first compiles test_mutable_defaults you will see the same memory ID for x each call (and likewise for y), and x will be [1], then [1, 2] and then [1, 2, 3].