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?
Related
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()))
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'))
I have db that I cannot modify, it has two tables 'people' and 'relation'. The table 'people' has names, ids and the column parent (yes/no). The table 'relation' contains a foreign key 'people.id' for parent and a 'people.id' for its child. I want to join columns in the people table so I can
People.query.filter_by(id='id of the parent')
to get the name of the parent and it's childs. This is my code:
class People(db.model):
__tablename__ = 'people'
id = db.Column(db.integer(), primary_key=True
name = db.Column(db.String())
parent = db.Column(db.Integer()) ##0 for no 1 for yes
parent_id=db.relationship('Link',backref=db.backref('Link.parent_id')
class Link(db.Model):
_tablename__ = 'link'
parent_id=db.Column(db.Integer(),db.ForeignKey('people.id'),primary_key=True)
id = db.Column(db.Integer(), db.ForeignKey('people.id'), primary_key=True)
dateofbirth = db.Column(db.Integer())
SQLAlchemy tells me:
ArgumentError: relationship 'parent_id' expects a class or a mapper argument (received: <class 'sqlalchemy.sql.schema.Table'>)
Excuse me if I messed up, but it's my first question here (and also the first steps with SQLAlchemy)
Typically you would want to set up the foreign key and backref in the same table, like this:
class Link(db.Model):
_tablename__ = 'link'
parent_id = db.Column(db.Integer(),db.ForeignKey('people.id'),primary_key=True)
parent = db.relationship('People', backref='links')
Now you can access each Link entries parent via Link.parent, and you can get a list of each People entries links via People.links (assuming this is a one-to-many relationship).
Also, if People.parent is supposed to represent a boolean value then:
1.) you should follow the standard naming convention and call it something like is_parent
2.) you should declare People.parent as a db.Boolean type, not a db.Integer. In most (probably all) database implementations, using booleans instead of integers (when appropriate) is more memory efficient.
I hope this helped.
For example, using Flask-SQLAlchemy and jsontools to serialize to JSON like shown -here-, and given a model like this:
class Engine(db.Model):
__tablename__ = "engines"
id = db.Column(db.Integer, primary_key=True)
this = db.Column(db.String(10))
that = db.Column(db.String(10))
parts = db.relationship("Part")
schema = ["id"
, "this"
, "that"
, "parts"
]
def __json__(self):
return self.schema
class Part(db.Model):
__tablename__ = "parts"
id = db.Column(db.Integer, primary_key=True)
engine_id = db.Column(db.Integer, db.ForeignKey("engines.id"))
code = db.Column(db.String(10))
def __json__(self):
return ["id", "code"]
How do I change the schema attribute before query so that it takes effect on the return data?
enginelist = db.session.query(Engine).all()
return enginelist
So far, I have succeeded with subclassing and single-table inheritance like so:
class Engine_smallschema(Engine):
__mapper_args__ = {'polymorphic_identity': 'smallschema'}
schema = ["id"
, "this"
, "that"
]
and
enginelist = db.session.query(Engine_smallschema).all()
return enginelist
...but it seems there should be a better way without needing to subclass (I'm not sure if this is wise). I've tried various things such as setting an attribute or calling a method to set an internal variable. Problem is, when trying such things, the query doesn't like the instance object given it and I don't know SQLAlchemy well enough yet to know if queries can be executed on pre-made instances of these classes.
I can also loop through the returned objects, setting a new schema, and get the wanted JSON, but this isn't a solution for me because it launches new queries (I usually request the small dataset first).
Any other ideas?
The JSON serialization takes place in flask, not in SQLAlchemy. Thus, the __json__ function is not consulted until after you return from your view function. This has therefore nothing to do with SQLAlchemy, and instead it has to do with the custom encoding function, which presumably you can change.
I would actually suggest not attempting to do it this way if you have different sets of attributes you want to serialize for a model. Setting a magic attribute on an instance that affects how it's serialized violates the principle of least surprise. Instead, you can, for example, make a Serializer class that you can initialize with the list of fields you want to be serialized, then pass your Engine to it to produce a dict that can be readily converted to JSON.
If you insist on doing it your way, you can probably just do this:
for e in enginelist:
e.__json__ = lambda: ["id", "this", "that"]
Of course, you can change __json__ to be a property instead if you want to avoid the lambda.
A question on the syntax involved in SQLAlchemy.
class Parent(Base):
__tablename__ = 'parent'
id = Column(Integer, primary_key=True)
child_id = Column(Integer, ForeignKey('child.id'))
child = relationship("Child")
class Child(Base):
__tablename__ = 'child'
id = Column(Integer, primary_key=True)
Why is it ForeignKey('child.id') and not ForeignKey("Child.id")?
Why is it relationship("Child") and not relationship("child")? Is there something fundamental about how databases and SQLAlchemy work that I don't understand which is why I have to ask this question? Thanks!
relationship(Child) is also valid. By capitalising inside string, sqlalchemy will look for respective model.
Relationship isn't sql standard so SQLAlchemy is using its own convention, whereas ForeignKey is SQL Standard so tablename.column is used.
In general: A relationship is defined on orm level while ForeignKey represents a database model. Now, it well might be the case that sqlalchemy is smart enough to figure from from the other, but if you keep this separation in mind, you are safe.
Specifically to your question: just read the documentation. Extract below (verbatim)
From relationship:
argument – a mapped class, or actual Mapper instance, representing the
target of the relationship.
argument may also be passed as a callable function which is evaluated
at mapper initialization time, and may be passed as a Python-evaluable
string when using Declarative.
From ForeignKey
column – A single target column for the key relationship. A Column
object or a column name as a string: tablename.columnkey or
schema.tablename.columnkey. columnkey is the key which has been
assigned to the column (defaults to the column name itself), unless
link_to_name is True in which case the rendered name of the column is
used.