Update relations of one to many - python

class User(UserMixin, db.Model):
id = db.Column(db.Integer, primary_key=True)
logs = db.relationship("Logs", uselist=False, backref="user")
class Logs(db.Model):
id = db.Column(db.Integer, primary_key=True)
user_id = db.Column(db.Integer, db.ForeignKey('users_user.id'))
...
Both ways will work. Basically my question is about the preferable and why. I am talking about this because in my current work i am using always the second option, and i need to decide if is needed to refactor to make the code like the first example.
logs = Logs()
logs.user = current_user._get_current_object()
logs = Logs()
logs.user_id = current_user.get_id()

Related

SQLAlchemy - Class to increment the parent in case a child (containing 2 foreign keys) is added to the DB as Obj

I found many different ways of writing down relationships between parent and children; backref, relationship, db.column(incl foreign_keys arg.), foreign_keys argument in the DB.relationship.
Also tried many examples found on the internet. Errors range from actual coding errors to DB migration errors.
The below works, but I am quite sure I should add something like a class definition to the Model. The way it's setup now it looks to me that whenever I require the rating of the parent, I need to fetch all ratings in the DB, calculate the average, etc. This does not seem to be effecient. Is there a way to increment +x(x=rating) the parent in case the child is created with the parent - in this case - as 'user_receiver_id / user_receiver'
class Parent(UserMixin, db.Model):
__tablename__ = 'parent'
id = db.Column(db.Integer, primary_key=True)
rating = db.Column(db.Float(), index=True, unique=False)
def __rating__(self):
#if child is created in DB with parent as holder -> do some python
class Child(db.Model):
__tablename__ = 'child'
id = db.Column(db.Integer, primary_key=True)
datum = db.Column(db.DateTime, server_default=db.func.now(), index=False)
content = db.Column(db.Text)
rating = db.Column(db.Float(), index=True, unique=False)
''' receiver details '''
Parent_receiver_id = db.Column(db.Integer, db.ForeignKey(Parent.id))
''' giver details '''
Parent_giver_id = db.Column(db.Integer, db.ForeignKey(Parent.id))
Parent_receiver = db.relationship("Parent", foreign_keys=[Parent_receiver_id])
Parent_giver = db.relationship("Parent", foreign_keys=[Parent_giver_id])

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 ]

"has_many :through" construct in sqlalchemy

In rails we can simply define relationships with the has_many :through syntax in order to access 2nd, 3rd .. nth degree relations.
In SQLAlchemy however, this seems to be more difficult. I'm trying to avoid going down the route of writing joins, as I find them to be anti-patterns in trying to keep a clean code base.
My tables look like following:
class Message(db.Model):
__tablename__ = 'message'
id = db.Column(db.Integer, primary_key=True)
text = db.Column(db.String())
user_id = db.Column(db.ForeignKey("user.id"))
user = db.relationship('User', backref="messages")
class User(db.Model):
__tablename__ = 'user'
id = db.Column(db.Integer, primary_key=True)
name = db.Column(db.String())
class Level(db.Model):
__tablename__ = 'level'
number = db.Column(db.Integer, nullable=False, primary_key=True)
name = db.Column(db.String(), nullable=False, primary_key=True)
users = db.relationship(
"User",
secondary="user_level",
backref="levels")
class UserLevel(db.Model):
__tablename__ = 'user_level'
user_id = db.Column(db.Integer, db.ForeignKey('user.id'), primary_key=True)
number = db.Column(db.Integer, primary_key=True)
name = db.Column(db.String(), primary_key=True)
__table_args__ = (
db.ForeignKeyConstraint(
['number', 'name'],
['level.number', 'level.name']
),
)
The idea is that a user can have multiple authorisation levels (e.g. a user can be at level 1, 3 and 6 at the same time). As the data I have does not contain unique sequence numbers for available levels, I had to resort to the use of composite keys to keep the data consistent with future updates.
To get all messages for a level I can currently do something like this:
users = Level.query[0].users
for user in users:
results.append(user.messages)
return results
This gives me all users on a level. But in order to get all messages for a certain level, I have to loop through these users and append them to a results list.
What I'd like to do is:
return Level.query[0].users.messages
This is more like the syntax I am used to from rails. How would one accomplish this in flask-SQLAlchemy?

SQLAlchemy: Override relationship-defined "order_by" in a query

So, I have a model that is something like:
class Foo(model):
__tablename__ = "foo"
id = Column(Integer, primary_key=True)
data = relationship(
"FooData",
cascade="all, delete-orphan",
backref="foo",
lazy="dynamic",
order_by="desc(FooData.timestamp)"
)
#property
def first_item(self):
# the problem is here:
return self.data.order_by(asc("timestamp")).first()
#property
def latest_item(self):
return self.data.first()
class FooData(Model):
__tablename__ = "foo_data"
foo_id = Column(Integer, ForeignKey("foo.id"), primary_key=True)
timestamp = Column(DateTime, primary_key=True)
actual_data = Column(Float, nullable=False)
So, the problem is with the first_item method there: when it is defined as above, the SQL looks like this:
SELECT foo_data.timestamp AS foo_data_timestamp, foo_data.actual_data AS foo_data_actual_data, foo_data.foo_id AS foo_data_foo_id
FROM foo_data
WHERE :param_1 = foo_data.foo_id ORDER BY foo_data.timestamp DESC, foo_data.timestamp ASC
-- ^^^^^^^^^^^^^^^^^^^^^^
Obviously, the order_by specified in the query is being appended to the one specified in the relationship definition, instead of replacing it; is there a way for a query to override the original order_by? I know I could specify a separate query directly on the FooData class, but I would like to avoid that if possible.
According to documentation:
All existing ORDER BY settings can be suppressed by passing None - this will suppress any ORDER BY configured on mappers as well.
So the simple solution is to reset ORDER BY clause and then apply the one you need. Like:
self.data.order_by(None).order_by(asc("timestamp")).first()
In case you don't want to reset whole ORDER BY clause, but only want to override one column order, AFAIK there is no built-in way for it.
I know this is an old post, but it showed up when I was searching, so maybe this will be useful to someone else
class Foo(model):
__tablename__ = "foo"
id = Column(Integer, primary_key=True)
data = relationship(
"FooData",
cascade="all, delete-orphan",
backref="foo",
lazy="dynamic",
order_by=lambda: FooData.__table__.columns.timestamp.desc()
)
...
class FooData(Model):
__tablename__ = "foo_data"
foo_id = Column(Integer, ForeignKey("foo.id"), primary_key=True)
timestamp = Column(DateTime, primary_key=True)
actual_data = Column(Float, nullable=False)

Combining relationships

I have a model Zone, a model Entity and a model Transit. Transit is minimally defined as:
class Entity(db.Model):
__tablename__ = 'entities'
id = db.Column(db.Integer, primary_key=True)
contained_by = db.Column(db.Integer, db.ForeignKey('entities.id'))
contains = db.relationship('Entity', backref='container')
discriminator = db.Column('type', db.String(50))
__mapper_args__ = {'polymorphic_on': discriminator}
class Zone(Entity):
__mapper_args__ = {'polymorphic_identity': 'zones'}
routes = db.relationship('Transit')
(stuff goes here)
class Transit(db.Model):
__tablename__ = "transits"
start = db.Column(db.Integer, db.ForeignKey('zones.id'))
end = db.Column(db.Integer, db.ForeignKey('zones.id'))
Zone also has a couple of bits about distance and how defensible it is, but that is irrelevant for this.
First off, due to the fact that Zone is subclassed from Entity using single-table inheritance can I reference zones.id?
Secondly, will the Zone.routes property merge Transit.start and Transit.end?
no, you need to use the table name, which in your case (Single-table inheritance) is entities
no, these will not be merged. You can create two relationships, and have a (hybrid) property which would combine both, but this will only be for reading purposes, as when you would like to modify this property (for example, add Transits), you would still need to specify both sides (start and end).
I am not sure I understand the question here
update-1: as requested in comment, Concrete-Table inheritance code below:
class Zone(Entity):
__mapper_args__ = {'polymorphic_identity': 'zones'}
__tablename__ = "zones"
id = Column(Integer, ForeignKey('entities.id'), primary_key=True)
#property
def transits(self):
return self.transits_from_here + self.transits_to_here
class Transit(Base):
__tablename__ = "transits"
id = Column(Integer, primary_key=True)
start = Column(Integer, ForeignKey('zones.id'))
end = Column(Integer, ForeignKey('zones.id'))
s_zone = relationship(Zone, primaryjoin=(start==Zone.id), backref="transits_from_here")
e_zone = relationship(Zone, primaryjoin=(end==Zone.id), backref="transits_to_here")

Categories