Angular与Flask全栈应用中实现用户个性化数据展示教程


angular与flask全栈应用中实现用户个性化数据展示教程

本教程将指导您如何在基于Angular前端和Flask后端的全栈应用中,实现一个核心的个性化功能:确保登录用户只能看到属于自己的预订记录。我们将从后端数据库设计、用户认证机制,到前端服务与组件的实现,全面解析这一过程,并特别关注在数据查询中可能遇到的参数绑定问题。

一、 后端架构:Flask与SQLite的数据管理

后端采用Flask框架,结合SQLite数据库,负责用户管理、认证以及预订数据的存储与检索。

1. 数据库初始化与表结构

首先,定义并创建两个核心数据库表:users 用于存储用户信息,reservations 用于存储预订信息,并通过 user_id 建立关联。

import sqlite3
import hashlib
from flask import Flask, request, jsonify, session
from flask_cors import CORS

app = Flask(__name__)
CORS(app)
app.config['SECRET_KEY'] = 'your_secret_password_here' # 生产环境请使用更复杂的密钥

def hash_password(password):
    """对密码进行SHA256哈希处理"""
    return hashlib.sha256(password.encode()).hexdigest()

def create_users_table():
    """创建用户表"""
    with sqlite3.connect('rental-users.db') as conn:
        cursor = conn.cursor()
        cursor.execute('''
            CREATE TABLE IF NOT EXISTS users (
                id INTEGER PRIMARY KEY AUTOINCREMENT,
                firstname TEXT NOT NULL,
                lastname TEXT NOT NULL,
                email TEXT UNIQUE NOT NULL,
                mobile TEXT NOT NULL,
                gender TEXT NOT NULL,
                hashed_password TEXT NOT NULL
            )
        ''')
        conn.commit()

def create_reservations_table():
    """创建预订表,包含外键user_id关联用户表"""
    with sqlite3.connect('rental-users.db') as conn:
        cursor = conn.cursor()
        cursor.execute('''
            CREATE TABLE IF NOT EXISTS reservations (
                id INTEGER PRIMARY KEY AUTOINCREMENT,
                user_id INTEGER,
                brand TEXT NOT NULL,
                from_location TEXT NOT NULL,
                to_location TEXT NOT NULL,
                FOREIGN KEY (user_id) REFERENCES users (id)
            )
        ''')
        conn.commit()

create_users_table()
create_reservations_table()

2. 用户认证与会话管理

  • 注册 (/register): 接收用户数据,哈希密码后存入 users 表。
  • 登录 (/login): 验证用户凭据,成功后将用户ID存储在Flask会话 (session['user_id']) 中。这是实现个性化功能的关键。
  • 登出 (/logout): 清除会话中的用户ID。
@app.route('/register', methods=['POST'])
def register_user():
    data = request.json
    # ... (字段校验逻辑省略,与原代码一致) ...
    hashed_password = hash_password(data['pwd'])
    with sqlite3.connect('rental-users.db') as conn:
        cursor = conn.cursor()
        cursor.execute('''
            INSERT INTO users (firstname, lastname, email, mobile, gender, hashed_password)
            VALUES (?, ?, ?, ?, ?, ?)
        ''', (data['firstname'], data['lastname'], data['email'], data['mobile'], data['gender'], hashed_password))
        conn.commit()
    return jsonify({'message': 'Registration successful'})

@app.route('/login', methods=['POST'])
def login_user():
    data = request.json
    # ... (字段校验逻辑省略,与原代码一致) ...
    with sqlite3.connect('rental-users.db') as conn:
        cursor = conn.cursor()
        cursor.execute('SELECT id, hashed_password FROM users WHERE email = ?', (data['email'],))
        user_record = cursor.fetchone()

    if user_record:
        user_id, stored_hashed_password = user_record
        if hash_password(data['pwd']) == stored_hashed_password:
            session['user_id'] = user_id # 存储用户ID到会话
            return jsonify({'message': 'Login successful', 'user_id': user_id}) # 可选择返回user_id给前端
        else:
            return jsonify({'error': 'Invalid password'}), 401
    else:
        return jsonify({'error': 'User not found'}), 404

@app.route('/logout', methods=['POST'])
def logout_user():
    session.pop('user_id', None)
    return jsonify({'message': 'Logout successful'})

3. 预订管理

  • 创建预订 (/make-reservation/): 接收预订数据和用户ID,将其存入 reservations 表。
  • 获取用户预订 (/user-reservations/): 这是实现个性化功能的核心。根据路径中提供的 user_id 查询对应的预订记录。
@app.route('/make-reservation/<int:user_id>', methods=['POST'])
def make_reservation(user_id):
    data = request.json
    with sqlite3.connect('rental-users.db') as conn:
        cursor = conn.cursor()
        cursor.execute('''
            INSERT INTO reservations (user_id, brand, from_location, to_location)
            VALUES (?, ?, ?, ?)
        ''', (user_id, data.get('brand'), data.get('from_location'), data.get('to_location')))
        conn.commit()
    return jsonify({'message': 'Reservation successful'})

@app.route('/user-reservations/<int:user_id>', methods=['GET'])
def get_user_reservations(user_id):
    with sqlite3.connect('rental-users.db') as conn:
        cursor = conn.cursor()
        # 标准且推荐的SQLite参数绑定方式:使用单元素元组
        cursor.execute('SELECT id, user_id, brand, from_location, to_location FROM reservations WHERE user_id = ?', (user_id,))
        reservations = [
            {'id': row[0], 'user_id': row[1], 'brand': row[2], 'from_location': row[3], 'to_location': row[4]}
            for row in cursor.fetchall()
        ]
    return jsonify(reservations)

关于 sqlite3.execute 参数绑定的注意事项:

在Python的 sqlite3 模块中,cursor.execute() 方法的第二个参数期望一个序列(如元组或列表)来绑定SQL查询中的占位符 ?。即使只有一个参数,也应该将其包装在一个单元素元组中,例如 (user_id,)。这是标准且最健壮的做法。

虽然在某些特定环境或 sqlite3 版本中,直接传递一个非序列类型(如整数 user_id)可能在特定情况下“奏效”或解决某些问题,但这不符合 sqlite3 API 的通用约定,并且可能在其他环境中导致 TypeError 或意外行为。为了代码的兼容性和可维护性,强烈建议始终使用序列进行参数绑定。

PHP与MySQL程序设计3 PHP与MySQL程序设计3

本书是全面讲述PHP与MySQL的经典之作,书中不但全面介绍了两种技术的核心特性,还讲解了如何高效地结合这两种技术构建健壮的数据驱动的应用程序。本书涵盖了两种技术新版本中出现的最新特性,书中大量实际的示例和深入的分析均来自于作者在这方面多年的专业经验,可用于解决开发者在实际中所面临的各种挑战。 本书内容全面深入,适合各层次PHP和MySQL开发人员阅读,既是优秀的学习教程,也可用作参考手册。

PHP与MySQL程序设计3 253 查看详情 PHP与MySQL程序设计3

二、 前端集成:Angular的服务与组件

Angular前端负责用户界面、API调用以及用户状态管理。

1. AuthorizationService:用户认证与状态管理

该服务处理用户登录、注册、登出,并维护用户的登录状态和用户ID。

import { Injectable } from '@angular/core';
import { Router } from '@angular/router';
import { HttpClient } from '@angular/common/http';
import { Observable, throwError } from 'rxjs';
import { catchError, tap } from 'rxjs/operators';

@Injectable({
  providedIn: 'root'
})
export class AuthorizationService {
  private apiUrl = 'http://localhost:5000';
  private currentUser: { id: number | null; firstname: string; lastname: string; email: string } = { id: null, firstname: '', lastname: '', email: '' };
  private _isLoggedIn = false;

  get isLoggedIn(): boolean {
    return this._isLoggedIn;
  }

  constructor(private router: Router, private http: HttpClient) {}

  loginUser(loginData: any): Observable<any> {
    const url = `${this.apiUrl}/login`;
    return this.http.post(url, loginData).pipe(
      tap((response: any) => {
        // 假设后端在登录成功时返回 user_id
        this.currentUser.id = response.user_id;
        this._isLoggedIn = true;
        // 实际应用中,可能还需要存储 token 或其他用户信息
      }),
      catchError((error) => {
        this._isLoggedIn = false;
        this.currentUser.id = null;
        return throwError(error);
      })
    );
  }

  logout(): Observable<any> {
    const url = `${this.apiUrl}/logout`;
    return this.http.post(url, {}).pipe(
      tap(() => {
        this.currentUser = { id: null, firstname: '', lastname: '', email: '' };
        this._isLoggedIn = false;
        this.router.n*igate(['/login']);
      })
    );
  }

  // 注册用户逻辑与原代码类似,此处省略

  getUserId(): number | null {
    return this.currentUser.id;
  }
}

重要提示: 在 loginUser 成功后,后端返回的 user_id 应该被前端捕获并存储,以便后续的个性化请求使用。原后端代码中 login_user 仅返回 {'message': 'Login successful'},建议修改为 return jsonify({'message': 'Login successful', 'user_id': user[0]}),以便前端能获取到用户ID。

2. ReservationService:预订相关的API调用

该服务封装了与后端预订相关的API请求。

import { Injectable } from '@angular/core';
import { HttpClient } from '@angular/common/http';
import { Observable } from 'rxjs';

@Injectable({
  providedIn: 'root',
})
export class ReservationService {
  private apiUrl = 'http://localhost:5000';

  constructor(private http: HttpClient) {}

  makeReservation(userId: number, brand: string, fromLocation: string, toLocation: string): Observable<any> {
    const url = `${this.apiUrl}/make-reservation/${userId}`;
    const reservationData = {
      brand: brand,
      from_location: fromLocation,
      to_location: toLocation,
    };
    return this.http.post(url, reservationData);
  }

  getUserReservations(userId: number): Observable<any[]> {
    const url = `${this.apiUrl}/user-reservations/${userId}`;
    return this.http.get<any[]>(url);
  }
}

3. MyReservationsComponent:展示用户预订

此组件负责显示用户界面以进行预订,并在页面加载时获取并显示当前用户的预订历史。

import { Component, OnInit } from '@angular/core';
import { AuthorizationService } from '../authorization.service';
import { ReservationService } from '../reservation.service';
import { ToastrService } from 'ngx-toastr';

@Component({
  selector: 'app-my-reservations',
  templateUrl: './my-reservations.component.html',
  styleUrls: ['./my-reservations.component.css'],
})
export class MyReservationsComponent implements OnInit {
  reservationData = {
    brand:  '',
    from_location: '',
    to_location: '',
  };

  reservations: any[] = [];

  constructor(
    private reservationService: ReservationService,
    private authService: AuthorizationService,
    private toastr: ToastrService
  ) {}

  ngOnInit() {
    this.loadReservationHistory();
  }

  makeReservation() {
    const userId = this.authService.getUserId();
    if (userId === null) {
      this.toastr.error('Please log in to make a reservation.', 'Error');
      return;
    }

    this.reservationService
      .makeReservation(
        userId, // 确保这里传递的是非null的userId
        this.reservationData.brand,
        this.reservationData.from_location,
        this.reservationData.to_location
      )
      .subscribe(
        (res) => {
          this.toastr.success('Reservation successful!', 'Success');
          this.loadReservationHistory(); // 预订成功后刷新列表
          // 清空表单
          this.reservationData = { brand: '', from_location: '', to_location: '' };
        },
        (err) => {
          console.error('Error making reservation:', err);
          this.toastr.error('Error making reservation', 'Error');
        }
      );
  }

  loadReservationHistory() {
    const userId = this.authService.getUserId();
    if (userId === null) {
      this.toastr

以上就是Angular与Flask全栈应用中实现用户个性化数据展示教程的详细内容,更多请关注其它相关文章!


# 安平网站推广怎么做的呀  # 程序设计  # 两种  # 将其  # 书中  # 如何使用  # 自己的  # 平度营销型网站推广  # 加强新媒体营销推广的措施  # 本书  # 酉阳中小企业seo推广  # 工艺品模型网站推广方式  # 武安抖音seo优化  # 深圳网站优化排名电话  # 怎样做好营销网站建设  # 福永国内网站优化  # 专业微信营销推广价格  # css  # 这是  # 绑定  # 会话管理  # ai  #   # 后端  # session  # app  # go  # json  # 前端  # js  # html  # python  # word 


相关栏目: 【 Google疑问12 】 【 Facebook疑问10 】 【 优化推广96088 】 【 技术知识133117 】 【 IDC资讯59369 】 【 网络运营7196 】 【 IT资讯61894


相关推荐: 微信朋友圈怎么设置三天可见 微信朋友圈设置指定天数可见步骤【教程】  b站怎么设置动态仅粉丝可见_b站动态粉丝可见设置方法  《下一站江湖2》独孤剑诀习得方法  鸣潮历史学家灯塔位置一览  mysql如何限制远程访问_mysql远程访问限制方法  CSS如何使用outline-offset与颜色组合突出元素边框  《米姆米姆哈》米姆获取及技能攻略  《书耽》更换手机号方法  Pydantic 中“schema”字段命名冲突的解决方案  不吃碳水化合物是健康减肥的好办法吗  优化CSS动画与J*aScript定时器协同:构建稳定Toast提示  win11如何诊断DirectX问题 Win11运行dxdiag工具排查显卡故障【排错】  抖音号升级成企业资质怎么弄?有什么好处?  Google Cloud Functions 时区处理指南:理解与最佳实践  Win10锁屏时间怎么设置 Win10调整自动锁屏时间方法  Go App Engine 项目结构与包管理深度指南  sublime如何配置PHP开发环境_在sublime中运行与调试PHP代码  如何自定义苹果手机铃声  解决C#跨线程访问XML对象的异常 安全的并发XML处理模式  《360浏览器》设置摄像头权限方法  响应式设计中动态背景颜色条的实现指南  Win11如何分屏操作_Win11多窗口分屏技巧  Lar*el Eloquent中通过Join查询关联数据表:解决多行子查询问题  有道AI翻译入口 智能写作官方网站入口  GBA模拟器手柄按键设置  抖音视频如何添加标题?添加标题有哪些好处?  手机自动关机是怎么回事?如何修复?手机异常关机的原因排查与修复技巧  B站怎么快速升级 B站用户等级提升攻略【详解】  重返未来:1999卡戎全方位攻略  漫蛙官网(首页入口)_漫蛙漫画稳定访问教程分享  J*aScript包管理器_Npm与Yarn对比  批改网官网首页登录 批改网学生用户登录入口  雨课堂官网在线登录 网页版雨课堂登录链接  《漫蛙manwa2》防走失网页版链接2025  Highcharts雷达图径向轴数值标签实现教程  uc浏览器官网网页版使用 uc浏览器官网免费在线首页  126手机126邮箱登录_126邮箱手机登录入口官网  使用document.execCommand实现Web文本编辑器加粗/取消加粗  HTML中多图片上传与预览:解决ID冲突的专业指南  网页版网易云音乐入口_网易云音乐在线官网登录  word邮件合并怎么插入个性化图片_Word邮件合并插入个性化图片方法  b站如何管理订阅_b站订阅标签分类管理  猫眼电影app如何设置电影上映提醒_猫眼电影上映提醒设置教程  惠普电脑BIOS界面看不懂怎么办_HP电脑BIOS功能选项解读与设置  抖音赚钱快速入门_新手必看的抖音赚钱步骤  解决CSS布局中意外顶部空白问题的教程  如何在CSS中设置背景图像:一个全面指南  Firefox OS应用开发:解决XMLHttpRequest跨域请求阻塞问题  Python对象引用与属性赋值:理解链表中的行为  Golang中的rune与byte类型区别是什么_Golang字符与字节处理详解 

 2025-11-12

了解您产品搜索量及市场趋势,制定营销计划

同行竞争及网站分析保障您的广告效果

点击免费数据支持

提交您的需求,1小时内享受我们的专业解答。

运城市盐湖区信雨科技有限公司


运城市盐湖区信雨科技有限公司

运城市盐湖区信雨科技有限公司是一家深耕海外推广领域十年的专业服务商,作为谷歌推广与Facebook广告全球合作伙伴,聚焦外贸企业出海痛点,以数字化营销为核心,提供一站式海外营销解决方案。公司凭借十年行业沉淀与平台官方资源加持,打破传统外贸获客壁垒,助力企业高效开拓全球市场,成为中小企业出海的可靠合作伙伴。

 8156699

 13765294890

 8156699@qq.com

Notice

We and selected third parties use cookies or similar technologies for technical purposes and, with your consent, for other purposes as specified in the cookie policy.
You can consent to the use of such technologies by closing this notice, by interacting with any link or button outside of this notice or by continuing to browse otherwise.