Nested fields with mashmallow_sqlalchemy and flask_marshmallow - flask-sqlalchemy

I am trying to get a nested serialized output, but all I get is the 'many' side of the many-to-one's table's primary key.
Expected output:
[{'part_numbers':
{'part_number': '23103048', 'description': 'blue product'},
'machines': 2,
'percent_complete': 5.0,
'serial_number': '001',
'timestamp_created': 2020-03-12T15:4:23.135098},
{'part_numbers':
{'part_number': '44444009', 'description': 'red product'},
'machines': 1,
'percent_complete': 60.0,
'serial_number': '002',
'timestamp_created': '2020-03-12T15:44:23.135098'}]
Actual output:
[{'id': 3,
'machines': 2,
'percent_complete': 5.0,
'serial_number': '001',
'timestamp_created': 2020-03-12T15:4:23.135098},
{'id': 1,
'machines': 1,
'percent_complete': 60.0,
'serial_number': '0002',
'timestamp_created': '2020-03-12T15:44:23.135098'}]
These are my files. I have tried adding part_numbers = ma.Nested(PartNumbers) to the Machine schema inside models.py, but it didn't change the result!
my_app
|--- my_app
|--- \__init\__.py
|--- models.py
|--- views.py
models.py
from flask_sqlalchemy import SQLAlchemy
from flask_marshmallow import Marshmallow
from marshmallow_sqlalchemy import SQLAlchemyAutoSchema
db = SQLAlchemy()
ma = Marshmallow()
### Models ###
class Machine(db.Model):
id = db.Column(db.Integer, primary_key=True)
serial_number = db.Column(db.Text, unique=True)
percent_complete = db.Column(db.Float)
part_number_id = db.Column(db.Integer, db.ForeignKey('PartNumbers.id'))
timestamp_created = db.Column(db.DateTime, default=datetime.utcnow)
class PartNumbers(db.Model):
__tablename__ = 'PartNumbers'
id = db.Column(db.Integer, primary_key=True)
part_number = db.Column(db.Text, unique=True)
description = db.Column(db.Text)
machines = db.relationship('Machines', backref='machines', lazy='dynamic')
### Schemas ###
class PartNumbersSchema(SQLAlchemyAutoSchema):
class Meta:
model = PartNumbers
include_fk = True
class MachineSchema(SQLAlchemyAutoSchema):
class Meta:
model = Machines
include_relationships = True
__init__.py
from flask import Flask
from .models import db, ma
from .views import my_app
app = Flask(__name__)
db.init_app(app)
ma.init_app(app)
app.register_blueprint(my_app)
views.py
from flask import Blueprint
from .models import db, Machines, MachineSchema, PartNumbers
my_app = Blueprint("my_app", __name__)
machines_schema = MachineSchema(many=True)
#my_app.route('/')
def home()
machines = db.session.query(Machines).all()
return machines_schema.dump(machines)

Define part_number in the Machine class:
class Machine(db.Model):
id = db.Column(db.Integer, primary_key=True)
serial_number = db.Column(db.Text, unique=True)
percent_complete = db.Column(db.Float)
part_number_id = db.Column(db.Integer, db.ForeignKey('PartNumbers.id'))
timestamp_created = db.Column(db.DateTime, default=datetime.utcnow)
part_numbers = db.relationship('PartNumbers')
You have to define part_numbers in the MachineSchema explicitly:
from marshmallow_sqlalchemy import fields
class MachineSchema(SQLAlchemyAutoSchema):
class Meta:
model = Machines
part_numbers = fields.Nested(PartNumbersSchema)
I assumed part_numbers is a single object (1:1 relationship). Otherwise, you have to add many=True to part_numbers.
More details: https://marshmallow.readthedocs.io/en/stable/nesting.html
[edited to incorporate correction in comment]

Related

ArgumentError: Mapper mapped class Roles_users->User Roles could not assemble any primary key columns for mapped table 'User Roles'

I'm trying to write a web app with user roles and I keep getting this error no matter the changes I make.
Could you please help me find what's wrong?
Thank you in advance!!
This is my models.py file:
from sqlalchemy import Column, Integer, String, Float, Boolean, ForeignKey
from flask_login import UserMixin
from sqlalchemy.orm import relationship, backref
import db
class Rol(db.Base):
__tablename__ = 'Roles'
id_rol = Column(Integer, primary_key=True)
nombre_rol = Column(String, nullable=False)
user_rol = relationship("User rol", back_populates="rol", uselist =False)
def __init__(self, nombre_rol):
self.nombre_rol = nombre_rol
class User(UserMixin, db.Base):
__tablename__ = 'Users'
user_id = Column(Integer, primary_key=True)
username = Column(String, unique=True, nullable=False)
password = Column(String, unique=True, nullable=False)
is_active = Column(Boolean, default=False)
user_rol = relationship("User rol", back_populates="user", uselist =False)
def __init__(self,username,password,is_active):
self.username = username
self.password = password
self.is_active = is_active
def get_id(self):
return self.id
def is_active(self):
return self.is_active
def activate_user(self):
self.is_active = True
def get_username(self):
return self.username
def get_rol(self):
return self.rol
class Roles_users(db.Base):
__tablename__ = 'User Roles'
user_id = Column(Integer, ForeignKey("Users.user_id"))
id_rol = Column(Integer, ForeignKey("Roles.id_rol"))
user = relationship("User", back_populates="user roles")
rol = relationship("Rol", back_populates="user roles")

How do I check if many-to-many row already exists in sqlalchemy to prevent duplicate?

Here is code for my models:
class IllustratorAsset(db.Model):
id = db.Column(db.Integer, primary_key=True)
timestamp = db.Column(db.DateTime, index=True, default=datetime.utcnow)
uploader = db.Column(db.Integer, db.ForeignKey('user.id'))
project = db.Column(db.Integer, db.ForeignKey('project.id'))
fragments = db.relationship('AssetFragment', backref='ai', lazy='dynamic')
file_hash = db.Column(db.String(128), unique=True)
file_name = db.Column(db.String(128))
def __repr__(self):
return '<Picture {}>'.format(self.id)
tags_association = db.Table(
'tags_association',
db.Column('fragment_id', db.Integer(), db.ForeignKey('asset_fragment.id'),index=True),
db.Column('tag_id', db.Integer(), db.ForeignKey('tag.id'),index=True),
PrimaryKeyConstraint('fragment_id', 'tag_id')
)
class AssetFragment(db.Model):
id = db.Column(db.Integer, primary_key=True)
asset = db.Column(db.Integer, db.ForeignKey('illustrator_asset.id'))
timestamp = db.Column(db.DateTime, index=True, default=datetime.utcnow)
uploader = db.Column(db.Integer, db.ForeignKey('user.id'))
tags = db.relationship('Tag', secondary=tags_association, backref='fragments', lazy='dynamic')
file_name = db.Column(db.String(128))
file_hash = db.Column(db.String(128))
class Tag(db.Model):
id = db.Column(db.Integer, primary_key=True)
tag = db.Column(db.String(64), index=True, unique=True)
def __repr__(self):
return '<Tag {}>'.format(self.tag)
The idea is illustrations storage, there is Asset (Adobe Illustrator file), it has Fragments (renders of its different fragments), the fragment has some Tags for quick search which should be working through many-to-many relationship tags_association.
Currently, I'm stuck at trying to check if an association already exists, I'm sure there is some simple way to do it, but I'm missing some point. I came up with the code below, and I still get the exception of constraints.
for tag in new_tags: #list of tags from form
t = Tag.query.filter_by(tag=tag).first()
if (t is not None) and not(t in fragment.tags):
fragment.tags.append(t)
else:
new_tag = Tag(tag=tag)
if not(new_tag in fragment.tags):
fragment.tags.append(new_tag)
DB is Postgres
Well answer came shortly after I've tried to do it in flask shell. Shell output
I've realized, that it seems to be something wrong with the comparator, simple override makes it work:
def __eq__(self, __o: object) -> bool:
return self.tag == __o.tag
Also needed to change checking by a bit:
for tag in new_tags:
t = Tag.query.filter_by(tag=tag).first()
if t is None:
new_tag = Tag(tag=tag)
print(new_tag)
if not(new_tag in fragment.tags):
fragment.tags.append(new_tag)
else:
print(t)
if not(t in fragment.tags):
fragment.tags.append(t)

Flask REST API TypeError

When i try to use on "/register" POST-body-raw {"name" : "mike", "password" : "demo"} in postman i get this bug, pls help to correct:
line 62, in signup_user
hashed_password = generate_password_hash(data['password'], method='sha256') TypeError: 'NoneType' object is not subscriptable
from flask import Flask, request, jsonify, make_response
from flask_sqlalchemy import SQLAlchemy
from werkzeug.security import generate_password_hash, check_password_hash
import uuid
import jwt
import datetime
from functools import wraps
app = Flask(__name__)
app.config['SECRET_KEY'] = 'Th1s1ss3cr3t'
app.config['SQLALCHEMY_DATABASE_URI'] = 'sqlite:///library.db'
app.config['SQLALCHEMY_TRACK_MODIFICATIONS'] = True
db = SQLAlchemy(app)
class Users(db.Model):
id = db.Column(db.Integer, primary_key=True)
public_id = db.Column(db.Integer)
name = db.Column(db.String(50))
password = db.Column(db.String(50))
admin = db.Column(db.Boolean)
class Authors(db.Model):
id = db.Column(db.Integer, primary_key=True)
name = db.Column(db.String(50), unique=True, nullable=False)
book = db.Column(db.String(20), unique=True, nullable=False)
country = db.Column(db.String(50), nullable=False)
booker_prize = db.Column(db.Boolean)
user_id = db.Column(db.Integer)
def token_required(f):
#wraps(f)
def decorator(*args, **kwargs):
token = None
if 'x-access-tokens' in request.headers:
token = request.headers['x-access-tokens']
if not token:
return jsonify({'message': 'a valid token is missing'})
try:
data = jwt.decode(token, app.config[SECRET_KEY])
current_user = Users.query.filter_by(public_id=data['public_id']).first()
except:
return jsonify({'message': 'token is invalid'})
return f(current_user, *args, **kwargs)
return decorator
#app.route('/register', methods=['GET', 'POST'])
def signup_user():
data = request.get_json()
hashed_password = generate_password_hash(data['password'], method='sha256')
new_user = Users(public_id=str(uuid.uuid4()), name=data['name'], password=hashed_password, admin=False)
db.session.add(new_user)
db.session.commit()
return jsonify({'message': 'registered successfully'})
Most likely your request is missing content-type header.
Content-Type:application/json
In Postman you can set it in Headers tab. Another option is to select "raw" and "JSON (application/json)" in Body tab just above the text area.

import and mapping csv to sqlalchemy dynamically

I am creating database using sqlalchemy in flask app and filling the database with existing CSV with selected columns from it so I use pandas here is my classes creation
I need to add company objects and commit them in dynamic way , but that way does not work , the csv file is not small about 20,000 record I can not add them manually ,so any suggestions to add them in dynamic way?
from sqlalchemy.ext.declarative import declarative_base
from sqlalchemy.orm import relationship
from sqlalchemy import create_engine
from flask import jsonify
Base = declarative_base()
class Company(Base):
__tablename__ = 'forbesglobal2000_2016'
id = Column(Integer, primary_key=True)
name = Column(String(250), nullable=False)
profits = Column(String(250), nullable=False)
marketValue = Column(String(250), nullable=False)
revenue = Column(String(250), nullable=False)
industry = Column(String(250), nullable=False)
class SIC(Base):
__tablename__ = "SIC"
id = Column(Integer, primary_key=True)
SIC = Column(Integer, nullable=False)
Industry_name = Column(String(250),ForeignKey('forbesglobal2000_2016.industry'))
Indusrty = relationship(Company)
# configuration part
engine = create_engine('sqlite:///CompainesData.db')
Base.metadata.create_all(engine)
import sqlalchemy
from sqlalchemy.orm import sessionmaker
from database_setup import *
import pandas as pd
# opening connection with database
engine = create_engine('sqlite:///CompainesData.db')
Base.metadata.bind = engine
# Clear database
Base.metadata.drop_all(engine)
Base.metadata.create_all(engine)
DBSession = sessionmaker(bind=engine)
session = DBSession()
df = pd.read_csv("forbesglobal2000-2016.csv")
df1 = pd.read_csv("SIC.csv")
# market valuation, revenue, profits and industry
profit_column = df.profits
name_column = df.name
industry_column = df.industry
revenue_column = df.revenue
marketvalue_column = df.marketValue
industry_column_f = df1.Description
SIC_column = df1.SICCode
company = []
i = 1
while i < name_column.__len__():
company[i] = Company(name = name_column[i] , industry=industry_column[i], marketValue = marketvalue_column[i] , profits = profit_column[i] ,
revenue = revenue_column[i] )
i = i +1
for i in company:
session.add(i)
session.commit()
# printing test
com = session.query(Company).all()
for f in com:
print(f.name)
print(f.industry)
print(f.profits)
print(f.revenue)
print(f.marketValue)
If you want to load data from csv files to database just use df.to_sql() function it allows you to do that. For example :
df.to_sql(con=engine, name=airlines.__tablename__, if_exists='replace',index=False)
Pay attention to index=False, it's used to ignore pandas id column.
I think the index will start at 0 and not 1:
i = 1
should be
i = 0
can you try that?

Sqlalchemy: Propagation of updates across multiple (linked) relationships

I show here an (artificial) example of three linked tables: ParentA, ChildA, and ChildAA. ChildA is related to the primary key (PK) of ParentA via foreign key, and ChildAA relates to the same key in ChildA. In this way ChildAA links to the primary key of the ParentA. I would expect that when I make a change to the ParentA PK this change propagates back to the corresponding ChildAA's attribute, but it doesn't.
Thanks in advance!
(I apologize if this has been answered or documented before, I really couldn't find anything.)
The Code:
from sqlalchemy import *
from sqlalchemy import orm
from sqlalchemy.ext.declarative import declarative_base
Base = declarative_base()
class ParentA(Base):
__tablename__ = 'tbl_parentA'
pid = Column(Integer, primary_key=True)
childA = orm.relationship("ChildA", passive_updates=False, backref='parentA')
class ChildA(Base):
__tablename__ = 'tbl_childA'
attrib1 = Column(String, nullable=True)
parentA_id = Column(Integer, ForeignKey(ParentA.pid), primary_key=True)
childAA = orm.relationship("ChildAA", passive_updates=False, backref="childA")
# This class is related to Parents through ChildA
class ChildAA(Base):
__tablename__ = 'tbl_childAA'
cid = Column(Integer, primary_key=True)
attrib1 = Column(String, nullable=True)
parentA_id = Column(Integer, ForeignKey(ChildA.parentA_id))
def clear_db(db):
tmp = db.echo
db.echo = False
metadata = MetaData(bind=db)
metadata.reflect(db)
for table in reversed(metadata.sorted_tables):
table.drop(db)
metadata.clear()
db.echo = tmp
if __name__ == '__main__':
# SQLite Connection
db = create_engine('sqlite:///linked_updates.db')
# db.echo = True
# Initalize Objects
pa1 = ParentA()
ca1 = ChildA(attrib1='ca1 str')
caa1= ChildAA(attrib1='caa1 str')
# Assign a parent to ChildA
ca1.parentA = pa1
# Assign a parent to ChildAA
caa1.childA = ca1
# Initialize clean DB & session
clear_db(db)
Base.metadata.create_all(db)
session = orm.create_session(db)
# Write to DB
session.add_all([pa1, ca1, caa1])
session.flush()
print 'After flush, we have: ', caa1.parentA_id, '==', caa1.childA.parentA_id
# Induce change, check propagation
pa1.pid = 2
session.flush()
print 'I expect: ', caa1.parentA_id, '==', caa1.childA.parentA_id
print 'END'