SQLAlchemy set parents column value - sqlalchemy

Wanting a Base which defines a version column, and then populate that version in inheriting classes.
the idea is something like:
my_declarative_base.py:
def declarative_base():
Base = sqlalchemy.ext.declarative.declarative_base()
class MyBase(Base):
__abstract__ = True
version = Column(VARCHAR)
return MyBase
__init__.py
from {path.to}.my_declarative_base import declarative_base
Base = declarative_base()
my_model.py:
from {path.to} import Base
class MyModel_1(Base):
col_1 = Column(...)
col_2 = Column(...)
...
version = "v3.3.3"
class MyModel_2(Base):
col_11 = Column(...)
col_22 = Column(...)
...
version = "v33.33-alpha"
(as expected, trying this wouldn't work)
the focus is that setting the version in the inheriting classes is simple and straightforward, like assigning a variable (that is, I would not want version declaration to have default property set to a callable, where each inheriting class would have to know it has to implement, or other solutions of this "framework" style; looking for something plain, simple, clear and straightforward).
Ideas how to accomplish that?
Thanks in advance!

Related

conditionals inside SQLAlchemy's column_property

I'm using SQLAlchemy to map a class:
class Model(sqlalchemy.declarative_base()):
attr_a = Column(String)
attr_b = Column(Integer)
attr_c = Column(Integer)
aggr = column_property(attr_b + attr_c IF attr_a=='go' ELSE attr_b - attr_c)
Last line is pseoudo code that requires some conditional logic. Is such logic even possible inside column_property? How can I implement it as a simple conditional aggregate?
Actually it turns out to be a common technique, sqlalchemy provides a tool set inside sqlalchemy.sql, one can easily write SQL logic such as case:
from sqlalchemy.sql import case
...
aggr = column_property(case([(attr_a=="go", attr_b + attr_c), (attr_a=="return", attr_b + attr_c + attrition]
Just note here that case takes a python iterable as parameter.

Modify all numbers before insert or update

In SqlAlchemy I use:
price = Column(Numeric(18, 5))
in various placed throught my app. When I get a number formatted in swedish, with a comma instead of a dot (0,34 instead of 0.34) and try to change the price column the number gets set to 0.00000.
To solve this I have this code:
obj.price = price.replace(',','.')
But having this all over the code makes it pretty ugly and the risk is that I forget one place. Would it be possible to have some kind of generic converter function which gets called before a value is converted from a string to a Numeric? And that I have that in one place only.
Check the validates decorator of SQLAlchemy: http://docs.sqlalchemy.org/en/rel_1_0/orm/mapped_attributes.html
A quick way to add a “validation” routine to an attribute is to use
the validates() decorator. An attribute validator can raise an
exception, halting the process of mutating the attribute’s value, or
can change the given value into something different.
In your case the code could look similar to:
from sqlalchemy.orm import validates
class Obj(Base):
__tablename__ = 'obj'
id = Column(Integer, primary_key=True)
price = Column(Numeric(18, 5))
#validates('price')
def validate_price(self, key, price):
if ',' in price:
return float(price.replace(',','.'))
else:
return float(price)

Unique Sequencial Number to column

I need create sequence but in generic case not using Sequence class.
USN = Column(Integer, nullable = False, default=nextusn, server_onupdate=nextusn)
, this funcion nextusn is need generate func.max(table.USN) value of rows in model.
I try using this
class nextusn(expression.FunctionElement):
type = Numeric()
name = 'nextusn'
#compiles(nextusn)
def default_nextusn(element, compiler, **kw):
return select(func.max(element.table.c.USN)).first()[0] + 1
but the in this context element not know element.table. Exist way to resolve this?
this is a little tricky, for these reasons:
your SELECT MAX() will return NULL if the table is empty; you should use COALESCE to produce a default "seed" value. See below.
the whole approach of inserting the rows with SELECT MAX is entirely not safe for concurrent use - so you need to make sure only one INSERT statement at a time invokes on the table or you may get constraint violations (you should definitely have a constraint of some kind on this column).
from the SQLAlchemy perspective, you need your custom element to be aware of the actual Column element. We can achieve this either by assigning the "nextusn()" function to the Column after the fact, or below I'll show a more sophisticated approach using events.
I don't understand what you're going for with "server_onupdate=nextusn". "server_onupdate" in SQLAlchemy doesn't actually run any SQL for you, this is a placeholder if for example you created a trigger; but also the "SELECT MAX(id) FROM table" thing is an INSERT pattern, I'm not sure that you mean for anything to be happening here on an UPDATE.
The #compiles extension needs to return a string, running the select() there through compiler.process(). See below.
example:
from sqlalchemy import Column, Integer, create_engine, select, func, String
from sqlalchemy.ext.declarative import declarative_base
from sqlalchemy.sql.expression import ColumnElement
from sqlalchemy.schema import ColumnDefault
from sqlalchemy.ext.compiler import compiles
from sqlalchemy import event
class nextusn_default(ColumnDefault):
"Container for a nextusn() element."
def __init__(self):
super(nextusn_default, self).__init__(None)
#event.listens_for(nextusn_default, "after_parent_attach")
def set_nextusn_parent(default_element, parent_column):
"""Listen for when nextusn_default() is associated with a Column,
assign a nextusn().
"""
assert isinstance(parent_column, Column)
default_element.arg = nextusn(parent_column)
class nextusn(ColumnElement):
"""Represent "SELECT MAX(col) + 1 FROM TABLE".
"""
def __init__(self, column):
self.column = column
#compiles(nextusn)
def compile_nextusn(element, compiler, **kw):
return compiler.process(
select([
func.coalesce(func.max(element.column), 0) + 1
]).as_scalar()
)
Base = declarative_base()
class A(Base):
__tablename__ = 'a'
id = Column(Integer, default=nextusn_default(), primary_key=True)
data = Column(String)
e = create_engine("sqlite://", echo=True)
Base.metadata.create_all(e)
# will normally pre-execute the default so that we know the PK value
# result.inserted_primary_key will be available
e.execute(A.__table__.insert(), data='single row')
# will run the default expression inline within the INSERT
e.execute(A.__table__.insert(), [{"data": "multirow1"}, {"data": "multirow2"}])
# will also run the default expression inline within the INSERT,
# result.inserted_primary_key will not be available
e.execute(A.__table__.insert(inline=True), data='single inline row')

How do i remove a .limit() that is already applied to a query object

question: How do i remove a .limit() that is already applied to a query object.
I have a Query which already has .limit(N) applied. Then i would like to remove the limit from the query, to have a .order_by applied. Order by has to be applied before any limit or offset.
example which fails:
query = session.query(Object).limit(10)
query = query.order_by(Object.field)
I tried doing:
query = session.query(Object).limit(10)
query = query.limit(None) # or False
query = query.order_by(Object.field)
But that does not work.
The reason i want this, is that the limit actually happens at another place as a sensible default limit.
Thanks
limit(None) will cancel the limit. If that's not working, you might be on a super-old version of SQLAlchemy perhaps, I tested it all the way back to 0.6.8:
from sqlalchemy import Column, Integer
from sqlalchemy.orm import Session
from sqlalchemy.ext.declarative import declarative_base
Base = declarative_base()
class A(Base):
__tablename__ = "a"
id = Column(Integer, primary_key=True)
s = Session()
q = s.query(A).limit(5)
q = q.limit(None).order_by(A.id)
print q

Constraining a django Many-to-Many Relationship using unique_together

I think this may be more SQL than Django but Django is what I'm working in. What I am trying to do is to come up with a object model which can have many properties but is constrained to only 1 property type per object.
Say we have 3 property types:
is_cool
is_happy
is_mean
Suppose I have an object (MyObject) which can have * (0-All) of these properties applied to it but only one of each.
So I think this is diagramed as follows (please correct me if I'm wrong):
In Django I am stuggling with this constraint. I want it at the db level i.e using unique_together.
Here is what I have..
PROP_VALUE_CHOICES = (("URL", "url"),
("Boolean", "bool"),
("String", "char"),
("Person", "person"))
class PropertyType(models.Model):
name = models.CharField(max_length=32)
value_type = models.CharField(max_length=32, choices=PROP_VALUE_CHOICES)
class Property(models.Model):
type = models.ForeignKey(PropertyType)
value = models.CharField(max_length=32)
class MyObjectA(models.Model):
properties = models.ManyToManyField(Property, related_name="MyObjectA")
class MyObjectB(models.Model):
properties = models.ManyToManyField(Property, related_name="MyObjectB")
So the questions:
Is the above picture the correct way to document what I'm trying to accomplish.
My model is not complete - what am I missing and where do I apply the unique together constraint on the Object name and property type.
BTW - This is similar to this post but they used a through which I'm not sure I need??
Thanks!!
In case anyone really is looking for this answer...
Using Abstract Base Class I created the following structure which should work. Granted it no longer represents the picture completely but does solve the problem.
PROP_VALUE_CHOICES = (("URL", "url"),
("Boolean", "bool"),
("String", "char"),
("Person", "person"))
class PropertyType(models.Model):
name = models.CharField(max_length=32)
value_type = models.CharField(max_length=32, choices=PROP_VALUE_CHOICES)
class Property(models.Model):
type = models.ForeignKey(PropertyType, unique=True, related_name="%(app_label)s_%(class)s_related")
value = models.CharField(max_length=32)
class Meta:
abstract = True
class ObjectAProperties(Property): pass
class ObjectA(models.Model):
properties = models.ManyToManyField(Property, through="ObjectAProperties")
class ObjectBProperties(Property): pass
class ObjectB(models.Model):
properties = models.ManyToManyField(Property, through="ObjectBProperties")
Posted in case I need this again in the future!