Filed in SQLAlchemy of user's list - python

I have one model that is Users in which there is a field in this model that I would like to store a list of Users. The idea is that you can add frieds and store them somewhere.
class User (db.Model):
__tablename__ = "users"
id = Column(Integer, primary_key=True)
username = Column(String(50), unique = True)
email = Column(String(120), unique = True)
password = Column(String(50))
date = Column(DateTime(), default=datetime.now())
friends = "Should be a list of users"
I have thought to have a string with the id of each user but, is there any posibility to do it with a relationship to the same model? like this:
friends = relationship("User")
Thanks a lot!

Proposed solutions based on Adjacency List Relationships would only work in case when someone can be a friend of maximum one person, which I do not believe to be the case in the real world.
A pattern you need to apply in this case is called Self-Referential Many-to-Many Relationship. Please read the sample linked to above. In order to make it work for your model, you would need to create additional table to keep the pairs of friends, and configure the relationship as below:
# object model
t_userfriend = Table("user_friend", Base.metadata,
Column("user_id", Integer, ForeignKey("users.id"), primary_key = True),
Column("friend_id", Integer, ForeignKey("users.id"), primary_key = True),
)
class User (Base):
__tablename__ = "users"
id = Column(Integer, primary_key=True)
name = Column(String(50), unique = True)
# ...
friends = relationship("User",
secondary = t_userfriend,
primaryjoin = (id == t_userfriend.c.user_id),
secondaryjoin = (id == t_userfriend.c.friend_id),
backref = "friend_of",
)
I guess that the other question you need to ask yourself is whether in your model if A is a friend of B, does this mean that B is a friend of A? In case this is true, you might want/need to:
either store just one side of the relationship, and calculate the other
make sure you always store both sides to the relationship

You can use Adjacency List Relationship and this link have the same issue so you can learn from it.
How to create relationship many to many in SQLAlchemy (python, flask) for model User to itself

Yes you can do it with Adjacency List Relationships.
class User (db.Model):
__tablename__ = "users"
id = Column(Integer, primary_key=True)
username = Column(String(50), unique = True)
email = Column(String(120), unique = True)
password = Column(String(50))
date = Column(DateTime(), default=datetime.now())
friends = relationship("User",
backref=backref('parent', remote_side=[id])
)

Related

Python SQLalchemy Join lookup by User

Wrapping my head around a way to get a list of Jobs associated to a User. My DB Model goes a little something like this.
class Job(db.Model):
id = db.Column(db.Integer, primary_key=True)
# Relationship Rows
actions = db.relationship('JobAction', backref='job')
class JobAction(db.Model):
id = db.Column(db.Integer, primary_key=True)
# Linked Rows
user_id = db.Column(db.Integer, db.ForeignKey('users.id'))
# Relationship Rows
user = db.relationship('User', foreign_keys=[user_id], backref='jobactions')
I need to get a list of Jobs that are associated to a User. I can use either the User already matching a logged in users details. Or the user.id.
I was looking at something like the below, but no dice. I can see it's overly optimistic a query, but can't see what's up. Potentially a missing Join.
# Get User first.
user = User.query.filter_by(id=1).first()
# Get their Jobs
jobs = Job.query.filter_by(actions.user=user).all()
Any ideas would be greatly appreciated.
Cheers,
I'm guessing you are missing a foreign key. If your database model looked like this:
class User(db.Model):
__tablename__ = 'users'
id = db.Column(db.Integer, primary_key=True)
jobactions = db.relationship("JobAction", back_populates="user")
class Job(db.Model):
__tablename__ = 'jobs'
id = db.Column(db.Integer, primary_key=True)
jobactions = db.relationship('JobAction', backref='job')
class JobAction(db.Model):
__tablename__ = 'jobactions'
id = db.Column(db.Integer, primary_key=True)
user_id = db.Column(db.Integer, db.ForeignKey('users.id'))
job_id = db.Column(db.Integer, db.ForeignKey('jobs.id'))
user = db.relationship(User, back_populates="jobactions")
job = db.relationship(Job, back_populates="jobactions")
Then you could use:
jobs = [ jobaction.job for jobaction in user.jobactions ]

SqlAlchemy Table and Query Issues

Still wrapping my head around SqlAlchemy and have run into a few issues. Not sure if it is because I am creating the relationships incorrectly, querying incorrect, or both.
The general idea is...
one-to-many from location to user (a location can have many users but users can only have one location).
many-to-many between group and user (a user can be a member of many groups and a group can have many members).
Same as #2 above for desc and user.
My tables are created as follows:
Base = declarative_base()
class Location(Base):
__tablename__ = 'location'
id = Column(Integer, primary_key=True)
name = Column(String)
group_user_association_table = Table('group_user_association_table', Base.metadata,
Column('group_id', Integer, ForeignKey('group.id')),
Column('user_id', Integer, ForeignKey('user.id')))
class Group(Base):
__tablename__ = 'group'
id = Column(Integer, primary_key=True)
name = Column(String)
users = relationship('User', secondary=group_user_association_table, backref='group')
desc_user_association_table = Table('desc_user_association', Base.metadata,
Column('desc_id', Integer, ForeignKey('desc.id')),
Column('user_id', Integer, ForeignKey('user.id')))
class Desc(Base):
__tablename__ = 'desc'
id = Column(Integer, primary_key=True)
name = Column(String)
users = relationship('User', secondary=desc_user_association_table, backref='desc')
class User(Base):
__tablename__ = 'user'
id = Column(Integer, primary_key=True)
user_name = Column(String)
location_id = Column(Integer, ForeignKey('location.id'))
groups = Column(String, ForeignKey('group.id'))
descs = Column(String, ForeignKey('desc.id'))
location = relationship('Location', backref='user')
Here are some examples as to how I am creating the data (all being scraped from the web):
location = Location(id=city[1], name=city[0]) #city = ('name', id)
profile = User()
profile.id = int(str(span2class[0].a['href'][7:]))
profile.user_name = str(span2class[0].a.img['alt'])
profile.location_id = location.id
g = Group(id=gid, name=str(group.contents[0])) # add the group to the Group table
self.db_session.add(g)
# Now add the gid to a list that will be added to the profile that eventually gets added to the user table
profile.groups.append(str(gid)) # stick the gid into the list
profile.groups = ','.join(profile.groups) # convert list to csv string
# Repeat basically same thing above for desc
self.db_session.add(profile)
self.db_session.commit()
As far as queries go, I've got some of the basic ones working such as:
for instance in db_session.query(User).all():
print instance.id, instance.user_name
But when it comes to performing a join to get (for example) group.id and group.name for a specific user.id... nothing I've tried has worked. I am guessing that the form would be something like the following:
db_session.query(User, Group).join('users').filter(User.id==42)
but that didn't work.
Joins works from left to right, so you should join on the relationship from User to Group:
db_session.query(User, Group).join(User.group).filter(User.id == 42)
But this return you a list of tuples (<User>, <Group>), so if the user belongs to 2 or more groups, you will receive 2 or more rows.
If you really want to load both the user and its groups in one (SQL) query, a better way would be to load a user, but configure query to preload groups:
u = (session.query(User)
.options(joinedload(User.group))
.get(42)
)
print("User = {}".format(u))
for g in u.group:
print(" Group = {}".format(g))

delete one-to-one relationship in flask

I'm currently developing an application using flask and I'm having a big problem to delete items in an one-to-one relationship. I have the following structure in my models:
class User(db.Model):
__tablename__ = 'user'
user_id = db.Column(db.String(8), primary_key = True)
password = db.Column(db.String(26))
class Student(db.Model):
__tablename__ = 'student'
user_id = Column(db.String(8), ForeignKey('user.user_id'), primary_key = True)
user = db.relationship('User')
class Professor(db.Model):
__tablename__ = 'professor'
user_id = Column(db.String(8), ForeignKey('user.user_id'), primary_key = True)
user = db.relationship('User')
What I want to do is delete the Student or the Professor if I delete the user. I have tried to test it using the code below, however, when I check my database, the student and professor are still there and my tests don't pass. I tried to include the cascade parameter when I set the relationship but it doesn't work. I have found in the internet to use this parameter: single_parent=True, but it also doesn't work.
user1 = User(user_id='user1234',password='alsdjwe1')
user2 = User(user_id='user2345',password='asfr5421')
student1 = Student(user = user1)
professor1 = Professor(user = user2)
db.session.delete(user1)
db.session.delete(user2)
I'd be glad if somebody can help me with this.
Thank you very much,
Thiago.
Use the cascade argument in your relationships.
class Student(db.Model):
__tablename__ = 'student'
user_id = Column(db.String(8), ForeignKey('user.user_id'), primary_key = True)
user = db.relationship('User', cascade='delete')
class Professor(db.Model):
__tablename__ = 'professor'
user_id = Column(db.String(8), ForeignKey('user.user_id'), primary_key = True)
user = db.relationship('User', cascade='delete')
You might want to look into delete-orphan if your use case needs it.

Flask-SQLAlchemy query in a many to many ITSELF relationship

I have a many to many relationship to itself, that means, my model is User and one field is friends, which would be Users. I have done it the relationship but my problem comes when I try to do the query for the friends of an User. My model and the relation looks like this:
friendship = db.Table('friends',
Column('friend_id', db.Integer, db.ForeignKey('monkeys.id')),
Column('myfriend_id', db.Integer, db.ForeignKey('monkeys.id'))
)
class Monkey (db.Model):
_tablename__ = "monkeys"
id = Column(Integer, primary_key=True)
username = Column(String(50), unique = True)
email = Column(String(120), unique = True)
password = Column(String(50))
date = Column (DateTime(), default=datetime.now())
friends = relationship('Monkey',
secondary = friendship,
primaryjoin = (friendship.c.friend_id == id),
secondaryjoin = (friendship.c.myfriend_id == id),
backref = backref('friendship', lazy = 'dynamic'),
lazy = 'dynamic')
And from de view, if I want to do the query, I have tried with something
friends_list = Monkey.query.join(Monkey.friends).filter(Monkey.id == user.id).all()
Bu it does not work... any help please? thanks!
You don't need to create join by yourself.
change lazy='joined' - items will be loaded “eagerly” in the same query as that of the parent.
When You get object Monkey you already have access to friends
monkey = session.query(Monkey).get(user.id)
friends_list = monkey.friends

Create An Object With A Many-To-Many Relationship

How do I initialize a group object with the list of members that are part of the group (and the users have to be aware of the groups that they are a part of)?
Users have many groups. Groups have many users.
I have also tried an add_to_group method in the User class, but that didn't really work out.
This is my first time dealing with a many-to-many relationship, so I haven't figured out how to do it yet, and all SO posts refer to query many-to-many relationships rather than creating objects that use them.
class User(db.Model):
__tablename__ = 'User'
id = db.Column(db.Integer, primary_key=True)
name = db.Column(db.String(80), unique = False, nullable = False)
username = db.Column(db.String(80), unique = True, nullable = False)
fb_id = db.Column(db.String(80), unique = True, nullable = False)
groups = db.relationship('Groups', secondary=groups, backref=db.backref('User', lazy='dynamic'))
groups = db.Table('groups',
db.Column('group_id', db.Integer, db.ForeignKey('Group.id')),
db.Column('user_id', db.Integer, db.ForeignKey('User.id'))
)
class Group(db.Model):
__tablename__ = 'Group'
id = db.Column(db.Integer, primary_key=True)
last_updated = db.Column(db.DateTime, mutable=True)
def __init__(self):
self.last_updated = datetime.utcnow();
Thanks!
In SQLAlchemy, many-to-many relations are modeled as attributes whose values are lists.
u = User()
g = [Group(), Group(), Group()]
u.groups = g
You can also alter the list in-place:
g1 = Group()
u.groups.append(g)
The other side of the relationship would work in the same way:
g.users.append(User())
When you alter one side of a many-to-many relationship, SQLAlchemy keeps the other side up-to-date—in other words, if you remove a User from a Group's users list, then that Group will no longer appear in that User's groups list.

Categories