How do I properly model a ratings table? - sqlalchemy

I have some stories that need to be reviewed by certain users given different criteria. I'm a bit torn how to model the ratings table given that there are three foreign keys here: story_id, user_id and rating_criterion_id.
class Story(db.Model):
__tablename__ = 'stories'
id = db.Column(db.Integer, primary_key=True, index = True)
story_hash = db.Column(db.String(256), index = True)
creation_date = db.Column(db.DateTime(), default=datetime.now)
modification_date = db.Column(db.DateTime(), default=datetime.now,onupdate=datetime.now)
content = db.Column(db.String())
def __repr__(self):
return f"Story {self.id}: {self.story_hash}"
class RatingCriterion(db.Model):
__tablename__ = 'rating_criteria'
id = db.Column(db.Integer, primary_key=True, index = True)
name = db.Column(db.String(256), index = True)
de_alias = db.Column(db.String(256))
en_alias = db.Column(db.String(256))
tooltip = db.Column(db.String())
evaluation_key_id = db.Column(db.Integer, db.ForeignKey('evaluation_keys.id'), index = True) # there's a foreign key to the evaluation key here
def __repr__(self):
return f"RatingCriterion {self.id}: {self.name}"
class Rating(db.Model):
__tablename__ = 'ratings'
id = db.Column(db.Integer, primary_key=True, index = True)
user_id = db.Column(db.Integer, db.ForeignKey('user.id'), index = True) #there's also a foreign key here to a Users table.
story_id = db.Column(db.Integer, db.ForeignKey('stories.id'), index = True)
rating_criterion_id = db.Column(db.Integer, db.ForeignKey('rating_criteria.id'), index = True)
value = db.Column(db.Float())
My concern with this is that if I save ratings based on this model, there will be several rows pertaining to a rating per criterion. I would then need to pivot this table on user and story so that I get the rating values with the rating criteria as columns. Is there a better way of doing this?

Related

Fastapi delete only one record in many to many table

I am building a Fastapi application that uses SQLAlchemy, and I am trying to implement a many-to-many relationship. My problem is when I try to delete a record in intermediate table it deletes all records.
Example of table 'device_protocol'
device_id
protocol_id
status_id
1
1
1
1
2
1
1
3
3
If I try to remove only device_id=1 with protocol_id=2 it actually removes all records with device_id=1
Models:
class DeviceProtocolAssociation(Base):
__tablename__ = "device_protocol"
device_id = Column(Integer, ForeignKey("device.id", ondelete="CASCADE"), primary_key=True)
device = relationship("Device", back_populates="device_protocols")
protocol_id = Column(Integer, ForeignKey("protocol.id"), primary_key=True)
protocol = relationship("Protocol", back_populates="device_protocols")
status_id = Column(Integer, ForeignKey("status.id"), nullable=True)
status = relationship("Status", back_populates="device_protocols")
class Device(Base):
__tablename__ = "device"
id = Column(Integer, primary_key=True, unique=True, index=True)
name = Column(String(255))
status_id = Column(Integer, ForeignKey('status.id'))
status = relationship("Status", back_populates="devices")
device_protocols = relationship(DeviceProtocolAssociation, back_populates="device")
protocols = association_proxy("device_protocols", "protocols")
class Protocol(Base):
__tablename__ = "protocol"
id = Column(Integer, primary_key=True, unique=True, index=True)
name = Column(String(255))
device_protocols = relationship(DeviceProtocolAssociation, back_populates="protocol")
devices = association_proxy("device_protocols", "devices")
class Status(Base):
__tablename__ = "status"
id = Column(Integer, primary_key=True, unique=True, index=True)
name = Column(String(255))
description = Column(String(255), nullable=True)
devices = relationship("Device", back_populates="status")
device_protocols = relationship(DeviceProtocolAssociation, back_populates="status")
Router:
#router.delete('/{device_id}/{protocol_id}')
async def delete_status(device_id: int, protocol_id: int, db:Session=Depends(get_db)):
relation_query = db.query(DeviceProtocolAssociation).filter(DeviceProtocolAssociation.device_id==device_id and DeviceProtocolAssociation.protocol== protocol_id)
db_relation = relation_query.first()
if not db_relation:
raise HTTPException(status_code=status.HTTP_404_NOT_FOUND,
detail=f'No relation with this id: {id} found')
relation_query.delete(db_relation)
db.commit()
return {"relation": "deleted"}
How can I remove only a record?
You can't use python's and when filtering in SQLAlchemy. You can either pass several conditions to the filter
relation_query = db.query(DeviceProtocolAssociation).filter(
DeviceProtocolAssociation.device_id == device_id,
DeviceProtocolAssociation.protocol == protocol_id,
)
or use the binary and-operator &:
relation_query = db.query(DeviceProtocolAssociation).filter(
(DeviceProtocolAssociation.device_id == device_id)
& (DeviceProtocolAssociation.protocol == protocol_id)
)
Solution to my problem is:
relation_query = db.query(DeviceProtocolAssociation).filter(DeviceProtocolAssociation.device_id==device_id, DeviceProtocolAssociation.protocol_id==protocol_id)

how to fetch data from multiple tables without doing join in sqlalchemy (Using fastApi)

class Actions(Base):
__tablename__ = "actions"
id = Column(Integer, primary_key=True, index=True)
resource_id = Column(Integer, ForeignKey('resources.id'))
action_short_name = Column(String(20),nullable=True)
description = Column(String(250),nullable=True)
status = Column(String(1),nullable=True)
action_resource = relation(Resource, backref = "actions")
detailsQ = session.query(Actions).join(Resource).filter(Actions.resource_id == Resource.id).all()
I don't want to use join so how I am able to fetch data though I have made relations ?

duplicate records in my results

my model defines like this
class UserAppWeekStatistics(db.Model):
__tablename__ = 'user_app_week_statistics'
id = db.Column(db.Integer, primary_key=True)
imei = db.Column(db.String(15), primary_key=True, index=True)
year = db.Column(db.Integer, primary_key=True,
default=int(datetime.now().strftime('%Y')), index=True)
week = db.Column(db.Integer, primary_key=True,
default=int(datetime.now().strftime('%W')), index=True)
count = db.Column(db.Integer, default=0)
def __repr__(self):
return '<UserAppWeekStatistics %r-%r-%r>' % (self.imei, self.year, self.week)
and in my views
user_app_week_statistics = UserAppWeekStatistics.query.filter_by(imei=json_req['imei'],
year=int(datetime.now().strftime('%Y')),
week=int(datetime.now().strftime('%W'))).first()
if user_app_week_statistics is None:
user_app_week_statistics = UserAppWeekStatistics()
user_app_week_statistics.imei = json_req['imei']
user_app_week_statistics.count = 1
else:
user_app_week_statistics.count += 1
db.session.add(user_app_week_statistics)
db.session.commit()
and bellow is capture my results in my database.
In my opinion, there would be just one record with same imei, year and week, but in my project there may be many duplicate records with same imei ,year and week. I am really confused.My database is mysql 5.6.
1) Fix Your model, there can be only 1 primary_key.
2) No need for index on every field, especially on statistics tables - it makes inserts, updates slower, cuz every time db engine does re-indexing.
3) add unique composite index to prevent duplications.
finally here is Your model:
class UserAppWeekStatistics(db.Model):
__tablename__ = 'user_app_week_statistics'
id = db.Column(db.Integer, primary_key=True)
imei = db.Column(db.String(15), index=True)
year = db.Column(db.Integer, default=int(datetime.now().strftime('%Y')))
week = db.Column(db.Integer, default=int(datetime.now().strftime('%W')))
count = db.Column(db.Integer, default=0)
# add this
__table_args__ = db.UniqueConstraint('imei', 'year', 'week', name='uq_user_app_week_statistics_imei_year_week')
def __repr__(self):
return '<UserAppWeekStatistics %r-%r-%r>' % (self.imei, self.year, self.week)

sqlalchemy.orm.exc.UnmappedInstanceError

I keep getting this error whenever i try deleting the grand-child item in sqlalchemy in pyramid application
UnmappedInstanceError: Class 'sqlalchemy.ext.declarative.api.DeclarativeMeta' is not mapped; was a class (beatstore.models.Song) supplied where an instance was required?
Here is my Delete code in both views and models
Models
class Song(Base):
__tablename__ = 'songs'
id = Column('song_id', Integer, primary_key=True)
# foreing key
# nullable = false, the song must have an artist
artist_id = Column(Integer, ForeignKey('artists.artist_id', onupdate='CASCADE', ondelete='CASCADE'), nullable=False)
artist = relationship("Artist")
# foreing key
album_id = Column(Integer, ForeignKey('albums.album_id', onupdate='CASCADE', ondelete='CASCADE'), nullable=False)
album = relationship("Album")
# foreing key
genre_id = Column(Integer, ForeignKey('genres.genre_id', onupdate='CASCADE', ondelete='CASCADE' ))
genre = relationship("Genre")
picture_path = image_attachment('PictureSong')
title = Column(Unicode(100), nullable=False)
download_link = Column(Unicode(100), nullable=False)
artist = Column(Unicode(100), nullable=False)
duration = Column(Unicode(50))
Price = Column(Unicode(50))
created = Column(DateTime, default=datetime.now , nullable=False)
Views
#view_config(route_name="song_delete")
def song_delete(song, request):
"""song delete """
id = request.matchdict['id']
dbsession = DBSession()
song = dbsession.query(Song).filter_by(id = id).first()
if song is None:
request.session.flash("error;Song not found!")
return HTTPFound(location=request.route_url("media"))
try:
transaction.begin()
dbsession.delete(Song);
transaction.commit()
request.session.flash("warning;The song is deleted!")
except IntegrityError:
# delete error
transaction.abort()
request.session.flash("error;The song could not be deleted!")
return HTTPFound(location=request.route_url("media"))
You are trying to delete the class name:
dbsession.delete(Song)
instead of the object:
dbsession.delete(song) (note lowercase s)

SQL alchemy query filter syntax is not working

Here are the models I am working with:
class User(db.Model):
__tablename__ = 'user'
uid = db.Column(db.Integer, primary_key=True, index=True)
firstName = db.Column(db.String(100))
lastName = db.Column(db.String(100))
emailAddress = db.Column(db.String(120), unique=True, index=True)
pwHash = db.Column(db.String(256))
userLevel = db.Column(db.Integer())
userAccountType = db.Column(db.Integer())
isUserActive = db.Column(db.Boolean())
isUserLockedOut = db.Column(db.Boolean())
userLastLogin = db.Column(db.DateTime())
lastInvalidLogin = db.Column(db.DateTime())
userCreatedAt = db.Column(db.DateTime())
userConfirmedAt = db.Column(db.DateTime())
userUpdatedAt = db.Column(db.DateTime(), onupdate=datetime.datetime.now())
userAddress = db.relationship('Address', backref='user', lazy='dynamic')
userContactMethod = db.relationship('UserContactMethod', backref='user', lazy='dynamic')
userSensor = db.relationship('Sensor', backref='user', lazy='dynamic')
userReading = db.relationship('Reading', backref='user', lazy='dynamic')
deliveryEvents = db.relationship('logSMTPDeliveryEvents', backref='user', lazy='dynamic')
class Reading(db.Model):
__tablename__ = 'reading'
rid = db.Column(db.Integer, primary_key=True)
uid = db.Column(db.Integer, db.ForeignKey('user.uid'))
sid = db.Column(db.Integer, db.ForeignKey('sensor.sid'))
readingTimestamp = db.Column(db.DateTime())
readingLightValue = db.Column(db.Integer)
readingLightStatus = db.Column(db.String(6))
readingTemp1 = db.Column(db.Float)
readingTemp2 = db.Column(db.Float)
readingHumidity = db.Column(db.Float)
So my table of readings has the User Id set as the foreign key in the readings table. Now when I try and issue a query like this:
queryResult = db.session.query(Reading).filter(Reading.uid == User.uid)
I get all the rows, which is incorrect. How should I be constructing this query?
Thanks!
C
It's not clear what you're trying to filter out from your question; Are you trying to find the Reading rows that correspond to a particular User row?
Supposing you have the email address of a user, and want to find the Reading's that belong to that user, you would need to build your query in three steps:
First, Start with a query that returns rows out of Reading:
q = session.query(Reading)
Next, extend the query to say that you want follow the user link to attributes of User.
q = q.join(Reading.user)
Finally Filter out only the rows that have the desired User features. Make sure you're filtering on a concrete, actual value.
q = q.filter(User.emailAddress == 'alice#example.com')