understanding marshmallow nested schema with list data - python

Am new to python and am usign marshmallow serialization. unable to use the nested scehma.
, my code
from sqlalchemy import Column, Float, Integer, String, Text, text,ForeignKey
from sqlalchemy.ext.declarative import declarative_base
from sqlalchemy.orm import relationship
Base = declarative_base()
metadata = Base.metadata
class CompanyDemo(Base):
__tablename__ = 'company_demo'
company_id = Column(Integer, primary_key=True,
server_default=text("nextval('company_demo_company_id_seq'::regclass)"))
name = Column(Text, nullable=False)
address = Column(String(50))
location = Column(String(50))
class UsersDemo(Base):
__tablename__ = 'users_demo'
id = Column(Integer, primary_key=True,
server_default=text("nextval('users_demo_id_seq'::regclass)"))
company_id = Column(Integer,ForeignKey('company_demo.company_id'), nullable=False)
email = Column(String)
company = relationship('CompanyDemo')
schema
from marshmallow import Schema, fields, pprint
class CompanySchema(Schema):
company_id = fields.Int(dump_only=True)
name = fields.Str()
address = fields.Str()
location = fields.Str()
class UserSchema(Schema):
email = fields.Str()
company = fields.Nested(CompanySchema)
user = UserSchema()
user = UserSchema(many=True)
company = CompanySchema()
company = CompanySchema(many=True)
and my flask app
from flask import Flask, jsonify, url_for, render_template
from sqlalchemy import create_engine
from sqlalchemy.orm import sessionmaker
from flask_sqlalchemy import SQLAlchemy
from model import CompanyDemo, UsersDemo
from schemas.userschema import user, company
app = Flask(__name__)
app.secret_key = "shiva"
def db_connect():
engine = create_engine('postgresql://ss#127.0.0.1:5432/test')
Session = sessionmaker(autocommit=False, autoflush=False, bind=engine)
# create a Session
session = Session()
session._model_changes = {}
return session
#app.route('/company', methods=["GET", "POST"])
def get_all_company():
db = db_connect()
allcompany = db.query(CompanyDemo).join(UsersDemo).all()
return jsonify(company.dump(allcompany, many=True).data) # company is marshmallow schema
if __name__ == '__main__':
app.run(host='0.0.0.0', port=15418, debug=True)
anything wrong in my code? and am facing problem with nested schema and unable to get the nested data in output.
the output below
[ {
"address": "qqq ",
"company_id": 1,
"location": "www ",
"name": "eee" }, {
"address": "www ",
"company_id": 2,
"location": "qqq ",
"name": "aaa" } ]

Self contained example using in-memory SQLite:
from flask import Flask, jsonify
from flask_sqlalchemy import SQLAlchemy
from marshmallow import Schema, fields, pprint
app = Flask(__name__)
app.config['DEBUG'] = True
app.config['SECRET_KEY'] = 'super-secret'
app.config['SQLALCHEMY_TRACK_MODIFICATIONS'] = False
app.config['SQLALCHEMY_DATABASE_URI'] = 'sqlite:///:memory:'
app.config['SQLALCHEMY_ECHO'] = True
db = SQLAlchemy(app)
class CompanyDemo(db.Model):
__tablename__ = 'company_demo'
company_id = db.Column(db.Integer, primary_key=True)
name = db.Column(db.Text, nullable=False)
address = db.Column(db.String(50))
location = db.Column(db.String(50))
def __unicode__(self):
return u"{name} ({address})".format(name=self.name, address=self.address)
class UsersDemo(db.Model):
__tablename__ = 'users_demo'
id = db.Column(db.Integer, primary_key=True,)
company_id = db.Column(db.Integer, db.ForeignKey('company_demo.company_id'), nullable=False)
company = db.relationship('CompanyDemo')
email = db.Column(db.String)
def __unicode__(self):
return u"{email}".format(email=self.email)
class CompanySchema(Schema):
company_id = fields.Int(dump_only=True)
name = fields.Str()
address = fields.Str()
location = fields.Str()
class UserSchema(Schema):
email = fields.Str()
company = fields.Nested(CompanySchema)
user_schema = UserSchema()
company_schema = CompanySchema()
#app.route('/')
def index():
return "<a href='/dump_company'>Dump Company</a><br><a href='/dump_user'>Dump User</a>"
#app.route('/dump_user')
def dump_user():
user = UsersDemo.query.first()
return jsonify(user_schema.dump(user).data)
#app.route('/dump_company')
def dump_company():
company = CompanyDemo.query.first()
return jsonify(company_schema.dump(company).data)
def build_db():
db.drop_all()
db.create_all()
company = CompanyDemo(name='Test 1', address='10 Downing Street', location='wherever')
db.session.add(company)
user = UsersDemo(email='fred#example.com', company=company)
db.session.add(user)
db.session.commit()
#app.before_first_request
def first_request():
build_db()
if __name__ == '__main__':
app.run(debug=True, port=7777)

Related

Empty Json using Flask-Marshmallow and SQLAlchemy

I am trying to create a endpoint to return data from two tables indicator and metadata so I created the model below with indicator = db.relationship('Indicator', backref='metadatas') and indicator_id = db.Column(db.Integer, db.ForeignKey('indicator.id')) but when I call the api/indicators I get empty json as you can see at the end, why?
models.py
from datetime import datetime
from app import db, ma
class Indicator(db.Model):
id = db.Column(db.Integer, primary_key=True)
unique_key = db.Column(db.String(32))
name = db.Column(db.String(255))
short_name = db.Column(db.String(255))
valid = db.Column(db.Boolean)
created_at = db.Column(db.DateTime())
last_updated_at = db.Column(db.DateTime())
class Metadata(db.Model):
id = db.Column(db.Integer, primary_key=True)
indicator_id = db.Column(db.Integer, db.ForeignKey('indicator.id'))
indicator = db.relationship('Indicator', backref='metadatas')
type = db.Column(db.String(255))
inputs = db.Column(db.String(255))
options = db.Column(db.String(255))
outputs = db.Column(db.String(255))
class IndicatorSchema(ma.SQLAlchemySchema):
class Meta:
model = Indicator
id: ma.auto_field()
unique_key: ma.auto_field()
name: ma.auto_field()
short_name: ma.auto_field()
valid: ma.auto_field()
class MetadataSchema(ma.SQLAlchemyAutoSchema):
class Meta:
model = Metadata
include_fk = True
Below you can see the other files:
app.py
from flask_jwt_extended import JWTManager
from flask import Flask, jsonify, request
from flask_sqlalchemy import SQLAlchemy
from flask_marshmallow import Marshmallow
app = Flask(__name__)
app.config["JWT_SECRET_KEY"] = "fe7e8955db51c0ff78550419434128cb"
app.config["JWT_ACCESS_TOKEN_EXPIRES "] = 28800
app.config['SQLALCHEMY_DATABASE_URI'] = "sqlite:////tmp/test.db"
app.config['SQLALCHEMY_TRACK_MODIFICATIONS'] = True
app.config['JSON_SORT_KEYS'] = False
db = SQLAlchemy(app)
ma = Marshmallow(app)
controller.py
from flask_jwt_extended import jwt_required, create_access_token, get_jwt_identity
from flask import Flask, request, jsonify
from models import Indicator, Metadata, IndicatorSchema, MetadataSchema
from config import conn_string
from app import app, db
import services
import hashlib
import json
#app.route('/api/indicators', methods=['GET'])
#jwt_required()
def indicators():
short_name = request.args.get('short_name',None)
if short_name is not None:
indicator = Indicator.query.filter_by(short_name = short_name).first()
indicator_schema = IndicatorSchema()
output = indicator_schema.dump(indicator)
else:
indicators = Indicator.query.all()
indicators_schema = IndicatorSchema(many=True)
output = indicators_schema.dump(indicators)
return jsonify(output), 200
When I call the /api/indicators I get response 200 but empty JSON:
$ http :5000/api/indicators
HTTP/1.0
Content-Type: application/json
Server: Werkzeug/2.0.0 Python/3.8.5
[
{},
{},
{},
{},
{},
{}
]
I think there are two issues:
You should be defining the relationship on the Indicator model (one indicator with many metadatas)
I believe you're looking for the meta attribute include_relationships, since you're looking to return more than just the foreign key ids
Your code with the adjustments:
from datetime import datetime
from app import db, ma
class Indicator(db.Model):
__tablename__ = "indicator"
id = db.Column(db.Integer, primary_key=True)
unique_key = db.Column(db.String(32))
name = db.Column(db.String(255))
short_name = db.Column(db.String(255))
valid = db.Column(db.Boolean)
created_at = db.Column(db.DateTime())
last_updated_at = db.Column(db.DateTime())
metadatas = db.relationship('Metadata', backref='indicator')
class Metadata(db.Model):
__tablename__ = "metadata"
id = db.Column(db.Integer, primary_key=True)
indicator_id = db.Column(db.Integer, db.ForeignKey('indicator.id'))
type = db.Column(db.String(255))
inputs = db.Column(db.String(255))
options = db.Column(db.String(255))
outputs = db.Column(db.String(255))
class IndicatorSchema(ma.SQLAlchemyAutoSchema):
class Meta:
model = Indicator
include_relationships = True
id: ma.auto_field()
unique_key: ma.auto_field()
name: ma.auto_field()
short_name: ma.auto_field()
valid: ma.auto_field()
class MetadataSchema(ma.SQLAlchemyAutoSchema):
class Meta:
model = Metadata

How to get data from two tables using SQLALCHEMY

Let say I have a model
import os
from flask import Flask
from flask_sqlalchemy import SQLAlchemy
db = SQLAlchemy()
class Students(db.Model):
__tablename__ = "students"
id = db.Column(db.Integer, primary_key=True)
name = db.Column(db.String, nullable=False)
description = db.Column(db.String, nullable=False)
class Hobbies(db.Model):
__tablename__ = "hobbies"
id = db.Column(db.Integer, primary_key=True)
title = db.Column(db.String, nullable=False)
description = db.Column(db.String)
student_id = db.Column(db.Integer, db.ForeignKey("students.id"), nullable=False)
Now in flask I have following code
import os
from flask import Flask, render_template, request
from models import *
app = Flask(__name__)
app.config["SQLALCHEMY_DATABASE_URI"] = "postgresql://junaid:junaid#localhost:5432/UOB"
app.config["SQLALCHEMY_TRACK_MODIFICATIONS"] = False
db.init_app(app)
Now I get all the record in the table Hobbies
from sqlalchemy.sql import select
with app.app_context():
a = Hobbies.query.all()
I want to get the result like
Hobby.ID, Hobby.title, Hobby.description, Student.name (The filter is Hobby.student_id = Students.id)
I want to print all the record in the hobbies table with student name in short.
Here is the solution. Sharing if someone else can benefit from this.
hobbies_with_stud = db.session.query(Students, Hobbies).join(Hobbies, Hobbies.student_id == Students.id).all()
for record in hobbies_with_stud:
print(record.Hobbies.title + " is the hobby of "+ record.Students.name)

How to get user registered signal in Flask-User

With Flask-User, I would like to get user registered signal like this.
However the error "NameError: name 'app' is not defined" has occured.
I do not know where I have to place "#user_registered" hook.
How can do that?
mdsys__init__.py
import os
from flask import (Flask, render_template, url_for)
from mdsys.db import init_db, db
from flask_mail import Mail
from flask_user import UserManager, SQLAlchemyAdapter
from mdsys.model.auth import User, Role
def create_app(test_config=None):
# create and configure the app
app = Flask(__name__, instance_relative_config=True)
app.config.from_mapping(
SECRET_KEY='dev',
SQLALCHEMY_DATABASE_URI='postgresql://{user}:{password}#{host}/{dbname}'.format(**{
'user': 'hoge',
'password': 'pass',
'host': 'localhost',
'dbname': 'hogehoge',
}),
SQLALCHEMY_TRACK_MODIFICATIONS=False,
# Flask-Mail settings
MAIL_USERNAME = 'hoge#example.com',
MAIL_PASSWORD = 'pass',
MAIL_DEFAULT_SENDER = '"MdSys" <no-reply#example.com>',
MAIL_SERVER = 'smtp.gmail.com',
MAIL_PORT = 465,
MAIL_USE_SSL = True,
# Flask-User settings
USER_ENABLE_USERNAME = False, # Register and Login with username
USER_ENABLE_CHANGE_USERNAME=False,
USER_APP_NAME = "MdSys", # Used by email templates
USER_CHANGE_PASSWORD_TEMPLATE = 'auth/change_password.html',
USER_CHANGE_USERNAME_TEMPLATE = 'auth/change_username.html',
USER_FORGOT_PASSWORD_TEMPLATE = 'auth/forgot_password.html',
USER_INVITE_TEMPLATE = 'auth/invite.html',
USER_INVITE_ACCEPT_TEMPLATE = 'auth/register.html',
USER_LOGIN_TEMPLATE = 'auth/login.html',
USER_MANAGE_EMAILS_TEMPLATE = 'auth/manage_emails.html',
USER_REGISTER_TEMPLATE = 'auth/register.html',
USER_RESEND_CONFIRM_EMAIL_TEMPLATE = 'auth/resend_confirm_email.html',
USER_RESET_PASSWORD_TEMPLATE = 'auth/reset_password.html',
USER_CHANGE_PASSWORD_URL = '/auth/change-password',
USER_CHANGE_USERNAME_URL = '/auth/change-username',
USER_CONFIRM_EMAIL_URL = '/auth/confirm-email/<token>',
USER_EMAIL_ACTION_URL = '/auth/email/<id>/<action>',
USER_FORGOT_PASSWORD_URL = '/auth/forgot-password',
USER_INVITE_URL = '/auth/invite',
USER_LOGIN_URL = '/auth/login',
USER_LOGOUT_URL = '/auth/logout',
USER_MANAGE_EMAILS_URL = '/auth/manage-emails',
USER_REGISTER_URL = '/auth/register',
USER_RESEND_CONFIRM_EMAIL_URL = '/auth/resend-confirm-email',
USER_RESET_PASSWORD_URL = '/auth/reset-password/<token>',
USER_AFTER_LOGOUT_ENDPOINT = 'auth.login',
USER_UNCONFIRMED_EMAIL_ENDPOINT = 'auth.login',
USER_UNAUTHENTICATED_ENDPOINT = 'auth.login',
USER_ENABLE_RETYPE_PASSWORD = False,
)
with app.app_context():
init_db(app)
# Setup Flask-User
db_adapter = SQLAlchemyAdapter(db, User) # Register the User model
user_manager = UserManager(db_adapter, app) # Init Flask-User and bind to app
if not Role.query.filter(Role.name=='admin').first():
role1 = Role(name='admin')
db.session.add(role1)
db.session.commit()
mail = Mail(app)
if test_config is None:
# load the instance config, if it exists, when not testing
app.config.from_pyfile('config.py', silent=True)
else:
# load the test config if passed in
app.config.from_mapping(test_config)
# ensure the instance folder exists
try:
os.makedirs(app.instance_path)
except OSError:
pass
from . import auth
app.register_blueprint(auth.bp)
return app
mdsys\db.py
from flask_sqlalchemy import SQLAlchemy
from flask_migrate import Migrate
db = SQLAlchemy() # Initialize Flask-SQLAlchemy
def init_db(app):
db.init_app(app)
Migrate(app, db)
mdsys\model\auth.py
from flask_user import UserMixin, user_registered
from datetime import datetime
from mdsys.db import db
class User(db.Model, UserMixin):
__tablename__ = 'users'
__table_args__ = {'extend_existing': True}
id = db.Column(db.Integer, primary_key=True)
email = db.Column(db.String(255), nullable=False, unique=True)
password = db.Column(db.String(255), nullable=False, default='')
confirmed_at = db.Column(db.DateTime())
expire_at = db.Column(db.DateTime())
is_enabled = db.Column(db.Boolean(), nullable=False, default=False)
created_at = db.Column(db.DateTime, nullable=False, default=datetime.now)
updated_at = db.Column(db.DateTime, nullable=False, default=datetime.now, onupdate=datetime.now)
# Relationships
roles = db.relationship('Role', secondary='user_roles',
backref=db.backref('users', lazy='dynamic'))
def is_active(self):
return self.is_enabled
class Role(db.Model):
__tablename__ = 'roles'
__table_args__ = {'extend_existing': True}
id = db.Column(db.Integer(), primary_key=True)
name = db.Column(db.String(50), unique=True)
class UserRoles(db.Model):
__table_args__ = {'extend_existing': True}
id = db.Column(db.Integer(), primary_key=True)
user_id = db.Column(db.Integer(), db.ForeignKey('users.id', ondelete='CASCADE'))
role_id = db.Column(db.Integer(), db.ForeignKey('roles.id', ondelete='CASCADE'))
mdsys\auth.py
import functools
from flask import (
Blueprint, flash, g, redirect, render_template, request, session, url_for
)
from werkzeug.security import check_password_hash, generate_password_hash
from mdsys.db import db
from mdsys.model.auth import User, Role
from flask_user import user_registered
bp = Blueprint('auth', __name__, url_prefix='/auth')
#bp.route('/register', methods=('GET', 'POST'))
def register():
return render_template('auth/register.html')
#user_registered.connect_via(app)
def _after_user_registered_hook(sender, user, **extra):
user.roles.append(Role(name='free'))
I do not know how to separate the code to multiple files like models/hogehoge.py, db.py, init.py.
Most of tutorials I can find define all in one file.
According to the newest flask-user docs you can use
from flask_user.signals import user_registered
#user_registered.connect_via(app)
def _after_registration_hook(sender, user, **extra):
print('user is registered', sender, user)
and place it after the app.config

How to query with many tables

from flask import Flask, render_template
from flask_sqlalchemy import SQLAlchemy
#import sqlite3 as sql
app = Flask(__name__)
app.config['SQLALCHEMY_DATABASE_URI'] = 'mysql://ahmad:ahmad#192.168.3.103/utama'
db = SQLAlchemy(app)
class ak(db.Model):
__tablename__ = 'ak'
id = db.Column(db.Integer, primary_key=True)
nama = db.Column(db.String)
alamat = db.Column(db.String)
akreditasi = db.Column(db.String)
def __init__(self, id, nama, alamat, akreditasi):
self.id = id
self.city = nama
self.alamat = alamat
self.akreditasi = akreditasi
class av(db.Model):
__tablename__ = 'av'
id = db.Column(db.Integer, primary_key=True)
nama = db.Column(db.String)
alamat = db.Column(db.String)
akreditasi = db.Column(db.String)
def __init__(self, id, nama, alamat, akreditasi):
self.id = id
self.city = nama
self.alamat = alamat
self.akreditasi = akreditasi
id_jurusan = db.Table('id_jurusan',
db.Column('id', db.Integer, db.ForeignKey('ak.id')),
db.Column('id', db.Integer, db.ForeignKey('av.id'))
)
#app.route('/ak')
def jurusan(jurusan):
return render_template('index.html', rows=ak.query.all() )
#app.route('/av')
def Akuntansi():
return render_template('index.html', rows=av.query.all() )
if __name__ == '__main__':
app.run(debug=True, host='1.1.1.1', port=80)
I am a new to learn python, in this case I studied the framework flask and I had trouble on the declaration SQLAlchemy, precisely displays the contents of the table but with the same structure,when executed will be like this.....
[
which one success
You are using the decorator
#app.route('/av')
The method which succeeds Akuntansi() does not require a parameter. So this works. The method which fails expects a parameter jurusan(jurusan) but your decorator #app.route('/ak') does not consider this.
To pass a parameter you need to use the decorator like this:
#app.route("/ak/<jurusan>") and then also pass the parameter in the request.

SQLAlchemy / WTForms - QuerySelectField

I'm using WTForms with the SQLAlchemy extension on a Pyramid application.
My session is:
from zope.sqlalchemy import ZopeTransactionExtension
DBSession = scoped_session(sessionmaker(extension=ZopeTransactionExtension()))
Base = declarative_base()
My model is:
class Client(Base):
__tablename__ = 'client'
id = Column(Integer, primary_key=True, nullable=False)
name = Column(Unicode(48))
street = Column(Unicode(48))
city = Column(Unicode(32))
task = relationship("Task", backref="client")
#classmethod
def active(cls):
return DBSession.query(Client).options(load_only("id", "name")).order_by(sa.desc(Client.name)).filter(Client.status == True)
class Task(Base):
__tablename__ = 'task'
id = Column(Integer, primary_key=True, nullable=False)
name = Column(String(48))
status = Column(Boolean)
client_id = Column(Integer, ForeignKey('client.id'))
My form is:
def enabled_client():
return Client.active()
class TaskCreateForm(ModelForm):
name = TextField('Task name', [validators.Length(min=1, max=48)], filters=[strip_filter])
status = BooleanField('Status')
client_id = QuerySelectField('Client', query_factory=enabled_client, get_label='name', allow_blank=False)
My view is:
#view_config(route_name='task_action', match_param='action=create', renderer='arx:templates/task_edit.mako', permission='edit')
def task_create(request):
task = Task()
form = TaskCreateForm(request.POST)
if request.method == 'POST' and form.validate():
form.populate_obj(task)
DBSession.add(task)
return HTTPFound(location=request.route_url('home'))
return {'form':form, 'action':request.matchdict.get('action')}
Form displays select box with proper Client names but the problem emerges when I'm trying to submit form. WTForm should use real ID of Client but it passes SQLAlchemy object eg:
<arx.models.Client object at 0x7fdfb139ddd0>
What am I doing wrong?
My form was too specific (it should be client instead of client_id), so my working code looks like this:
Session:
from zope.sqlalchemy import ZopeTransactionExtension
DBSession = scoped_session(sessionmaker(extension=ZopeTransactionExtension()))
Base = declarative_base()
Model:
class Client(Base):
__tablename__ = 'client'
id = Column(Integer, primary_key=True, nullable=False)
name = Column(Unicode(48))
street = Column(Unicode(48))
city = Column(Unicode(32))
task = relationship("Task", backref="client")
#classmethod
def active(cls):
return DBSession.query(Client).options(load_only("id", "name")).order_by(sa.desc(Client.name)).filter(Client.status == True)
class Task(Base):
__tablename__ = 'task'
id = Column(Integer, primary_key=True, nullable=False)
name = Column(String(48))
status = Column(Boolean)
client_id = Column(Integer, ForeignKey('client.id'))
Form:
def enabled_client():
return Client.active()
class TaskCreateForm(ModelForm):
name = TextField('Task name', [validators.Length(min=1, max=48)], filters=[strip_filter])
status = BooleanField('Status')
client = QuerySelectField('Client', query_factory=enabled_client, get_label='name', allow_blank=False)
View:
#view_config(route_name='task_action', match_param='action=create', renderer='arx:templates/task_edit.mako', permission='edit')
def task_create(request):
task = Task()
form = TaskCreateForm(request.POST)
if request.method == 'POST' and form.validate():
form.populate_obj(task)
DBSession.add(task)
return HTTPFound(location=request.route_url('home'))
return {'form':form, 'action':request.matchdict.get('action')}

Categories