Setting a default value in sqlalchemy - python

I would like to set a column default value that is based on another table in my SQLAlchemy model.
Currently I have this:
Column('version', Integer, default=1)
What I need is (roughly) this:
Column('version', Integer, default="SELECT MAX(1, MAX(old_versions)) FROM version_table")
How can I implement this in SQLAlchemy?

The documentation gives the following possibilities for default:
A scalar, Python callable, or ClauseElement representing the default
value for this column, which will be invoked upon insert if this
column is otherwise not specified in the VALUES clause of the insert.
You may look into using a simple function, or you may just be able to use a select() object.
In your case, maybe something along the lines of:
from sqlalchemy.sql import select, func
...
Column('version', Integer, default=select([func.max(1,
func.max(version_table.c.old_versions))]))

You want server_default
Column('version', Integer, server_default="SELECT MAX(1, MAX(old_versions)) FROM version_table")

If you want to use a DML statement to generate the default value, you can simply use the text method to indicate that you are passing DML. You may also need an extra set of parentheses if the engine wants to write this inside a VALUES clause , e.g.:
from sqlachemy import text
Column('version', Integer, default=text("(SELECT MAX(1, MAX(old_versions)) FROM version_table)"))
I've used this technique to use a sequence to override the server default ID generation, e.g.:
Column('version', Integer, default=text("NEXT VALUE FOR someSequence"))

Related

How does model class default keyword works in sqlalchemy

I have set mapping for a column in my model class as
class Plan(Base):
__tablename__ = "plans"
`default_c = Column(Boolean, default=False)`
But when i am inserting data to this table using . below code. I am still getting (exceptions.TypeError) Not a boolean value: '' . I have default_c field empty in my dictionary. I was wondering if the default should have handled this.
conn.execute(Plan.__table__.insert(), Plan_dict)
Plan_dict is my list of dictionaries which I want to insert into plans table.
According to documentation if you don't provide the column then it will populate the default value
A scalar, Python callable, or ColumnElement expression representing the default value for this column, which will be invoked upon insert if this column is otherwise not specified in the VALUES clause of the insert. This is a shortcut to using ColumnDefault as a positional argument; see that class for full detail on the structure of the argument.
I feel that you are giving the default_c an empty value. Remove the default_c from your Plan_dict and give it a try.

How do I define a SQLAlchemy function and use it as default for a table field?

The documentation has this example:
t = Table('test', meta,
Column('abc', MyType, default=func.generate_new_value(), primary_key=True)
)
Where above, when Table.insert() is used, the func.generate_new_value() expression will be pre-executed in the context of a scalar SELECT statement, and the new value will be applied to the subsequent INSERT, while at the same time being made available to the ResultProxy.inserted_primary_key attribute.
However it doesn't say how this func.generate_new_value() is defined.
I need to define a function that will select the max abc element (not a primary key in my case) from all test rows, then increment it by 1, and then pass this value to SQLAlchemy which hopefully can then INSERT it in the new row.
func.ANYTHING() is actually calling an SQL function from your database. 'ANYTHING' is the name of that function. So you will have to define your function directly on your database as an SQL function.
See also:
http://docs.sqlalchemy.org/en/latest/core/sqlelement.html?highlight=func#sqlalchemy.sql.expression.func

sqlalchemy: Making schema reflection find/use a custom type for all instances

I'm trying to override the SQLAlchemy DATETIME2 type (from MS SQL Server) to discard extra digits of subsecond precision if necessary to coerce to a native Python datetime type, as described in Replacing the Bind Result Processing of Existing Types section of the docs, like so:
import re
import sqlalchemy.dialects as sa_dialects
class TruncatingPrecisionDATETIME2(sa_dialects.mssql.base.DATETIME2):
__visit_name__ = 'DATETIME2'
_reg = re.compile(r"(\d+):(\d+):(\d+)(?:\.(\d{0,6})\d*)?")
def process_result_value(self, value, dialect):
if isinstance(value, util.string_types):
return datetime.time(*[
int(x or 0)
for x in self._reg.match(value).groups()])
else:
return value
def adapt(self, impltype):
return TruncatingPrecisionDATETIME2()
However, when I call meta.reflect(), and inspect the type of one a column of type DATETIME2 in the database, it's an instance of sqlalchemy.dialects.mssql.base.DATETIME2 rather than TruncatingPrecisionDATETIME2. What do I need to do to change this globally for all DATETIME2 instances?
This is probably unofficial as hell, and I'm not sure it would even work on MSSQL as opposed to PostgreSQL (as this changelog might seem to suggest), but by replacing the 'datetime2' item on the sqlalchemy.dialects.mssql.base.ischema_names dictionary by your custom subclass, you may be able to override the SQLAlchemy type used for all reflections of that type, if the official, recommended way of modifying result processing fails.

Filter by an object in SQLAlchemy

I have a declared model where the table stores a "raw" path identifier of an object. I then have a #hybrid_property which allows directly getting and setting the object which is identified by this field (which is not another declarative model). Is there a way to query directly on this high level?
I can do this:
session.query(Member).filter_by(program_raw=my_program.raw)
I want to be able to do this:
session.query(Member).filter_by(program=my_program)
where my_program.raw == "path/to/a/program"
Member has a field program_raw and a property program which gets the correct Program instance and sets the appropriate program_raw value. Program has a simple raw field which identifies it uniquely. I can provide more code if necessary.
The problem is that currently, SQLAlchemy simply tries to pass the program instance as a parameter to the query, instead of its raw value. This results in a Error binding parameter 0 - probably unsupported type. error.
Either, SQLAlchemy needs to know that when comparing the program, it must use Member.program_raw and match that against the raw property of the parameter. Getting it to use Member.program_raw is done simply using #program.expression but I can't figure out how to translate the Program parameter correctly (using a Comparator?), and/or
SQLAlchemy should know that when I filter by a Program instance, it should use the raw attribute.
My use-case is perhaps a bit abstract, but imagine I stored a serialized RGB value in the database and had a property with a Color class on the model. I want to filter by the Color class, and not have to deal with RGB values in my filters. The color class has no problems telling me its RGB value.
Figured it out by reading the source for relationship. The trick is to use a custom Comparator for the property, which knows how to compare two things. In my case it's as simple as:
from sqlalchemy.ext.hybrid import Comparator, hybrid_property
class ProgramComparator(Comparator):
def __eq__(self, other):
# Should check for case of `other is None`
return self.__clause_element__() == other.raw
class Member(Base):
# ...
program_raw = Column(String(80), index=True)
#hybrid_property
def program(self):
return Program(self.program_raw)
#program.comparator
def program(cls):
# program_raw becomes __clause_element__ in the Comparator.
return ProgramComparator(cls.program_raw)
#program.setter
def program(self, value):
self.program_raw = value.raw
Note: In my case, Program('abc') == Program('abc') (I've overridden __new__), so I can just return a "new" Program all the time. For other cases, the instance should probably be lazily created and stored in the Member instance.

How to use avg and sum in SQLAlchemy query

I'm trying to return a totals/averages row from my dataset which contains the SUM of certain fields and the AVG of others.
I could do this in SQL via:
SELECT SUM(field1) as SumFld, AVG(field2) as AvgFld
FROM Rating WHERE url=[url_string]
My attempt to translate this into SQLAlchemy is as follows:
totals = Rating.query(func.avg(Rating.field2)).filter(Rating.url==url_string.netloc)
But this is erroring out with:
TypeError: 'BaseQuery' object is not callable
You should use something like:
from sqlalchemy.sql import func
session.query(func.avg(Rating.field2).label('average')).filter(Rating.url==url_string.netloc)
You cannot use MyObject.query here, because SqlAlchemy tries to find a field to put result of avg function to, and it fails.
You cannot use MyObject.query here, because SqlAlchemy tries to find a field to put result of avg function to, and it fails.
This isn't exactly true. func.avg(Rating.field2).label('average') returns a Column object (the same type object that it was given to be precise). So you can use it with the with_entities method of the query object.
This is how you would do it for your example:
Rating.query.with_entities(func.avg(Rating.field2).label('average')).filter(Rating.url == url_string.netloc)
attention = Attention_scores.query
.with_entities(func.avg(Attention_scores.score))
.filter(classroom_number == classroom_number)
.all()
I tried it like this and it gave the correct average.

Categories