I have created many-to-many relationship as per below code, but i have added additional columns to the association table. I want to retrieve those columns by specific user id. Could someone advise how can query it? I have read many posts but none of them has additional columns.
If I do:
u = Users.query.filter_by(id='8').first()
u.subscriptions[0].id
I can see data from Subscription table, but if i do:
u.subscriptions[0].subscription_id
OR
u.subscriptions[0].User_Subscription
I am getting 'Subscription' object has no attribute, as I am trying to get data of columns in User_Subscription association table.
User_Subscription = db.Table('user_subscription',
db.Column('user_id', db.Integer, db.ForeignKey('users.id')),
db.Column('subscription_id', db.Integer, db.ForeignKey('subscription.id')),
db.Column('stripe_subscription_id', db.String(100), nullable=True),
db.Column('paypal_subscription_id', db.String(100), nullable=True)
)
class Subscription(db.Model):
__tablename__ = "subscription"
id = db.Column(db.Integer, primary_key=True)
subscription_name = db.Column(db.String(100), unique=True, nullable=False)
subscription_desc = db.Column(db.String(100), unique=False, nullable=False)
date_created = db.Column(db.DateTime, default=datetime.datetime.utcnow)
date_updated = db.Column(db.DateTime, default=datetime.datetime.utcnow, onupdate=datetime.datetime.utcnow)
subscriptions = db.relationship('Users', secondary=User_Subscription, backref= db.backref('subscriptions', lazy='dynamic'))
class Users(db.Model, UserMixin):
__tablename__ = "users"
id = db.Column(db.Integer, primary_key=True)
firstname = db.Column(db.String(50), unique=False, nullable=False)
lastname = db.Column(db.String(50), unique=False, nullable=False)
email = db.Column(db.String(120), unique=True, nullable=False)
password = db.Column(db.String(60), nullable=False)
date_created = db.Column(db.DateTime, default=datetime.datetime.utcnow)
date_updated = db.Column(db.DateTime, default=datetime.datetime.utcnow, onupdate=datetime.datetime.utcnow)
user_status_id = db.Column(db.String(2), db.ForeignKey('user_status.id'))
You've got the error ('Subscription' object has no attribute) because it is an relationship object, not table.
In this line of code:
u.subscriptions[0].User_Subscription
you are trying to access attribute of the object User_Subcription which is the relationship object.
If you want to create new association table, create same table in your database and define one more class UserSubscription:
class UserSubscription(db.Model)
__tablename__ = 'user_subscription'
user_id = db.Column(db.Integer, db.ForeignKey('users.id'))
subscription_id = db.Column(db.Integer, db.ForeignKey('subscription.id'))
stripe_subscription_id = db.Column(db.String(100), nullable=True)
paypal_subscription_id = db.Column(db.String(100), nullable=True)
user = db.relationship('Users', backref='user_subscription')
And then you can access attribute of UserSubscription class:
u.user_subscription.subscription_id
Related
I have manager employee tables modeled like this using Flask SQLAlchemy:
manager_employee = db.Table('manager_employee',
db.Column('manager_id', db.Integer, db.ForeignKey('manager.manager_id'), primary_key=True),
db.Column('employee_id', db.Integer, db.ForeignKey('employee.employee_id'), primary_key=True),
)
class Manager(db.Model):
manager_id = db.Column(db.Integer, primary_key=True)
manager_employee_id = db.Column(db.Integer, db.ForeignKey('employee.employee_id'))
created_date = db.Column(db.DateTime, default=datetime.utcnow)
updated_date = db.Column(db.DateTime, index=True, nullable=True, default=None)
deleted_date = db.Column(db.DateTime, index=True, nullable=True, default=None)
employees = db.relationship('Employee', secondary=manager_employee, lazy='dynamic', backref=db.backref('manager', lazy='dynamic'))
class Employee(db.Model):
employee_id = db.Column(db.Integer, primary_key=True)
first_name = db.Column(db.String(60), index=True, nullable=False)
last_name = db.Column(db.String(60), index=True, nullable=False)
start_date = db.Column(db.Date, default=date.today())
is_admin = db.Column(db.Boolean, default=False, nullable=False)
created_date = db.Column(db.DateTime, default=datetime.utcnow)
updated_date = db.Column(db.DateTime, index=True, nullable=True, default=None)
deleted_date = db.Column(db.DateTime, index=True, nullable=True, default=None)
user_id = db.Column(db.Integer, db.ForeignKey('user.user_id'))
manager_id = db.Column(db.Integer, db.ForeignKey('manager.manager_id'))
When I add employees and managers (managers are also employees), I want the linking table (manager_employee) to auto populate with the correct id's but this is not happening. Any ideas what is wrong?
I'm quite new to SQLAlchemy.
For this project I'm using flask and sqlalchemy.
One of the routes I need is to make is to add users to a certain group. So I need to create a list of users which are not included in this group.
I made an SQL statement and tried it in my database, which gives me the result I need, I'm just not good at converting this into SQLAlchemy
select id, email from users where id not in ( select user_id from user_group where group_id = 1);
my models look like this
UserGroup = db.Table(
"user_group",
db.Model.metadata,
db.Column("user_id", db.Integer, db.ForeignKey("users.id")),
db.Column("group_id", db.Integer, db.ForeignKey("groups.id")),
)
class Group(db.Model, RestrictionsMixin):
__tablename__ = "groups"
__versioned__ = {}
__restrictions__ = {}
id = db.Column(db.Integer, primary_key=True)
name = db.Column(db.String(255), nullable=False, unique=True)
class User(db.Model):
__tablename__ = "users"
__versioned__ = {}
id = db.Column(db.Integer, primary_key=True)
email = db.Column(db.String(250), unique=True, nullable=False)
_password = db.Column(db.LargeBinary(60), nullable=False)
authenticated = db.Column(db.Boolean, default=False)
active = db.Column(db.Boolean, default=True, nullable=False)
email_confirmation_sent_on = db.Column(db.DateTime, nullable=True)
email_confirmed = db.Column(db.Boolean, nullable=True, default=False)
email_confirmed_on = db.Column(db.DateTime, nullable=True)
registered_on = db.Column(db.DateTime, nullable=True)
last_logged_in = db.Column(db.DateTime, nullable=True)
groups = db.relationship("Group", secondary="user_group", backref="users")
You could do something like this:
user_group_subquery = (
db.session.query(UserGroup.c.user_id).filter(UserGroup.c.group_id == 1).subquery()
)
db.session.query(User.id, User.email).filter(~User.id.in_(user_group_subquery))
The tilde (~) returns the negation of a clause.
I am trying to set up a one-to-one relationship in flask. I have a script that runs db.drop_all() when I want to clear my DB. But I am getting cascade errors when doing that. I have played with many combinations using cascade and single_parent arguments in the model relationships and nothing is working. Any help would be appreciated to help set up a one-to-one relationship that will allow me to use db.drop_all() without error. Below is my latest iteration.
from sqlalchemy.sql import func
from project import db, bcrypt
class User(db.Model):
__tablename__ = 'user'
id = db.Column(db.Integer, primary_key=True, autoincrement=True)
email = db.Column(db.String(150), nullable=False)
password = db.Column(db.String(250), nullable=False)
active = db.Column(db.Boolean(), default=True, nullable=False)
created_date = db.Column(db.DateTime, default=func.now(), nullable=False)
profile = db.relationship("Profile", cascade="all, delete, delete-orphan")
def __init__(self, email, password):
self.email = email
self.password = bcrypt.generate_password_hash(password).decode('utf-8')
class Profile(db.Model):
__tablename__ = 'profile'
id = db.Column(db.Integer, primary_key=True, autoincrement=True)
first_name = db.Column(db.String(150), nullable=True)
last_name = db.Column(db.String(250), nullable=True)
github_url = db.Column(db.String(250), nullable=True)
user_id = db.Column(db.Integer, db.ForeignKey("user.id"), nullable=False)
user = db.relationship("User")
from sqlalchemy.sql import func
from project import db, bcrypt
class User(db.Model):
__tablename__ = 'user'
id = db.Column(db.Integer, primary_key=True, autoincrement=True)
email = db.Column(db.String(150), nullable=False)
...
def __init__(self, email, password):
self.email = email
self.password = bcrypt.generate_password_hash(password).decode('utf-8')
class Profile(db.Model):
__tablename__ = 'profile'
id = db.Column(db.Integer, primary_key=True, autoincrement=True)
first_name = db.Column(db.String(150), nullable=True)
user_id = db.Column(db.Integer, db.ForeignKey("user.id"), nullable=False)
...
user = db.relationship(
'User', cascade='all', backref=db.backref('profile', cascade='all'))
note how relationship between the two models is defined in profile model only.
I am trying to implement 1-1 relation for User and Profile Tabels like below.
class User(db.Model, UserMixin):
id = db.Column(db.Integer, primary_key=True)
email = db.Column(db.String(255), unique=True)
password = db.Column(db.String(255))
active = db.Column(db.Boolean())
confirmed_at = db.Column(db.DateTime(), default=datetime.now())
roles = db.relationship('Role', secondary=roles_users,
backref=db.backref('users', lazy='dynamic'))
def __repr__(self):
return '<User {self.email}>'.format(self=self)
class CustomerProfile(db.Model):
__tablename__ = 'customer_profiles'
id = db.Column(db.Integer(), primary_key=True)
full_name = db.Column(db.String(200), nullable=True)
user_id = db.Column(db.Integer(), db.ForeignKey('user.id'), unique=True)
user = db.relationship('User', backref=db.backref("CustomerProfile", uselist=False))
def __repr__(self):
return '<CustomerProfile {self.full_name}>'.format(self=self)
My question is:
Is this the correct representation of 1-1 mapping? Cause when I try to reverse engineer the database with MySQL Workbench, it does show me one to many mapping
If this is the correct representation, then inserting a duplicate row is being allowed unless I give unique=True in user_id. I would have expected that since I told MySql about the mapping, it should not allow duplicate row...Is that not true?
after having a look at http://docs.sqlalchemy.org/en/latest/orm/basic_relationships.html#one-to-one
I changed my code to below:
class User(db.Model, UserMixin):
id = db.Column(db.Integer, primary_key=True)
email = db.Column(db.String(255), unique=True)
password = db.Column(db.String(255))
active = db.Column(db.Boolean())
confirmed_at = db.Column(db.DateTime(), default=datetime.now())
roles = db.relationship('Role', secondary=roles_users,
backref=db.backref('users', lazy='dynamic'))
customer_profiles = db.relationship('CustomerProfile', uselist=False, back_populates="user")
def __repr__(self):
return '<User {self.email}>'.format(self=self)
class CustomerProfile(db.Model):
__tablename__ = 'customer_profiles'
id = db.Column(db.Integer(), primary_key=True)
full_name = db.Column(db.String(200), nullable=True)
user_id = db.Column(db.Integer(), db.ForeignKey('user.id'))
user = db.relationship('User', back_populates='customer_profiles')
def __repr__(self):
return '<CustomerProfile {self.full_name}>'.format(self=self)
but still, I am able to insert a duplicate row into profiles....
Since I am mapping it at the DB level, I am assuming that DB will stop me from inserting a duplicate row even though I do not give unique=True.. Is my understanding correct?
I am using Flask-admin and SQLAlchemy.
I want to limit the choices of a foreign key based on a choice of the foreign key of the parent table.
class City(Base):
__tablename__ = 'city'
id = Column(Integer, primary_key=True)
name = Column(String, nullable=False)
def __unicode__(self):
return self.name
class Street(Base):
__tablename__ = 'street'
id = Column(Integer, primary_key=True)
name = Column(String, nullable=False)
city = Column(Integer, ForeignKey(City.id), nullable=False)
city_ref = relationship(City)
def __unicode__(self):
return self.name
class Adress(Base):
__tablename__ = 'adress'
id = Column(Integer, primary_key=True)
familiyname = Column(String, nullable=False)
street = Column(Integer, ForeignKey(Street.id), nullable=False)
street_ref = relationship(Street)
city_ref = relationship("City",
secondary="join(Street,City,Street.city==City.id)",
primaryjoin="and_(Adress.street==Street.id)",
secondaryjoin="City.id == Street.city")
def __unicode__(self):
return self.name
Now I want to add an adress for a family. But first I want to select the city and based on that choice I want to filter the available streets.
How can this be done?
admin.add_view(sqla.ModelView(Adress, db.session))
This also shows the city but the street column is not filtered when a city is chosen.