I have a MySQL table, defined in sqlalchemy with following structure:
class User(Base):
__tablename__ = 'user'
__table_args__ = {'mysql_charset': 'utf8', 'mysql_engine': 'InnoDB'}
id = Column(Integer, primary_key=True)
handle = Column(String(250), nullable=False)
owned = Column(Boolean(), default=False)
owner_id = Column(Integer, ForeignKey("user.id"), nullable=True, default=null )
current_price = Column(Integer, nullable=False, default=1)
balance = Column(Integer, nullable=False, default=0)
I want a relationship so that the owner_id can either be null, OR if it is set it must refer to a valid user.id, in the same table.
I don't quite understand the sqlalchemy relationship stuff well enough to be able to do it. The special stuff at the top of this page http://docs.sqlalchemy.org/en/latest/orm/relationship_persistence.html seems to suggest that it's possible, but I can't figure it out.
I want to then be able to either add Users like:
u1 = User(handle="bob")
u2 = User(handle="jim", owner=u1)
Thanks for any help!
I should add that sqlalchemy has no problem doing the CREATE TABLE with the correct FOREIGN KEY constraint, and I can manually INSERT data into the table that obeys the rules as I want them in MySQL, it's only using the sqlalchemy model that fails.
EDIT: SOLVED
The 'default=null' on owner_id was causing the problem for some reason. Helpful docs were here: http://docs.sqlalchemy.org/en/rel_1_0/orm/self_referential.html and code example from that page here: http://docs.sqlalchemy.org/en/rel_1_0/orm/examples.html#examples-adjacencylist
For the google spider bots, errors that I got during this process were:
sqlalchemy.exc.IntegrityError: (_mysql_exceptions.IntegrityError) (1452, 'Cannot add or update a child row: a foreign key constraint fails (`twitfriends`.`tree`, CONSTRAINT `tree_ibfk_1` FOREIGN KEY (`parent_id`) REFERENCES `tree` (`id`))') [SQL: u'INSERT INTO tree (parent_id, name) VALUES (%s, %s)'] [parameters: (<sqlalchemy.sql.elements.Null object at 0x7fe7e8c468d0>, 'rootnode')]
And
ArgumentError: Node.next and back-reference Node.prev are both of the same direction <symbol 'ONETOMANY>. Did you mean to set remote_side on the many-to-one side ?
Since there is only one foreign key for User, I would expect sqlalchemy to automatically figure out the join conditions. You can also add a backref so you can get the other side of the relationship.
class User(Base):
...
owner = relationship('User', remote_side=['id'], backref='owned_users')
Docs
Ex.
u1 = User(handle="bob")
u2 = User(handle="jim", owner=u1)
print u2.owned_users[0] == u1
# True
Related
I just want to delete a survey record in the Survey table, and the record in SurveyQuestions should be deleted too. I've tried cascade, passive_deletes, and ondelete. I keep getting the foreign key violation error no matter what I try from the documentation. Is it the way my tables are set up?
class Survey(Base):
__tablename__ = 'survey'
id = Column(Integer, primary_key=True, autoincrement=True)
survey_description = Column(String(100))
survey_start_date = Column(Date)
survey_end_date = Column(Date)
survey_is_active = Column(Boolean)
survey_questions = relationship(Question, secondary='survey_questions',cascade="all, delete",passive_deletes=True)
class SurveyQuestions(Base):
__tablename__ = 'survey_questions'
id = Column(Integer, primary_key=True, autoincrement=True)
survey_id = Column(Integer, ForeignKey('survey.id', ondelete='CASCADE'))
question_id = Column(Integer, ForeignKey('question.id', ondelete='CASCADE'))
Are there other models that use Survey as a foreign key?
Perhaps the foreign key violation happens because you didn't specify ondelete strategy in another model that points to Survey
So I had to drop all the tables using Base.metadata.drop_all(engine) before the foreign key relations would take effect. Since then we have adopted alembic to remedy this issue through the use of migrations.
I know this question has already been asked by I could not find a clean solution.
My problem is simple: I have a table with a many to many association. Where users have many workspaces and workspaces have many users.
invitations_table = db.Table('invitations', db.Model.metadata,
db.Column('user_id', db.Integer, db.ForeignKey('user.id', ondelete='cascade'), primary_key=True),
db.Column('workspace_id', db.Integer, db.ForeignKey('workspace.id', ondelete='cascade'), primary_key=True)
)
Now my two class are declared like that:
class Workspace(db.Model):
id = db.Column(db.Integer, primary_key = True)
...
u_invited = db.relationship("User",
secondary = invitations_table,
cascade = 'all',
back_populates = "w_invited")
class User(db.Model):
id = db.Column(db.Integer, primary_key = True)
...
w_invited = db.relationship("Workspace",
secondary = invitations_table,
back_populates = "u_invited")
Now, If I delete my workspace, I can't cascade the deletion on the associative table as well:
def delete_all_workspace(self):
workspace = Workspace.query.filter_by(owner=self)
# for w in workspace.all():
# w.u_invited.clear()
succeed = workspace.delete()
db.session.commit()
The problem is: I need, as some people suggest, to manually loop into my list of objects then call clear() in order to not get this exception:
sqlalchemy.exc.IntegrityError: (psycopg2.IntegrityError) update or delete on table "workspace" violates foreign key constraint "invitations_workspace_id_fkey" on table "invitations"
DETAIL: Key (id)=(142) is still referenced from table "invitations".
I though ondelete='cascade' will be enough to tell sqlalchemy to delete the associative table as well but that's not working as expected.
My question is: Is there a way to tell SQLAlchemy to clear the associative table invitations as well without having the burden to clear it manually?
Thanks for your answers
Do you set your foreign keys as nullable=false if always expect a foreign key on that column in the database?
I'm using sqlalchemy and have set my models with required foreign keys. This sometimes causes me to run session.commit() more often, since I need the parent model to have an id and be fully created in order to build a child object in the ORM. What is considered best practice? My models are below:
class Location(Base):
__tablename__ = 'locations'
id = Column(Integer, primary_key=True)
city = Column(String(50), nullable=False, unique=True)
hotels = relationship('Hotel', back_populates='location')
class Hotel(Base):
__tablename__ = 'hotels'
id = Column(Integer, primary_key=True)
name = Column(String(100), nullable=False, unique=True)
phone_number = Column(String(20))
parking_fee = Column(String(10))
location_id = Column(Integer, ForeignKey('locations.id'), nullable=False)
location = relationship('Location', back_populates='hotels')
You don't need to do session.commit() to get an ID; session.flush() will do.
Even better, you don't need to get an ID at all if you set the relationship because SQLalchemy will figure out the order to do the INSERTs in. You can simply do:
loc = Location(city="NYC", hotels=[Hotel(name="Hilton")])
session.add(loc)
session.commit()
and it will work fine.
I would suggest that you'd better not set nullable=False. Make foreign key nullable is very reasonable in many situations. In your scenario, for example, if I want to insert a hotel whose location is currently underdetermined, you can not accomplish this with the foreign key not null. So the best practice when using foreign key is to set it nullable.
See necessary nullable foreign key Any example of a necessary nullable foreign key?
I have this code, thats been mostly taken from the sqlalchemy site
class Order(Base):
__tablename__ = 'order'
id = Column(Integer, Sequence('tri_id_seq'), primary_key=True)
text = Column(String(1024), nullable=False)
items = relationship("Item", cascade="save-update, delete-orphan, merge, delete", backref="parent")
class Item(Base):
__tablename__ = 'item'
id = Column(Integer, Sequence('tri_id_seq'), primary_key=True)
text = Column(String(1024), nullable=False)
parent_id = Column(Integer, ForeignKey('order.id'))
I want deletes to Order to cascade down and delete its items as well. In code:
# test insert/delete - save data to mssql server
i1 = Item(text="item one")
i2 = Item(text="item two")
o = Order(text="one", items=[i1, i2])
session.add(o)
session.commit()
session.delete(o) # delete it
# tests to make sure items/order gone ....
session.commit()
This works ok.
BUT if I try and delete an Order in MS SQL management studio. i.e.
DELETE FROM [dbo].[order] WHERE id = 1
I get the error
"the DELETE statement conflicted with the REFERENCE constraint FK__item__parent_id_1D00044F" The conflict error blah blah....
I guess theres something missing on the relationship definitions but I can't see it.
Any help/thoughts?
ta.
class Comment(Base):
__tablename__ = 'comments'
id = Column(Integer, primary_key=True)
user_id = Column(Integer, ForeignKey("users.id", ondelete='CASCADE'), nullable=False)
user = relationship("User", backref=backref('comments', cascade="all,delete", order_by=id))
This kind of setup works for me, my User class doesn't have any special fields except for primary key.
So basically, this works as intended, when I delete the user - his comments are gone as well.
It doesn't have to be deletion through SQLAlchemy either, this code creates an appropriate table structure, that even if you delete user manually (sql query) - cascade deletion will still work.
I'm new to python(sqlalchemy), and I'm learning to build web site with pylons and sqlalchemy.
I have a problem when I declare the relationship between models. I've tried it several hours, but failed. But I think it should be a basic question.
I have two classes: User and Article, user can create articles, and modified the other people's article(like wiki).
So a user has created-articles and edited-articles.
class Article(Base):
__tablename__ = 'articles'
id = Column(Integer, primary_key=True)
title = ...
user_id = Column(Integer, ForeignKey('users.id'))
editor_id = Column(Integer, ForeignKey('users.id'))
# relations
user = relationship('User', backref='articles') # -> has error
class User(Base):
__tablename__ = "users"
id = Column(Integer, primary_key=True)
name = Column(String(20))
def __init__(self):
pass
But there is an error displayed:
InvalidRequestError: One or more mappers failed to compile. Exception was probably suppressed within a hasattr() call. Message was: Could not determine join condition between parent/child tables on relationship Article.user. Specify a 'primaryjoin' expression. If this is a many-to-many relationship, 'secondaryjoin' is needed as well.
I tried to add primaryjoin to the line('has error'), but don't know what it should be. I tried some codes, but none works.
Thank you in advance!
Ah, thats obvious one.
Article class has two references to User, user_id and editor_id, so SQLA does not know which one of them to use for your relation. Just use explicit primaryjoin:
user = relation('User', backref='articles', primaryjoin="Article.user_id==User.id")