One-to-one delete for SQLAlchemy classes with inheritance - python

I have a class inheritance scheme as layed out in http://docs.sqlalchemy.org/en/latest/orm/inheritance.html#joined-table-inheritance
from sqlalchemy import Column, Integer, String, ForeignKey
from sqlalchemy.ext.declarative import declarative_base
Base = declarative_base()
class Parent(Base):
__tablename__ = 'parent'
id = Column(Integer, primary_key=True)
type = Column(String)
__mapper_args__ = {'polymorphic_on': type}
class Child(Parent):
__tablename__ = 'child'
id = Column(Integer, ForeignKey('parent.id'), primary_key=True)
__mapper_args__ = {'polymorphic_identity': 'child'}
Having created an instance of Child, I'd like to be able to delete either the Child or the Parent reference without the other hanging around, but that's not what happens. When I fire up IPython...
In [1]: from stackoverflow.question import Parent, Child
In [2]: from sqlalchemy import create_engine
In [3]: from sqlalchemy.orm import sessionmaker
In [4]: session = sessionmaker(bind=create_engine(...), autocommit=True)()
In [5]: with session.begin():
...: session.add(Child())
...:
In [6]: session.query(Parent).all()
Out[6]: [<stackoverflow.question.Child at 0x7fe408030240>]
In [7]: session.query(Parent).delete()
---------------------------------------------------------------------------
IntegrityError Traceback (most recent call last)
/.../lib/python3.4/site-packages/sqlalchemy/engine/base.py in _execute_context(self, dialect, constructor, statement, parameters, *args)
1062 parameters,
-> 1063 context)
1064 except Exception as e:
...
--> Big traceback here...
...
IntegrityError: (IntegrityError) update or delete on table "parent" violates foreign key constraint "child_id_fkey" on table "child"
DETAIL: Key (id)=(1) is still referenced from table "child".
'DELETE FROM parent' {}
In [8]: session.query(Child).delete()
Out[8]: 1
In [9]: session.query(Parent).all()
Out[9]: [<stackoverflow.question.Child at 0x7fe408030240>]
Deleting the Parent instance doesn't work, and deleting the Child instance leaves the Parent instance alive and well...
After experimenting a bit, setting the keyword argument ondelete='CASCADE' in the ForeignKey in Child allows me to delete the Parent (which cascades to the Child) but the reverse doesn't happen. Is there any way to configure these two classes so that deleting the Child also deletes the Parent?

I think you are confusing few things.
Lets leave the inheritance aside for a moment.
1. session.query(MyTable).delete():
When you call session.query(MyTable).delete(), sqlalchemy will basically generate following SQL DELETE FROM my_table.
This means: delete all entries from table my_table
2. session.delete(my_instance); ... session.commit()
When you perform the following:
my_instance = session.query(MyTable).filter(MyTable.name == some_value).first()
session.delete(my_instance) # !!!
# ...
session.commit()
sqlalchemy will generate following SQL: DELETE FROM my_table WHERE id = ?.
This means: delete a row from table my_table for given id (primary key)`.
Good thing is that in this case sqlalchemy is smart enough to delete rows from multiple tables in case the joined inheritance is used.
(Attempted) Answer
Now, the code you show is basically the first case. I do not think that you care about any references if you really want to delete all rows from some tables. But I really think that you want to simple remove just one instance (which might be few data rows from different tables). In this case, you should simply do the following:
# add a child
child = Child()
session.add(child)
session.commit()
# find the Child you want to delete by its primary key
child = session.query(Child).get(child_id)
# OR: find it by other filters
child = session.query(Child).filter(...).first()
# mark it for deletion
session.delete(child)
# SA will delete all properly
session.commit()

Related

SQLAlchemy creating a backref on a single table inheritance subclass?

I'm trying to create a backref in a Single table inheritance situation, where one of the subclassed objects have a mixin that declares the relationship (HasFooMixin), but this results in the following issue:
sqlalchemy.exc.ArgumentError: Error creating backref 'bar' on relationship 'ImplementedBase.foos': property of that name exists on mapper 'mapped class Foo->foos'
I thought maybe the name 'bar' was used soemwhere else, but regardless of what I name it, the same error will be generated. The error seems to be due to the fact that there's already a backref with that name, but I cant find any in all of my codebase, and regardless of what I name it, it throws the same error.
Any idea how to solve this? Basically I want a two-way reference from bar --> foos and from foo --> bars, (but only for the polymorphic class ImplementedBase if possible)
Below are the model details.
def BaseClass(db.Model):
type = db.Column(db.Text, default="base")
...
__mapper_args__ = {
'polymorphic_identity' : 'base',
'polymorphic_on' : type
}
def ImplementedBase(BaseClass, HasFooMixin):
__mapper_args__ = {
'polymorphic_identity': 'implementedbase'
}
def HasFooMixin(object):
#declared_attr
def foos(cls)
return cls.__table__.c.get('foos', db.relationship('Foo', secondary=mtm_foo_bar, backref="bar"))
#Table for MTM mapping Foo <---> Bar
mtm_foo_bar = db.Table('associate_foo_bar',
db.Model.metadata,
db.Column("foo_id", db.Integer, db.ForeignKey("foo.id")),
db.Column("bar_id", db.Integer, db.ForeignKey("bar.id"))
)

How to update a declarative SQLAlchemy model using a dictionary data while running validators

The question really is how to update a SQLAlchemy declarative model so that it runs the validators. In my case using setters like User.name = name is not really an option.
Below is a runnable example of what I mean
from sqlalchemy import create_engine
from sqlalchemy.orm import sessionmaker
from sqlalchemy import Column, String, Integer
from sqlalchemy.orm import validates
from sqlalchemy.ext.declarative import declarative_base
some_engine = create_engine('sqlite://')
Session = sessionmaker(bind=some_engine)
session = Session()
Base = declarative_base()
class User(Base):
__tablename__ = 'user'
id = Column(Integer, primary_key=True)
name = Column(String, nullable=False)
#validates('name')
def validate_name(self, key, value):
if value != 'asd':
raise ValueError('not asd')
return value
Base.metadata.create_all(bind=some_engine)
user = User(id=1, name='qwe')
# >>> ValueError: not asd
user = User(id=1, name='asd')
session.add(user)
session.commit()
session.query(User).filter(User.id=1).update({'name': 'qwe'})
session.query(User).filter(User.id==1)[0].name
# >>> 'qwe'
You could add a mixin to your models that provides a rather simple update method that just uses setattr() to set attributes of an instance.
class UpdateMixin:
"""
Add a simple update() method to instances that accepts
a dictionary of updates.
"""
def update(self, values):
for k, v in values.items():
setattr(self, k, v)
User class would then be defined as
class User(UpdateMixin, Base):
...
And to update a single instance from a given dictionary you could for example run
session.query(User).get(1).update({ 'name': 'qwe' })
# or since you have the user instance from before
user.update({ 'name': 'qwe' })
Note the use of Query.get(). If there is no user with the given id, it will return None and trying to call the method update on it will raise. Another caveat is that if you do not rollback if any exceptions are raised, you cannot predict what, if any, updates took place (were added to the session) because a dictionary has no ordering. So always rollback on any errors.
I'd also recommend actually naming the method updateSelf or some such to prevent risk of confusing it with Query.update().
The short answer is not to use query.update when you want model level constraints. It's exactly for the times when performance is more important than enforcing those sorts of model level constraints. Other answers have provided specifics on solutions, but the fundamental answer is that Query.update is not intended to enforce python-level constraints.
General categories of solutions are:
Use some session-level method and Query.get or a loop on Query.filter.all
Check constraints
Triggers and stored procedures

SQLAlchemy classical mapping does not map selected row to instance of model class

I’m stumped. I really must be missing something basic about how the SQLAlchemy ORM works.
For the same reason I want my Java models to be POJOs (Plain Old Java Objects), I want my Python objects to be independent of the ORM. (Can I call them POPO’s?).
That’s why I’ve chosen to use the SQLAlchemy ORM’s “classical” mapping. The documentation repeatedly states that the declarative and classical mapping methods are equivalent and that, in fact, the former uses the latter under the hood.
The problem is that it ain’t workin’ for me. Specifically, when I do a select to read an object, it’s not being mapped to an instance of my object class. I’m getting back a what my debugger labels a “result” object --a raw row object.
As evidence, I submit two minimal applications. The first one uses declarative mapping and works just fine. My model goes in as a class instance and comes out as one. The second uses classical mapping and my model goes in as a class instances and comes out as a row result.
What am I missing here?
I’m using Python 2.7.10 and SQLAlchemy 1.0.11.
Declarative mapping, which does work:
from sqlalchemy import create_engine, Column, Integer, orm
from sqlalchemy.ext.declarative import declarative_base
Base = declarative_base()
class Thing(Base):
__tablename__ = 'thing'
id = Column(Integer, primary_key=True)
engine = create_engine("sqlite://")
Base.metadata.create_all(engine)
Session = orm.sessionmaker(bind=engine)
session = Session()
thing = Thing()
print "In: " + str(thing)
session.add(thing)
session.commit()
thing = session.query(Thing).one()
print "Out: " + str(thing)
Output:
In: <__main__.Thing object at 0x10c94c490>
Out: <__main__.Thing object at 0x10c94c490>
Classical mapping version, which does not work:
from sqlalchemy import create_engine, MetaData, Table, Column, Integer, orm
class Thing(object):
pass
engine = create_engine("sqlite://")
metadata = MetaData()
thing_table = Table(
'thing',
metadata,
Column('id', Integer, primary_key=True)
)
orm.mapper(Thing, thing_table)
metadata.create_all(engine)
Session = orm.sessionmaker(bind=engine)
session = Session()
thing = Thing()
print "In: " + str(thing)
session.add(thing)
session.commit()
thing = session.query(thing_table).one()
print "Out: " + str(thing)
Output:
In: <__main__.Thing object at 0x10cda2b10>
Out: (1,)

How do I set attribute default values in sqlalchemy declarative?

In SQLAlchemy Declarative, how do I set up default values for columns, such that transient or pending object instances will have those default values? A short example:
from sqlalchemy import Column, Integer, String
from sqlalchemy.ext.declarative import declarative_base
Base = declarative_base()
class A(Base):
__tablename__ = "A"
id = Column(Integer, primary_key=True)
word = Column(String, default="adefault")
a = A()
print a.word
Naively, I would expect the output from this to be adefault. Of course, the output is actually None. Even when adding to a session, it staysNone and only gets filled when I commit (or flush) the session, and re-read the instance value from the database.
Is there any way to set an attribute default without flushing the instance to the database? I tried investigating the ColumnDefault documentation, and there doesn't seem to be an obvious way to inspect the type/python value, so as to manually set it in a custom declarative baseclass.
Add a constructor to your class and set the default value there. The constructor doesn't run when the rows are loaded from the database so it is fine to do this.
class A(Base):
__tablename__ = "A"
id = Column(Integer, primary_key=True)
word = Column(String)
def __init__(self):
self.word = "adefault"
a = A()
print a.word
There are examples of using __init__ in similar ways in the SA Docs.

Dynamic Table Creation and ORM mapping in SqlAlchemy

I'm fairly new to using relational databases, so I prefer using a good ORM to simplify things. I spent time evaluating different Python ORMs and I think SQLAlchemy is what I need. However, I've come to a mental dead end.
I need to create a new table to go along with each instance of a player I create in my app's player table. I think I know how to create the table by changing the name of the table through the metadata then calling the create function, but I have no clue on how to map it to a new dynamic class.
Can someone give me some tips to help me get past my brain freeze? Is this even possible?
Note: I'm open to other ORMs in Python if what I'm asking is easier to implement.Just show me how :-)
We are spoiled by SQLAlchemy.
What follows below is taken directly from the tutorial,
and is really easy to setup and get working.
And because it is done so often,
the documentation moved to full declarative in Aug 2011.
Setup your environment (I'm using the SQLite in-memory db to test):
>>> from sqlalchemy import create_engine
>>> engine = create_engine('sqlite:///:memory:', echo=True)
>>> from sqlalchemy import Table, Column, Integer, String, MetaData
>>> metadata = MetaData()
Define your table:
>>> players_table = Table('players', metadata,
... Column('id', Integer, primary_key=True),
... Column('name', String),
... Column('score', Integer)
... )
>>> metadata.create_all(engine) # create the table
If you have logging turned on, you'll see the SQL that SQLAlchemy creates for you.
Define your class:
>>> class Player(object):
... def __init__(self, name, score):
... self.name = name
... self.score = score
...
... def __repr__(self):
... return "<Player('%s','%s')>" % (self.name, self.score)
Map the class to your table:
>>> from sqlalchemy.orm import mapper
>>> mapper(Player, players_table)
<Mapper at 0x...; Player>
Create a player:
>>> a_player = Player('monty', 0)
>>> a_player.name
'monty'
>>> a_player.score
0
That's it, you now have a your player table.
It's a very old question. Anyway if you prefer ORM, it's quite easy to generate table class with type:
from sqlalchemy.ext.declarative import declarative_base
from sqlalchemy import Column, Integer, String
Base = declarative_base()
Test = type('Test', (Base,), {
'__tablename__': 'test',
'test_id': Column(Integer, primary_key=True, autoincrement=True),
'fldA': Column(String),
... other columns
}
)
Base.metadata.create_all(engine)
# passed session create with sqlalchemy
session.query(Test).all()
Making a class factory, it's easy to assign names to a class and database table.
If you are looking to create dynamic classes and tables you can use the following technique based from this tutorial URL I found here (http://sparrigan.github.io/sql/sqla/2016/01/03/dynamic-tables.html), I modified how he did it a bit.
from sqlalchemy import create_engine
engine = create_engine('sqlite:///test.db', echo=True)
from sqlalchemy import Column, Integer,Float,DateTime, String, MetaData
metadata = MetaData()
from sqlalchemy.ext.declarative import declarative_base
from sqlalchemy.orm import sessionmaker
Session = sessionmaker(bind=engine)
session = Session() # create a Session
Base = declarative_base()
First include all the needed dependencies and create your session and Base.
The key to creating it dynamically is this here:
attr_dict = {'__tablename__': 'default','id': Column(Integer, primary_key=True, auto_increment=True)}
you could create a table from just this by taking advantage of the 'type' function in python.
myClass = type('ClassnameHere', (Base,), attr_dict)
Note that we are passing in attr_dict, this will give the required tablename and column information to our class, but the difference is we are defining the class name through a string! This means you could create a loop for example going through an array of strings to start creating tables dynamically!
Next all you have to do is simply call
Base.metadata.create_all(engine)
Because the dynamic class we created inherits from Base the command will simply create the tables!
You add to this table for example like this now:
SomeRow = myClass(id='2')
session.add(SomeRow)
session.commit()
This can go even further if you you don't know the column names as well. Just refer to the article to learn how to do that.
You would essentially do something like this though:
firstColName = "Ill_decide_later"
secondColName = "Seriously_quit_bugging_me"
new_row_vals = myClass(**{firstColName: 14, secondColName: 33})
The ** operator takes the object and unpacks it so that firstColName and secondColName are added with assignment operators so it would essentially be the same thing as this:
new_row_vals = myClass(firstColName=14, secondColName=33)
The advantage of this technique is now you can dynamically add to the table without even having to define the column names!
These column names could be stored in a string array for example or whatever you wanted and you just take it from there.
Maybe look at SQLSoup, which is layer over SQLAlchemy.
You can also create the tables using plain SQL, and to dynamically map, use these libraries if they already don't have create table function.
Or alternatively create a dynamic class and map it:
tableClass = type(str(table.fullname), (BaseTable.BaseTable,), {})
mapper(tableClass, table)
where BaseTable can be any Python class which you want all your table classes to inherit from, e.g. such Base class may have some utility or common methods, e.g. basic CRUD methods:
class BaseTable(object): pass
Otherwise you need not pass any bases to type(...).
you can use declarative method for dynamically creating tables in database
from sqlalchemy.ext.declarative import declarative_base
from sqlalchemy import Table, Column, Integer, String, MetaData, ForeignKey
Base = declarative_base()
class Language(Base):
__tablename__ = 'languages'
id = Column(Integer, primary_key=True)
name = Column(String(20))
extension = Column(String(20))
def __init__(self, name, extension):
self.name = name
self.extension = extension
I faced the same problem when I was trying to automate simple CRUD tasks using SQLAlchemy.
Here is simple explanation and some code: http://www.devx.com/dbzone/Article/42015
maybe i didn't quite understand what you want, but this recipe create identical column in different __tablename__
class TBase(object):
"""Base class is a 'mixin'.
Guidelines for declarative mixins is at:
http://www.sqlalchemy.org/docs/orm/extensions/declarative.html#mixin-classes
"""
id = Column(Integer, primary_key=True)
data = Column(String(50))
def __repr__(self):
return "%s(data=%r)" % (
self.__class__.__name__, self.data
)
class T1Foo(TBase, Base):
__tablename__ = 't1'
class T2Foo(TBase, Base):
__tablename__ = 't2'
engine = create_engine('sqlite:///foo.db', echo=True)
Base.metadata.create_all(engine)
sess = sessionmaker(engine)()
sess.add_all([T1Foo(data='t1'), T1Foo(data='t2'), T2Foo(data='t3'),
T1Foo(data='t4')])
print sess.query(T1Foo).all()
print sess.query(T2Foo).all()
sess.commit()
info in example sqlalchemy

Categories

Resources