【问题标题】:How to display constraint errors from form in JSON using Flask-SQL Alchemy?如何使用 Flask-SQL Alchemy 在 JSON 中显示约束错误?
【发布时间】:2020-11-07 20:26:22
【问题描述】:

现在,我的项目接受来自表单的用户信息,并将信息作为 JSON 中的 POST 请求返回。创建的用户也被保存到数据库中。这正是我想要做的,但是如果用户输入的信息不满足其约束,我希望最终路由列出显示错误的 JSON。例如:

{
  "errors": {
    "color": [
      "Invalid value, must be one of: red, blue, yellow"
    ],
    "email": [
      "This field is required."
    ]
  }
}

我认为您可以在模型类中创建一些东西来在不满足约束时处理这些错误。但是,我在 Flask-SQL Alchemy 文档或在线搜索中找不到任何关于此的内容。现在,如果用户输入了错误的输入类型(即年份字段中的字母),则会显示 sqlalchemy.exc.DataError 以及一堆回溯。如果用户的输入超出字段的字符限制,也会发生这种情况。此外,当跳过输入字段时,它只会在创建的 JSON 中的字段中显示“”,而不是说明“此字段是必需的”。我将如何创建上述 JSON 来显示这些错误?我的代码如下

"""app.py"""

from flask import Flask, render_template, redirect, session, request, jsonify
from flask_debugtoolbar import DebugToolbarExtension
from werkzeug.exceptions import Unauthorized

from models import *

app = Flask(__name__)

app.config['SQLALCHEMY_DATABASE_URI'] = "postgres:///database_example"
app.config['SQLALCHEMY_TRACK_MODIFICATIONS'] = False
app.config['SQLALCHEMY_ECHO'] = True
app.config['SECRET_KEY'] = "shhhhh"
app.config['DEBUG_TB_INTERCEPT_REDIRECTS'] = False

connect_db(app)
db.drop_all()
db.create_all()

toolbar = DebugToolbarExtension(app)


@app.route("/")
def homepage():
    """Show homepage."""
    return redirect("/api")

@app.route("/api", methods=["GET", "POST"])
def json_api():
    """JSON API Endpoint"""
    if request.method == "POST":
        user = User(
            name = request.form["name"], 
            year = request.form["birth_year"],
            email = request.form["email"],
            color = request.form["color"],
        )
        
        db.session.add(user)
        db.session.commit()

        user_dict = user.to_dict()
        return jsonify(user_dict)
        # return jsonify(user_dict)

    else:
        return render_template("index.html")

######################## """ models.py """############## ############

from flask_sqlalchemy import SQLAlchemy

db = SQLAlchemy()


class User(db.Model):
    """User."""

    __tablename__ = "users"

    id = db.Column(db.Integer, primary_key=True, autoincrement=True)
    name = db.Column(db.String(50), nullable=False)
    email = db.Column(db.String(50), nullable=False)
    year = db.Column(db.Float(4), nullable=False)
    color = db.Column(db.String(20), nullable=False)

    def to_dict(self):
        """Serialize user to a dict of user info."""

        return {
            "id": self.id,
            "name": self.name,
            "email": self.email,
            "year": self.year,
            "color": self.color,
        }


def connect_db(app):
    """Connect to database."""

    db.app = app
    db.init_app(app)

######################### """ index.html """ ############# ##############

<!doctype html>
<html>
<head>
  <title>Title</title>
  <script src="https://unpkg.com/jquery"></script>
</head>
<body>

<form id="form" method="POST">
  <div>Name: <input id="name" name="name"> <b id="name-err"></b> </div>
  <div>Birth Year: <input id="year" name="birth_year"> <b id="year-err"></b> </div>
  <div>Email: <input id="email" name="email"> <b id="email-err"></b> </div>
  <div>Color: <input id="color" name="color"> <b id="color-err"></b> </div>
  <button>Submit</button>
</form>



</body>
</html>

【问题讨论】:

标签: python flask sqlalchemy flask-sqlalchemy


【解决方案1】:

你可以看看这个库:

Marshmallow 将使用对象处理用户输入验证。 它与 SQL Alchemy 模型非常相似。

混合用户输入和数据库模型可能会产生更多的复杂性。 您可以将它们分开。

您可以为您的请求创建验证对象,如下所示:

from flask import Flask, jsonify
from marshmallow import Schema, fields, validate
from webargs.flaskparser import use_args
from dataclasses import dataclass, asdict

app = Flask(__name__)
app.config['JSONIFY_PRETTYPRINT_REGULAR'] = True


@app.errorhandler(422)
@app.errorhandler(400)
def handle_error(err):
    """Return validation errors as JSON"""
    headers = err.data.get("headers", None)
    messages = err.data.get("messages", ["Invalid request."])
    if headers:
        return jsonify({"errors": messages}), err.code, headers
    else:
        return jsonify({"errors": messages}), err.code


class UserSchema(Schema):
    """This class handle user input and validation"""
    name = fields.Str(required=True)
    year = fields.Int(required=True, strict=True,
                      validate=validate.Range(min=1900, max=2100))
    email = fields.Email(required=True)
    color = fields.String(required=True, validate=validate.OneOf([
        "red",
        "blue",
        "yellow",
    ]))


@dataclass
class User:
    """This class can be replaced by sql alchemy or whatever
    business logic concerned object"""
    name: str
    year: int
    email: str
    color: str


@app.route("/api", methods=["POST"])
@use_args(UserSchema(), location="json")
def json_api(args):
    # Create user object
    user = User(**args)

    # Process it
    ...

    # Render response
    return jsonify(asdict(user))

使用以下 curl 请求对其进行测试:

set -x
curl -X POST localhost:5000/api \
    --header "Content-Type: application/json" \
    --data '{}'

curl -X POST localhost:5000/api \
    --header "Content-Type: application/json" \
    --data '{"color": "green", "foobar": 42}'

curl -X POST localhost:5000/api \
    --header "Content-Type: application/json" \
    --data '{"color": "red", "name": "me", "year": 1950, "email": "me@web.com"}'

你得到了以下结果:

+ curl -X POST localhost:5000/api --header 'Content-Type: application/json' --data '{}'
{
  "errors": {
    "json": {
      "color": [
        "Missing data for required field."
      ], 
      "email": [
        "Missing data for required field."
      ], 
      "name": [
        "Missing data for required field."
      ], 
      "year": [
        "Missing data for required field."
      ]
    }
  }
}
+ curl -X POST localhost:5000/api --header 'Content-Type: application/json' --data '{"color": "green", "foobar": 42}'
{
  "errors": {
    "json": {
      "color": [
        "Must be one of: red, blue, yellow."
      ], 
      "email": [
        "Missing data for required field."
      ], 
      "foobar": [
        "Unknown field."
      ], 
      "name": [
        "Missing data for required field."
      ], 
      "year": [
        "Missing data for required field."
      ]
    }
  }
}
+ curl -X POST localhost:5000/api --header 'Content-Type: application/json' --data '{"color": "red", "name": "me", "year": 1950, "email": "me@web.com"}'
{
  "color": "red", 
  "email": "me@web.com", 
  "name": "me", 
  "year": 1950
}

【讨论】:

    猜你喜欢
    • 2020-11-11
    • 2022-12-09
    • 1970-01-01
    • 1970-01-01
    • 2014-08-26
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多