#python #sqlite
#python #sqlite
Вопрос:
Эй, кто-нибудь может сказать мне, что я делаю неправильно в своем коде? Я хочу проверить, есть ли имя пользователя уже в базе данных или нет.
Весь маршрут. Это не выдаст мне никаких ошибок, но также не заполнит мою базу данных.
@app.route('/regist', methods=['GET', 'POST'])
def regist():
if request.method == "POST":
with sql.connect("database.db") as con:
cur = con.cursor()
try:
# flash("register attempted")
username = request.form['username']
password = request.form['password']
passwordencr = request.form['password']
email = request.form['email']
x = cur.execute("SELECT * FROM users WHERE name = ?", (username))
if int(len(x)) > 0:
flash("That username is already taken, please choose another")
return render_template('register.html')
else:
cur.execute("INSERT INTO users (name,password,email) VALUES (?,?,?)",(username,passwordencr,email) )
con.commit()
flash ("Successfully registrated")
except:
con.rollback()
msg = "error in insert operation"
finally:
session['logged_in'] = True
session['username'] = username
gc.collect()
msg = Message('Hello', sender='yourId@gmail.com', recipients=[email])
msg.body = "your username for ak047 is: %s and your password is %s" %(username,password)
mail.send(msg)
return render_template("home.html", msg=msg)
con.close()
gc.collect()
Комментарии:
1. Я думаю, что ваш код определенно содержит ошибки, вы никогда не должны использовать blanket, за исключением того, что у вас есть, если бы вы этого не сделали, вы бы увидели свою ошибку
2. Вам не нужно вызывать
int(len(x))
.len
возвращает целое число. Вам также не нужно сравнивать длину чего-либо с 0.if len(x) > 0
то же самое, чтоif x
.
Ответ №1:
Во-первых, я думаю, что у меня есть рабочий пример кода для исходной проблемы. Тем не менее, я думаю, вы можете решить эту повторяющуюся проблему пользователя лучше, используя ограничения в базе данных. Смотрите нижнюю часть моего ответа.
Сначала давайте проверим текущий код. Есть пара проблем, которые я вижу здесь:
- Неправильный отступ для
try/finally
означает, что во времяtry/finally
нет активного соединения. - Способ, которым вы проверяете, есть ли запись с именем пользователя, неверен.
Отступ
В текущем коде try/finally
блок в строке 6 нуждается в дополнительном отступе, чтобы иметь возможность использовать соединение, установленное подключением, сделанным в with
инструкции в строке 4.
В текущем виде кода соединение будет закрыто к моменту его использования, поэтому весь доступ к базе данных завершится ошибкой.
Проверка пользователя
Код, используемый для проверки пользователя, завершится ошибкой, выдав исключение, которое приведет к попаданию finally и rollback
выполнению. Возвращаемое значение из execute
выдает исключение при вызове len
, независимо от того, есть ли какие-либо записи.
Вот что я получаю из оболочки python, чтобы показать, что я имею в виду:
>>> int(len(cur.execute("select * from people where name_last=:who", {"who": "mike"})))
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
TypeError: object of type 'sqlite3.Cursor' has no len()
Вместо этого, чтобы проверить, возвращает ли select
оператор значение, используйте fetchone
и посмотрите, возвращает ли оно None
:
# No users named `fred`:
>>> cur.execute("select * from people where name_last=:who", {"who": "fred"})
<sqlite3.Cursor object at 0x10bbd0180>
>>> cur.fetchone() is None
True
# At least one user named `mike`:
>>> cur.execute("select * from people where name_last=:who", {"who": "mike"})
<sqlite3.Cursor object at 0x10bbd0180>
>>> cur.fetchone() is None
False
Поэтому я думаю, что что-то вроде этого может сработать:
def regist():
if request.method == "POST":
with sql.connect("database.db") as con:
cur = con.cursor()
try:
# ... Collecting form info ...
cur.execute("SELECT * FROM users WHERE name = ?", (username))
if cur.fetchone() is not None:
flash("That username is already taken...")
return render_template('register.html')
else:
cur.execute("INSERT INTO users (name,password,email) VALUES (?,?,?)",(username,passwordencr,email) )
con.commit()
flash (...)
except:
con.rollback()
finally:
session['logged_in'] = True
session['username'] = username
# ... mailing code ...
Альтернативный подход
Более надежным подходом было бы позволить базе данных взять на себя ответственность за предотвращение дублирования пользователей.
Создайте name
столбец unique
при создании таблицы. Затем вставка записи с тем же именем пользователя вызовет исключение. Берем пример из документов sqlite3:
import sqlite3
con = sqlite3.connect(":memory:")
con.execute("create table person (id integer primary key, firstname varchar unique)")
# Successful, con.commit() is called automatically afterwards
with con:
con.execute("insert into person(firstname) values (?)", ("Joe",))
# con.rollback() is called after the with block finishes with an exception, the
# exception is still raised and must be caught
try:
with con:
con.execute("insert into person(firstname) values (?)", ("Joe",))
except sqlite3.IntegrityError:
print "couldn't add Joe twice"
Комментарии:
1. Обновленный код, чтобы, надеюсь, было понятно, о чем я говорил 🙂
2. Оно все равно не заполнит мою базу данных. Что я пытался сказать: Когда я удаляю фрагмент, где он проверяет наличие имени пользователя в базе данных (если оно уже там), мой код работает идеально и заполняет мою базу данных..
3. Есть ли способ, которым я мог бы связаться с вами через чат? Может быть, IRC или что-то в этомроде?
4. Теперь я сделал строку своего имени уникальной. таким образом, он автоматически попадет в исключение: когда он заметит, что имя пользователя уже есть в этой базе данных?
5. Это то, что указано в документах и что происходит в моем тестировании. Я предполагаю, что вы имеете в виду столбец имени, а не строку имени.
Ответ №2:
Создайте одну функцию, которая проверяет, есть имя пользователя и электронная почта уже в базе данных или нет.
#Models
class User(UserMixin, db.Model):
id = db.Column(db.Integer, primary_key=True, autoincrement=True)
username = db.Column(db.String(15), unique=True, nullable=False)
email = db.Column(db.String(50), unique=True, nullable=False)
password = db.Column(db.String(120), unique=True, nullable=False)
created_on = db.Column(db.DateTime, server_default=db.func.now())
updated_on = db.Column(db.DateTime, server_default=db.func.now(), server_onupdate=db.func.now())
tasks = db.relationship('Task', backref='author', lazy='dynamic')
@classmethod
def is_user_name_taken(cls, username):
return db.session.query(db.exists().where(User.username==username)).scalar()
@classmethod
def is_email_taken(cls, email):
return db.session.query(db.exists().where(User.email==email)).scalar()
def __repr__(self):
return '<User %r>' % (self.username)
#User Signup Api
@app.route('/todo/api/v1.0/signup', methods=['POST'])
def signup():
if 'username' not in request.json:
return jsonify({'username': 'must include username'})
if 'email' not in request.json:
return jsonify({'email': 'must include email'})
if 'password' not in request.json:
return jsonify({'password' : 'must include password' })
if User.is_user_name_taken(request.json['username']):
return jsonify({'username': 'This username is already taken!'}), 409
if User.is_email_taken(request.json['email']):
return jsonify({'email': 'This email is already taken!'}), 409
if request.json :
hashed_password = generate_password_hash(request.json['password'], method='sha256')
new_user = User(username=request.json['username'], email=request.json['email'], password=hashed_password)
db.session.add(new_user)
db.session.commit()
return jsonify({'user': 'user created successfully'}), 201
return jsonify({'username': 'must include username',
'password': 'must include password',
'email' : 'must include email' })
Ответ №3:
этот код дважды подключается к database.db
в строках 1 и 9. Вероятно, это не то, что изначально предполагалось
Комментарии:
1. Это не выдает мне никаких ошибок, но информация по-прежнему не передается в базу данных.
2. Я поместил свой код сверху. Было бы любезно, если бы вы могли взглянуть на это.
3. Я думаю, что @Mike Rhodes прав, блок try / except должен иметь отступ еще на один уровень