This question essentially two parts.
1. I have a situation where I require things to be unique together i.e the elements in db need to be unique together with each other.
Lets say we have a model Things ( Rough PseudoCode)
Class ShoppingList( object ):
thing1_id = Column(Integer)
thing2_id = Column(Integer)
Now I need thing1_id and thing2_id to be a unique together ie the set of thing1_id and thing2_id needs to be unique together. Coming from django world I know that you can do a meta declaration in django models of unique_together. But how can do this in turbogears .
Also how do I actually apply a unique_together on a legacy system.
You simply want to add a UniqueConstraint to your table definition (using a primary key would achive similar effects, but with different semantics nevertheless).
This is as simple as:
Class ShoppingList( object ):
thing1_id = Column(Integer)
thing2_id = Column(Integer)
__table_args__ = (
UniqueConstraint('thing1_id', 'thing2_id'),
)
See also https://docs.sqlalchemy.org/en/latest/orm/extensions/declarative/table_config.html#table-configuration
For the first part of your question, if I understand your question correctly, I believe you are talking about the need for defining composite primary keys. As stated in http://docs.sqlalchemy.org/en/latest/core/schema.html#describing-databases-with-metadata:
Multiple columns may be assigned the primary_key=True flag which denotes a multi-column primary key, known as a composite primary key.
Defining such a relationship on a class using the declarative ORM way in SQLAlchemy, should be as simple as:
class ShoppingList(Base):
thing1_id = Column(Integer, primary_key=True)
thing2_id = Column(Integer, primary_key=True)
As for the second part of your question, I believe you mean how one would define the same SQLAlchemy mapping for an existing, legacy database. If so, you should be able to use the above approach, just don't create the database from the ORM definition. You may also use the classic mapping way, described in: http://docs.sqlalchemy.org/en/rel_0_8/orm/mapper_config.html?highlight=composite%20primary%20key#classical-mappings
Related
I'm trying to model the following situation: A program has many versions, and one of the versions is the current one (not necessarily the latest).
This is how I'm doing it now:
class Program(Base):
__tablename__ = 'programs'
id = Column(Integer, primary_key=True)
name = Column(String)
current_version_id = Column(Integer, ForeignKey('program_versions.id'))
current_version = relationship('ProgramVersion', foreign_keys=[current_version_id])
versions = relationship('ProgramVersion', order_by='ProgramVersion.id', back_populates='program')
class ProgramVersion(Base):
__tablename__ = 'program_versions'
id = Column(Integer, primary_key=True)
program_id = Column(Integer, ForeignKey('programs.id'))
timestamp = Column(DateTime, default=datetime.datetime.utcnow)
program = relationship('Filter', foreign_keys=[program_id], back_populates='versions')
But then I get the error: Could not determine join condition between parent/child tables on relationship Program.versions - there are multiple foreign key paths linking the tables. Specify the 'foreign_keys' argument, providing a list of those columns which should be counted as containing a foreign key reference to the parent table.
But what foreign key should I provide for the 'Program.versions' relationship? Is there a better way to model this situation?
Circular dependency like that is a perfectly valid solution to this problem.
To fix your foreign keys problem, you need to explicitly provide the foreign_keys argument.
class Program(Base):
...
current_version = relationship('ProgramVersion', foreign_keys=current_version_id, ...)
versions = relationship('ProgramVersion', foreign_keys="ProgramVersion.program_id", ...)
class ProgramVersion(Base):
...
program = relationship('Filter', foreign_keys=program_id, ...)
You'll find that when you do a create_all(), SQLAlchemy has trouble creating the tables because each table has a foreign key that depends on a column in the other. SQLAlchemy provides a way to break this circular dependency by using an ALTER statement for one of the tables:
class Program(Base):
...
current_version_id = Column(Integer, ForeignKey('program_versions.id', use_alter=True, name="fk_program_current_version_id"))
...
Finally, you'll find that when you add a complete object graph to the session, SQLAlchemy has trouble issuing INSERT statements because each row has a value that depends on the yet-unknown primary key of the other. SQLAlchemy provides a way to break this circular dependency by issuing an UPDATE for one of the columns:
class Program(Base):
...
current_version = relationship('ProgramVersion', foreign_keys=current_version_id, post_update=True, ...)
...
This design is not ideal; by having two tables refer to one another, you cannot effectively insert into either table, because the foreign key required in the other will not exist. One possible solution in outlined in the selected answer of
this question related to microsoft sqlserver, but I will summarize/elaborate on it here.
A better way to model this might be to introduce a third table, VersionHistory, and eliminate your foreign key constraints on the other two tables.
class VersionHistory(Base):
__tablename__ = 'version_history'
program_id = Column(Integer, ForeignKey('programs.id'), primary_key=True)
version_id = Column(Integer, ForeignKey('program_version.id'), primary_key=True)
current = Column(Boolean, default=False)
# I'm not too familiar with SQLAlchemy, but I suspect that relationship
# information goes here somewhere
This eliminates the circular relationship you have created in your current implementation. You could then query this table by program, and receive all existing versions for the program, etc. Because of the composite primary key in this table, you could access any specific program/version combination. The addition of the current field to this table takes the burden of tracking currency off of the other two tables, although maintaining a single current version per program could require some trigger gymnastics.
HTH!
I've got two models: User and Group.
User can be in one group so:
class User(db.Model):
# other fields
group_id = db.Column(db.Integer(), db.ForeignKey('group.id'))
but on the other hand I would also have some info about user who create that specific group:
class Group(db.Model):
# other fields
users = db.relationship("User", backref='group')
created_by = db.Column(db.Integer(), db.ForeignKey('user.id'))
Result is:
sqlalchemy.exc.CircularDependencyError: Can't sort tables for DROP; an unresolvable foreign key dependency exists between tables: group, user. Please ensure that the ForeignKey and ForeignKeyConstraint objects involved in the cycle have names so that they can be dropped using DROP CONSTRAINT.
I tried use_alter=True, but it gives me:
sqlalchemy.exc.CompileError: Can't emit DROP CONSTRAINT for constraint ForeignKeyConstraint(
Interestingly I'd expect you to get an AmbiguousForeignKeyError but instead you seem to get a CircularDependencyError? According to the docs this is caused by two scenarios:
In a Session flush operation, if two objects are mutually dependent on each other, they can not be inserted or deleted via INSERT or
DELETE statements alone; an UPDATE will be needed to post-associate or
pre-deassociate one of the foreign key constrained values. The
post_update flag described at Rows that point to themselves / Mutually
Dependent Rows can resolve this cycle.
In a MetaData.sorted_tables
operation, two ForeignKey or ForeignKeyConstraint objects mutually
refer to each other. Apply the use_alter=True flag to one or both, see
Creating/Dropping Foreign Key Constraints via ALTER.
I'm not sure what you're executing that's causing this particular error, but most likely you'll be able to solve it by solving the ambiguous reference.
The ambigious reference is due to SQLAlchemy not being able to figure out how to perform the join when there are multiple references (users and created_by in this case). This can be resolved by specifying how the relationship should join which can be done by either giving the specific foreign key it should use or by explicitly determining the join condition.
You can see these being applied to your example here:
class User(Base):
# Other setup / fields
group_id = Column(Integer, ForeignKey('group.id'))
class Group(Base):
# Other setup / fields
created_by_id = Column(Integer, ForeignKey('user.id'), nullable=False)
created_by = relationship("User", foreign_keys=[created_by_id])
users = relationship("User", backref="group", primaryjoin=id==User.group_id)
Documentation regarding relationship joins: http://docs.sqlalchemy.org/en/latest/orm/join_conditions.html#configuring-how-relationship-joins
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?
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.
I'm not sure what this is called since it is new to me, but here is what I want to do:
I have two tables in my database: TableA and TableB. TableA has pk a_id and another field called a_code. TableB has pk b_id and another field called b_code.
I have these tables mapped in my sqlalchemy code and they work fine. I want to create a third object called TableC that doesn't actually exist in my database, but that contains combinations of a_code and b_code, something like this:
class TableC:
a_code = String
b_code = String
Then I'd like to query TableC like:
TableC.query.filter(and_(
TableC.a_code == x,
TableC.b_code == y)).all()
Question 1) Does this type of thing have a name? 2) How do I do the mapping (using declarative would be nice)?
I don't really have a complete understanding of the query you are trying to express, weather it's a union or a join or some third thing, but that aside, it certainly is possible to map an arbitrary selectable (anything you can pass to a database that returns rows).
I'll start with the assumption that you want some kind of union of TableA and TableB, which would be all of the rows in A, and also all of the rows in B. This is easy enough to change to a different concept if you reveal more information about the shape of the data you are expressing.
We'll start by setting up the real tables, and classes to map them, in the declarative style.
from sqlalchemy import *
import sqlalchemy.ext.declarative
Base = sqlalchemy.ext.declarative.declarative_base()
class TableA(Base):
__tablename__ = 'a'
id = Column(Integer, primary_key=True)
a_code = Column(String)
class TableB(Base):
__tablename__ = 'b'
id = Column(Integer, primary_key=True)
b_code = Column(String)
Since we've used declarative, we don't actually have table instances to work from, which is neccesary for the next part. There are many ways to access the tables, but the way I prefer is to use sqlalchemy mapping introspection methods, since that will work no matter how the class was mapped.
from sqlalchemy.orm.attributes import manager_of_class
a_table = manager_of_class(TableA).mapper.mapped_table
b_table = manager_of_class(TableB).mapper.mapped_table
Next, we need an actual sql expression that represents the data we are interested in.
This is a union, which results in columns that look the same as the columns defined in the first class, id and a_code. We could rename it, but that's not a very important part of the example.
ab_view_sel = sqlalchemy.alias(a_table.select().union(b_table.select()))
Finally, we map a class to this. It is possible to use declarative for this, but it's actually more code to do it that way instead of classic mapping style, not less. Notice that the class inherits from object, not base
class ViewAB(object):
pass
sqlalchemy.orm.mapper(ViewAB, ab_view_sel)
And that's pretty much it. Of course there are some limitations with this; the most obvious being there's no (trivial) way to save instances of ViewAB back to the database.
There isn't really a concept of 'virtual tables', but it is possible to send a single query that 'joins' the data from multiple tables. This is probably as close as you can get to what you want.
For example, one way to do this in sqlalchemy/elixir would be (and this isn't far off from what you've shown, we're just not querying a 'virtual' table):
result = session.query(TableA, TableB).filter(TableA.a_code==x).filter(TableB.b_code==y).all()
This is similar to an SQL inner join, with some qualifying conditions in the filter statements. This isn't going to give you an sqlalchemy table object, but will give you a list of objects from each real table.
It looks like SQLAlchemy allows you to map an arbitrary query to a class. e.g. From SQLAlchemy: one classes – two tables:
usersaddresses = sql.join(t_users, t_addresses,
t_users.c.id == t_addresses.c.user_id)
class UserAddress(object):
def __repr__(self):
return "<FullUser(%s,%s,%s)" % (self.id, self.name, self.address)
mapper(UserAddress, usersaddresses, properties={
'id': [t_users.c.id, t_addresses.c.user_id],
})
f = session.query(UserAddress).filter_by(name='Hagar').one()