Regenerate SQLalchemy Sequence on update - python

Imagine I have a class in my models like this:
class Sample(Base):
__tablename__ = 'sample'
id = Column(Integer, primary_key=True)
firstname = Column(String(50))
lastname = Column(String(50))
auto_generated_code = Column(
Integer,
Sequence('sample_auto_generated_code_sequence'),
unique=True
)
When I add an instance to Sample class, after flushing the session, my instance get an integer number automatically. so far so good.
What I also want is, when I update any of other columns of the mentioned instance, it should get a new auto_generated_code automatically.
In simple word I want my Sequence to generate another code on update too. How can I achieve this?

I found an answer that we can get the Sequence object in Sqlalchemy like this:
from sqlalchemy import Sequence
seq = Sequence('sample_auto_generated_code_sequence')
Then we can get the next Sequence by executing it on our connection or session
instance.auto_generated_code = session.execute(seq) # or conn.execute(seq)
Then by adding it to the session it will go just fine.

Related

SQLAlchemy: Map a column value before storage

Perhaps I am approaching this wrongly.
Say I have an enum like so:
class MyEnum(Enum):
type0 = 'type0'
type1 = 'type1'
And a SQLAlchemy ORM Schema like so:
class MyEntity(Base):
__tablename__ = 'my_entity'
id = Column(BigInteger, primary_key=True)
name = Column(String, nullable=False)
I don't want to store MyEnum in the database as an entity type. I just want it to be as a string, with no validation. However, whenever I access MyEntity.name, I want it to have been cast to an enum.
Is there any way to do a simple mapping so that, from the outside, it looks as though MyEntity.name is of type MyEnum, but is persisted as a raw string?
The closest thing I have found was to use a hybrid_property, but this doesn't seem to be applied for my exact use-case.
Any ideas?

Generic query method based of an object from its model class

In my model class, I want to create a generic method say get_list(obj) which accept an argument of its object which contains values of their corresponding attribute, and returns all appropriate records that match with corresponding column.
Suppose that I have a users class in my model, and to use the get_list(obj) method. I just need to pass an object of users with its values. Obviously, this will save a lot of time instead of creating repetitive filter_by().
class Users(db.Model):
__tablename__ = 'users'
id = Column(Integer, primary_key=True, nullable=False, autoincrement=True)
username = Column(String(200), nullable=False)
password = Column(String(200))
email = Column(String(200), nullable=False, unique=True)
def as_query(self):
query = []
for c in self.__table__.columns:
if getattr(self, c.name) is not None:
query.append(c.name+'='+str(getattr(self, c.name)))
return ' and '.join(query)
#classmethod
def get_list(cls, statement):
return cls.query.filter_by(statement).all()
To use the method, we can expect something more like this
user = Users(username='admin')
results = Users.get_list(user.as_query()) # result as a list
I'm aware that we can achieve the same thing with just write the query into filter_by instead of creating a meaningless object. However, in one of my APIs the object will be created automatically-meaning it will be automatic on the fly.
However, this solution is only a hack to just flatten the attribute and its value into filter_by() statement and obviously not working?
Do you have a better solution for this?
Not sure, If this is what you were looking for, but I believe with Python's dict unpacking, it is already possible to unpack all the object attributes to use as filter in the filter_by function.
results = session.query(models.Users).filter_by(**user.dict()).all()
You can also use the or_ method to match any of the attribute values like below:
session.query(Users).filter(or_(**user.dict()))

sqlachemy update value in table doen't update value in associated table

I want to update a column in a table with a relation one to many.
but i can't figure out a way to make the update impact also the relation table. Here is a sample of the code, i tried to change it with back_populates, with or without onupdate or cascade but it doesn't seem to have any effect on what i want to do
class Employe(Base):
__tablename__ = 'employes'
id = Column(Integer, primary_key=True)
service_name = Column(String, ForeignKey('services.name', onupdate="CASCADE"))
class Service(Base):
__tablename__ = 'services'
name = Column(String, primary_key=True)
employers = relationship('Employe', backref=backref('service', cascade="all"))
a = session.query(Service).filter_by(name='ECTC').update({'name' : 'SECTC'})
session.commit()
That does change the services.name but let the column service_name untouched.
And so when i want to query session.query(Employe).filter_by(service_name='SECTC') i get no result
Is there a way to make the update impact both tables ?
Otherwise i can just select, delete and recreate the employes everytime i need to update services but that doesn't seem optimal to me.
Thank you for your help !

slqlalchemy UniqueConstraint VS Index(unique=True)

I am using MySQL (running InnoDB), and wrapped the entire thing using sqlalchemy. Now, I would like to generate changes in my database by using (see docs)
sqlalchemy_utils.functions.create_database(...)
Generally the above function does what it is supposed to. The only exception being the generation of unique indexes.
Say, I define a table like this:
## ...
# DeclBase = declarative_base()
## ...
class MyTable(DeclBase):
__tablename__ = 'my_table'
id = Column(Integer, primary_key=True)
attr_1 = Column(String(32))
attr_2 = Column(Integer, nullable=False)
attr_3 = Column(DateTime)
attr_4 = Column(
Integer,
ForeignKey('other_table.id', onupdate='CASCADE', ondelete='CASCADE'),
nullable=False
)
u_idx = UniqueConstraint(attr_2, attr_3, 'my_table_uidx')
when I call create_database I will get sqlalchemy to create the table 'my_table' with all columns as specified. The foreign key is also setup fine, but no unique index can be found on the database side. I then tried using a Index(unique=True) instead. So instead of
u_idx = UniqueConstraint(attr_2, attr_3, 'my_table_uidx')
I put
u_idx_1 = Index('my_table_uidx', attr_2, attr_3, unique=True)
My impression was this logically produces a similar result. This time sqlalchemy indeed created the unique index on the db.
Maybe I am miserably misunderstanding something about the difference between UniqueConstraint and Index(unique=True), or the way sqlalchemy uses them to automate generation of databases.
Can anyone shed some light on this?
The main difference is that while the Index API allows defining an index outside of a table definition as long as it can reference the table through the passed SQL constructs, a UniqueConstraint and constraints in general must be defined inline in the table definition:
To apply table-level constraint objects such as ForeignKeyConstraint to a table defined using Declarative, use the __table_args__ attribute, described at Table Configuration.
The thing to understand is that during construction of a declarative class a new Table is constructed, if not passed an explicit __table__. In your example model class the UniqueConstraint instance is bound to a class attribute, but the declarative base does not include constraints in the created Table instance from attributes. You must pass it in the table arguments:
class MyTable(DeclBase):
__tablename__ = 'my_table'
...
# A positional argument tuple, passed to Table constructor
__table_args__ = (
UniqueConstraint(attr_2, attr_3, name='my_table_uidx'),
)
Note that you must pass the constraint name as a keyword argument. You could also pass the constraint using Table.append_constraint(), if called before any attempts to create the table:
class MyTable(DeclBase):
...
MyTable.__table__.append_constraint(
UniqueConstraint('attr_2', 'attr_3', name='my_table_uidx'))

flask sqlalchemy UniqueConstraint with foreignkey attribute

I have an app I am building with Flask that contains models for Projects and Plates, where Plates have Project as a foreignkey.
Each project has a year, given as an integer (so 17 for 2017); and each plate has a number and a name, constructed from the plate.project.year and plate.number. For example, Plate 106 from a project done this year would have the name '17-0106'. I would like this name to be unique.
Here are my models:
class Project(Model):
__tablename__ = 'projects'
id = Column(Integer, primary_key=True)
name = Column(String(64),unique=True)
year = Column(Integer,default=datetime.now().year-2000)
class Plate(Model):
__tablename__ = 'plates'
id = Column(Integer, primary_key=True)
number = Column(Integer)
project_id = Column(Integer, ForeignKey('projects.id'))
project = relationship('Project',backref=backref('plates',cascade='all, delete-orphan'))
#property
def name(self):
return str(self.project.year) + '-' + str(self.number).zfill(4)
My first idea was to make the number unique amongst the plates that have the same project.year attribute, so I have tried variations on
__table_args__ = (UniqueConstraint('project.year', 'number', name='_year_number_uc'),), but this needs to access the other table.
Is there a way to do this in the database? Or, failing that, an __init__ method that checks for uniqueness of either the number/project.year combination, or the name property?
There are multiple solutions to your problem. For example, you can de-normalize project.year-number combination and store it as a separate Plate field. Then you can put a unique key on it. The question is how you're going to maintain that value. The two obvious options are triggers (assuming your DB supports triggers and you're ok to use them) or sqla Events, see http://docs.sqlalchemy.org/en/latest/orm/events.html#
Both solutions won't emit an extra SELECT query. Which I believe is important for you.
your question is somewhat similar to Can SQLAlchemy events be used to update a denormalized data cache?

Categories

Resources