4

I'm trying to insert a record in my database through my Flask API. The relationship between the User and Project classes is One to Many (a User can have many projects). I'm having no problems at inserting a new User, but when I try to insert a new Project in the database, it actually works, but the User attributte in the Project cant't be be converted to JSON. Here is my code:

User Model file:

class Usuario(db.Model):
 id = db.Column(db.BigInteger, primary_key=True)
 login = db.Column(db.String(80), unique=True, nullable=False)
 email = db.Column(db.String(120), unique=True, nullable=False)
 nome = db.Column(db.String(80), nullable=False)
 senha = db.Column(db.String(80), nullable=False)
 telefone = db.Column(db.String(80))
 receivedNotification = db.Column(
    db.Boolean, default=False, nullable=False)
 receivedEmail = db.Column(db.Boolean, default=False, nullable=False)
 emailVerificated = db.Column(db.Boolean, default=False, nullable=False)
 imagem = db.Column(db.LargeBinary(length=(2 ** 32) - 1))

 def __init__(self, login='', email='', nome='', senha='',
             telefone='', received_notification=False,
             received_email=False, email_verificated=False, imagem=None, init_dict=None):
    if init_dict is None:
        self.login = login
        self.email = email
        self.nome = nome
        self.senha = senha
        self.telefone = telefone
        self.receivedNotification = received_notification
        self.receivedEmail = received_email
        self.emailVerificated = email_verificated
        self.imagem = imagem
    else:
        for key in init_dict:
            setattr(self, key, init_dict[key])
class UsuarioSchema(ma.Schema):
class Meta:
    fields = ('id', 'login', 'email', 'nome', 'telefone',
              'receivedNotification', 'receivedEmail', 'emailVerificated', 'imagem')

user_schema = UsuarioSchema()
users_schema = UsuarioSchema(many=True)

Project Model file:

class Projeto(db.Model):
  codigo = db.Column(db.BigInteger, primary_key=True)
  nome = db.Column(db.String(100))
  descricao = db.Column(db.String(200))
  dataCriacao = db.Column(db.Date)
  dataPrevFinalizacao = db.Column(db.Date)
  dataFinalizacao = db.Column(db.Date)
  usuarioAdmId = db.Column(db.BigInteger,
                         db.ForeignKey('usuario.id'), nullable=False)
  usuarioAdm = db.relationship('Usuario',
                             backref=db.backref('projetos', lazy=True))

  def __init__(self, nome, descricao, data_criacao,
             data_prev_finalizacao, usuario_adm_id, data_finalizacao=None):
    self.nome = nome
    self.descricao = descricao
    self.dataCriacao = data_criacao
    self.dataPrevFinalizacao = data_prev_finalizacao
    self.dataFinalizacao = data_finalizacao
    self.usuarioAdmId = usuario_adm_id

class ProjetoSchema(ma.Schema):
class Meta:
    fields = ('nome', 'descricao', 'dataCriacao',
              'dataPrevFinalizacao', 'dataFinalizacao', 'usuarioAdm')

projeto_schema = ProjetoSchema()
projetos_schema = ProjetoSchema(many=True)

My route code to add a project:

@projeto_controller.route(mapping, methods=['POST'])
def add_projeto():
  nome = request.json['nome']
  descricao = request.json['descricao']
  data_criacao = request.json['dataCriacao']
  data_criacao = datetime.strptime(
    data_criacao, '%b %d, %Y %I:%M:%S %p').date()
  data_prev_finalizacao = request.json['dataPrevFinalizacao']
  data_prev_finalizacao = datetime.strptime(
    data_prev_finalizacao, '%b %d, %Y %I:%M:%S %p').date()
  usuario_adm_id = request.json['usuarioAdm']['id']
  usuario_adm = request.json['usuarioAdm']

  usuario = mUsuario.Usuario.query.get(usuario_adm_id)
  usuarioAdm = usuario

  new_projeto = mProjeto.Projeto(nome=nome, descricao=descricao,
                               data_criacao=data_criacao,

  data_prev_finalizacao=data_prev_finalizacao,
                               usuario_adm_id=usuario_adm_id)

  new_projeto.usuarioAdm = usuarioAdm
  usuarioAdm.projetos.append(new_projeto)

  db.session.commit()
  return mProjeto.projeto_schema.jsonify(new_projeto) 

Error message:

  File "/anaconda3/envs/KeepSoft/lib/python3.7/site-packages/flask/app.py", line 2309, in __call__
return self.wsgi_app(environ, start_response)
 File "/anaconda3/envs/KeepSoft/lib/python3.7/site-packages/flask/app.py", line 2295, in wsgi_app
response = self.handle_exception(e)
  File "/anaconda3/envs/KeepSoft/lib/python3.7/site-packages/flask/app.py", line 1741, in handle_exception
reraise(exc_type, exc_value, tb)
  File "/anaconda3/envs/KeepSoft/lib/python3.7/site-packages/flask/_compat.py", line 35, in reraise
raise value
  File "/anaconda3/envs/KeepSoft/lib/python3.7/site-packages/flask/app.py", line 2292, in wsgi_app
response = self.full_dispatch_request()
  File "/anaconda3/envs/KeepSoft/lib/python3.7/site-packages/flask/app.py", line 1815, in full_dispatch_request
rv = self.handle_user_exception(e)
  File "/anaconda3/envs/KeepSoft/lib/python3.7/site-packages/flask/app.py", line 1718, in handle_user_exception
reraise(exc_type, exc_value, tb)
  File "/anaconda3/envs/KeepSoft/lib/python3.7/site-packages/flask/_compat.py", line 35, in reraise
raise value
  File "/anaconda3/envs/KeepSoft/lib/python3.7/site-packages/flask/app.py", line 1813, in full_dispatch_request
rv = self.dispatch_request()
  File "/anaconda3/envs/KeepSoft/lib/python3.7/site-packages/flask/app.py", line 1799, in dispatch_request
return self.view_functions[rule.endpoint](**req.view_args)
  File "/Users/user/Python/KeepSoftPythonAPI/controller/ProjetoController.py", line 93, in add_projeto
return mProjeto.projeto_schema.jsonify(new_projeto)
  File "/anaconda3/envs/KeepSoft/lib/python3.7/site-packages/flask_marshmallow/schema.py", line 42, in jsonify
return flask.jsonify(data, *args, **kwargs)
  File "/anaconda3/envs/KeepSoft/lib/python3.7/site-packages/flask/json/__init__.py", line 321, in jsonify
dumps(data, indent=indent, separators=separators) + '\n',
  File "/anaconda3/envs/KeepSoft/lib/python3.7/site-packages/flask/json/__init__.py", line 179, in dumps
rv = _json.dumps(obj, **kwargs)
  File "/anaconda3/envs/KeepSoft/lib/python3.7/json/__init__.py", line 238, in dumps
**kw).encode(obj)
  File "/anaconda3/envs/KeepSoft/lib/python3.7/json/encoder.py", line 201, in encode
chunks = list(chunks)
  File "/anaconda3/envs/KeepSoft/lib/python3.7/json/encoder.py", line 431, in _iterencode
yield from _iterencode_dict(o, _current_indent_level)
  File "/anaconda3/envs/KeepSoft/lib/python3.7/json/encoder.py", line 405, in _iterencode_dict
yield from chunks
  File "/anaconda3/envs/KeepSoft/lib/python3.7/json/encoder.py", line 438, in _iterencode
o = _default(o)
  File "/anaconda3/envs/KeepSoft/lib/python3.7/site-packages/flask/json/__init__.py", line 81, in default
return _json.JSONEncoder.default(self, o)
  File "/anaconda3/envs/KeepSoft/lib/python3.7/json/encoder.py", line 179, in default
raise TypeError(f'Object of type {o.__class__.__name__} '
  TypeError: Object of type Usuario is not JSON serializable

I expected the a Project JSON returned, so i can convert to the Project class that exists in my mobile application.

Victor Yan
  • 181
  • 3
  • 9
  • Possible duplicate of [TypeError: Python object is not JSON serializable](https://stackoverflow.com/questions/36555602/typeerror-python-object-is-not-json-serializable) –  Jan 12 '19 at 01:32
  • Have you ensured this? "Flask-SQLAlchemy must be initialized before Flask-Marshmallow." – Arunmozhi Jan 12 '19 at 04:26
  • Here is my answer to [How to jsonify objects from sqlalchemy?](https://stackoverflow.com/a/53519960/6705684) It works to this question as well. – NicoNing Jan 12 '19 at 06:16
  • For SQL Alchemy I think this answer is the cleanest: https://stackoverflow.com/a/46180522/3559330 – mattyb Mar 05 '21 at 19:41

2 Answers2

3

You should have a function that returns the objects of the Projeto class in a dictionary format, and then convert it into jsonify it. Something like

def get_objects(self):
    """
    This function creates a dictionary and assigns
    the keys of the dictionary with the objects of
    the class and returns it in a json format. 
    """
    data = {
        'nome': self.nome,
        'descricao': self.descricao,
        'data_criacao': self.dataCriacao,
        'data_prev_finalizacao': self.dataPrevFinalizacao,
        'data_finalizacao': self.dataFinalizacao,
        'usuario_adm_id': self.usuarioAdmId
    }
    return jsonify(data)

If you need the data to be returned in a JSON format for a model, you'll need the data to be returned in a JSON format. Currently with this line of code,

return mProjeto.projeto_schema.jsonify(new_projeto)

what you're doing is that you are trying to convert a Usuario and Projeto object into a JSON object, which is not a valid data type the jsonify function supports. That is what is indicated by the message

raise TypeError(f'Object of type {o.__class__.__name__} '
  TypeError: Object of type Usuario is not JSON serializable

The same can be done for the Usuario class as well.

2

After searching a lot, I was able to solve my problem. I had to define explicitly that the object usuario defined in the ProjetoSchema class was Nested object, so only them when retrieving a Projeto, this object would be properly loaded as a JSON.

So, now this the modified Schema:

class ProjetoSchema(ma.Schema):

  class Meta:
      fields = ('codigo', 'nome', 'descricao', 'dataCriacao',
              'dataPrevFinalizacao', 'dataFinalizacao', 'usuarioAdm')
      model = Projeto
  usuarioAdm = ma.Nested('UsuarioSchema', exclude=('projetos',))
  perfis = ma.Nested('PerfilSchema', many=True, exclude=('projeto',))


  projeto_schema = ProjetoSchema()
  projetos_schema = ProjetoSchema(many=True)

And this is the modified UsuarioSchema:

class UsuarioSchema(ma.Schema):
  class Meta:
      fields = ('id', 'login', 'email', 'nome', 'telefone', 'receivedNotification',
              'receivedEmail', 'emailVerificated', 'imagem')
      model = Usuario
  projetos = ma.Nested('ProjetoSchema', many=True, exclude=('usuarioAdm',))
  perfis = ma.Nested('PerfilSchema', many=True, exclude=('usuario',))


  user_schema = UsuarioSchema()
  users_schema = UsuarioSchema(many=True)

This way, I won't need to deal with infinite nested objects as I did before I got the final code. Some of these lines wasn't necessary after all, because when I retrive a Usuario, I omitted the field that would contain it's Projeto children and when retrive a Projeto, it properly shows it's parent now.

Forgive my English, I'm brazilian.

Thanks for all the tips and solutions.

Victor Yan
  • 181
  • 3
  • 9