flask-sqlalchemy query association table having additional columns - flask

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

Flask SqlAlchemy Many to Many Auto Populate

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?

Flask SQLAlchemy reverse inclusion a many to many query

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.

flask sqlalchemy one-to-one relationship issues

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.

one-one relation sqlalchemy flask

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?

Filter foreign key column on choice in flask admin

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.