Support duplicate entries in a M2M associative table - python

I am working on a SQL-Alchemy app using flask and flask-db and have been scratching my head over how to solve this question. My models looks like this:
class event_schematics_map():
event_schematics_table = db.Table(
'event_schematics_table',
db.Column('fk_schematic_id', db.Integer, db.ForeignKey('schematics.id')),
db.Column('fk_event_id', db.Integer, db.ForeignKey('events.id'))
)
class Events(db.Model):
id = db.Column(db.Integer, primary_key=True)
title = db.Column(db.String(120), index=True, unique=False)
date = db.Column(db.String(120), unique=False)
owner = db.Column(db.Integer, db.ForeignKey('user.id'))
schematics = db.relationship('Recipe', secondary=event_schematics_map.event_schematics_table, backref='schematic')
class Schematics(db.Model):
__tablename__ = 'schematics'
id = db.Column(db.Integer, primary_key=True, autoincrement=True)
name = db.Column(db.VARCHAR(70), index=True)
schematics_description = db.Column(db.String(1024), index=True)
creator_id = db.Column(db.Integer, db.ForeignKey('user.id'))
Schematics are created and are on the many side of O2M with a user table separately not shown above. The map table is used as the glue in the M2M relationship.
Currently I am adding new schematics to each event and updating the assoc table like so: events.schematics.append(SomeNewSchematic) which works fine until I attempt to enter multiple instances of the exact same Schematic like this:
schem1 = Schematics(name='TheOnlySchematic')
schem2 = Schematics(name='TheOnlySchematic')
event.schematics.append(schem1)
event.schematics.append(schem2)
etc
in which case I can only apply one as I think the entry is being duplicated. I believe this may be solved by an additional field in the assoc table event_schematics_map, but unsure if I am overlooking something simpler or how to implement this.
Effectively I want to support multiple entries of the exact same model
I believe my problem is along the same lines as can I append twice the same object to an instrumentedlist in sqlalchemy - but I could not see a solution for this.
Really appreciate any pointers or to know how to solve this problem.

Thank you for your reply and setting me straight here,
You are quite right, duplicate entries does not make sense.
I ended up solving this by using an associative table as discussed in other answers to track the occurrence of each schematic.

Related

How to disable SQLAlchemy´s lazy loading?

I´m developing a python app which uses Flask-SQLAlchemy and it´s ORM Mapper.
I´ve got two tables with a Many To One relation. The main problem is that I want to load the content of both Objects with one join query and not later, when I access the object of the child table.
I already tried to get these behavior by using the joinedload option like this:
Event.query.filter(Event.timestamp == day)
.join(Event.user)
.options(joinedload(Event.user))
.all()
Also tried to set the lazy="joined" attribute in the entity-class for the relationship, which caused no difference.
The SQL Query looks right and the join is correctly generated, but if I access the user attribute later, another join query is sent. With other strategies it´s also not working, like the contains_eager option.
So the expected behavior would be, to save all information on the first load and don´t execute a query later on.
All SQLAlchemy options are default, except the DATABASE_URI. Is there any global option to disable this behavior or to override the default?
The Entities are the following:
class Event(db.Model):
__tablename__ = "event"
__table_args__ = (
db.ForeignKeyConstraint(
["username", "userfirstname"], ["users.name", "users.firstname"]
),
)
timestamp = db.Column(db.Date, primary_key=True, index=True)
username= db.Column(db.String, primary_key=True)
userfirstname= db.Column(db.String, primary_key=True)
...
user = db.relationship("UserEntity")
class UserEntity(db.Model):
__tablename__ = "users"
name= db.Column(db.String, primary_key=True)
firstname= db.Column(db.String, primary_key=True)
...
try this
class Event(db.Model):
__tablename__ = "event"
__table_args__ = (
db.ForeignKeyConstraint(
["username", "userfirstname"], ["users.name", "users.firstname"]
),
)
timestamp = db.Column(db.Date, primary_key=True, index=True)
username= db.Column(db.String, primary_key=True)
userfirstname= db.Column(db.String, primary_key=True)
user = db.relationship("UserEntity", back_populates="events")
user_id = Column(Integer, ForeignKey('user.id'))
class UserEntity(db.Model):
__tablename__ = "users"
name= db.Column(db.String, primary_key=True)
firstname= db.Column(db.String, primary_key=True)
events = relationship(MyOtherClass, lazy='joined')
from sqlalchemy docs:
joined applies a JOIN to the given SELECT statement so that related rows are loaded in the same result set. Joined eager loading is detailed at Joined Eager Loading.
I´ve found the mistake. The basic idea of disabling lazyloading was right and worked fine. I just messed up the object refs on the validation step and destroyed the SQLAlchemy background logic.

How to design a referral code system in python flask sqlalchemy and postgresql

I'm a bit of a database noob. I'm using flask sqlalchemy and postgresql to design a user system on a website, where each user has a unique, not null referral code. Other users can each use a single referral code, and when they do, both the owner and the user of the code receive some benefits. I'm wondering what the best way to design this is. A many to many relationship?
What I'm currently doing on the user table (other columns like email/password omitted) is:
class User(db.Model):
__tablename__ = 'user'
id = db.Column(db.Integer, primary_key=True)
referral_code = db.relationship('Refcodes', backref='owner', uselist=False)
referrals = db.relationship('Referrals', backref='user', uselist=False)
And on the 'refcodes' and 'referrals' table:
class Refcodes(db.Model):
__tablename__ = 'refcodes'
id = db.Column(db.Integer, primary_key=True)
code = db.Column(db.String(10), nullable=False, unique=True)
owner_id = db.Column(db.ForeignKey('user.id'))
referrals = db.Relationship('Referrals', backref='code')
class Referrals(db.Model):
__tablename__ = 'referrals'
id = db.Column(db.Integer, primary_key=True)
code_id = db.Column(db.ForeignKey('refcodes.id'))
user_id = db.Column(db.ForeignKey('users.id'))
I haven't tested it yet since I'm still designing the login/registration page, but my question is if there is some sort of best practice/exact method for similar problems. Apologies for the bad English and the possibly vague/confusing initial questions and thanks in advance for any replies.

SQLAlchemy Join to retrieve data from multiple tables

I'm trying to retrieve data from multiple tables with SQLAlchemy using the .join() method.
When I run the query I was expecting to get a single object back which had all the data from the different tables joined so that I could use a.area_name and so on where area_name is on one of the joined tables. Below is the query I am running and the table layout, if anyone could offer insight into how to achieve the behavior I'm aiming for I would greatly appreciate it! I've been able to use the .join() method with this same syntax to match results and return them, I figured it would return the extra data from the rows as well since it joins the tables (perhaps I'm misunderstanding how the method works or how to retrieve the information via the query object?).
If it helps with the troubleshooting I'm using MySQL as the database
query:
a = User.query.filter(User.user_id==1).join(UserGroup,
User.usergroup==UserGroup.group_id).join(Areas, User.area==Areas.area_id).first()
and the tables:
class User(db.Model):
user_id = db.Column(db.Integer, primary_key=True)
name = db.Column(db.String(20), unique=True)
usergroup = db.Column(db.Integer, db.ForeignKey('user_group.group_id'), nullable=False)
area = db.Column(db.Integer, db.ForeignKey('areas.area_id'), nullable=False)
class UserGroups(db.Model):
id = db.Column(db.Integer, primary_key=True)
group_id = db.Column(db.Integer, nullable=False, unique=True)
group_name = db.Column(db.String(64), nullable=False, unique=True)
class Areas(db.Model):
id = db.Column(db.Integer, primary_key=True)
area_id = db.Column(db.Integer, nullable=False, unique=True)
area_name = db.Column(db.String(64), nullable=False, unique=True)
So it seems that I need to use a different approach to the query, and that it returns a tuple of objects which I then need to parse.
What worked is:
a = db.session.query(User, UserGroups, Areas
).filter(User.user_id==1
).join(UserGroup,User.usergroup==UserGroup.group_id
).join(Areas, User.area==Areas.area_id
).first()
The rest remaining the same. This then returned a tuple that I could parse where the data from User is a[0], from UserGroups is a[1], and Areas is a[2]. I can then access the group_name column with a[1].group_name etc.
Hopefully this helps someone else who's trying to work with this!
Take a look at SQLAlchemy's relationship function:
http://docs.sqlalchemy.org/en/latest/orm/basic_relationships.html#one-to-many
You may want to add a new attribute to your User class like so:
group = sqlalchemy.relationship('UserGroups', back_populates='users')
This will automagically resolve the one-to-many relationship between User and UserGroups (assuming that a User can only be member of one UserGroup at a time). You can then simply access the attributes of the UserGroup once you have queried a User (or set of Users) from your database:
a = User.query.filter(...).first()
print(a.group.group_name)
SQLAlchemy resolves the joins for you, you do not need to explicitly join the foreign tables when querying.
The reverse access is also possible; if you just query for a UserGroup, you can access the corresponding members directly (via the back_populates-keyword argument):
g = UserGroup.query.filter(...).first()
for u in g.users:
print(u.name)

Define Many to Many without a foreignKey in SQLAlchemy

I have existing data that I want to model. Its essentially:
class Investor(db.Model):
id = db.Column(id, primary_key=True)
investments = db.relationship('Investments', backref='investor')
class Round(db.Model):
id = db.Column('id', primary_key=True)
investments = db.Table(
'investments',
db.Column('investor_id', db.ForeignKey('investor.id')),
db.Column('round_id', db.ForeignKey('round.id')),
)
Now, every time I try execute this little model, I get the following error:
expression 'Investments' failed to locate a name
I understand, that investments, needs to be a class, but I've tried making a dummy class with db.model, and it hasn't really worked. In that version I get problems with asking for a primary join or a mapper. I'm quite confused, and a little guidance would help greatly.
If the many to many relationship is between Investor and Round, You can define the model as follows:
class Investor(db.Model):
id = db.Column(db.Integer, primary_key=True)
rounds = db.relationship('Round', secondary=investments, backref='investor')
class Round(db.Model):
id = db.Column(db.Integer, primary_key=True)
investments = db.Table(
'investments',
db.Column('investor_id', db.Integer, db.ForeignKey('investor.id')),
db.Column('round_id', db.Integer, db.ForeignKey('round.id')))

Flask Restless: increment column on many to many table

I am currently working with Flask, Flask-SQLAchlemy and Flask-Restless to provide endpoints for an AngularJS backend application. I have set up the necessary many to many relationships between tables thanks to another user.
I have a new issue, which is that I have to set the count column in my junction table. In addition, I also have to increment count by 1. I have been looking through the Flask Restless Docs to find a solution, but I haven't seen any reference to accessing a junction table column. I have also tried accessing it through include columns without success. Here are my involved tables:
surveyOptions = db.Table('survey_options',
db.Column('survey_id', db.Integer, db.ForeignKey('survey.id')),
db.Column('option_id', db.Integer, db.ForeignKey('option.id')),
db.Column('count', db.Integer) # increment this value
)
class Survey(db.Model):
id = db.Column(db.Integer, primary_key=True)
category = db.Column(db.String(50))
question = db.Column(db.Text)
startDate = db.Column(db.DateTime)
endDate = db.Column(db.DateTime)
options = db.relationship('Option', secondary=surveyOptions,
backref=db.backref('surveys', lazy='dynamic'), lazy='dynamic')
class Option(db.Model):
id = db.Column(db.Integer, primary_key=True)
text = db.Column(db.Text)
Any guidance on how to increment the surveyOptions.count column would be greatly appreciated. I am more than willing to provide more code samples if you need any.
Edit
count will be incremented everytime that a user selects one of the options, to keep track of how many people prefer each option

Categories