Reputation: 1073
Using SQLalchemy and Flask-SQLAlchemy, I'm able to set up the following User class and sub classes Customer and Provider :
class User(Base):
__tablename__ = "user"
id = Column(Integer, primary_key=True)
name = Column(String(50), unique=False)
user_type = Column(String(32), nullable=False)
__mapper_args__ = {
'polymorphic_on':'user_type',
'polymorphic_identity':'user_type'
}
class Customer(User):
__tablename__ = "customer"
id = Column(Integer, ForeignKey('user.id'), primary_key=True)
note_customer = Column(Integer)
__mapper_args__ = {
'polymorphic_identity':'customer'
}
class Provider(User):
__tablename__ = "provider"
id = Column(Integer, ForeignKey('user.id'), primary_key=True)
note_provider = Column(Integer)
__mapper_args__ = {
'polymorphic_identity':'provider'
}
class Address(Base):
__tablename__ = "address"
id = Column(Integer, primary_key=True)
number = Column(Integer)
street = Column(String(100))
user_id = Column(Integer, ForeignKey('user.id'))
user = relationship(User)
Then I am able to create a new customer :
u = Customer(name= 'Mark', user_type='customer', note_customer = 15)
db.session.add(u)
db.session.commit()
But I would like to add an address value to my Customer Mark during his creation and I don't succeed in and can't figure out how doing it. I guess I'm wrong with my relationship : for me it's a "one-to-one" type, but many solutions I tried give me errors telling that the address argument is not available for a Customer object type.
Thanks!
Upvotes: 1
Views: 1121
Reputation: 52929
The problem is that you neither use a backref or explicitly create 2 back populating relationships. Because of this the User
model class has no idea of the relationship and no Address
instance can be passed to it or subclasses when constructing. The error you're receiving should be:
TypeError: 'address' is an invalid keyword argument for Customer
from sqlalchemy.orm import backref
class Address(Base):
__tablename__ = "address"
id = Column(Integer, primary_key=True)
number = Column(Integer)
street = Column(String(100))
user_id = Column(Integer, ForeignKey('user.id'))
user = relationship(User, backref=backref('address', uselist=False))
This creates a relationship attribute address on User
model as if you'd added it manually:
address = relationship('Address', uselist=False, ...)
You need to use a backref()
object in order to pass uselist=False
since you want a one to one relationship.
class User(Base):
__tablename__ = "user"
id = Column(Integer, primary_key=True)
name = Column(String(50), unique=False)
user_type = Column(String(32), nullable=False)
address = relationship('Address', uselist=False, back_populates='user')
class Address(Base):
__tablename__ = "address"
id = Column(Integer, primary_key=True)
number = Column(Integer)
street = Column(String(100))
user_id = Column(Integer, ForeignKey('user.id'))
user = relationship(User, back_populates='address')
The back_populates parameters will establish event listeners that will mirror attribute operations between the classes.
With a proper relationship configuration in place creating a new Customer
with an address can be done like this:
u = Customer(name='Mark', user_type='customer', note_customer=15,
address=Address(number=221, street='Baker Street'))
session.add(u)
session.commit()
or in separate steps:
u = Customer(name='Mark', user_type='customer', note_customer=15)
u.address = Address(number=221, street='Baker Street')
session.add(u)
session.commit()
Upvotes: 2
Reputation: 6596
The error is because your customer class isn't defined to have an address.
You could simply add another few lines to put Mark's address in, and tie the relationship to Mark.
mark_address = Address(number='123', street='Main St', user = u)
db.session.add(mark_address)
db.session.commit()
Upvotes: 0