我正在尝试构建一个带有登录、个人资料和聊天页面的小型 Flask 应用程序。但是,当我尝试单击个人资料页面时,我一直遇到___。我无法真正找到我的问题,但就我而言,这是唯一不起作用的部分。尽管以防万一我展示了我项目中的所有其他内容。
如果我的问题是业余的,我很抱歉,但我对此非常陌生,所以请注意。
因此,我仍在使用模拟数据库,但希望切换到本地数据库。任何建议将不胜感激。
代码快速概览
代码是一个创建简单聊天室的 Flask 应用程序。用户可以登录、向聊天室发送消息以及邀请其他用户加入聊天室。该应用程序使用 Flask-Login 来处理用户身份验证和授权,并使用 Flask-SocketIO 来实现客户端和服务器之间的 WebSocket 通信。
应用程序有四种方法:
/注册: 该路由处理用户登录。使用 GET 请求呈现登录表单,并使用 POST 请求处理表单数据。如果提交的凭据有效,用户将登录并重定向到主页。否则,您将收到一条错误消息。
/注销: 此路由注销当前用户并重定向到登录页面。
/: 这是应用程序的主页。出现一个聊天室,用户可以在其中发送消息并邀请其他用户加入聊天室。
/简介: 此路由显示用户的个人资料页面,其中包括他们的姓名和电子邮件地址。只有经过身份验证的用户才能访问此页面。
应用程序使用 Flask-SocketIO 来处理客户端和服务器之间的 WebSocket 通信。当用户在聊天室中发送消息时,消息通过 WebSocket 发送到服务器,服务器将消息广播到所有连接的客户端。类似地,当用户邀请另一个用户进入聊天室时,邀请通过 WebSocket 发送到服务器,并向受邀用户发送电子邮件。
app.py
from flask import Flask, render_template, request, redirect, url_for, flash
from flask_login import LoginManager, login_user, logout_user, login_required, UserMixin
from flask_bcrypt import Bcrypt
app = Flask(__name__)
app.secret_key = 'kO=:6KafW}GT~~l[rle/;yAyE_nAQ^' # change this to a more secure secret key
bcrypt = Bcrypt(app)
# configure Flask-Login
login_manager = LoginManager()
login_manager.init_app(app)
login_manager.login_view = 'login'
# create a mock user database
users = {'user1': {'password': bcrypt.generate_password_hash('password').decode('utf-8'),
'name': 'John Doe',
'email': '[email protected]'},
'user2': {'password': bcrypt.generate_password_hash('password2').decode('utf-8'),
'name': 'Jane Smith',
'email': '[email protected]'}}
# create a User class for Flask-Login
class User(UserMixin):
def __init__(self, id):
self.id = id
def __repr__(self):
return self.id
# define a function to load users from the database
@login_manager.user_loader
def load_user(user_id):
if user_id in users:
return User(user_id)
return None
# define the login route
@app.route('/login', methods=['GET', 'POST'])
def login():
if request.method == 'POST':
# check if the submitted credentials are valid
if request.form['username'] in users and bcrypt.check_password_hash(users[request.form['username']]['password'], request.form['password']):
user = User(request.form['username'])
login_user(user)
flash('Logged in successfully.')
return redirect(url_for('index'))
else:
flash('Invalid username or password.')
return render_template('login.html')
# define the logout route
@app.route('/logout')
@login_required
def logout():
logout_user()
flash('Logged out successfully.')
return redirect(url_for('login'))
# define the index route
@app.route('/')
@login_required
def index():
return render_template('index.html')
# define the profile route
@app.route('/profile')
@login_required
def profile():
user = users.get(current_user.id)
return render_template('profile.html', user=user)
if __name__ == '__main__':
app.run(debug=True)
模板/base.html
<!DOCTYPE html>
<html>
<head>
<title>{% block title %}{% endblock %}</title>
<link rel="stylesheet" type="text/css" href="{{ url_for('static', filename='css/style.css') }}">
</head>
<body>
<header>
<nav>
<ul>
<li><a href="{{ url_for('index') }}">Home</a></li>
{% if current_user.is_authenticated %}
<li><a href="{{ url_for('logout') }}">Logout</a></li>
<li><a href="{{ url_for('profile') }}">Profile</a></li>
{% else %}
<li><a href="{{ url_for('login') }}">Login</a></li>
{% endif %}
</ul>
</nav>
</header>
<main>
{% with messages = get_flashed_messages() %}
{% if messages %}
<ul class="flashes">
{% for message in messages %}
<li>{{ message }}</li>
{% endfor %}
</ul>
{% endif %}
{% endwith %}
{% block content %}{% endblock %}
</main>
<footer>
<p>© 2023 My Flask App</p>
</footer>
</body>
</html>
模板/index.html
{% extends "base.html" %}
{% block title %}Chat Room{% endblock %}
{% block content %}
<h1>Chat Room</h1>
<div id="messages"></div>
<form id="message-form">
<input type="text" id="message-input" placeholder="Type your message...">
<button type="submit">Send</button>
</form>
<h2>Invite People</h2>
<form id="invite-form">
<input type="text" id="invite-input" placeholder="Enter email address...">
<button type="submit">Invite</button>
</form>
{% endblock %}
{% block scripts %}
<script src="https://code.jquery.com/jquery-3.6.0.min.js"></script>
<script>
// connect to the WebSocket server
const socket = new WebSocket("ws://" + location.host + "/ws");
// when the WebSocket connection is opened, register the user
socket.addEventListener("open", function (event) {
socket.send(JSON.stringify({
"type": "register",
"data": "{{ current_user.id }}"
}));
});
// when the WebSocket receives a message, add it to the chat log
socket.addEventListener("message", function (event) {
const message = JSON.parse(event.data);
if (message.type === "message") {
$("#messages").append(`<p><strong>${message.sender}: </strong>${message.text}</p>`);
}
});
// when the message form is submitted, send the message over the WebSocket
$("#message-form").submit(function (event) {
event.preventDefault();
const messageInput = $("#message-input");
const messageText = messageInput.val();
socket.send(JSON.stringify({
"type": "message",
"data": {
"sender": "{{ current_user.id }}",
"text": messageText
}
}));
messageInput.val("");
});
// when the invite form is submitted, send an email invitation
$("#invite-form").submit(function (event) {
event.preventDefault();
const inviteInput = $("#invite-input");
const emailAddress = inviteInput.val();
// TODO: send email invitation using an email API
alert(`Invitation sent to ${emailAddress}`);
inviteInput.val("");
});
</script>
{% endblock %}
templates/login.html
{% extends "base.html" %}
{% block content %}
<div class="wrap">
<div class="avatar">
<img src="https://static.vecteezy.com/system/resources/previews/007/636/859/original/community-logo-design-free-vector.jpg">
</div>
<form method="POST" action="{{ url_for('login') }}">
<input type="text" name="username" placeholder="username" required>
<div class="bar">
<i></i>
</div>
<input type="password" name="password" placeholder="password" required>
<button type="submit">Sign in</button>
{{ get_flashed_messages() }}
</form>
</div>
{% endblock %}
templates/profile.html
{% extends 'base.html' %}
{% block title %}Profile{% endblock %}
{% block content %}
<div class="profile-container">
<div class="profile-header">
<img src="{{ url_for('static', filename='img/profile-pic.jpg') }}" alt="Profile Picture" class="profile-picture">
<h1>{{ current_user.username }}</h1>
</div>
<div class="profile-info">
<h2>Profile Information</h2>
<ul>
<li><strong>Name:</strong> {{ current_user.name }}</li>
<li><strong>Email:</strong> {{ current_user.email }}</li>
<li><strong>Location:</strong> {{ current_user.location }}</li>
</ul>
</div>
</div>
{% endblock %}
{% block scripts %}
<script src="{{ url_for('static', filename='js/profile.js') }}"></script>
{% endblock %}
static/css/styles.css
@font-face {
font-family: 'Lato';
font-style: normal;
font-weight: 400;
src: local('Lato Regular'), local('Lato-Regular'), url(http://themes.googleusercontent.com/static/fonts/lato/v7/qIIYRU-oROkIk8vfvxw6QvesZW2xOQ-xsNqO47m55DA.woff) format('woff');
}
body {
background: #448ed3 ;
font-family: "Lato" ;
}
.wrap {
width:250px;
height: auto;
margin: auto;
margin-top: 10%;
}
.avatar {
width: 100%;
margin: auto;
width: 65px;
border-radius: 100px;
height: 65px;
background: #448ed3 ;
position: relative;
bottom: -15px;
}
.avatar img {
width: 55px;
height: 55px;
border-radius: 100px;
margin: auto;
border:3px solid #fff;
display: block;
}
.wrap input {
border: none;
background: #fff;
font-family:Lato ;
font-weight:700 ;
display: block;
height: 40px;
outline: none;
width: calc(100% - 24px) ;
margin: auto;
padding: 6px 12px 6px 12px;
}
.bar {
width: 100%;
height: 1px;
background: #fff ;
}
.bar i {
width: 95%;
margin: auto;
height: 1px ;
display: block;
background: #d1d1d1;
}
.wrap input[type="text"] {
border-radius: 7px 7px 0px 0px ;
}
.wrap input[type="password"] {
border-radius: 0px 0px 7px 7px ;
}
.forgot_link {
color: #83afdf ;
color: #83afdf;
text-decoration: none;
font-size: 11px;
position: relative;
left: 193px;
top: -36px;
}
.wrap button {
width: 100%;
border-radius: 7px;
background: #b6ee65;
text-decoration: center;
border: none;
color: #51771a;
margin-top:-5px;
padding-top: 14px;
padding-bottom: 14px;
outline: none;
font-size: 13px;
border-bottom: 3px solid #307d63;
cursor: pointer;
}
.profile-container {
max-width: 800px;
margin: 0 auto;
padding: 30px;
background-color: #fff;
border-radius: 10px;
box-shadow: 0 0 10px rgba(0, 0, 0, 0.1);
}
.profile-header {
display: flex;
align-items: center;
}
.profile-picture {
width: 150px;
height: 150px;
border-radius: 50%;
margin-right: 30px;
}
.profile-info h2 {
margin-bottom: 20px;
}
.profile-info ul {
list-style: none;
padding: 0;
margin: 0;
}
.profile-info li {
margin-bottom: 10px;
}
.profile-info li strong {
display: inline-block;
width: 100px;
font-weight: bold;
margin-right: 10px;
}
static/js/profile.js
document.addEventListener("DOMContentLoaded", function(event) {
// Code to run when the page loads
console.log("Profile page loaded.");
});