SQLAlchemy - operate with primary key instance inside model - python

I want to generate some unique URLs from IDs with the purpose of appending at the end of some endpoints. The result will be something like .../32dwr4. I would like to insert these short urls into the database on instantiation, based on the primary key id.
I do not know if there is some kind of 'flushing' for operating inside the model:
class Storm(db.Model):
id = db.Column(db.Integer, primary_key=True)
_url = db.Column(db.String(200), index=True)
## Relationships
#hybrid_property
def url(self):
return self._url
#url.setter
def _set_url(self, plaintext):
self._url= base64.b64encode(plaintext)
def __init__(self, name, question, *):
self.name = name
self.question = question
self.url = self.id # <<------ is it possible to pass its id on the fly, convert it through the setter and store it?
If it is not possible, which approach do you recommend?

Related

Update function for a Flask-SQLAlchemy model

I often use an update function when working with Flask-SQLAlchemy models:
from app import db
class User(db.Model):
__tablename__ = 'User'
id = db.Column(db.Integer, primary_key=True)
email = db.Column(db.String(255))
name = db.Column(db.String(255))
def update(self, email=None, name=None):
if email is not None:
self.email = email
if name is not None:
self.name = name
def dump(self):
return dict([(k, v) for k, v in vars(self).items() if not k.startswith("_")])
This allows me to directly update an object with a json body:
user.update(**body)
But with a table containing a lot of columns, writing this function can be really annoying.
Do you know a more concise approach?
You can iterate over dict fields and use setattr to update:
for field, value in body.items():
if value is not None:
setattr(self, field, value)

dynamic __tablename__ in flask-sqlalchemy models

Is there a possibility to make the __tablename__ in flask-sqlalchemy models dynamic with the declarative base approach?
Usually you set it as this one:
class User(Base):
__tablename__ = 'users'
id = Column(Integer, primary_key=True)
name = Column(String(50), unique=True)
email = Column(String(120), unique=True)
def __init__(self, name=None, email=None):
self.name = name
self.email = email
def __repr__(self):
return '<User %r>' % (self.name)
I would like to change it through a parameter (maybe in the constructor?), so that I can have a table per user.
I found some other approaches in this guide here
Approaches
but I would like to use the session for that as I am already using it for the other models.
You can utilize python's type() function to dynamically build SQLAlchemy models.
Here's a example:
# define columns in an abstract model class
class Log(Base):
__abstract__ = True # this line is necessary
# the columns id, content and user_id are just examples, just ignore it.
id = Column(BIGINT(64), primary_key=True)
content = Column(VARCHAR(200), nullable=False)
user_id = Column(INTEGER(unsigned=True))
# build a model class with a specific table name
def get_log_model(year):
tablename = 'logs_%s' % year # dynamic table name
Model = type('Model', (Log,), {
'__tablename__': tablename
})
return Model
# Log2022 correspond to table "logs_2022"
Log2022 = get_step_model(2022)
# use the dynamically built model in the same way as regular models
print(session.query(Log2022).count()) # row count of table "logs_2022"
I also wrote an article about it on my website, it may help you too: https://easydevguide.com/posts/dynamic_table

With SQLAlchemy, how do I make a dynamic relation?

I have a SyncEntities class (shown below).
I have several other classes (such as CommodityTypes also shown below) related to the SyncEntities class.
All of my Base subclasses have this column uuidKey = Column(String, primary_key=True)
Assume se is an instance of SyncEntities.
se.entityKind is the name of a Base subclass.
How do I query for an object that is in the se.entityKind class filtering for se.uuidKey?
class SyncEntities(Base):
__tablename__ = 'SyncEntities'
uuidKey = Column(String, primary_key=True)
dateCreated = Column(DateTime, index=True)
dateModified = Column(DateTime, index=True)
dateSynced = Column(DateTime, index=True)
username = Column(String)
entityKind = Column(String)
deleted = Column(Boolean)
def __init__(self, entity, security):
self.uuidKey = newUUID()
self.dateCreated = security.now
self.dateModified = security.now
self.dateSynced = security.then
self.username = security.username
self.entityKind = entity.__tablename__
self.deleted = False
def modified(self, security):
self.dateModified = security.now
self.username = security.username
class CommodityTypes(Base):
__tablename__ = 'CommodityTypes'
uuidKey = Column(String, ForeignKey('SyncEntities.uuidKey'), primary_key=True)
myName = Column(String, unique = True)
sortKey = Column(Integer, unique = True)
mySyncEntity = relationship("SyncEntities")
def __init__(self, security, myName, sortKey):
self.syncEntity = SyncEntities(self, security)
self.uuidKey = self.syncEntity.uuidKey
self.myName = myName
self.sortKey = sortKey
The structure here is similar, though not quite the same, as a "polymorphic association", and you can read about this pattern over at this blog post: http://techspot.zzzeek.org/2007/05/29/polymorphic-associations-with-sqlalchemy/ . It's an old post but the example at http://techspot.zzzeek.org/files/2007/discriminator_on_association.py was added later as an updated example.
This case is a little different in that an object like CommodityTypes only refers to a single SyncEntities, not multiple as in the usual polymorphic association. The SyncEntities also can only refer to a single type of related object since you have entityKind on it locally.
I would note that a potential problem with this design is that you could have rows in other tables that have a uuidKey pointing to a particular SyncEntities instance, but are not of a type that matches "entityKind". If the relationship between CommodityTypes and SyncEntities is actually one-to-one, that changes everything - this pattern is really simple joined table inheritance and you'd use the patterns described at http://docs.sqlalchemy.org/en/rel_0_7/orm/inheritance.html.
You also don't have backrefs between the target and SyncEntities, which is often a way to automate these styles of lookup. But you can still approximate things using a lookup of entityKind types to classes:
def lookup_related(se):
types = {
'commodity':CommodityTypes,
'foobar':FooBarTypes
}
cls = types[se.entityKind]
session = object_session(se)
return session.query(cls).filter(cls.mySyncEntity==se).all()
here's a mixin that could do it also, using a backref:
class HasSyncEntity(object):
entity_kind = None
"subclasses need to populate this"
#declared_attr
def uuidKey(cls):
return Column(String, ForeignKey("SyncEntities.uuidKey"), primary_key=True)
#declared_attr
def mySyncEntity(cls):
return relationship("SyncEntities", backref="_%s_collection" % cls.entity_kind)
CommodityTypes becomes:
class CommodityTypes(HasSyncEntity, Base):
entity_kind = "commodity"
# ...
You then add a method like this to SyncEntities, which looks up the appropriate backref, and you're done:
def get_related(self):
return getattr(self, "_%s_collection" % self.entityKind)

SQLAlchemy column synonym with different type

I'm using the SQLAlchemy recipe here to magically JSON encode/decode a column from the DB in my model like:
class Thing(Base):
__tablename__ = 'things'
id = Column(Integer(), primary_key=True)
data = Column(JSONEncodedDict)
I hit a snag when I wanted to create an extra "raw_data" field in my model to access the same underlying JSON data, but without encoding/decoding it:
raw_data = Column("data", VARCHAR)
SQLAlchemy seems to get confused by the name collision and leave one column un-mapped. Is there any way I can convince SQLAlchemy to actually map both attributes to the same column?
I would just define the raw_data column through SQLAlchemy and then use Python's property/setter to make transparent use of data. I.e.:
class Thing(Base):
__tablename__ = 'things'
id = Column(Integer(), primary_key=True)
raw_data = Column(String())
#property
def data(self):
# add some checking here too
return json.loads(self.raw_data)
#data.setter
def data(self, value):
# dito
self.raw_data = json.dumps(value)

SQLAlchemy with multiple primary keys does not automatically set any

I had a simple table:
class test(Base):
__tablename__ = 'test'
id = Column(Integer, primary_key=True)
title = Column(String)
def __init__(self, title):
self.title = title
When using this table, id was set automatically. I want to add another field that is unique and efficient to search, so I added the field:
id2 = Column(String, primary_key=True)
And updated the constructor:
def __init__(self, id2, title):
self.id2 = id2
self.title = title
Now, id is no longer automatically set, or rather I get the error:
IntegrityError: (IntegrityError) test.id may not be NULL u'INSERT INTO test (id2, title) VALUES (?, ?)' [u'a', u'b']
Is there a way to maintain a second primary key without removing the autoincrement behavior of the first?
I have few problems here
1) What is a purpose of your hand-made __init__? If it does just what you wrote, you can omit constructor completely since SQLAlchemy machinery generates exactly the same constructor for all your models automagically. Although if you take some additional actions and thus have to override __init__ you probably want to call super-constructor:
def __init__(self, lalala, *args, **kwargs):
# do something with lalala here...
super(test, self).__init__(*args, **kwargs)
# ...or here
2) Once you have more than one field with primary_key=True you get a model with composite primary key. Composite primary keys are not generated automatically since there is ambiguity here: how should subsequent key differ from previous?
I suspect what you're trying can be achieved using unique indexed column and not using composite key:
class test(Base):
__tablename__ = 'test'
id = Column(Integer, primary_key=True)
id2 = Column(String, index=True, unique=True)
title = Column(String)
# def __init__(self) is not necessary

Categories