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

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.

Related

Django - calling attribute from queryset with a string

I'm trying to loop over different query sets while not repeating myself too much and have encountered a problem using the queryset class.
This is not necessarily completely a Django-problem.
What I'm trying to do is to use my keylist, which corresponds to a django model's column names, to create a list of the data from those column names, what i want to do is something like this:
if needthisdata==1:
needdata=['column1', 'column2', 'column3']
else:
needdata=['column1', 'column4', 'column7']
entry=djangomodel.get.all().filter(identifier='id')
dictitems=[]
for n in range(0, len(needdata)):
if n==0:
dictitems=[entry.needdata[n]]
else:
dictitems.append(entry.needdata[n])
Which of course doesn't work since the queryset doesn't have a need data attribute, is there some way to call an attribute for a class with a string in this way?
A valid Django statement to obtain a single entry
First of all, there are some semantical problems here:
itentifier should probably be identifier, id, or pk;
you use .all immedately instead of first obtaining a manager (probably .objects); and
you here use a .filter(..) on the queryset to filter on an identifier, but usually this should be a .get(..), since by using a filter, zero, one or more results can be returned in an iterable.
entry = djangomodel.objects.get(id=some_id)
So now we obtain a single entry, but that of course does not resolve
obtaining the columns.
If all elements are real Django columns
In case the columns are real Django fields (so no #propertys, etc.) then we can use values_list, and perform a list(..) constructor on it:
dictitems = list(djangomodel.objects.values_list(*needdata).get(id=some_id))
If case some elements are #propertys
In case not all those fields are real Django fields, then we can use attrgetter instead:
from operator import attrgetter
dictitems = list(attrgetter(*needdata)(djangomodel.objects.get(id=some_id)))

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)

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.

Override return values of a column in SQLAlchemy; hybrid property or custom type?

I would need a very quick advice. I have a table field which can contain NULL, one or more strings, separated by ';'.
At the moment the column is defined in the model as usual:
aliases = Column(String(255))
I have an hybrid property that splits the strings and returns a list:
def my_aliases(self):
if self.aliases:
return [i.strip() for i in self.aliases.split(';')]
How can change the default behaviour of the model to get rid of the useless 'self.aliases' and always get the list or None of 'self.my_aliases'?
Is it possible to override the attribute?
Using mapper or the declarative API you can create a computed attribute in your class. Options would include:
An attribute computed from a query
Using a descriptor to parse/assemble your semicolon-separated list
And I'm assuming here that you don't have the option of changing the fields of your tables. But if you do, putting lists that you have to parse inside a single column is a "smell". For example, what happens when your list is too long? Better to have a separate table for that data and use a straight-forward join to get your aliases list.

Django models - how to filter out duplicate values by PK after the fact?

I build a list of Django model objects by making several queries. Then I want to remove any duplicates, (all of these objects are of the same type with an auto_increment int PK), but I can't use set() because they aren't hashable.
Is there a quick and easy way to do this? I'm considering using a dict instead of a list with the id as the key.
In general it's better to combine all your queries into a single query if possible. Ie.
q = Model.objects.filter(Q(field1=f1)|Q(field2=f2))
instead of
q1 = Models.object.filter(field1=f1)
q2 = Models.object.filter(field2=f2)
If the first query is returning duplicated Models then use distinct()
q = Model.objects.filter(Q(field1=f1)|Q(field2=f2)).distinct()
If your query really is impossible to execute with a single command, then you'll have to resort to using a dict or other technique recommended in the other answers. It might be helpful if you posted the exact query on SO and we could see if it would be possible to combine into a single query. In my experience, most queries can be done with a single queryset.
Is there a quick and easy way to do this? I'm considering using a dict instead of a list with the id as the key.
That's exactly what I would do if you were locked into your current structure of making several queries. Then a simply dictionary.values() will return your list back.
If you have a little more flexibility, why not use Q objects? Instead of actually making the queries, store each query in a Q object and use a bitwise or ("|") to execute a single query. This will achieve your goal and save database hits.
Django Q objects
You can use a set if you add the __hash__ function to your model definition so that it returns the id (assuming this doesn't interfere with other hash behaviour you may have in your app):
class MyModel(models.Model):
def __hash__(self):
return self.pk
If the order doesn't matter, use a dict.
Remove "duplicates" depends on how you define "duplicated".
If you want EVERY column (except the PK) to match, that's a pain in the neck -- it's a lot of comparing.
If, on the other hand, you have some "natural key" column (or short set of columns) than you can easily query and remove these.
master = MyModel.objects.get( id=theMasterKey )
dups = MyModel.objects.filter( fld1=master.fld1, fld2=master.fld2 )
dups.all().delete()
If you can identify some shorter set of key fields for duplicate identification, this works pretty well.
Edit
If the model objects haven't been saved to the database yet, you can make a dictionary on a tuple of these keys.
unique = {}
...
key = (anObject.fld1,anObject.fld2)
if key not in unique:
unique[key]= anObject
I use this one:
dict(zip(map(lambda x: x.pk,items),items)).values()

Categories