SqlAlchemy Help: Trying to add a child to a child - python

I have this database schema:
class Profile_Eval(Base):
__tablename__ = 'profile_eval'
id = Column(Integer, primary_key=True)
url = Column(String(), unique=True)
job_descriptions = relationship("Job_Description", back_populates='profile')
def __init__(self, url=None):
self.url = url
class Job_Description(Base):
__tablename__ = 'job_description'
id = Column(Integer, primary_key=True)
data = Column(String())
profile_eval_id = Column(Integer, ForeignKey('profile_eval.id'))
profile = relationship("Profile_Eval", back_populates='job_descriptions')
job_predictions= relationship("Job_Prediction", back_populates='job_description')
def __init__(self, data=None, profile_eval_id=None):
self.data = data
class Job_Prediction(Base):
__tablename__ = 'job_prediction'
id = Column(Integer, primary_key=True)
label = Column(String())
score = Column(String())
job_description_id = Column(Integer, ForeignKey('job_description.id'))
job_description = relationship("Job_Description", back_populates="job_predictions")
def __init__(self, label=None, score=None, job_description_id=None):
self.label = label
self.score = score
The structure is that each profile has many jobs. Each job has many predictions.
I am able to add a profile like:
profile = Profile_Eval(url=url)
and then I am able to add the job descriptions to the profile like:
profile_record.job_descriptions.append(Job_Description(description))
but now I'm a little confused as to how I can add the predictions to each description.
Should I break it out into their own tables and get rid of the relationship? Or is there a way to add them?
Thanks!

Should I break it out into their own tables and get rid of the relationship?
The Job_Prediction is already its own table.
Or is there a way to add them?
yes, add predictions to the descriptions at the same time you're adding the descriptions to the jobs:
job_description = Job_Description(description)
job_description.job_predictions.append(...)
# ^^^ whetever you need, here
profile_record.job_descriptions.append(job_description)

Related

sqlalchemy - how to copy deep copy a entry and all its foreign relation

Now I have a database schema:
For a video it contains many segment and for a segment its contains many jobs and in each job it contains many paths and so on...
Now I want to copy segment and combine together to make a new video. I know I could write a script to loop over from segment to jobs to... to bboxs and copy all the entry one by one. But is there a better solution that I could do the deep copy and all its foreign relation entry in a smarter way?
I tried to find a solution for this but ended up adding manual copy methods as suggested by some of the users in the comment links above but I opted for a more manual approach which, in my case, simplified things. With your example, my solution would have looked like this:
class Video(Model):
__tablename__ = 'videos'
id = Column(Integer, primary_key=True)
vidcol1 = Column(...)
vidcol2 = Column(...)
segments = relationship('Segment', uselist=True)
def copy(self):
new = Video()
new.vidcol1 = self.vidcol1
new.vidcol2 = self.vidcol2
for segment in self.segments:
new.segments.append(segment.copy())
return new
class Segment(Model):
__tablename__ = 'segments'
id = Column(Integer, primary_key=True)
video_id = Column(Integer, ForeignKey('videos.id'))
segcol1 = Column(...)
segcol2 = Column(...)
jobs = relationship('Job', uselist=True)
def copy(self):
new = Segment()
new.segcol1 = self.segcol1
new.segcol2 = self.segcol2
for job in self.jobs:
new.jobs.append(job.copy())
return new
class Job(Model):
__tablename__ = 'jobs'
id = Column(Integer, primary_key=True)
segment_id = Column(Integer, ForeignKey('segments.id'))
jobcol1 = Column(...)
jobcol2 = Column(...)
paths = relationship('Path', uselist=True)
def copy(self):
new = Job()
new.jobcol1 = self.jobcol1
new.jobcol2 = self.jobcol2
for path in self.paths:
new.paths.append(path.copy())
return new
class Path(Model):
__tablename__ = 'paths'
id = Column(Integer, primary_key=True)
job_id = Column(Integer, ForeignKey('jobs.id'))
pthcol1 = Column(...)
pthcol2 = Column(...)
bboxs = relationship('BBox', uselist=True)
def copy(self):
new = Path()
new.pthcol1 = self.pthcol1
new.pthcol2 = self.pthcol2
for bbox in self.bboxs:
new.bboxs.append(bbox.copy())
return new
class BBox(Model):
__tablename__ = 'bboxs'
id = Column(Integer, primary_key=True)
path_id = Column(Integer, ForeignKey('paths.id'))
boxcol1 = Column(...)
boxcol2 = Column(...)
def copy(self):
new = BBox()
new.boxcol1 = self.boxcol1
new.boxcol2 = self.boxcol2
return new
Each model is responsible for copying its own columns and calling the copy method of its direct relationships. This way, videos don't need to be aware of all the deeper relationships and you can do something like this:
video_copy = existing_video.copy()
session.add(video_copy)
session.commit()
In my situation I also had many-to-many relations (as secondary tables and AssociationObjects). If you wanted to add other types of relations it wouldn't be too complicated.

SqlAlchemy Database Issue

I created a Table a Bmarks which has two foreign keys which have relation with same table Url_hash
class Hashed(Base):
__tablename__ = "url_hash"
hash_id = Column(Unicode(22), primary_key=True)
url = Column(UnicodeText)
clicks = Column(Integer, default=0)
def __init__(self, url):
cleaned_url = str(unidecode(url))
self.hash_id = unicode(generate_hash(cleaned_url))
self.url = url
class Bmark(Base):
__tablename__ = "bmarks"
bid = Column(Integer, autoincrement=True, primary_key=True)
hash_id = Column(Unicode(22), ForeignKey('url_hash.hash_id'))
clean_hash_id = Column(Unicode(22), ForeignKey('url_hash.hash_id'))
description = Column(UnicodeText())
extended = Column(UnicodeText())
stored = Column(DateTime, default=datetime.utcnow)
updated = Column(DateTime, onupdate=datetime.utcnow)
clicks = Column(Integer, default=0)
inserted_by = Column(Unicode(255))
username = Column(Unicode(255), ForeignKey('users.username'),
nullable=False,)
tag_str = Column(UnicodeText())
hashed = relation(Hashed,
foreign_keys="Bmark.hash_id",
backref="bmark",
uselist=False
)
clean_hashed = relation(Hashed,
foreign_keys="Bmark.clean_hash_id",
backref="bmark",
uselist=False
)
I am trying to store url after cleaning it a little bit like removing headers,utm parameters etc for indexing purposes
Error is occurring while creating the database
sqlalchemy.exc.ArgumentError: Error creating backref 'bmark' on relationship 'Bmark.clean_hashed': property of that name exists on mapper 'Mapper|Hashed|url_hash'
Actually the error message is very informative.
Just rename one of your backref="bmark" to something else like backref="my_clean_bmark".

SQLAlchemy Using relationship()

I am using SQLAlchemy here, trying to make a couple tables and link them and am having problems implementing this.
class Team(Base):
__tablename__ = "teams"
id = Column(Integer, primary_key=True)
espn_team_id = Column(Integer, unique=True, nullable=False)
games = relationship("Game", order_by="Game.date")
def __init__(self, name):
self.name = name
self.espn_team_id = espn_team_id
self.games = games
class Game(Base):
__tablename__ = "games"
id = Column(Integer, primary_key=True)
espn_game_id=Column(Integer, unique=True, nullable=False)
date = Column(Date)
h_espn_id = Column(Integer, ForeignKey('teams.espn_team_id'))
a_espn_id = Column(Integer, ForeignKey('teams.espn_team_id'))
I have this in one file which I use to create the tables. Then in another file I use the insert() function to put values into both tables. I think if I have a team with espn_team_id 360, and then I put in multiple games into the game table which have either h_espn_id=360, or a_espn_id=360, i should be able to do:
a = Table("teams", metadata, autoload=True)
a = session.query(a).filter(a.c.espn_team_id==360).first().games
and it should give me a list of all games team with ID 360 has played. But instead I get this error
AttributeError: 'NamedTuple' object has no attribute 'games'
What am I misunderstanding about SQLAlchemy or relational databases here?
Firstly, you don't have to create another Table object, as it is available as Team.__table__. Anyway, you can just query the mapped class, e.g.
query = Session.query(Team).filter(Team.espn_team_id == 360)
team360 = query.one()
games = team360.games
Refer to the documentation for methods .one(), .first(), and .all(): http://docs.sqlalchemy.org/en/latest/orm/query.html
Here is the solution I found, took way too long to understand this...
class Team(Base):
__tablename__ = "teams"
id = Column(Integer, primary_key=True)
name = Column(String)
espn_team_id = Column(Integer, unique=True, nullable=False)
h_games = relationship(
"Game",
primaryjoin="Game.h_espn_id==Team.espn_team_id",
order_by="Game.date")
a_games = relationship(
"Game",
primaryjoin="Game.a_espn_id==Team.espn_team_id",
order_by="Game.date")
#hybrid_property
def games(self):
return self.h_games+self.a_games
def __init__(self, name):
self.name = name
self.espn_team_id = espn_team_id
self.h_games = h_games
self.a_games = a_games
self.games = games

Count number of rows in a many-to-many relationship (SQLAlchemy)

I have a many-to-many relationship between say blog entries and tags. Now I want to know how many entries a specific tag has.
Imagine the following models (simplified):
rel_entries_tags = Table('rel_entries_tags', Base.metadata,
Column('entry_id', Integer, ForeignKey('entries.id')),
Column('tag_id', Integer, ForeignKey('tags.id'))
)
class Entry(Base):
__tablename__ = 'entries'
id = Column(Integer, primary_key=True)
title = Column(String(80))
text = Column(Text)
tags = relationship('Tag', secondary=rel_entries_tags, backref=backref('entries'))
def __init__(self, title, text):
self.title = title
self.text = text
self.tags = tags
class Tag(Base):
__tablename__ = 'tags'
id = Column(Integer, primary_key=True)
name = Column(String(80), unique=True, nullable=False)
def __init__(self, name):
self.name = name
My approach to count the amount of entries for a tag is len(db_session.query(Tag).get(1).entries). The problem is that when it gets db_session.query(Tag).get(1).entries SQLAlchemy selects all the entries with all their columns for a tag, however, I want only the amount of the entries, not the entries themselves. Is there a more optimal approach for this problem?
Thanks.
session.query(Entry).join(Entry.tags).filter(Tag.id==1).count()
or if you have a Tag already
session.query(Entry).with_parent(mytag, "entries").count()

AttributeError: 'unicode' object has no attribute '_sa_instance_state'

I'm just learning how to use SQLAlchemy. I'm trying to do the following, but storing title and link in two separate tables:
temp = Submissions(title=u'Facebook Homepage', link=u'http://facebook.com')
session.add(temp)
session.flush()
transaction.commit()
via:
class Links(Base):
__tablename__ = 'links'
id = Column(Integer, primary_key=True)
link = Column(Text)
created = Column(TIMESTAMP(), default=datetime.now())
def __init__(self, link):
self.link = link
class Submissions(Base):
__tablename__ = 'submissions'
id = Column(Integer, primary_key=True)
created = Column(TIMESTAMP(), default=datetime.now())
title = Column(Text)
link_id = Column(Integer, ForeignKey('links.id'))
link = relation(Links)
def __init__(self, title, link):
self.title = title
self.link = link
However, I always get this error:
AttributeError: 'unicode' object has no attribute '_sa_instance_state'
What is going on? Is there a better way to code this?
You can't quite do that with relationship.
You need to arrange for the Link to be looked up in some way.
The most obvious is to just look it up directly.
submission_link = session.query(Links) \
.filter(Links.link == u'http://facebook.com') \
.first()
if submission_link is None:
submission_link = Links(link=u'http://facebook.com')
session.add(submission_link)
submission = Submissions(title=u'Facebook Homepage', link=submission_link)
session.add(submission)
session.commit()
You can also use hybrid attributes to get something that looks a bit more like your example, but its substantially more convoluted.
also, it's relationship, relation is deprecated.
I would configure the relation as one-to-one (uselist=False) and add a property which would wrap the link relationship. The SA configuration would then look like below and your code should work just fine creating, updating and deleting the link. You might need to configure the relation for have delete-orphan option in a cascade.
...
class Submissions(Base):
__tablename__ = 'submissions'
id = Column(Integer, primary_key=True)
created = Column(DateTime(), default=datetime.now())
title = Column(Text)
link_id = Column(Integer, ForeignKey('links.id'))
link_rel = relation(Links, backref=backref("_submission", uselist=False))
def __init__(self, title, link=None):
self.title = title
self.link = link
#property
def link(self):
return self.link_rel and self.link_rel.link
#link.setter
def link(self, value):
if value is None:
self.link_rel = None
elif self.link_rel is None:
self.link_rel = Links(value)
else:
self.link_rel.link = value
...

Categories