SQLAlchemy look up and merge object before adding - python

I have an object which has foreign keys to another object. Let's call the first object A and the second object B. A can be represented as (integer id, integer b_id, integer some_data) and B can be represented as (integer id, integer datum_one, integer datum_two)
datum_one and datum_two form a unique composite in B (I think this is the right term - I don't want more than on entry in B with the same combination of these fields). However, I just want to reference the id in the ForeignKey pointing from A to B. It seems tedious and redundant to do something like a composite foreign key like here.
I want to have functionality such that when I add A to my database, it first searches for a matching B which has the same datum_one and datum_two. If this exists, it uses the matching entry in the database, and otherwise it adds a new row to the table representing B.
Is there a way to accomplish this? I suspect the solution have have something to do with the merge directive, but I'm not sure how to get this functionality exactly.
One potential solution I considered was actually using the UNIQUE directive, but it seems like SQLAlchemy doesn't play nice with unique - I would basically need to just write my own error handling, which is an option but I was hoping SQLAlchemy would have a more elegant solution to this.

I think you should just handle this yourself before inserting your object to the database. From the docs... http://docs.sqlalchemy.org/en/latest/core/events.html
from sqlalchemy import event
# standard decorator style
#event.listens_for(SomeSchemaClassOrObject, 'before_create')
def receive_before_create(target, connection, **kw):
"listen for the 'before_create' event"
# ... (event handling logic) ...

Related

SQLalchemy select using ORM the resulting row contains only memory location

I think I have misunderstood something critical and obvious, I am attempting to use the SqlAlchemy ORM to select a row from a table in a database and return the values. The first step is to get the row and access it, from there I think I'm good however, when I select the row and try and investigate the object that is returned, all I find is an address for the object in memory: <main.UserMap object at 0x000001F65A54B490>,
Attempting to use objprint to investigate the object gives no further information, I am confused as per my understanding the resulting row object should behave like a tuple so at least objprint should find a variety of entries within it even if it can't print them due to them being differen't data types.
The statement in question:
select(UserMap).where(UserMap.google_auth == '***********************')
a more basic select(UserMap) also seems to give a similar result.
The table contains some fields as strings, some as integers and some in date and time formats but obviously only one type per column.
I am using session.execute, I would like to avoid query as I understand that it's functionality is being deprecated in the 2.x API, if I have misunderstood this then I am happy to attempt that but would still like to understand what I am getting wrong about the result and row objects.
As I said, I think I have missed something important, but don't realise what, as far as I can see I am executing the statements in much the same way as shown in the ORM documentation e.g:
>>> stmt = select(User).where(User.name == 'spongebob')
>>> with Session(engine) as session:
... for row in session.execute(stmt):
... print(row)
<main.UserMap object at 0x000001F65A54B490> is the default string representation of an ORM object. You can retrieve the attributes (properties) of the object using the standard syntax:
me = UserMap(user_name="Gord")
print(me) # <main.UserMap object at 0x000001F65A54B490>
print(me.user_name) # Gord

SQLalchemy "load_only" doesn't load only the columns specified

I am trying to select a subset of columns from a table with sqlalchemy's load_only function. Unfortunately it doesn't seem to return only the columns specified in the functional call - specifically, it also seems to fetch the primary key (in my case, an auto_increment id field).
A simple example, if I use this statement to build a query,:
query = session.query(table).options(load_only('col_1', 'col_2'))
Then the query.statement looks like this:
SELECT "table".id, "table"."col_1", "table"."col_2"
FROM "table"
Which is not what I would have expected - given I've specified the "only" columns to use...Where did the id come from - and is there a way to remove it?
Deferring the primary key would not make sense, if querying complete ORM entities, because an entity must have an identity so that a unique row can be identified in the database table. So the query includes the primary key though you have your load_only(). If you want the data only, you should query for that specifically:
session.query(table.col1, table.col2).all()
The results are keyed tuples that you can treat like you would the entities in many cases.
There actually was an issue where having load_only() did remove the primary key from the select list, and it was fixed in 0.9.5:
[orm] [bug] Modified the behavior of orm.load_only() such that primary key columns are always added to the list of columns to be “undeferred”; otherwise, the ORM can’t load the row’s identity. Apparently, one can defer the mapped primary keys and the ORM will fail, that hasn’t been changed. But as load_only is essentially saying “defer all but X”, it’s more critical that PK cols not be part of this deferral.

How to filter on calculated column of a query and meanwhile preserve mapped entities

I have a query which selects an entity A and some calculated fields
q = session.query(Recipe,func.avg(Recipe.somefield).join(.....)
I then use what I select in a way which assumes I can subscript result with "Recipe" string:
for entry in q.all():
recipe=entry.Recipe # Access KeyedTuple by Recipe attribute
...
Now I need to wrap my query in an additional select, say to filter by calculated field AVG:
q=q.subquery();
q=session.query(q).filter(q.c.avg_1 > 1)
And now I cannot access entry.Recipe anymore!
Is there a way to make SQLAlchemy adapt a query to an enclosing one, like aliased(adapt_on_names=True) orselect_from_entity()`?
I tried using those but was given an error
As Michael Bayer mentioned in a relevant Google Group thread, such adaptation is already done via Query.from_self() method. My problem was that in this case I didn't know how to refer a column which I want to filter on
This is due to the fact, that it is calculated i.e. there is no table to refer to!
I might resort to using literals(.filter('avg_1>10')), but 'd prefer to stay in the more ORM-style
So, this is what I came up with - an explicit column expression
row_number_column = func.row_number().over(
partition_by=Recipe.id
).label('row_number')
query = query.add_column(
row_number_column
)
query = query.from_self().filter(row_number_column == 1)

SQLAlchemy: Execute/Add a function to a column in sqlalchemy

I'm using SQLAlchemy and I have a query from which one of the columns I obtain is a constant QUOTE_STATUS_ERROR, the values in this column are integers. Since the constant value doesn't mean anything to the end-user, I'd like to convert that value from within the query itself to show a string by mapping the values of that column to a dictionary that I have in the app using a function I have in place for that purpose already. I haven't been able to find a way to implement it since Columns in the query are object not value of the column itself. To make my question clear this is an example of what I have:
Query:
q = meta.session.query(MyModel.id, MyModel.quote_status).join(AnotherModel).subquery("q")
Function I want to use:
def get_status_names(status_value):
return QUOTE_STATUS_NAMES[status_value]
Is there a way to this directly from SQLAlchemy by attaching/passing a function (get_status_names()) to the column (MyModel.quote_status). If not what could be the best approach? I prefer not iterate over the values once I get the results in case the list of results is extensive. I would appreciate a push in the right direction.
UPDATE: I'm joining the resulting subquery with other tables
There are a few things you can do.
Off the top of my head...
If you just want to display things, you can use a property decorator:
QUOTE_STATUS__ID_2_NAME = {}
class MyModel(object):
id = Column()
quote_status_id = Column()
#property
def quote_status_string(self):
if self.quote_status_id:
return QUOTE_STATUS__ID_2_NAME[self.quote_status_id]
return None
If you want to render/accept strings and have sqlalchemy convert from string/int transparently, you can use a TypeDecorator -- http://docs.sqlalchemy.org/en/rel_0_9/core/types.html#custom-types
Personally, I usually go for the property decorator.

column names and types for insert operation in sqlalchemy

I am building a sqlite browser in Python/sqlalchemy.
Here is my requirement.
I want to do insert operation on the table.
I need to pass a table name to a function. It should return all columns along with the respective types.
Can anyone tell me how to do this in sqlalchemy ?
You can access all columns of a Table like this:
my_table.c
Which returns a type that behaves similar to a dictionary, i.e. it has values method and so on:
columns = [(item.name, item.type) for item in my_table.c.values()]
You can play around with that to see what you can get from that. Using the declarative extension you can access the table through the class' __table__ attribute. Furthermore, you might find the Runtime Inspection API helpful.

Categories