从Sqlite3到Sqlalchemy

时间:2019-08-09 23:47:16

标签: python sqlite flask sqlalchemy flask-sqlalchemy

我一直在开发带有sqlite3数据库的flask应用程序。现在要将其在线发布,我将数据库从sqlite3切换到Sqlalchemy,我非常迷失。我想创建一个用户表(“ Usuarios”),并防止重复注册。

我已经创建了表:

class Usuarios(db.Model):
    id = db.Column(db.Integer, primary_key=True)
    username = db.Column(db.String(80), unique=True, nullable=False)
    hash = db.Column(db.String(120), unique=True, nullable=False)
    provincia = db.Column(db.String(80))
    mail = db.Column(db.String(80), nullable=False)
    def __init__(self, username, hash, provincia, mail):
        self.username = username
        self.hash = hash
        self.provincia = provincia
        self.mail = mail

我认为在用户名列中数据应该是唯一的,所以我将unique设置为True

这是我的python代码:

@app.route("/regescuela", methods=["GET", "POST"])
def register():
    session.clear()

    if request.method == "POST":
        if not request.form.get("mail"):
            return apology("No ha introducido el correo electrónico!")
        if "@" not in request.form.get("mail"):
            return apology("No ha introducido un correo electrónico valido!")
        if not request.form.get("username"):
            return apology("No ha introducido un nombre de usuario!")
        elif not request.form.get("password"):
            return apology("No ha introducido una contraseña!")
        elif request.form.get("password") != request.form.get("confirmation"):
            return apology("Las contraseñas no coinciden.")
    else:
        usumayu = request.form.get("username")
        return render_template("regescuela.html")

    nuevaentrada = Usuarios(username = request.form.get("username").upper(), hash = generate_password_hash(request.form.get("password")), provincia = request.form.get("provincia"), mail = request.form.get("mail"))
    db.session.add(nuevaentrada)
    db.session.commit()



    if not nuevaentrada:
        return apology("Este usuario ya existe! Prueba con otro!")

    session["user_id"] = nuevaentrada
    flash("Registrado!")
    return redirect("/")

问题是,如果我使用已经使用的用户名注册用户名,则会出现此错误:

sqlalchemy.exc.IntegrityError: (psycopg2.errors.UniqueViolation) duplicate key value violates unique constraint "usuarios_username_key"

这是合乎逻辑的,因为我将unique设置为True。 但是我不想收到该错误,如果要使用用户名,我想返回道歉,这就是为什么我添加了if not nuevaentrada: return apology(),但它似乎没有用,我也不知道为什么。 在sqlite3中,我的代码是:

db.execute("INSERT INTO usuarios (username, hash, provincia, mail) VALUES(:username, :hash, :provincia, :mail)", username=request.form.get("username").upper(),
hash=generate_password_hash(request.form.get("password")), provincia=request.form.get("provincia"), mail=request.form.get("mail"))

效果很好。

我认为问题在于if not。你怎么看?预先感谢

1 个答案:

答案 0 :(得分:1)

如果您仔细检查堆栈跟踪记录,就会发现IntegrityError在举起您的支票之前就已经举起了,该支票应该决定是否应该道歉。

这并不奇怪,因为数据库无法使用重复的用户名执行插入操作。

IntegrityError可以传达这一点,并且自然会成为try-except的候选人,但实际上并没有一种很好的干净方法。

仅将错误消息移交给用户是有问题的:

  • 对于不熟悉关系数据库的人来说可能很神秘
  • 如果您的应用程序是多语言的,它的语言可能与他们选择的语言不同
  • 这可能是一个安全问题,因为它可能会公开有关应用程序内部工作的信息
  • 可能还有其他我现在无法想到的问题

解析错误消息也是有问题的,因为在切换基础数据库时错误消息可能是不同的消息,此外,即使在最佳情况下,这样的方法也会导致混乱且难以维护的代码。

话虽如此,我建议您将其视为业务逻辑问题,并在插入尝试之前执行必要的检查。

例如,重复的用户名检查可能看起来像这样:

username = request.form.get("username").upper()

...

dup_username = db.session.query(db.exists().where(Usarios.username == username)).scalar()

if dup_username:
    return apology("Este usuario ya existe! Prueba con otro!")

...
# possibly more checks, e.g. email already in use or something    
...

# since we didn't drop out so far with a failed check, we can perform the insert

nuevaentrada = Usuarios(...)
db.session.add(nuevaentrada)
db.session.commit()

这种方法为您提供了更好的控制,因为它避免了弄乱代码以试图弄清楚发生了什么,并且如果您以后决定移至另一个数据库或更改数据库适配器实现的详细信息等,则更加健壮。