How does flask-sqlAlchemy deal with primary keys that are json? - postgresql

I am new to flask and sqlAlchemy. Any help will be great.
I have my models as:
class ReferentialDatum(db.Model):
__tablename__ = 'referential_data'
sid = db.Column(db.Integer, primary_key=True, nullable=False)
venues = db.Column(db.JSON, primary_key=True, nullable=False)
exchange_tickers = db.Column(db.JSON)
trading_date = db.Column(db.Date, primary_key=True, nullable=False, index=True)
total_volume = db.Column(db.BigInteger)
last_trade_price = db.Column(db.Numeric)
last_reference_price = db.Column(db.Numeric)
exchange_close_price = db.Column(db.Numeric)
adjusted_close_price = db.Column(db.Numeric)
close_price = db.Column(db.Numeric)
close_price_source = db.Column(db.Text)
insertion_time_utc = db.Column(db.DateTime(True), server_default=db.FetchedValue())
last_updated_time = db.Column(db.BigInteger)
Note that I am using venues as a json field in my models and it is also a primary key.
In my function I have:
def ListProtobuf(Reference_data):
venues = {"Canada1":1, "Canada2":1}
exchange_tickers = {"AAPL":1, "APL":1}
trading_date = datetime.date.today()
item = ReferentialDatum.query.filter_by(sid=Reference_data.security_id,trading_date=trading_date,venues=venues)
if item.count()>0:
item.update({"total_volume":sum(Reference_data.total_volume),"last_trade_price":Reference_data.last_trade_price,"last_reference_price":Reference_data.reference_price,"last_updated_time":Reference_data.update_time})
db.session.commit()
else:
entry = ReferentialDatum(sid=Reference_data.security_id,exchange_tickers=exchange_tickers,venues=venues,trading_date=trading_date,total_volume=sum(Reference_data.total_volume),last_trade_price=Reference_data.last_trade_price,last_reference_price=Reference_data.reference_price,last_updated_time=Reference_data.update_time)
db.session.add(entry)
db.session.commit()
When I try to insert a new row(which does not exist earlier) I get the following error:
File "mini_conda/envs/env/lib/python2.7/site-packages/sqlalchem /orm/identity.py", line 97,
in __contains__ if key in self._dict:
TypeError: unhashable type: 'dict'
However if I remove primary_key=True from my models on the venues field.I do not get this error.
Any idea on why is this happening. Does ORM use fields that are primary keys as keys of some dict which gives this kind of error.How do I correct this error given that I require primary_key=True.

Related

Conflicts with relationship between tables

I've been constantly getting a warning on the console and I'm going crazy from how much I've been reading but I haven't been able to resolve this:
SAWarning: relationship 'Book.users' will copy column user.uid to column user_book.uid, which conflicts with relationship(s): 'User.books' (copies user.uid to user_book.uid). If this is not intention, consider if these relationships should be linked with back_populates, or if viewonly=True should be applied to one or more if they are read-only. For the less common case that foreign key constraints are partially overlapping, the orm.foreign() annotation can be used to isolate the columns that should be written towards. The 'overlaps' parameter may be used to remove this warning.
The tables the console cites in this notice are as follows:
user_book = db.Table('user_book',
db.Column('uid', db.Integer, db.ForeignKey('user.uid'), primary_key=True),
db.Column('bid', db.Text, db.ForeignKey('book.bid'), primary_key=True),
db.Column('date_added', db.DateTime(timezone=True), server_default=db.func.now())
)
class User(db.Model):
__tablename__ = 'user'
uid = db.Column(db.Integer, primary_key=True)
email = db.Column(db.String(25), nullable=False)
hash = db.Column(db.String(), nullable=False)
first_name = db.Column(db.String(30), nullable=True)
last_name = db.Column(db.String(80), nullable=True)
books = db.relationship('Book', secondary=user_book)
class Book(db.Model):
__tablename__ = 'book'
bid = db.Column(db.Text, primary_key=True)
title = db.Column(db.Text, nullable=False)
authors = db.Column(db.Text, nullable=False)
thumbnail = db.Column(db.Text, nullable=True)
users = db.relationship('User', secondary=user_book)
I use the user_book table to show the user the books he has added.
What am I missing? I take this opportunity to ask, semantically the relationship between tables and foreign keys is being done correctly?
As the warning message suggests, you are missing the back_populates= attributes in your relationships:
class User(db.Model):
# …
books = db.relationship('Book', secondary=user_book, back_populates="users")
# …
class Book(db.Model):
# …
users = db.relationship('User', secondary=user_book, back_populates="books")
# …
I kind of figure this out.
As the code in official tutorial.
from sqlalchemy import Column, ForeignKey, Integer, String, Table
from sqlalchemy.orm import declarative_base, relationship
Base = declarative_base()
class User(Base):
__tablename__ = "user"
id = Column(Integer, primary_key=True)
name = Column(String(64))
kw = relationship("Keyword", secondary=lambda: user_keyword_table)
def __init__(self, name):
self.name = name
class Keyword(Base):
__tablename__ = "keyword"
id = Column(Integer, primary_key=True)
keyword = Column("keyword", String(64))
def __init__(self, keyword):
self.keyword = keyword
user_keyword_table = Table(
"user_keyword",
Base.metadata,
Column("user_id", Integer, ForeignKey("user.id"), primary_key=True),
Column("keyword_id", Integer, ForeignKey("keyword.id"), primary_key=True),
)
Doesn't it make you wander why the relationship only exists in User class rather than both class ?
The thing is, it automatically creates the reverse relationship in Keyword class (a "backref='users' liked parameter is required I supposed ?)

How to insert foreign keys into a table based on current user

I am building a web app in Flask-SQLAlchemy with 3 main types of users (carrier, broker, shipper) who can each access different parts of the site. I am wanting to connect employees and other components to their organizations through foreign keys. My tables look like this:
carrier_org:
id = db.Column(db.Integer, primary_key=True)
org_name = db.Column(db.String(50), nullable=False)
email = db.Column(db.String(120), nullable=False)
contact:
id = db.Column(db.Integer, primary_key=True)
carrier_org = db.Column(db.Integer, db.ForeignKey('carrier_org.id'),
nullable=False)
When the user signs up, it creates both a newUser and newCarrier object.
When I am signed in as a carrier, I have a method addContact that looks like this:
#app.route('/add_contact', methods = ['POST', 'GET'])
#required_roles('carrier')
#login_required
def addContact():
if request.method == 'POST':
f_name = request.form['f_name']
l_name = request.form['l_name']
title = request.form['title']
phone = request.form['phone']
email = request.form['email']
newContact = contact(f_name = f_name, l_name = l_name, phone = phone, email = email, title =
title)
db.session.add(newContact)
db.session.commit()
return redirect(url_for('carrierProfile'))
return render_template('addContact.html')
My question is this: How do I insert a carrier_org FK into contact to be able to track which organization employees belong to? I was thinking something along the lines of
carrier_id = ("SELECT carrier_org.id FROM carrier_org, users WHERE carrier_org.email = users.current_user.id")
Then my newContact will be
newContact = contact(carrier_id = carrier_id, f_name = f_name, l_name = l_name, phone = phone, email = email, title =
title)
But I am not sure how to tie this all together.

How to join-query without sqlalchemy ORM relationships

I have struggled so bad from this. I came from php/mysql in which a query like the following is easily executed:
SELECT Work_center, ... , MRPW, WCC.Total_Cost FROM tracking LEFT JOIN WCC ON WCC.Well_Type_Code = tracking.PDO_Well_Type
But now with python, postgresql and sqlalchemy, it is giving me this error:
sqlalchemy.exc.ProgrammingError: (psycopg2.errors.UndefinedTable) relation "wcc" does not exist
LINE 1: ...uit, MRPW, WCC.Total_Cost FROM tracking LEFT JOIN WCC ON WCC...
It asks me for relationships and relationships ask me for foreign keys but there aren't foreign keys here. I do not want to link the two tables. Although I tried to do it, but it gives me hard time because the two tables' data come from csv file uploads. How can I query with joins without relations and foreign keys?
knowing that I spent hours searching and trying and does not seem to work!
class WCC(Base):
__tablename__ = "WCC"
__table_args__ = {'extend_existing': True}
id = Column(Integer, primary_key=True, index=True)
Well_Type_Code = Column(String, index=True)
Well_Type_Code_year = Column(String, index=True, unique=True)
Year = Column(Integer)
Total_Cost = Column(Float)
#wells = relationship("tracking", back_populates="WCC",foreign_keys=[Well_Type_Code], primaryjoin="tracking.PDO_Well_Type == WCC.Well_Type_Code")
class tracking(Base):
__tablename__ = "tracking"
__table_args__ = {'extend_existing': True}
id = Column(Integer, primary_key=True, index=True)
Work_center = Column(String)
Operation_Short = Column(String)
Oper_Act = Column(String)
Opr_System_status = Column(String)
Earl_start_date = Column(DateTime)
EarliestEndDate = Column(DateTime)
Station_Code = Column(String)
Normal_duration = Column(Float)
Norm_duratn_un= Column(String)
Well_Name= Column(String)
Field = Column(String)
Responsible_asset = Column(String)
Well_ID = Column(Integer, unique=True, index=True)
Well_Location= Column(String)
Well_Function= Column(String)
Well_Category= Column(String)
PCAP_Category= Column(String)
Move_days= Column(Float)
PDO_Well_Type= Column(String)
pick_date = Column(DateTime)
pick_date_spud = Column(DateTime)
PI_date = Column(DateTime)
PI_date_spud = Column(DateTime)
location = Column(Float)
location_date = Column(DateTime)
location_date_spud = Column(DateTime)
HUA_date = Column(DateTime)
HUA_date_spud = Column(DateTime)
WPT_final_date = Column(DateTime)
WPT_final_date_spud = Column(DateTime)
WPT_highlights = Column(String)
expected_comm_date = Column(DateTime)
commissioning_date = Column(DateTime)
EWS_date = Column(DateTime)
cond_conduit= Column(Text)
gas_conduit= Column(Text)
MRPW= Column(Text)
#cost = relationship("WCC", back_populates="tracking",primaryjoin="tracking.PDO_Well_Type == WCC.Well_Type_Code")
In Postgres object names are case sensitive when double quoted, and you must ALWAYS double quote them. It looks as though the table is double quoted when created but not when referenced in the query:
class WCC(Base):
__tablename__ = "WCC"
but
LEFT JOIN WCC ON
Table name "WCC" is not the same as WCC. Try removing the double quotes from where table is created. If that is not possible due to your ORM then double quote on the query itself. But keep in mind you will have to double quote on every reference. Avoid double quotes if at all possible, any perceived benefit is just not worth the trouble.

one to many, Flask-SqlAlchemy, sqlalchemy.exc.ProgrammingError: (psycopg2.ProgrammingError) relation does not exist

I ran into a problem trying to test if I can persist some records into my database. I use postgres.app as database.
Before I try my test, I did:
dropdb testdb
createdb testdb
python db_create.py
My models.py looks like this:
class Municipality(db.Model):
id = db.Column(db.Integer, primary_key=True)
municipality_name = db.Column(db.String(120), unique=True)
population = db.Column(db.Integer)
region_id = db.Column(db.Integer, db.ForeignKey('region.id'))
def __init__(self, municipality_name, population, region_id):
self.municipality_name = municipality_name
self.population = population
self.region_id = region_id
def __repr__(self):
return '{0}'.format(self.municipality_name)
class Region(db.Model):
id = db.Column(db.Integer, primary_key=True)
region_name = db.Column(db.String(120), unique=True)
region_capital = db.Column(db.String(120), unique=True)
population = db.Column(db.Integer)
municipalities = db.relationship('Municipality', backref=db.backref('region'))
def __init__(self, region_name, region_capital, population):
self.region_name = region_name
self.region_capital = region_capital
self.population = population
def __repr__(self):
return '{0}'.format(self.region_name)
I try
todb = Region(region_name='Ahvenanmaa',region_capital='Maarianhamina',population=123)
db.session.add(todb)
db.session.commit()
And already at this step I get an error:
sqlalchemy.exc.ProgrammingError: (psycopg2.ProgrammingError) relation "region" does not exist
LINE 1: INSERT INTO region (region_name, region_capital, population)...
^
[SQL: 'INSERT INTO region (region_name, region_capital, population) VALUES (%(region_name)s, %(region_capital)s, %(population)s) RETURNING region.id'] [parameters: {'population': 123, 'region_capital': 'Maarianhamina', 'region_name': 'Ahvenanmaa'}]
I guess I'm missing some step I had to do after python db_create.py.
Can you suggest something to approach this issue?
Looks like you haven't done (first) migrations. I believe you need to $ ./db_migrate.py

Deleting from many-to-many SQL-Alchemy and Postgresql

I'm trying to delete a child object from a many-to-many relationship in sql-alchemy.
I keep getting the following error:
StaleDataError: DELETE statement on table 'headings_locations' expected to delete 1 row(s); Only 2 were matched.
I have looked at a number of the existing stackexchange questions
(SQLAlchemy DELETE Error caused by having a both lazy-load AND a dynamic version of the same relationship, SQLAlchemy StaleDataError on deleting items inserted via ORM sqlalchemy.orm.exc.StaleDataError, SQLAlchemy Attempting to Twice Delete Many to Many Secondary Relationship, Delete from Many to Many Relationship in MySQL)
regarding this as well as read the documentation and can't figure out why it isn't working.
My code defining the relationships is as follows:
headings_locations = db.Table('headings_locations',
db.Column('id', db.Integer, primary_key=True),
db.Column('location_id', db.Integer(), db.ForeignKey('location.id')),
db.Column('headings_id', db.Integer(), db.ForeignKey('headings.id')))
class Headings(db.Model):
__tablename__ = "headings"
id = db.Column(db.Integer, primary_key=True)
name = db.Column(db.String(80))
version = db.Column(db.Integer, default=1)
special = db.Column(db.Boolean(), default=False)
content = db.relationship('Content', backref=db.backref('heading'), cascade="all, delete-orphan")
created_date = db.Column(db.Date, default=datetime.datetime.utcnow())
modified_date = db.Column(db.Date, default=datetime.datetime.utcnow(), onupdate=datetime.datetime.utcnow())
def __init__(self, name):
self.name = name
class Location(db.Model):
__tablename__ = "location"
id = db.Column(db.Integer, primary_key=True)
name = db.Column(db.String(80), unique=True)
account_id = db.Column(db.Integer, db.ForeignKey('account.id'))
version = db.Column(db.Integer, default=1)
created_date = db.Column(db.Date, default=datetime.datetime.utcnow())
modified_date = db.Column(db.Date, default=datetime.datetime.utcnow())
location_prefix = db.Column(db.Integer)
numbers = db.relationship('Numbers', backref=db.backref('location'), cascade="all, delete-orphan")
headings = db.relationship('Headings', secondary=headings_locations,
backref=db.backref('locations', lazy='dynamic', cascade="all"))
def __init__(self, name):
self.name = name
And my delete code is as follows:
#content_blueprint.route('/delete_content/<int:location_id>/<int:heading_id>')
#login_required
def delete_content(location_id, heading_id):
import pdb
pdb.set_trace()
location = db.session.query(Location).filter_by(id = location_id).first()
heading = db.session.query(Headings).filter_by(id = heading_id).first()
location.headings.remove(heading)
#db.session.delete(heading)
db.session.commit()
flash('Data Updated, thank-you')
return redirect(url_for('content.add_heading', location_id=location_id))
Whichever way i try and remove the child object (db.session.delete(heading) or location.headings.remove(heading) I still get the same error.
Any help is much appreciated.
My database is postgresql.
Edit:
My code which adds the relationship:
new_heading = Headings(form.new_heading.data)
db.session.add(new_heading)
location.headings.append(new_heading)
db.session.commit()
I would assume that the error message is correct: indeed in your database you have 2 rows which link Location and Heading instances. In this case you should find out where and why did this happen in the first place, and prevent this from happening again
First, to confirm this assumption, you could run the following query against your database:
q = session.query(
headings_locations.c.location_id,
headings_locations.c.heading_id,
sa.func.count().label("# connections"),
).group_by(
headings_locations.c.location_id,
headings_locations.c.heading_id,
).having(
sa.func.count() > 1
)
Assuming, the assumption is confirmed, fix it by manually deleting all the duplicates in your database (leaving just one for each).
After that, add a UniqueConstraint to your headings_locations table:
headings_locations = db.Table('headings_locations',
db.Column('id', db.Integer, primary_key=True),
db.Column('location_id', db.Integer(), db.ForeignKey('location.id')),
db.Column('headings_id', db.Integer(), db.ForeignKey('headings.id')),
db.UniqueConstraint('location_id', 'headings_id', name='UC_location_id_headings_id'),
)
Note that you need to need to add it to the database, it is not enough to add it to the sqlalchemy model.
Now the code where the duplicates are inserted by mistake will fail with the unique constraint violation exception, and you can fix the root of the problem.