NotNullViolation when appending to relationship using an Association Object in SQLAlchemy? - sqlalchemy

Background
The documentation gives the following example of a parent-child-association being added by appending that association to p.children. The child is then accessed via p.children and I assume the other way is possible as well, i.e accessing the parent via c.parents.
class Association(Base):
__tablename__ = 'association'
left_id = Column(ForeignKey('left.id'), primary_key=True)
right_id = Column(ForeignKey('right.id'), primary_key=True)
extra_data = Column(String(50))
child = relationship("Child", back_populates="parents")
parent = relationship("Parent", back_populates="children")
class Parent(Base):
__tablename__ = 'left'
id = Column(Integer, primary_key=True)
children = relationship("Association", back_populates="parent")
class Child(Base):
__tablename__ = 'right'
id = Column(Integer, primary_key=True)
parents = relationship("Association", back_populates="child")
# create parent, append a child via association
p = Parent()
a = Association(extra_data="some data")
a.child = Child()
p.children.append(a)
# iterate through child objects via association, including association
# attributes
for assoc in p.children:
print(assoc.extra_data)
print(assoc.child)
Problem
I have three classes that are almost identical to those in the example:
class TopicSubcription(Base):
__tablename__ = 'topic_subscription'
topic_id = Column(ForeignKey('topic.id'), primary_key=True)
user_id = Column(ForeignKey('user.id'), primary_key=True)
subscription_date = Column('subscription_date', DateTime, nullable=True)
topic = relationship("Topic", back_populates="subscribed_users")
user = relationship("User", back_populates="followed_topics")
class Topic(Base):
__tablename__ = 'left'
id = Column(Integer, primary_key=True)
subscribed_users = relationship("Association", back_populates="topic")
class User(Base):
__tablename__ = 'right'
id = Column(Integer, primary_key=True)
followed_topics = relationship("Association", back_populates="user")
but when I run the following I get a NotNullViolation error stating that user_id violates not-null constaint.
u: User = session.query(User).filter(User.id == 'example_id1').one()
t: Topic = session.query(Topic).filter(Topic.id == 'example_id2').one()
sub = TopicSubscription(func.now())
sub.topic = t
u.followed_topics.append(sub)
session.add(u)
session.commit()
Question
Am I misinterpreting the documentation or is it wrong?

I had something quite similar just now and was going crazy trying to work it out. Abstracting to the parent/child example from the docs, what worked in my case was:
p = Parent()
a = Association(parent=p, extra_data="some data")
a.child = Child()
p.children.append(a)

Related

SQLAlchemy conditional many to many relationship with an additional column

This is the way that I usually use for m2m relationship implementation.
(Brought from docs.sqlalchemy.org)
association_table = Table('association', Base.metadata,
Column('left_id', Integer, ForeignKey('left.id')),
Column('right_id', Integer, ForeignKey('right.id'))
)
class Parent(Base):
__tablename__ = 'left'
id = Column(Integer, primary_key=True)
children = relationship("Child",
secondary=association_table,
backref="parents")
class Child(Base):
__tablename__ = 'right'
id = Column(Integer, primary_key=True)
Is there any way for using additional columns at the association_table table?
So it should be like
association_table = Table('association', Base.metadata,
Column('left_id', Integer, ForeignKey('left.id')),
Column('right_id', Integer, ForeignKey('right.id')),
Column('is_valid', Boolean, default=True) # Add the validation column
)
class Parent(Base):
__tablename__ = 'left'
id = Column(Integer, primary_key=True)
children = relationship("Child",
secondary=association_table,
backref="parents")
# How can I do implement this??
valid_children = relationship("Child",
secondary="and_(association_table.left_id == Parent.id, association_table.right_id == Child.id)"
class Child(Base):
__tablename__ = 'right'
id = Column(Integer, primary_key=True)
I want to do query depends on is_valid column. How can I modify "secondary" attr in Parent table? Or should I fix the other part?
In this question, time_create column has the same value for all children. But in this case, I need a flag that makes able to retrieve whether this connection is still alive or not.
For example, if you implement a one-on-one chatting, there will be a chatting room consist of two-person, right?
And the table should be like as below:
association_table = Table('association', Base.metadata,
Column('left_id', Integer, ForeignKey('left.id')),
Column('right_id', Integer, ForeignKey('right.id')),
Column('is_left', Boolean, default=False) # Whether the user left or not
)
class Match(Base):
__tablename__ = 'left'
id = Column(Integer, primary_key=True)
user = relationship("User",
secondary=association_table,
backref="matches")
# How can I do implement this??
exist_user = relationship("User",
secondary="and_(association_table.left_id == Parent.id, association_table.right_id == Child.id)"
class User(Base):
__tablename__ = 'right'
id = Column(Integer, primary_key=True)
nickname = Column(String, unique=True)
How can I do for this?

Alembic Migration: How to remove polymorphic Identity

I have the following setup
class Content(Base):
"""Content object"""
__tablename__ = "content"
id = Column(Integer, primary_key=True)
name = Column(Unicode(255),unique=True, nullable=False)
title = Column(Unicode(255))
body = Column(UnicodeText)
created = Column(DateTime, default=func.now())
modified = Column(DateTime, onupdate=func.now())
type = Column(String(20))
__mapper_args__ = {
'polymorphic_on':type,
'polymorphic_identity':'content',
'with_polymorphic':'*'
}
class Locality(Content):
__tablename__ = "local"
id = Column(Integer, ForeignKey('content.id'),primary_key=True)
city_name = Column(Unicode(80))
__mapper_args__ = {'polymorphic_identity':'city'}
Now I dropped the Locality table using alembic.
Each time I query Content, I get
AssertionError: No such polymorphic_identity 'city' is defined
How do I drop this polymorphic_identity
I got over this by applying MySQL 'delete from' command on the content through MySQL Console, finding those contents whose type is 'city'
delete from content where content.type='city';

Accessing extra column in many-to-many relationship with simplified association objects

I'm trying to work with the example in the SQLAlchemy docs: Simplifying Association Objects
What I am struggling with understanding is how I can access the special_key. Ultimately I'd like to be able to do something like this:
for user in users
for keyword in user.keywords
keyword.special_key
Here is the code from the example:
class User(Base):
__tablename__ = 'user'
id = Column(Integer, primary_key=True)
name = Column(String(64))
# association proxy of "user_keywords" collection
# to "keyword" attribute
keywords = association_proxy('user_keywords', 'keyword')
def __init__(self, name):
self.name = name
class UserKeyword(Base):
__tablename__ = 'user_keyword'
user_id = Column(Integer, ForeignKey('user.id'), primary_key=True)
keyword_id = Column(Integer, ForeignKey('keyword.id'), primary_key=True)
special_key = Column(String(50))
# bidirectional attribute/collection of "user"/"user_keywords"
user = relationship(User,
backref=backref("user_keywords",
cascade="all, delete-orphan")
)
# reference to the "Keyword" object
keyword = relationship("Keyword")
def __init__(self, keyword=None, user=None, special_key=None):
self.user = user
self.keyword = keyword
self.special_key = special_key
class Keyword(Base):
__tablename__ = 'keyword'
id = Column(Integer, primary_key=True)
keyword = Column('keyword', String(64))
def __init__(self, keyword):
self.keyword = keyword
def __repr__(self):
return 'Keyword(%s)' % repr(self.keyword)
Am I on the right track in following this pattern here?
My goal is essentially many-to-many with an extra column containing a boolean value.
This should work:
for user in users:
for keyword in user.user_keywords:
print keyword.special_key

Many-to-many self-referential relationship in sqlalchemy

I'm trying to make a self-referential many-to-many relationship (it means that Line can have many parent lines and many child lines) in sqlalchemy like this:
Base = declarative_base()
class Association(Base):
__tablename__ = 'association'
prev_id = Column(Integer, ForeignKey('line.id'), primary_key=True)
next_id = Column(Integer, ForeignKey('line.id'), primary_key=True)
class Line(Base):
__tablename__ = 'line'
id = Column(Integer, primary_key = True)
text = Column(Text)
condition = Column(Text)
action = Column(Text)
next_lines = relationship(Association, backref="prev_lines")
class Root(Base):
__tablename__ = 'root'
name = Column(String, primary_key = True)
start_line_id = Column(Integer, ForeignKey('line.id'))
start_line = relationship('Line')
But I get the following error:
sqlalchemy.exc.ArgumentError: Could not determine join condition between parent/
child tables on relationship Line.next_lines. Specify a 'primaryjoin' expressio
n. If 'secondary' is present, 'secondaryjoin' is needed as well.
Do you know how I could remedy this?
You should just need:
prev_lines = relationship(
Association,
backref="next_lines",
primaryjoin=id==Association.prev_id)
Since this specifies the next_lines back reference there is no need to have a next_lines relationship.
You can also do this using the remote_side parameter to a relationship: http://www.sqlalchemy.org/trac/browser/examples/adjacency_list/adjacency_list.py

How to insert the 'answer' model?

The title may be not exactly, but I don't know how to express it.
I have 3 class: User, Question, Answer. The simple code is:
Session = scoped_session(sessionmaker())
Base = declarative_base()
class User(Base):
__tablename__ = 'users'
id = Column(Integer, primary_key=True)
questions = relationship('Question', backref="user")
answers = relationship('Answer', backref="user")
class Question(Base):
__tablename__ = 'questions'
id = Column(Integer, primary_key=True)
user_id = Column(Integer, ForeignKey('users.id'))
answers = relationship('Answer', backref="user")
class Answer(Base):
__tablename__ = 'answers'
user_id = Column(Integer, ForeignKey('users.id'))
question_id = Column(Integer, ForeignKey('questions.id'))
id = Column(Integer, primary_key=True)
Now, A user asked a question, so there will be an answer created:
user = get_user_from_session()
question = get_question(question_id)
# create answer
answer = Answer()
answer.user = user
answer.question = question
Session.add(answer) # !!!
Session.commit()
I hope the answer will be inserted to database, but unfortunately, there is an error reported:
AttributeError: 'module' object has no attribute '_sa_instance_state'
Is there something I've missed? How to fix it?
UPDATE
Thanks for #dhaffey, I've fixed the typos. I recreate a test file to test this, found no error happened again, but answer.user_id and answer.question_id are null in database after commit.
This is my code, you can run it directly.
from sqlalchemy import *
from sqlalchemy.orm import *
from sqlalchemy.ext.declarative import *
engine = create_engine('sqlite:///test.sqlite', echo=True)
Session = scoped_session(sessionmaker())
Base = declarative_base()
Base.metadata.bind=engine
class User(Base):
__tablename__ = 'users'
id = Column(Integer, primary_key=True)
name = Column(String)
questions = relationship('Question')
answers = relationship('Answer')
class Question(Base):
__tablename__ = 'questions'
id = Column(Integer, primary_key=True)
user_id = Column(Integer, ForeignKey('users.id'))
title = Column(String)
answers = relationship('Answer')
class Answer(Base):
__tablename__ = 'answers'
user_id = Column(Integer, ForeignKey('users.id'))
question_id = Column(Integer, ForeignKey('questions.id'))
id = Column(Integer, primary_key=True)
Base.metadata.create_all()
user = User()
user.name = 'aaa'
Session.add(user)
Session.flush()
question = Question()
question.title = 'ttt'
question.user = user
Session.add(question)
Session.flush()
answer = Answer()
answer.user = user
answer.question = question
Session.add(answer)
Session.commit()
print answer.id # not None
found = Session.query(Answer).filter_by(id=answer.id).one()
print found.user.name # not None
print found.question.title # not None
# !!! It seems all models are saved correctly,
# but please open the test.sqlite database, (not querying by sqlahchemy)
# the question.user_id and answer.user_id and answer.question_id are null
Your class declarations don't "compile" for me, so I'm wondering if you've run this code, and which SQLAlchemy version you're using if so. The line
user_id = Column(Integer, ForeignKey='users.id')
raises
sqlalchemy.exc.ArgumentError: Unknown arguments passed to Column: ['ForeignKey']
with SQLAlchemy 0.6.4. You're trying to declare the foreign key with a keyword argument, but the correct usage is to construct a ForeignKey object and pass it positionally, like this:
user_id = Column(Integer, ForeignKey('users.id'))
With the foreign keys fixed, your example works as expected for me.
Note that you don't need to explicitly provide the primaryjoin argument on these relationships when the corresponding foreign keys are appropriately declared - SQLAlchemy infers the correct join.