Python 从Sqlite3到Sqlalchemy

Python 从Sqlite3到Sqlalchemy,python,sqlite,flask,sqlalchemy,flask-sqlalchemy,Python,Sqlite,Flask,Sqlalchemy,Flask Sqlalchemy,我一直在开发一个带有sqlite3数据库的flask应用程序。现在要在线发布,我正在将数据库从sqlite3切换到Sqlalchemy,我非常迷茫。我想创建一个users表(“Usuarios”)并防止在register上重复 我已经创建了表: class Usuarios(db.Model): id = db.Column(db.Integer, primary_key=True) username = db.Column(db.String(80), unique=True,

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

我已经创建了表:

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
我认为username列中的数据应该是唯一的,所以我设置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"))
它工作得很好


我认为问题在于
如果不是
。你怎么认为?提前感谢

如果您仔细检查stacktrace,您会发现您的
IntegrityError
在您到达支票之前就已经发出,该支票应该决定是否应该显示道歉

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

IntegrityError
传达了这一点,是一个自然的
尝试除外的候选者,但实际上没有一个好的干净的方法来这样做

仅将错误消息传递给用户有点问题:

  • 对于不熟悉关系数据库的人来说,这可能很神秘
  • 如果您的应用程序是多语言的,那么它可能使用不同于其所选语言的语言
  • 这可能是一个安全问题,因为它可能会暴露有关应用程序内部工作的信息
  • 可能还有很多我现在想不起来的问题
解析错误消息也有问题,因为在切换底层数据库时,它可能是另一条消息,而且这样的方法即使在最好的情况下也会导致混乱、难以维护的代码

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

例如,重复用户名检查可能看起来有点像:

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()

这种方法为您提供了更好的控制,因为它避免了试图弄清楚发生了什么的混乱代码,并且在您决定以后移动到另一个数据库或更改数据库适配器实现中的详细信息等情况下,这种方法更具鲁棒性。

谢谢您解决了这个问题。我相信你花了很长时间写了这个回复,所以谢谢:)因为我是炼金术新手,我想知道db.exists()和scalar的用途。由于我没有在SQLITE3上使用它们来增强健壮性,这可能会受到TOCTOU的影响,因为两次并发注册尝试可能都会观察到用户名未被使用,并尝试插入,因此完整性错误仍应得到处理。