IT数码 购物 网址 头条 软件 日历 阅读 图书馆
TxT小说阅读器
↓语音阅读,小说下载,古典文学↓
图片批量下载器
↓批量下载图片,美女图库↓
图片自动播放器
↓图片自动播放器↓
一键清除垃圾
↓轻轻一点,清除系统垃圾↓
开发: C++知识库 Java知识库 JavaScript Python PHP知识库 人工智能 区块链 大数据 移动开发 嵌入式 开发工具 数据结构与算法 开发测试 游戏开发 网络协议 系统运维
教程: HTML教程 CSS教程 JavaScript教程 Go语言教程 JQuery教程 VUE教程 VUE3教程 Bootstrap教程 SQL数据库教程 C语言教程 C++教程 Java教程 Python教程 Python3教程 C#教程
数码: 电脑 笔记本 显卡 显示器 固态硬盘 硬盘 耳机 手机 iphone vivo oppo 小米 华为 单反 装机 图拉丁
 
   -> Python知识库 -> web项目开发(仿网易云课堂) -> 正文阅读

[Python知识库]web项目开发(仿网易云课堂)

flask框架

首先用flask搭建一个本地的网站,对其进行配置

from flask import Flask
# 定义一个应用,一个网站就是一个应用
app = Flask(__name__)
app.debug = True

debug为TRUE时表示在pycharm中对网页的任何修改都可以随时的显示在网页中。

from flask_sqlalchemy import SQLAlchemy
app.config['SECRET_KEY'] = 'daxiongketang'
# sqlalchemy配置
app.config['SQLALCHEMY_DATABASE_URI'] = (
    'mysql+pymysql://root:root@localhost/daxiong?charset=utf8mb4'
)#数据库名称+启动引擎//数据库用户名:密码@本机名称/数据库存放的名称?字符集
app.config['SQLALCHEMY_TRACK_MODIFICATIONS'] = False
db = SQLAlchemy(app)#配置数据库对象

对该网站进行配置,网站会使用到MySQL,而在pycharm中用pymysql来启动数据库

路由:用装饰器制定特定的网页路径,当输入该路径时就会执行特定的函数

@app.route('/')
def index():
    return 'hello world'

所以当网址为http://127.0.0.1:5000/时就会自动执行这个index函数,网页会显示hello world

if __name__ == "__main__":
    app.run();

当执行app.run方法时就可以看到网页了

数据库的设计

有三张数据表分别为course,sale,user,表示课程信息,销售情况,用户信息。
课程信息与销售情况时一对一的关系,所以销售情况要以course_id作为外键,而课程信息与用户时多对多的关系。

这里使用到了sqlalchemy:
介绍一下orm与sqlalchemy
ORM 全称 Object Relational Mapping, 叫对象关系映射。简单的说,ORM 将数据库中的表与面向对象语言中的类建立了一种对应关系。这样,我们要操作数据库,数据库中的表或者表中的一条记录就可以直接通过操作类或者类实例来完成。

数据表声明

class Course(db.Model):
    course_id = db.Column(db.String(100),primary_key=True,nullable=False)#主键
    product_id = db.Column(db.String(100),nullable=False)
    product_type = db.Column(db.Integer, nullable=False)
    product_name = db.Column(db.String(125), nullable=False)
    provider = db.Column(db.String(125), nullable=False)
    score = db.Column(db.Float(2))
    score_level = db.Column(db.Integer)
    learner_count = db.Column(db.Integer)
    lesson_count = db.Column(db.Integer)
    lector_name = db.Column(db.String(125))
    original_price = db.Column(db.Float(2))
    discount_price = db.Column(db.Float(2))
    discount_rate = db.Column(db.Float(2))
    img_url = db.Column(db.String(125))
    big_img_url = db.Column(db.String(125))
    description = db.Column(db.Text)


class Sale(db.Model):
    id = db.Column(db.Integer,autoincrement=True,primary_key=True,nullable=False)
    course_id = db.Column(db.String(100),db.ForeignKey('course.course_id'))
    product_name = db.Column(db.String(125), nullable=False)
    learner_count = db.Column(db.Integer)
    create_time = db.Column(db.Date,default= datetime.today())
    # 关联关系
    course = db.relationship('Course',backref=db.backref('sale',lazy='dynamic'))#与course表建立关联关系,再使用backref这样在course表中就不必再写关联关系了

表建立好之后就可以运行:

if __name__ == "__main__":
    #app.run();
    db.create_all()

这样就会在MySQL中创建两个表course与sale

flask_script与flask_migrate

Flask Script扩展提供向Flask插入外部脚本的功能,包括运行一个开发用的服务器,一个定制的Python shell,设置数据库的脚本,cronjobs,及其他运行在web应用之外的命令行任务;使得脚本和系统分开;原文链接

manager.add_command('db', MigrateCommand)
manager.add_command('import_data', ImportData())

Flask-Migrate是一个为Flask应用处理SQLAlchemy数据库迁移的扩展,使得可以通过Flask的命令行接口或者Flask-Scripts对数据库进行操作。原文链接

将数据导入数据库

当在终端执行import_data时会执行将数据导入数据库的操作,而将Excel文件中的内容导入数据库要用到openpyxl模块

import os
from flask_script import Command
import openpyxl
from app.config import BASE_DIR
from app.models import Course, Sale
from app import db

class ImportData(Command):
    def run(self):
        print('导入开始')
        dir = os.path.join(BASE_DIR, 'c_excel')#找到Excel文件的路径,这个路径下又包含几个Excel表
        for file_name in os.listdir(dir):
            file_path = os.path.join(dir, file_name)#遍历Excel文件下的表,并生成路径
            self.save_to_mysql(file_path)#保存文件到SQL中
        print('导入完成')

    def save_to_mysql(self, file_path):
        fileds = ['course_id', 'product_id', 'product_name', 'product_type', 'provider', 'score', 'score_level',
                  'learner_count', 'lesson_count', 'lector_name', 'original_price', 'discount_price', 'discount_rate',
                  'img_url', 'big_img_url', 'description']#课程的属性
        create_time = file_path.split('.')[0][-10:]
        #导入excel
        wb = openpyxl.load_workbook(file_path)
        #选择worksheet
        index = wb.sheetnames#可以取得工作簿中所有表名的列表
        ws = wb[index[0]]
        for row in ws.iter_rows(min_row=2):#遍历表中的每行数据,从第二行开始
            data = [cell.value for cell in row]#这里的cell应该是每个单元格
            dict_val = dict(zip(fileds, data))#将每行的数据与fields拼接为字典
            #存入到course表中
            course = Course(**dict_val)#可用字典方式初始化
            self.save_course(course);
            # 存入到sale表中
            sale = Sale(course_id=dict_val['course_id'], product_name=dict_val['product_name'],
                        learner_count=dict_val['learner_count'], create_time=create_time)
            self.save_sale(sale, create_time)

        wb.close()
    def save_course(self, course):
        try:
            db.session.merge(course)  # 重复数据会替换
            db.session.commit()
        except:
            db.session.rollback()

    def save_sale(self, sale, create_time):
        try:
            data = Sale.query.filter_by(course_id=sale.course_id, create_time=create_time).first()
            if not data:
                db.session.add(sale)  # 判断数据是否重复,是则不添加
                db.session.commit()
        except:
            db.session.rollback()

if __name__ == "__main__":
    print(BASE_DIR)

config.py

import os
BASE_DIR = os.path.dirname(os.path.dirname(os.path.abspath(__file__)))

关于openpyxl:openpyxl讲解

网页设计

  • 首页:
    首页会展示免费课程以及热门课程,均展示6条
@app.route('/')
def index():
    #获取免费课程,学习人数从高到低,前6条
    free_course = Course.query.filter_by(original_price=0).order_by(
        desc('learner_count')).limit(6).all()
    #获取热门课程,评分从高到低,前6条
    hot_course = Course.query.filter(Course.original_price>0).order_by(
        desc('score')).limit(6).all()
    return render_template('index.html', free_course=free_course, hot_course=hot_course)

当网址后加/index就会展示首页网页(render_template实现)
参考链接:render_template讲解

{% extends 'base.html' %}
#顶部与底部导航栏用的模板
{% block content %}
<div id="carouselExampleIndicators" class="carousel slide" data-ride="carousel">
  <ol class="carousel-indicators">
    <li data-target="#carouselExampleIndicators" data-slide-to="0" class="active"></li>
    <li data-target="#carouselExampleIndicators" data-slide-to="1"></li>
  </ol>
  <div class="carousel-inner">
    <div class="carousel-item active">
            <img src="../static/images/banner1.png"
           class="d-block w-100" alt="..." width="100%" height="380px">
    </div>
    <div class="carousel-item">
            <img src="../static/images/banner2.png"
           class="d-block w-100" alt="..." height="380px">
    </div>
  </div>
  <a class="carousel-control-prev" href="#carouselExampleIndicators" role="button" data-slide="prev">
    <span class="carousel-control-prev-icon" aria-hidden="true"></span>
    <span class="sr-only">Previous</span>
  </a>
  <a class="carousel-control-next" href="#carouselExampleIndicators" role="button" data-slide="next">
    <span class="carousel-control-next-icon" aria-hidden="true"></span>
    <span class="sr-only">Next</span>
  </a>
</div>
<div class="course_list" >
    <div style="text-align:center;padding-top:20px;">
        <h3>
            <img src="../static/images/title_left.png" height="28px">
            <span>免费课程</span>
            <img src="../static/images/title_right.png" height="28px">
        </h3>
    </div>
    <div class="container">
    <div class="row" style="text-align:center;">
        {% for course in free_course %}
        #遍历从数据库中查出的免费课程,对每一个课程进行网页设置
        <div class="col-sm-4" style="padding: 20px">
            <a href="/course/{{course.course_id}}" style="text-decoration: none;color:inherit;">
            <div class="card" style="height:280px">
                <img src="{{ course.big_img_url }}" class="card-img-top"
                     alt="..." height="170px">
                <div class="card-body">
                    <p class="card-title" style="font-weight: bold;">{{ course.product_name }}</p>
                    <p class="card-text">免费</p>
                </div>
            </div>
            </a>
        </div>
        {% endfor%}
    </div>
    </div>
</div>

<div class="course_list" >
    <div style="text-align:center;padding-top:20px;">
        <h3>
            <img src="../static/images/title_left.png" height="28px">
            <span>热门课程</span>
            <img src="../static/images/title_right.png" height="28px">
        </h3>
    </div>
<!--热门课程-->
    <div class="container">
        <div class="row" style="text-align:center;">
            {% for course in hot_course %}
            <div class="col-sm-4" style="padding: 20px">
                <a href="/course/{{ course.course_id }}" style="text-decoration: none;color:inherit;">#当点击某一个课程的时候就会跳到这个课程相关信息的网页中
                <div class="card" style="height:280px">
                    <img src="{{ course.big_img_url }}" class="card-img-top"
                         alt="..." height="170px">
                    <div class="card-body">
                        <p class="card-title" style="font-weight: bold;">{{ course.product_name }}</p>

                            <p class="card-text">{{ course.original_price }}</p>

                    </div>
                </div>
                </a>
            </div>
            {% endfor %}
        </div>
    </div>
</div>

</body>
</html>

{% endblock %}

关于课程详细信息的路由:

@app.route('/course/<course_id>')
def detail(course_id):
    course = Course.query.filter_by(course_id=course_id).first_or_404()#找不到则返回404页面
    return render_template('detail.html', course=course)

details.html:课程的详细信息

{% extends 'base.html' %}

{% block content%}

<div class="container">
    <div class="row" style="margin:20px auto;">
        <div class="col-sm-6">
            <img src="{{ course.big_img_url }}" class="card-img-top"
                 alt="..." width="450px" height="260px">
        </div>
        <div class="col-sm-6">
            <h4>{{ course.product_name }}</h4>
            <div class="course-info">课程讲师:{{ course.lector_name }}</div>
            <div class="course-info">所属机构:{{ course.provider }}</div>
            <div class="course-info">课程评分:{{ course.score }}</div>
            <div class="course-info">学习人数:{{ course.learner_count}}</div>
            <div class="course-price">
                {% if course.discount_price %}{{course.discount_price}}
                    <span style="color: #666666;font-size: 14px;text-decoration: line-through;">{{course.original_price}}</span>
                {% elif not course.original_price  %}
                    免费
                {% else %}{{course.original_price}}
                {% endif %}#如果有折扣价,则输出折扣价,否则返回原价,如果原价也没有,则显示免费
            </div>
            <div>
                {% if current_user.is_authenticated %}
                    <button class="btn btn-success" id="collect">
                        收藏
                    </button>
                {% endif %}
                <a href="https://study.163.com/course/introduction/{{course.course_id}}.htm" target="view_window">
                    <button type="button" class="btn btn-danger" >直达网易云</button>
                </a>
            </div>
        </div>
    </div>
    <div class="col-sm">
    <ul class="nav nav-tabs" id="myTab" role="tablist">
      <li class="nav-item">
        <a class="nav-link active" id="home-tab" data-toggle="tab" href="#home" role="tab" aria-controls="课程介绍" aria-selected="true">课程介绍</a>
      </li>
      <li class="nav-item">
        <a class="nav-link" id="week-tab" data-toggle="tab" href="#week" role="tab" aria-controls="本周销量" aria-selected="false">最近一周销量</a>
      </li>
      <li class="nav-item">
        <a class="nav-link" id="month-tab" data-toggle="tab" href="#month" role="tab" aria-controls="本月销量" aria-selected="true">最近一个月销量</a>
      </li>
      <li class="nav-item">
        <a class="nav-link" id="year-tab" data-toggle="tab" href="#year" role="tab" aria-controls="年度销量" aria-selected="false">每月销量</a>
      </li>
    </ul>
    <div class="tab-content" id="myTabContent" style="min-height:260px;padding:20px;background:#fbfbfb;">
      <div class="tab-pane fade show active" id="home" role="tabpanel" aria-labelledby="home-tab">
          {% for line in course.description.splitlines() %}
                {{ line }} <br>
          {% endfor %}
      </div>
      <div class="tab-pane fade" id="week" role="tabpanel" aria-labelledby="week-tab">
          <div id="sale-week" style="width: 1000px;height:400px;"></div>
      </div>
      <div class="tab-pane fade" id="month" role="tabpanel" aria-labelledby="month-tab">
          <div id="sale-month" style="width: 1000px;height:400px;"></div>
      </div>
      <div class="tab-pane fade" id="year" role="tabpanel" aria-labelledby="year-tab">
          <div id="sale-year" style="width: 1000px;height:400px;"></div>
      </div>
    </div>
    </div>
</div>
<script src="https://cdn.bootcdn.net/ajax/libs/jquery/3.5.1/jquery.js"></script>
<script src="https://cdn.bootcdn.net/ajax/libs/echarts/4.7.0/echarts-en.common.js"></script>
    <script type="text/javascript">
        $('.nav-link').click(function(){
            var id = $(this).attr('id')
            var type =  id.split("-")[0]
            var echarts_id = 'sale-' + type

            // 基于准备好的dom,初始化echarts实例
            var myChart = echarts.init(document.getElementById(echarts_id));

            // 指定图表的配置项和数据
            myChart.setOption({
                backgroundColor: new echarts.graphic.LinearGradient(0, 0, 0, 1, [{
                        offset: 0,
                        color: '#c86589'
                    },
                    {
                        offset: 1,
                        color: '#06a7ff'
                    }
                ], false),
                title: {
                    text: "OCTOBER 2015",
                    left: "center",
                    bottom: "5%",
                    textStyle: {
                        color: "#fff",
                        fontSize: 16
                    }
                },
                grid: {
                    top: '20%',
                    left: '10%',
                    right: '10%',
                    bottom: '15%',
                    containLabel: true,
                },
                xAxis: {
                    type: 'category',
                    boundaryGap: false,
                    data: ['14', '15', '16', '17', '18', '19', '20', '21', '22', '23'],
                    axisLabel: {
                        margin: 30,
                        color: '#ffffff63'
                    },
                    axisLine: {
                        show: false
                    },
                    axisTick: {
                        show: true,
                        length: 25,
                        lineStyle: {
                            color: "#ffffff1f"
                        }
                    },
                    splitLine: {
                        show: true,
                        lineStyle: {
                            color: '#ffffff1f'
                        }
                    }
                },
                yAxis: [{
                    type: 'value',
                    position: 'right',
                    axisLabel: {
                        margin: 20,
                        color: '#ffffff63'
                    },

                    axisTick: {
                        show: true,
                        length: 15,
                        lineStyle: {
                            color: "#ffffff1f",
                        }
                    },
                    splitLine: {
                        show: true,
                        lineStyle: {
                            color: '#ffffff1f'
                        }
                    },
                    axisLine: {
                        lineStyle: {
                            color: '#fff',
                            width: 2
                        }
                    }
                }],
                series: [{
                    name: '注册总量',
                    type: 'line',
                    smooth: true, //是否平滑曲线显示
                    showAllSymbol: true,
                    symbol: 'circle',
                    symbolSize: 6,
                    lineStyle: {
                        normal: {
                            color: "#fff", // 线条颜色
                        },
                    },
                    label: {
                        show: true,
                        position: 'top',
                        textStyle: {
                            color: '#fff',
                        }
                    },
                    itemStyle: {
                        color: "red",
                        borderColor: "#fff",
                        borderWidth: 3
                    },
                    tooltip: {
                        show: false
                    },
                    areaStyle: {
                        normal: {
                            color: new echarts.graphic.LinearGradient(0, 0, 0, 1, [{
                                    offset: 0,
                                    color: '#eb64fb'
                                },
                                {
                                    offset: 1,
                                    color: '#3fbbff0d'
                                }
                            ], false),
                        }
                    },
                    data: [393, 438, 485, 631, 689, 824, 987, 1000, 1100, 1200]
                }]
            });

            // 异步加载数据
            var url = '/course_data/' + {{ course.course_id }} + '/type/'+ type
            $.get(url).done(function (data) {
                data = JSON.parse(data)
                // 填入数据
                myChart.setOption({
                    title: {
                        text: data.title,
                    },
                    xAxis: {
                        data: data.categories
                    },
                    series: [{
                        // 根据名字对应到相应的系列
                        name: '销量',
                        data: data.data
                    }]
                });
            });
        });
    </script>
<script>
    $('#collect').click(function(){
        // 异步加载数据
        var url = '/collect/' + {{ course.course_id }}
        that = this
<!--        异步提交后this不指代原来的对象-->
        $.get(url).done(function (data) {
            if (data.result == 'error'){
                alert('系统异常,请稍后重试!')
            }
            // 请求成功,修改对于文字
            var text = $(that).text();
            if (text.trim() == '收藏'){
                $(that).text('取消收藏');
            } else {
                $(that).text('收藏');
            }
        })
    })
</script>

{% endblock %}

这里的详细信息会展示最近一个月,一周的销量,使用到了异步加载技术以及用echarts表格展示销量变化情况
异步加载参考:参考博客
表格展示:

@app.route('/course_data/<course_id>/type/<type>')
def course_data(course_id,type):
    if type == 'week':
        title = '最近一周销量'
        # 查询最近一周数据
        condition = 'DATE_SUB(CURDATE(),INTERVAL 7 DAY) <= date(create_time)'
        sql = f'select create_time,learner_count from sale where course_id = {course_id} and {condition} order by create_time'
        sale_data = db.session.execute(sql)
    elif type == 'month':
        title = '最近一个月销量'
        # 查询最近一个月的数据
        condition = 'DATE_SUB(CURDATE(),INTERVAL 1 MONTH) <= date(create_time)'
        sql = f'select create_time,learner_count from sale where course_id = {course_id} and {condition} order by create_time'
        sale_data = db.session.execute(sql)
    else:
        title = '本年度每月销量'
        # 查询每月1号的数据
        days = []
        current_year = datetime.datetime.today().year
        for i in range(1,13):
            # 2020-01-01
            days.append(f'{current_year}-{i:02}-01')#最近的数据没有
        condition = f'create_time in {tuple(days)}'
        sql = f'select create_time,learner_count from sale where course_id = {course_id} and {condition} order by create_time'
        sale_data = db.session.execute(sql)

    data = {}
    create_time = []
    learner_count = []
    for item in sale_data:
        print(item)
        create_time.append(item[0].strftime('%m-%d'))
        learner_count.append(item[1])

    data['title'] = title
    data['categories'] = create_time
    data['data'] = learner_count
    print(data)
    # dict_value = {
    #     'title': '一周销量',
    #     'categories':['2020-1-1','2020-1-2','2020-1-3'],
    #     'data':[500,600,700]
    # }
    result = json.dumps(data)#转化为json格式的数据
    return result
  Python知识库 最新文章
Python中String模块
【Python】 14-CVS文件操作
python的panda库读写文件
使用Nordic的nrf52840实现蓝牙DFU过程
【Python学习记录】numpy数组用法整理
Python学习笔记
python字符串和列表
python如何从txt文件中解析出有效的数据
Python编程从入门到实践自学/3.1-3.2
python变量
上一篇文章      下一篇文章      查看所有文章
加:2022-03-06 12:58:23  更:2022-03-06 13:01:44 
 
开发: C++知识库 Java知识库 JavaScript Python PHP知识库 人工智能 区块链 大数据 移动开发 嵌入式 开发工具 数据结构与算法 开发测试 游戏开发 网络协议 系统运维
教程: HTML教程 CSS教程 JavaScript教程 Go语言教程 JQuery教程 VUE教程 VUE3教程 Bootstrap教程 SQL数据库教程 C语言教程 C++教程 Java教程 Python教程 Python3教程 C#教程
数码: 电脑 笔记本 显卡 显示器 固态硬盘 硬盘 耳机 手机 iphone vivo oppo 小米 华为 单反 装机 图拉丁

360图书馆 购物 三丰科技 阅读网 日历 万年历 2024年11日历 -2024/11/15 22:51:37-

图片自动播放器
↓图片自动播放器↓
TxT小说阅读器
↓语音阅读,小说下载,古典文学↓
一键清除垃圾
↓轻轻一点,清除系统垃圾↓
图片批量下载器
↓批量下载图片,美女图库↓
  网站联系: qq:121756557 email:121756557@qq.com  IT数码