Peewee throws KeyError when trying to add user defined operator - python

I followed the short guide in the peewee docs on how to add user defined operators, but when I try to do it, it gives me a KeyError.
from peewee import *
from peewee import OP
from peewee import Expression
db = MySQLDatabase(app.config['MYSQL_DATABASE_DB'], host=app.config['MYSQL_DATABASE_HOST'], user=app.config['MYSQL_DATABASE_USER'], passwd=app.config['MYSQL_DATABASE_PASSWORD'])
OP['MOD'] = 'mod'
def mod(lhs, rhs):
return Expression(lhs, OP.MOD, rhs)
MySQLDatabase.register_ops({OP.MOD: '%'})
class Base(Model):
class Meta:
database = db
class User(Base):
user_id = PrimaryKeyField()
first_name = CharField(max_length = 150)
last_name = CharField(max_length = 150)
#app.route('/')
def test():
query = User.select().where(mod(User.user_id, 2) == 0)
return "Query: %r" % query
When I try to run it, it gives me this error:
KeyError: 'mod'
Any ideas what I might be doing wrong?

The problem is that you are defining your database before you are calling register_ops(). To fix the immediate bug you can move your db = MySQL... below the call to register_ops().
This does seem a little like a bug in peewee, though, so I've opened a github issue: https://github.com/coleifer/peewee/issues/599
Edit: I decided to after all not change the behavior. The solution I proposed should work, though -- register ops first, instantiate second. You can also specify custom ops when instantiating the database:
http://docs.peewee-orm.com/en/latest/peewee/api.html#Database
Example
OP['MOD'] = 'mod'
def mod(lhs, rhs):
return Expression(lhs, OP.MOD, rhs)
db = MySQLDatabase(
app.config['MYSQL_DATABASE_DB'],
host=app.config['MYSQL_DATABASE_HOST'],
user=app.config['MYSQL_DATABASE_USER'],
passwd=app.config['MYSQL_DATABASE_PASSWORD'],
ops={OP.MOD: '%'})

Before this
def mod(lhs, rhs):
return Expression(lhs, OP.MOD, rhs)
You need to define this
OP['MOD'] = 'mod'

OP['MOD'] = 'mod'
def mod(lhs, rhs):
return Expression(lhs, OP.MOD, rhs)
db = MySQLDatabase(
app.config['MYSQL_DATABASE_DB'],
host=app.config['MYSQL_DATABASE_HOST'],
user=app.config['MYSQL_DATABASE_USER'],
passwd=app.config['MYSQL_DATABASE_PASSWORD'])
Specifying the ops argument will lead to error, in fact, you don't have to do that.
The only thing you have to be careful is to import the function mod you defined to the module that you want to use it in.

Related

Mocking sqlite3-using function

I'm trying to write a unit test to the following function:
import sqlite3
def match_user_id_with_image_uid(image_uid):
# Given the note id, confirm if the current user is the owner of the note which is
# being operated.
_conn = sqlite3.connect(image_db_file_location)
_c = _conn.cursor()
command = "SELECT owner FROM images WHERE uid = '" + image_uid + "';"
_c.execute(command)
result = _c.fetchone()[0]
_conn.commit()
_conn.close()
return result
Pytest's mocker won't allow to mock sqlite3 functions with the following error:
can't set attributes of built-in/extension type 'sqlite3.Cursor'
I'm using mocker like so:
def test_match_user_to_image(mocker):
m = mocker.patch('sqlite3.connect')
m.return_value = mocker.Mock()
Other than that, I wonder that is the best practice to mock out those function? Do I want to patch each and every function (execute, cursor, commit, close).
Thanks!

How to convert a SQL Alchemy ORM to a dict [duplicate]

Is there a simple way to iterate over column name and value pairs?
My version of SQLAlchemy is 0.5.6
Here is the sample code where I tried using dict(row):
import sqlalchemy
from sqlalchemy import *
from sqlalchemy.ext.declarative import declarative_base
from sqlalchemy.orm import sessionmaker
print "sqlalchemy version:",sqlalchemy.__version__
engine = create_engine('sqlite:///:memory:', echo=False)
metadata = MetaData()
users_table = Table('users', metadata,
Column('id', Integer, primary_key=True),
Column('name', String),
)
metadata.create_all(engine)
class User(declarative_base()):
__tablename__ = 'users'
id = Column(Integer, primary_key=True)
name = Column(String)
def __init__(self, name):
self.name = name
Session = sessionmaker(bind=engine)
session = Session()
user1 = User("anurag")
session.add(user1)
session.commit()
# uncommenting next line throws exception 'TypeError: 'User' object is not iterable'
#print dict(user1)
# this one also throws 'TypeError: 'User' object is not iterable'
for u in session.query(User).all():
print dict(u)
Running this code on my system outputs:
Traceback (most recent call last):
File "untitled-1.py", line 37, in <module>
print dict(u)
TypeError: 'User' object is not iterable
You may access the internal __dict__ of a SQLAlchemy object, like the following:
for u in session.query(User).all():
print u.__dict__
As per #zzzeek in comments:
note that this is the correct answer for modern versions of
SQLAlchemy, assuming "row" is a core row object, not an ORM-mapped
instance.
for row in resultproxy:
row_as_dict = row._mapping # SQLAlchemy 1.4 and greater
# row_as_dict = dict(row) # SQLAlchemy 1.3 and earlier
background on row._mapping, new as of SQLAlchemy 1.4: https://docs.sqlalchemy.org/en/stable/core/connections.html#sqlalchemy.engine.Row._mapping
I couldn't get a good answer so I use this:
def row2dict(row):
d = {}
for column in row.__table__.columns:
d[column.name] = str(getattr(row, column.name))
return d
Edit: if above function is too long and not suited for some tastes here is a one liner (python 2.7+)
row2dict = lambda r: {c.name: str(getattr(r, c.name)) for c in r.__table__.columns}
In SQLAlchemy v0.8 and newer, use the inspection system.
from sqlalchemy import inspect
def object_as_dict(obj):
return {c.key: getattr(obj, c.key)
for c in inspect(obj).mapper.column_attrs}
user = session.query(User).first()
d = object_as_dict(user)
Note that .key is the attribute name, which can be different from the column name, e.g. in the following case:
class_ = Column('class', Text)
This method also works for column_property.
rows have an _asdict() function which gives a dict
In [8]: r1 = db.session.query(Topic.name).first()
In [9]: r1
Out[9]: (u'blah')
In [10]: r1.name
Out[10]: u'blah'
In [11]: r1._asdict()
Out[11]: {'name': u'blah'}
Assuming the following functions will be added to the class User the following will return all key-value pairs of all columns:
def columns_to_dict(self):
dict_ = {}
for key in self.__mapper__.c.keys():
dict_[key] = getattr(self, key)
return dict_
unlike the other answers all but only those attributes of the object are returned which are Column attributes at class level of the object. Therefore no _sa_instance_state or any other attribute SQLalchemy or you add to the object are included. Reference
EDIT: Forget to say, that this also works on inherited Columns.
hybrid_property extention
If you also want to include hybrid_property attributes the following will work:
from sqlalchemy import inspect
from sqlalchemy.ext.hybrid import hybrid_property
def publics_to_dict(self) -> {}:
dict_ = {}
for key in self.__mapper__.c.keys():
if not key.startswith('_'):
dict_[key] = getattr(self, key)
for key, prop in inspect(self.__class__).all_orm_descriptors.items():
if isinstance(prop, hybrid_property):
dict_[key] = getattr(self, key)
return dict_
I assume here that you mark Columns with an beginning _ to indicate that you want to hide them, either because you access the attribute by an hybrid_property or you simply do not want to show them. Reference
Tipp all_orm_descriptors also returns hybrid_method and AssociationProxy if you also want to include them.
Remarks to other answers
Every answer (like 1, 2 ) which based on the __dict__ attribute simply returns all attributes of the object. This could be much more attributes then you want. Like I sad this includes _sa_instance_state or any other attribute you define on this object.
Every answer (like 1, 2 ) which is based on the dict() function only works on SQLalchemy row objects returned by session.execute() not on the classes you define to work with, like the class User from the question.
The solving answer which is based on row.__table__.columns will definitely not work. row.__table__.columns contains the column names of the SQL Database. These can only be equal to the attributes name of the python object. If not you get an AttributeError.
For answers (like 1, 2 ) based on class_mapper(obj.__class__).mapped_table.c it is the same.
as #balki mentioned:
The _asdict() method can be used if you're querying a specific field because it is returned as a KeyedTuple.
In [1]: foo = db.session.query(Topic.name).first()
In [2]: foo._asdict()
Out[2]: {'name': u'blah'}
Whereas, if you do not specify a column you can use one of the other proposed methods - such as the one provided by #charlax. Note that this method is only valid for 2.7+.
In [1]: foo = db.session.query(Topic).first()
In [2]: {x.name: getattr(foo, x.name) for x in foo.__table__.columns}
Out[2]: {'name': u'blah'}
Old question, but since this the first result for "sqlalchemy row to dict" in Google it deserves a better answer.
The RowProxy object that SqlAlchemy returns has the items() method:
http://docs.sqlalchemy.org/en/latest/core/connections.html#sqlalchemy.engine.RowProxy.items
It simply returns a list of (key, value) tuples. So one can convert a row to dict using the following:
In Python <= 2.6:
rows = conn.execute(query)
list_of_dicts = [dict((key, value) for key, value in row.items()) for row in rows]
In Python >= 2.7:
rows = conn.execute(query)
list_of_dicts = [{key: value for (key, value) in row.items()} for row in rows]
A very simple solution: row._asdict().
sqlalchemy.engine.Row._asdict() (v1.4)
sqlalchemy.util.KeyedTuple._asdict() (v1.3)
> data = session.query(Table).all()
> [row._asdict() for row in data]
with sqlalchemy 1.4
session.execute(select(User.id, User.username)).mappings().all()
>> [{'id': 1, 'username': 'Bob'}, {'id': 2, 'username': 'Alice'}]
Following #balki answer, since SQLAlchemy 0.8 you can use _asdict(), available for KeyedTuple objects. This renders a pretty straightforward answer to the original question. Just, change in your example the last two lines (the for loop) for this one:
for u in session.query(User).all():
print u._asdict()
This works because in the above code u is an object of type class KeyedTuple, since .all() returns a list of KeyedTuple. Therefore it has the method _asdict(), which nicely returns u as a dictionary.
WRT the answer by #STB: AFAIK, anything that .all() returns is a list of KeypedTuple. Therefore, the above works either if you specify a column or not, as long as you are dealing with the result of .all() as applied to a Query object.
from sqlalchemy.orm import class_mapper
def asdict(obj):
return dict((col.name, getattr(obj, col.name))
for col in class_mapper(obj.__class__).mapped_table.c)
Refer to Alex Brasetvik's Answer, you can use one line of code to solve the problem
row_as_dict = [dict(row) for row in resultproxy]
Under the comment section of Alex Brasetvik's Answer, zzzeek the creator of SQLAlchemy stated this is the "Correct Method" for the problem.
I've found this post because I was looking for a way to convert a SQLAlchemy row into a dict. I'm using SqlSoup... but the answer was built by myself, so, if it could helps someone here's my two cents:
a = db.execute('select * from acquisizioni_motes')
b = a.fetchall()
c = b[0]
# and now, finally...
dict(zip(c.keys(), c.values()))
You could try to do it in this way.
for u in session.query(User).all():
print(u._asdict())
It use a built-in method in the query object that return a dictonary object of the query object.
references: https://docs.sqlalchemy.org/en/latest/orm/query.html
With python 3.8+, we can do this with dataclass, and the asdict method that comes with it:
from dataclasses import dataclass, asdict
from sqlalchemy.ext.declarative import declarative_base
from sqlalchemy.orm import sessionmaker
from sqlalchemy import Column, String, Integer, create_engine
Base = declarative_base()
engine = create_engine('sqlite:///:memory:', echo=False)
#dataclass
class User(Base):
__tablename__ = 'users'
id: int = Column(Integer, primary_key=True)
name: str = Column(String)
email = Column(String)
def __init__(self, name):
self.name = name
self.email = 'hello#example.com'
Base.metadata.create_all(engine)
SessionMaker = sessionmaker(bind=engine)
session = SessionMaker()
user1 = User("anurag")
session.add(user1)
session.commit()
query_result = session.query(User).one() # type: User
print(f'{query_result.id=:}, {query_result.name=:}, {query_result.email=:}')
# query_result.id=1, query_result.name=anurag, query_result.email=hello#example.com
query_result_dict = asdict(query_result)
print(query_result_dict)
# {'id': 1, 'name': 'anurag'}
The key is to use the #dataclass decorator, and annotate each column with its type (the : str part of the name: str = Column(String) line).
Also note that since the email is not annotated, it is not included in query_result_dict.
The expression you are iterating through evaluates to list of model objects, not rows. So the following is correct usage of them:
for u in session.query(User).all():
print u.id, u.name
Do you realy need to convert them to dicts? Sure, there is a lot of ways, but then you don't need ORM part of SQLAlchemy:
result = session.execute(User.__table__.select())
for row in result:
print dict(row)
Update: Take a look at sqlalchemy.orm.attributes module. It has a set of functions to work with object state, that might be useful for you, especially instance_dict().
I've just been dealing with this issue for a few minutes.
The answer marked as correct doesn't respect the type of the fields.
Solution comes from dictalchemy adding some interesting fetures.
https://pythonhosted.org/dictalchemy/
I've just tested it and works fine.
Base = declarative_base(cls=DictableModel)
session.query(User).asdict()
{'id': 1, 'username': 'Gerald'}
session.query(User).asdict(exclude=['id'])
{'username': 'Gerald'}
class User(object):
def to_dict(self):
return dict([(k, getattr(self, k)) for k in self.__dict__.keys() if not k.startswith("_")])
That should work.
You can convert sqlalchemy object to dictionary like this and return it as json/dictionary.
Helper functions:
import json
from collections import OrderedDict
def asdict(self):
result = OrderedDict()
for key in self.__mapper__.c.keys():
if getattr(self, key) is not None:
result[key] = str(getattr(self, key))
else:
result[key] = getattr(self, key)
return result
def to_array(all_vendors):
v = [ ven.asdict() for ven in all_vendors ]
return json.dumps(v)
Driver Function:
def all_products():
all_products = Products.query.all()
return to_array(all_products)
Two ways:
1.
for row in session.execute(session.query(User).statement):
print(dict(row))
2.
selected_columns = User.__table__.columns
rows = session.query(User).with_entities(*selected_columns).all()
for row in rows :
print(row._asdict())
Here is how Elixir does it. The value of this solution is that it allows recursively including the dictionary representation of relations.
def to_dict(self, deep={}, exclude=[]):
"""Generate a JSON-style nested dict/list structure from an object."""
col_prop_names = [p.key for p in self.mapper.iterate_properties \
if isinstance(p, ColumnProperty)]
data = dict([(name, getattr(self, name))
for name in col_prop_names if name not in exclude])
for rname, rdeep in deep.iteritems():
dbdata = getattr(self, rname)
#FIXME: use attribute names (ie coltoprop) instead of column names
fks = self.mapper.get_property(rname).remote_side
exclude = [c.name for c in fks]
if dbdata is None:
data[rname] = None
elif isinstance(dbdata, list):
data[rname] = [o.to_dict(rdeep, exclude) for o in dbdata]
else:
data[rname] = dbdata.to_dict(rdeep, exclude)
return data
With this code you can also to add to your query "filter" or "join" and this work!
query = session.query(User)
def query_to_dict(query):
def _create_dict(r):
return {c.get('name'): getattr(r, c.get('name')) for c in query.column_descriptions}
return [_create_dict(r) for r in query]
For the sake of everyone and myself, here is how I use it:
def run_sql(conn_String):
output_connection = engine.create_engine(conn_string, poolclass=NullPool).connect()
rows = output_connection.execute('select * from db1.t1').fetchall()
return [dict(row) for row in rows]
As OP stated, calling the dict initializer raises an exception with the message "User" object is not iterable. So the real question is how to make a SQLAlchemy Model iterable?
We'll have to implement the special methods __iter__ and __next__, but if we inherit directly from the declarative_base model, we would still run into the undesirable "_sa_instance_state" key. What's worse, is we would have to loop through __dict__.keys() for every call to __next__ because the keys() method returns a View -- an iterable that is not indexed. This would increase the time complexity by a factor of N, where N is the number of keys in __dict__. Generating the dict would cost O(N^2). We can do better.
We can implement our own Base class that implements the required special methods and stores a list of of the column names that can be accessed by index, reducing the time complexity of generating the dict to O(N). This has the added benefit that we can define the logic once and inherit from our Base class anytime we want our model class to be iterable.
class IterableBase(declarative_base()):
__abstract__ = True
def _init_keys(self):
self._keys = [c.name for c in self.__table__.columns]
self._dict = {c.name: getattr(self, c.name) for c in self.__table__.columns}
def __init__(self, *args, **kwargs):
super().__init__(*args, **kwargs)
self._init_keys()
def __setattr__(self, name, value):
super().__setattr__(name, value)
if name not in ('_dict', '_keys', '_n') and '_dict' in self.__dict__:
self._dict[name] = value
def __iter__(self):
self._n = 0
return self
def __next__(self):
if self._n >= len(self._keys):
raise StopIteration
self._n += 1
key = self._keys[self._n-1]
return (key, self._dict[key])
Now the User class can inherit directly from our IterableBase class.
class User(IterableBase):
__tablename__ = 'users'
id = Column(Integer, primary_key=True)
name = Column(String)
You can confirm that calling the dict function with a User instance as an argument returns the desired dictionary, sans "_sa_instance_state". You may have noticed the __setattr__ method that was declared in the IterableBase class. This ensures the _dict is updated when attributes are mutated or set after initialization.
def main():
user1 = User('Bob')
print(dict(user1))
# outputs {'id': None, 'name': 'Bob'}
user1.id = 42
print(dict(user1))
# outputs {'id': 42, 'name': 'Bob'}
if __name__ == '__main__':
main()
After querying the database using following SQLAlchemy code:
from sqlalchemy import create_engine
from sqlalchemy.orm import sessionmaker
SQLALCHEMY_DATABASE_URL = 'sqlite:///./examples/sql_app.db'
engine = create_engine(SQLALCHEMY_DATABASE_URL, echo=True)
query = sqlalchemy.select(TABLE)
result = engine.execute(query).fetchall()
You can use this one-liner:
query_dict = [record._mapping for record in results]
I have a variation on Marco Mariani's answer, expressed as a decorator. The main difference is that it'll handle lists of entities, as well as safely ignoring some other types of return values (which is very useful when writing tests using mocks):
#decorator
def to_dict(f, *args, **kwargs):
result = f(*args, **kwargs)
if is_iterable(result) and not is_dict(result):
return map(asdict, result)
return asdict(result)
def asdict(obj):
return dict((col.name, getattr(obj, col.name))
for col in class_mapper(obj.__class__).mapped_table.c)
def is_dict(obj):
return isinstance(obj, dict)
def is_iterable(obj):
return True if getattr(obj, '__iter__', False) else False
To complete #Anurag Uniyal 's answer, here is a method that will recursively follow relationships:
from sqlalchemy.inspection import inspect
def to_dict(obj, with_relationships=True):
d = {}
for column in obj.__table__.columns:
if with_relationships and len(column.foreign_keys) > 0:
# Skip foreign keys
continue
d[column.name] = getattr(obj, column.name)
if with_relationships:
for relationship in inspect(type(obj)).relationships:
val = getattr(obj, relationship.key)
d[relationship.key] = to_dict(val) if val else None
return d
class User(Base):
__tablename__ = 'users'
id = Column(Integer, primary_key=True)
first_name = Column(TEXT)
address_id = Column(Integer, ForeignKey('addresses.id')
address = relationship('Address')
class Address(Base):
__tablename__ = 'addresses'
id = Column(Integer, primary_key=True)
city = Column(TEXT)
user = User(first_name='Nathan', address=Address(city='Lyon'))
# Add and commit user to session to create ids
to_dict(user)
# {'id': 1, 'first_name': 'Nathan', 'address': {'city': 'Lyon'}}
to_dict(user, with_relationship=False)
# {'id': 1, 'first_name': 'Nathan', 'address_id': 1}
We can get a list of object in dict:
def queryset_to_dict(query_result):
query_columns = query_result[0].keys()
res = [list(ele) for ele in query_result]
dict_list = [dict(zip(query_columns, l)) for l in res]
return dict_list
query_result = db.session.query(LanguageMaster).all()
dictvalue=queryset_to_dict(query_result)
from copy import copy
def to_record(row):
record = copy(row.__dict__)
del record["_sa_instance_state"]
return record
If not using copy, you might run into errors.

Return single peewee record as dict

If I'm getting multiple records from a database with peewee, I can convert them to dicts like this:
users = User.select().where(User.attribute == some_value).dicts()
However, often I only want one record (or know that only one record will be returned), so I can do:
one_user = User.get(User.name == some_value)
But I can't call .dicts() on the object which is returned by that.
Is there a way to get the result of that get query in dict form?
At the moment the only thing I can think of is the unpythonic
one_user = User.select().where(User.name == some_value).dicts()[0]
peewee has an extension function model_to_dict, defined in playhouse.shortcuts. From the example:
>>> from playhouse.shortcuts import model_to_dict
>>> user = User.create(username='charlie')
>>> model_to_dict(user)
{'id': 1, 'username': 'charlie'}
You can use ".get()":
one_user = User.select().where(User.name == some_value).dicts().get()
Though you can also add a helper method:
class User(Model):
#classmethod
def get_as_dict(cls, expr):
query = cls.select().where(expr).dicts()
return query.get()
It's python. You can extend it.
in reference to Peewee model to JSON
i think you should implement a str method as defined in the link, and then when you do.
user = User.get(User.name == some_value)
userDict = json.dumps(str(user))
you will get the dictioanry of the user

Using F expression on django-money MoneyField

Using Django 1.8.5 and django-money 0.7.4, I would like to update a MoneyField using an F expression to avoid possible race conditions. Let's assume I have a wallet defined as
from moneyed.classes import Money
from django.db.models import F
from django.db import models
from djmoney.models.fields import MoneyField
class Wallet(models.Model):
balance = MoneyField(max_digits=5, decimal_places=2, default_currency='EUR')
The following code to top up a wallet fails with AttributeError: 'CombinedExpression' object has no attribute 'children'
amount_to_add = Money(amount="3", currency="EUR")
wallet = Wallet(balance=10)
wallet.balance = F('balance') + amount_to_add
I also tried wallet.balance.amount = F("balance__amount") + 3 which doesn't throw an exception straight away but then a subsequent wallet.save() fails with TypeError: a float is required. What would be the correct way to do it?
Since django-money 0.7.7 all basic F objects manipulations are supported.
It works for Django 1.4+.
Have you tried giving it a float instead of an integer? Try: wallet.balance.amount = F("balance__amount") + float(3)
I am not sure why their code doesn't automatically try to convert integers to float's if it that is what it requires.
For anyone using a serializer class, a possible source of problem (like for my case) wasn't due to MoneyField but rather due to the failure in serializing F() expressions.
File "/home/nponcian/Documents/GitHub/venv/lib/python3.8/site-packages/djmoney/contrib/django_rest_framework/fields.py", line 56, in to_representation
return super().to_representation(obj)
File "/home/nponcian/Documents/GitHub/venv/lib/python3.8/site-packages/rest_framework/fields.py", line 1148, in to_representation
value = decimal.Decimal(str(value).strip())
decimal.InvalidOperation: [<class 'decimal.ConversionSyntax'>]
You can perform the F increment expression through this:
>>> obj = Wallet.objects.filter(balance_currency="EUR").first()
>>> obj.balance = F('balance') + Money(Decimal("12.24"), "EUR")
>>> obj.save()
So when using e.g. an update in a serializer class:
class WalletModelSerializer(serializers.ModelSerializer):
class Meta:
model = Wallet
fields = "__all__"
def update(self, instance: Wallet, validated_data: dict) -> Wallet:
# Create a copy to not modify the original validation result
validated_data = deepcopy(validated_data)
if "balance" in validated_data:
validated_data["balance"] += models.F("balance")
# When the update is performed, the result is equivalent to performing:
# instance.balance = models.F("balance") + validated_data["balance"]
# Or more specifically:
# instance.balance = models.F("balance") + djmoney.money.Money("12.34", "USD")
return super().update(instance, validated_data)
But, calling serializer.save() and then accessing the serialized data via serializer.data which in turn calls serializer.to_representation() which in turn calls each of the field.to_representation() would fail for the field balance because its value is of type CombinedExpression e.g. <CombinedExpression: F(balance) + Value(12.34)> which can't be serialized. Note that the expression is what we initially set as the value of the balance = F("balance") + Money("12.34", "USD"). As documented, F() assignments persist after Model.save()
F() objects assigned to model fields persist after saving the model instance and will be applied on each save()... This persistence can be avoided by reloading the model object after saving it, for example, by using refresh_from_db().
>>> # Access the balance after the obj.save()
>>> type(obj.balance)
<class 'django.db.models.expressions.CombinedExpression'>
>>> obj.balance
<CombinedExpression: F(balance) + Value(1.2)>
To solve this, depending on your requirements, you can just refresh it to fetch its latest value. As documented, the refresh would replace the CombinedExpression value into its actual value, which then could be successfully serialized.
>>> obj.refresh_from_db()
>>> type(obj.balance)
<class 'djmoney.money.Money'>
>>> obj.balance
Money('10.28', 'EUR')
Which is translated to this on a serializer class:
class WalletModelSerializer(serializers.ModelSerializer):
...
def update(self, instance: Wallet, validated_data: dict) -> Wallet:
...
instance = super().update(instance, validated_data)
if any(isinstance(value, models.Expression) for value in validated_data.values()):
instance.refresh_from_db()
return instance
After this change, accessing serializer.data would be successful.

flask-sqlalchemy select count()

i have query like this
query = Notification.query(db.func.count(Notification.id))
query = query.filter(Notification.read == False)
query = query.filter(Notification.id == recv_id)
return query.all()
and i got error like this
query = Notification.query(db.func.count(Notification.id))
TypeError: 'BaseQuery' object is not callable
please help, thanks
Your first line raises the error. query is an instance of BaseQuery and its not callable.
What you are trying to do is similar to:
class A(object):
pass
a_obj = A()
print a_obj()
You can't call an instance.
You should call some method on the instance.
Not sure why you require the first line in your code.
You can do something like:
Notification.query.filter(Notification.read == False)

Categories