I'm experimenting with SQLModel (https://sqlmodel.tiangolo.com/) and I get to the point that I had to create a composite index between several fields and I can't how to do it using SQLModel library.
Db Model
The only work around I found was to use directly sqlalchemy Index, rather than index=true (from SQLModel documentation when creating indexes for unique fields - )
class Jump(SQLModel, table=True):
"""
SQL Table abstraction: Jump
Contains data belonging to a connection between a questionnaire-version and another
questionnaire-version
"""
origin_name: str = Field(primary_key=True)
origin_version: int = Field()
destination_name: str = Field()
__table_args__ = (
Index(
"compound_index_origin_name_version_destination_name", "origin_name", "origin_version", "destination_name"
),
)
This is not a "workaround". This is exactly how it is supposed to be done (as of now). The idea behind SQLModel presumably is to provide a toolkit for constructing table models that is very familiar to people coming from SQLAlchemy, while also providing most of the goodies coming from Pydantic models.
In some cases, SQLModel obviously does things differently and in some regards it tries to simplify existing interfaces. E.g. providing the foreign_key parameter on the Field constructor, so that you don't need to import and instantiate ForeignKey from SQLAlchemy.
But in this case, I really don't see the point in trying to change the existing tools. SQLAlchemy declarative ORM models allow you to set composite indices and other table parameters via the __table_args__ class-attribute. SQLModel's meta class inherits this feature. So why reinvent the wheel? 🙂
Unless you have an idea how to simplify this further. In that case, I am sure Sebastián will be more than happy about a corresponding PR or a suggestion in the issue tracker.
Related
Does the Python (Flask) SQL Alchemy uses both DAO and ORM design, or simply just ORM?
I am learning design strategies and I thought of SQLAlchemy. Is it considered a DAO (clearly ORM) as well?
By default, it does not look like DAO.
What if I defined a class for an existing model class , for example given I have the following class:
class User(db.model):
id = db.Column(db.Integer, primary_key=True)
email = db.Column(db.String(120), unique=True, nullable=False)
verified= db.Column(db.String(5), unique=False, nullable=False)
I define another class, UserDao
class UserDao:
def addNewUser(user):
pass
def retrieveAllUsers(user):
users = User.query.limit(5).all()
And I instantitate and object of this UserDao class and call the respective methods to do some database operations through the respective method, does this make it a "DAO Pattern"?
Regarding your question "And I instantitate and object of this UserDao class and call the respective methods to do some database operations through the respective method, does this make it a "DAO Pattern"?" I would say yes, it is in that direction however you need to look at your whole application overall to answer that question.
The idea of a DAO is to keep the code of your application that does the business logic separate from the code that handles how you get and store the data. So the easiest way to answer your question is to look at your whole application and ask yourself "if tomorrow I want to work with mongodb (for example) instead of mysql (whether you are using sqlachemy or not) what code of my application would I need to change?". If you need to change code from the business logic code then it means you don't have a dao or at least not a strong one. If you only need to touch the code that handles database operations then you have a DAO. You can also look it in another way: what if tomorrow you decide that you are better off using django instead of sqlachemy? what code would you need to change? would you need to touch the code that does the business logic?
Sqlalchemy implements repository pattern, not dao pattern.
You don't have to implement any dao classes in your code when you use sqlalchemy.
Using sqlalchemy orm session(or classes based on db.model) means you are using repository.
References
https://techspot.zzzeek.org/2012/02/07/patterns-implemented-by-sqlalchemy/
I'd like to create a 1:n relationship between two tables dynamically. My DB model is mapped via SQLAlchemy but due to some special features of my application I can not use the default declarative way.
E.g.
class Foo(Base):
id = Column(Integer, autoincrement=True, primary_key=True)
flag = Column(Boolean)
class Bar(Base):
id = Column(Integer, autoincrement=True, primary_key=True)
foo_id = Column(Integer, ForeignKey('foo.id'))
# declarative version:
# foo = relationship(Foo)
So I want to add relationship named "foo" to the mapped class "Bar" after Bar was defined and SQLAlchemy did its job of defining a mapper etc.
Update 2017-09-05: Why is this necessary for me? (I thought I could omit this because I think it mostly distracts from the actual problem to solve but since there were comments abouts it...)
First of all I don't have a single database but hundreds/thousands. Data in old databases must not be altered in any way but I want a single source code to access old data (even though data structure and calculation rules change significantly).
Currently we use multiple model definitions. Later definitions extend/modify previous ones. Often we manipulate SQLAlchemy models dynamically. We try not to have code in mapped classes because we think it will be much harder ensuring correctness of that code after changing a table many times (code must work in every intermediate step).
In many cases we extend tables (mapped classes) programatically in model X after it was initially defined in model X-1. Adding columns to an existing SQLAlchemy ORM class is manageable. Now we are adding a new reference column an existing table and a relationship() provides a nicer Python API.
Well, my question above is again a nice example of SQLAlchemy's super powers (and my limited understanding):
Bar.__mapper__.add_property('foo', relationship('Foo'))
Likely I was unable to get this working initially because some of my surrounding code mixed adding relationships and columns. Also there is one important difference to declaring columns:
Column('foo', Integer)
For columns the first parameter can be the column name but you can not use this for relationships. relationship('foo', 'Foo') triggers exceptions when passing it to .add_property().
I am working on implementing sets of sqlalchemy declarative models for multiple database schemas. These database schemas are highly similar in table design. I would like to be able to reuse as much of the common model code (column definitions, relations etc.) as possible across the sets of models for each schema.
(Please note, the schemas in question are 99% identical.)
As a small example:
from sqlalchemy.ext.declarative import declarative_base
from sqlalchemy import Integer, String
Base = declarative_base()
class Person(Base):
__tablename__ = 'person'
name = Column(String, primary_key=True)
age = Column(Integer)
If I now want to introduce a new attribute Person column 'height' that only exists in one schema and not the other, how can I go about defining it whilst reusing the existing Person column definitions?
So far, I have tried using declarative mixins to define the common functionality and then mix these into classes that inherit from different schema-specific bases. However, this is unsatisfactory because even the majority of identical models have to be declared as mixins, and a declarative class declared once for schema (inheriting from the appropriate base and the mixin). Given that the schemas are similar this seems like a lot of boilerplate code.
Is there a better way to do it?
There is something that is tripping me up with models, and I guess SQL tables in general.
Let us suppose you have these models:
class Manufacturer(models.Model):
name = models.CharField()
company_created = models.CharField()
class Car(models.Model):
manufacturer = models.ForeignKey(Manufacturer)
When you create an instance of Car like say, the following
civic = Car(manufacturer='honda')
A couple questions:
When you create an instance of Car, are you also creating an instance of Manufacturer as a by-product? Or does 'honda' need to exist as an instance already. If not, is there a way to make an instance of both, in say, one form.
Can I make calls on 'civic' for things pertaining to the manufacture? For example, could I get the 'company_created' data from the civic instance? If not, why bother having the relationship in the first place?
Thanks so much in advance. I would really appreciate a thorough answer so I can understand models and relationships fully. And yes, I have read the docs.
Firstly, the thing to remember is that these classes are representations of underlying database tables. A ForeignKey field in a Django model represents a one-to-many relationship in the database, with an _id field representing the ID of another table. Those tables are themselves independent, but are linked via the FK field (and the relevant index constraint, if the database supports them).
That said, in your Car model manufacturer is a required field (because you haven't defined it as null=True). So when you create a Car, you must point it at an already existing Manufacturer - and that manufacturer must have been saved already, so that Django can populate the underlying manufacturer_id field with the ID of the related object
Because Django is aware of the foreign key relationship between the two objects, you can use them when querying. In SQL this would be done via JOINs: Django gives you a special syntax to do queries across those joins, via double underscores. So, for example, if you wanted to get all the cars made by a manufacturer created in 1990 (assuming that's what you mean by the company_created field), you would do this:
Car.objects.filter(manufacturer__company_created='1990')
Django translates this into something like":
SELECT * from car JOIN manufacturer ON car.manufacturer_id=manufacturer.id WHERE manufacturer.company_created='1990';
If you already have your "civic" instance and just want to get access to the related data, this is pure Python object access: civic.manufacturer is the related Manufacturer object, so you can simply do civic.manufacturer.company_created to get the relevant data. Again, Django translates that into the database access, but from your point of view this is simple object composition.
Note that really all this is fully explained in the tutorial, with relationships between Poll and Choice which exactly match your Manufacturer and Car models.
Yes manufacturer need to exist as an instance already.
you can create car instance like this:
manuf= Manufacturer(name='honda',company_created='Benz')
manuf.save()
civic = Car(manufacturer=manuf)
you can get the company_created data from the civic instance by:
civic.manufacturer.company_created
For example, there are such models:
class User(Base):
photo_id = Column(ForeignKey('photo.id'))
class Group(Base):
photo_id = Column(ForeignKey('photo.id'))
class Photo(Base):
__tablename__ = 'photo'
user = relationship('User', backref='photo')
group = relationship('Group', backref='photo')
But in last model relationship to User and Group is not good because in one case first relationship will be None and in other case second relationship will be None (because photo owner can be only user or group, but not both)... And if there will be more than 2 models with foreignkeys to model Photo - situation will be even worse.
How to do such relationship correct?
Thanks in advance!
If your User and Group are not stored in the same table, there is nothing wrong to defined them with two relationship. These two relationship means two different SQL query,
and you actually needs these two different query in your case.
If your User and group can be stored in the same table, you can use inheritance.
and create a relationshop to the parent table
http://docs.sqlalchemy.org/en/latest/orm/inheritance.html
or create a view for that
http://docs.sqlalchemy.org/en/rel_0_7/core/schema.html#reflecting-views
Use table inheritance: http://docs.sqlalchemy.org/en/rel_0_7/orm/extensions/declarative.html#joined-table-inheritance
I recommend this slide to you: http://www.slideshare.net/tyler4long/quickorm . It is about quick_orm, which is base on SQLAlchemy. You will see how the same problem is resolved by means of table inheritance.
Slide 7: many models should have relationship with "comments"
Slide 8: add a parent class named "Commentable" to solve the problem.
The syntax is different from SQLAlchemy, but you can get the main idea.
I do not think there is one correct way of modeling this kind of relationships. Cardinality, navigability are also facts to consider.
To a solution very similar to your modeling problem, see Generic Associations examples. The examples might look somewhat complicated at first, but if you read Mike's blog on Polymorphic Associations with SQLAlchemy it should be pretty clear what is happening there. You will end up with somewhat different model, and navigating back from Photo to the correct parent by single attribute (parent or owner) might not be achievable, but do you really need to navigate the relationship from the side of Photo?