python新手案例——商品进销存管理系统(pymysql+xlwt)(一)
作者:mmseoamin日期:2023-12-27

一、pymysql和xlwt介绍

1.pymsq

         此模块的作用是让python语言能够对数据库的表进行操作,在此,我们需要简单地了解以下pymysql最基础的操作。

Python3 MySQL 数据库连接 – PyMySQL 驱动 | 菜鸟教程 (runoob.com)

2.xlwt

        此模块是excel写入模块,在该系统中,我们需要导出数据,最好的方式就是使用表格保存。

Python3 xlwt库基本教程_w3cschool

二、功能模块

        本系统设置了6个功能模块(商品信息管理、商品销售管理、商品信息导入、交易记录查询、信息导出、用户信息管理)。其中商品信息管理、商品销售管理以及用户信息管理,分别有字模块。商品信息管理的子模块为,商品信息修改、商品下架(删除)、商品销量查询、商品库存查询。商品销售管理的子模块为,商品销售和商品退货。用户信息管理的子模块为,密码修改、用户信息删除、用户信息导入。


                                                图1.商品进销存管理系统模块

 三、数据库设计

将由需求分析得到的用户需求抽象为信息结构,即概念模型的设计过程就是概念结构设计。概念结构设计是整个数据库设计的关键。概念模型独立于计算机硬件结构,独立于数据库的DBMS。

在进行数据库设计时,如果将现实世界中的客观对象直接转换为机器世界中的对象,会很不方便,注意力也往往被转移到更多的细节限制方面,而不能集中在最重要的信息的组织结构和处理模式上。因此,通常是将现实世界中的客观对象首先抽象为不依赖于任何具体机器的信息结构。这种信息结构不是DBMS所支持的数据模型,而是概念模型,然后再把概念模型转换为具体机器上DBMS支持的数据模型。设计概念模型的过程称为概念设计。

1. 概念结构设计

实体属性:

商品(商品号,商品名,商品类别,商品进价,商品售价,商品单位,商品库存,商品销量)

交易记录(商品号,交易量,交易业务,交易时间)

用户(用户账号,用户密码,用户权限)

选用何种概念模型完成概念设计任务,是进行概念设计前应该考虑的首要问题。用于概念设计的模型既要有足够的表达能力,使之可以表示各种类型的数据及其相互间的联系和语义,又要简单易懂。这种模型有很多,如 E-R模型、语义数据模型和函数数据模型等。其中,E-R模型提供了规范、标准的构造方法,成为应用最广泛的概念结构设计工具。

图1.商品进销存E-R模型

  2. 逻辑结构设计

概念设计中得到的E-R图是由实体、属性和联系组成的,而关系数据库逻辑设计的结果是一组关系模式的集合,所以将E-R图转换为关系模型实际上就是将实体、属性和联系转换成关系模式。

  E-R模型转关系模型:

商品(商品号,商品名,商品类别,商品进价,商品售价,商品单位,商品库存,商品销量)

交易记录(商品号,交易量,交易业务,交易时间

用户(用户账号,用户密码,用户权限)

进行(交易时间,商品号)

管理(商品号,用户账号)

查看(商品号交易时间,用户账号)

3.物理结构设计

数据库的物理结构设计是利用数据库管理系统提供的方法、技术,对已经确定的数据库逻辑结构,以较优的存储结构、数据存取路径、合理的数据库存储位置及存储分配,设计出一个高效的、可实现的物理数据库结构。

    由于不同的数据库管理系统提供的硬件环境和存储结构、存取方法不同,提供给数据库设计者的系统参数以及变化范围也不同,因此物理结构设计一般没有一个通用的准则,它只能提供一个技术和方法供参考。

  Goods表用于存放商品信息

  SQL语句:

  create table goods(
    gid bigint(5) not null PRIMARY KEY auto_increment,
    gname varchar(10),
    gcategory varchar(10),
    gprice float,
    sprice float,
    gunit char,
    gnum int,
    snum int not null default 0
);

表结构:


图2.goods表结构

gid 为商品号,非空,是主键,自增的,在导入商品时就不用考虑商品号重复的问题。gname 为商品名,gcategory 为商品类别,gprice 为商品进价,sprice 为商品售价,gunit 为商品单位,gnum 为商品库存量。Snum为商品销售量,由于在导入商品信息时不需要填写销售量,就使他默认值为0.

      Journal表用于存放交易记录

      SQL语句:

create table journal(
  gid bigint(5) not null,
  jnum int,
  jremarks varchar(10),
  jtime datetime
);

表结构:


图3.journal表结构

gid 为商品名,非空,jum 为交易量,jremarks 为交易业务,jtime 为交易时间。

User表用于存放用户信息

SQL语句:

create table user(
    uid varchar(7) not null PRIMARY KEY unique,
    upassword varchar(50) not null,
    root varchar(10)
);

      表结构:


图4.user表结构

Uid 为用户账号非空,主键,且唯一。Upassword 为用户密码,root 为用户权限。

四、python代码实现

import pymysql#导入pymysql,数据将储存在数据库中
import xlwt#导入xlwt,可将数据导出为表格形式
#显示菜单函数
def show():
    print('______________')
    print('1.商品信息管理\n2.商品销售管理\n3.导入商品信息\n4.交易记录查询\n5.导出信息\n6.用户管理\n7.退出系统\n')
    print('______________')
def goods_show():
    print('______________')
    print('1.商品信息修改\n2.商品下架\n3.商品库存查询\n4.商品销量查询\n5.返回\n')
    print('______________')
def sale_show():
    print('______________')
    print('1.出售商品\n2.商品退货\n3.返回\n')
    print('______________')
def user_show():
    print('______________')
    print('1.用户密码修改\n2.用户信息删除\n3.用户信息导入\n4.返回\n')
    print('______________')
#商品信息修改函数
def goods_change(gid):#参数为商品id
    cursor = db.cursor()#创建一个游标对象
    sql1 = '''select gid from goods;'''#编写SQL语句
    cursor.execute(sql1)#执行SQL语句
    re=cursor.fetchall()#返回查询结构
    idls=[]#空id列表,存放查询出来的id
    for i in re:
        idls.append(i[0])
    if int(gid) in idls:#判断该id是否存在
        a, b, c, d, e = input('请输入新的,商品类别,商品进价,商品售价,商品单位,商品库存,用空格隔开\n').split()
        sql2 = '''update goods set gcategory='{}',gprice={},sprice={},gunit='{}',gnum={} where gid={} ;'''\
            .format( a, float(b), float(c),d,int(e), int(gid))#此SQL语句实现商品信息表的数据更新
        cursor.execute(sql2)
        db.commit()#提交事务
        print('修改成功!!')
    else:#不存在就没有该商品
        print('没有该商品!!')
    cursor.close()#关闭游标
#下架商品函数
def goods_delete(gidls):#参数为商品id列表
    cursor = db.cursor()
    sql1 = '''select gid from goods;'''
    cursor.execute(sql1)
    re=cursor.fetchall()
    idls=[]
    for i in re:
        idls.append(i[0])
    for i in gidls:#建立循环,遍历输入的商品id列表
        if int(i) in idls:
            sql2 = '''delete from goods where gid={} '''.format(int(i))#删除的SQL语句
            cursor.execute(sql2)
            db.commit()#提交事务
            print('{}下架成功!!'.format(i))
        else:
            print('没有{}该商品!!'.format(i))
    cursor.close()#关闭游标
#查询商品库存函数
def inventory_inquire():
    cursor = db.cursor()
    sql = '''select gname,gunit,gnum from goods;'''#此SQL语句实现表的查询
    cursor.execute(sql)
    cl = ['商品名称', '商品单位', '商品库存量']#该列表存放字典的key
    re2 = []#该列表存放字典
    re = cursor.fetchall()
    for i in re:
        re1 = []#该列表存放查询出来的信息,是字典的value,并且每次循环都会清空
        for j in i:
            re1.append(j)
        re2.append(dict(zip(cl, re1)))#zip()将key列表和value列表链接起来
    re2.sort(key=lambda x:x['商品库存量'])#将列表排序,升序
    for i in re2:#循环输出
        print(i,end='\n')
    cursor.close()
#商品销量查询函数
#该函数与库存查询函数相似
def sale_inquire():
    cursor = db.cursor()
    sql = '''select gname,gprice,snum from goods;'''#与库存查询函数的不同点
    cursor.execute(sql)
    cl = ['商品名称', '商品售价', '商品销售量']
    re2 = []
    re = cursor.fetchall()
    for i in re:
        re1 = []
        for j in i:
            re1.append(j)
        re2.append(dict(zip(cl, re1)))
    re2.sort(key=lambda x:x['商品销售量'])
    for i in re2:
        print(i,end='\n')
    cursor.close()
#销售商品函数
def sale_goods(sidls,snumls):#参数是商品id列表和销售数量列表
    cursor=db.cursor()
    sql1='''select gid,gnum from goods;'''#此SQL语句是查询id和库存
    sql2='''select gid,snum from goods;'''#此SQL语句是查询id和销量
    cursor.execute(sql1)
    re=cursor.fetchall()#存放sql1的查询结果
    cursor.execute(sql2)
    re1=cursor.fetchall()#存放sql2的查询结果
    cursor.close()
    sales={}#键是gid,值是库存量
    sales1={}#键是gid,值是销售量
    id_num=dict(zip(sidls,snumls))#将商品id列表和销售数量列表链接成字典
    for i in re:
        sales[i[0]]=i[1]
    for i in re1:
        sales1[i[0]]=i[1]
    for sid,snum in id_num.items():#遍历字典的items形式
        if int(sid) in sales.keys():#判断是否有该商品
            if sales[int(sid)]>=int(snum):#判断是否库存充足
                cursor = db.cursor()
                sql1 = '''update goods set gnum = %f,snum = %f where gid = %d ''' %\
                       (sales[int(sid)] - int(snum), sales1[int(sid)] + int(snum), int(sid))#该SQL语句更新库存量和销量
                sql2 = '''insert into journal values('{}',{},'{}',now());'''\
                    .format(int(sid), int(snum), '出售')#该SQL语句在表journal插入交易记录
                cursor.execute(sql1)#执行
                cursor.execute(sql2)
                db.commit()#提交
                cursor.close()
                print("{}出售成功".format(sid))
            else:
                print('{}库存不足!!'.format(sid))
        else:
            print('没有{}该商品!!'.format(sid))
#商品退货函数
#该函数与销售商品函数相似
def return_goods(gidls,rnumls):#参数是id列表和退货量列表
    cursor = db.cursor()
    sql1 = '''select gid,gnum from goods;'''
    sql2 = '''select gid,snum from goods;'''
    cursor.execute(sql1)
    re = cursor.fetchall()
    cursor.execute(sql2)
    re1 = cursor.fetchall()
    cursor.close()
    sales = {}
    sales1 = {}
    id_num = dict(zip(gidls, rnumls))
    for i in re:
        sales[i[0]] = i[1]
    for i in re1:
        sales1[i[0]] = i[1]
    for gid,rnum in id_num.items():
        if int(gid) in sales.keys():
            cursor = db.cursor()
            sql1 = '''update goods set gnum = %f,snum = %f where gid = '%s' ''' % \
                   (sales[int(gid)] + int(rnum), sales1[int(gid)] - int(rnum), int(gid))#销量减少,库存增加
            sql2 = '''insert into journal values('{}',{},'{}',now());'''\
                .format(int(gid), int(rnum), '退货')#新增退货记录
            cursor.execute(sql1)
            cursor.execute(sql2)
            db.commit()
            cursor.close()
            print("{}退货成功".format(gid))
        else:
            print('{}没有该商品!!'.format(gid))
#商品导入函数
def import_goods(goods_ls):#参数是商品信息列表
    gname,gcategory,gprice,sprice,gunit,gnum=goods_ls.split()#分割商品信息
    cursor = db.cursor()
    sql2='''insert into goods(gname,gcategory,gprice,sprice,gunit,gnum) values('{}','{}',{},{},'{}',{});'''\
        .format(gname,gcategory,float(gprice),float(sprice),gunit,int(gnum))#插入商品信息
    cursor.execute(sql2)
    db.commit()#提交事务
    print("{}导入成功".format(gname))
    cursor.close()
#交易记录查询函数
def transaction_inquire():
    cursor = db.cursor()
    sql='''select *from journal'''
    cursor.execute(sql)
    re=cursor.fetchall()
    ls1=['商品名称','交易数量','交易业务','交易时间']
    for i in re:
        ls2=[]#存放数据的列表
        for j in i:
            ls2.append(j)
        ls2[-1]=str(ls2[-1])
        print(dict(zip(ls1,ls2)))#每次循环都输出一条数据
    cursor.close()
#导出商品数据函数,该函数是导出全部关于商品的信息
def out_file():
    workbook = xlwt.Workbook(encoding='utf-8')#创建一个表格对象
    #四张表的字段名
    ls1 = ['id', '名称', '类别', '售价', '单位']
    ls2 = ['id', '名称', '销量', '售价']
    ls3 = ['id', '名称', '库存量', '进价']
    ls4 = ['id', '交易量', '业务', '时间']
    #创建sheet对象,设置四张表的名称和字段名
    worksheet1 = workbook.add_sheet('商品基本信息')
    for i in range(len(ls1)):
        worksheet1.write(0, i, ls1[i])
    worksheet2 = workbook.add_sheet('商品销量信息')
    for i in range(len(ls2)):
        worksheet2.write(0, i, ls2[i])
    worksheet3 = workbook.add_sheet('商品库存信息')
    for i in range(len(ls3)):
        worksheet3.write(0, i, ls3[i])
    worksheet4 = workbook.add_sheet('交易记录')
    for i in range(len(ls4)):
        worksheet4.write(0, i, ls4[i])
    #四条SQL语句的查询结果是四张表将要写入的数据
    sql1 = '''select gid,gname,gcategory,sprice,gunit from goods;'''
    sql2 = '''select gid,gname,snum,sprice from goods;'''
    sql3 = '''select gid,gname,gnum,gprice from goods;'''
    sql4 = '''select *from journal;'''
    #执行SQL语句并将结果保存
    cursor = db.cursor()
    cursor.execute(sql1)
    re1 = cursor.fetchall()
    cursor.execute(sql2)
    re2 = cursor.fetchall()
    cursor.execute(sql3)
    re3 = cursor.fetchall()
    cursor.execute(sql4)
    re4 = cursor.fetchall()
    #写入数据操作
    j = 1#表示第2行
    for i in re1:#此循环是遍历查询出来的元组
        for k in range(5):#表示第几列,此循环的作用是第某行每列的写入
            worksheet1.write(j, k, i[k])
        j += 1#每次循环行数增加
    j = 1
    for i in re2:
        for k in range(4):
            worksheet2.write(j, k, i[k])
        j += 1
    j = 1
    for i in re3:
        for k in range(4):
            worksheet3.write(j, k, i[k])
        j += 1
    j = 1
    for i in re4:
        for k in range(4):
            worksheet4.write(j, k, str(i[k]))
        j += 1
    #保存表格
    workbook.save('全部商品信息.xls')
    print("导出成功!!")
#密码修改函数
def change_password(new_password):#参数是新的密码
    second=input("请再次输入新密码:")#再次输入密码,进行确认
    if new_password!=second:#两次密码是否一致
        print('两次密码不一致,退出业务')
    else:#一致就更新数据
        cursor = db.cursor()
        sql = '''update user set upassword = '{}' where uid = '{}';'''\
            .format(new_password,user_id)#user_id是一个全局变量
        cursor.execute(sql)
        db.commit()
        cursor.close()
        print("修改成功!!")
#判断是否有更高的权限的函数
def user_power(uid):
    cursor = db.cursor()
    sql='''select root from user where uid = '{}';'''.format(uid)
    cursor.execute(sql)
    r=cursor.fetchall()
    re=r[0][0]#存放结果
    cursor.close()
    #返回一个布尔值
    if re=='yes':
        return True
    else:
        return False
#用户删除函数
def user_delete(uidls):#参数是用户id列表
    #该函数的操作与商品下架函数相似
    cursor = db.cursor()
    sql1 = '''select uid from user;'''
    cursor.execute(sql1)
    re=cursor.fetchall()
    idls=[]
    for i in re:
        idls.append(i[0])
    for i in uidls:
        if i in idls:
            sql2 = '''delete from user where uid='{}' '''.format(i)
            cursor.execute(sql2)
            db.commit()
            print('{}删除成功!!'.format(i))
        else:
            print('没有{}该用户!!'.format(i))
    cursor.close()
#导入用户函数
def import_user(userls):
    uid,upassword,root=userls.split()
    #如果已经有了该用户,是无法插入的,在表结构中设置了唯一约束
    try:
        cursor = db.cursor()
        sql='''insert into user values('{}','{}','{}')'''.format(uid,upassword,root)
        cursor.execute(sql)
        db.commit()
        cursor.close()
        print("导入成功!!")
    except:#出现错误就反馈导入失败
        print("导入失败!!")
        db.rollback()
user_id=''#全局变量,登录之后就会被赋值
#登录函数
def login():
    cursor = db.cursor()
    sql='''select *from user;'''
    cursor.execute(sql)
    re=cursor.fetchall()
    users={}
    for i in re:
        users[i[0]]=i[1]
    cursor.close()
    id=input('请输入账号:\n')
    password=input('请输入密码:\n')
    #判断用户是否存在
    if id not in users.keys():
        print('用户不存在')
        return False
    global user_id#声明全局变量
    #判断密码是否正确
    for i in range(2):#如果错误可以再次输入两次
        if users[id]==password:
            user_id=id#将登录成功的id赋给全局变量user_id
            print('登录成功')
            return True
        else:
            password = input('密码错误,请重新请输入密码:\n')
    else:
        print('密码错误三次')
        return False
#主函数
def main():
    if login():#根据登录函数返回的布尔值,判断是否该执行以下程序
        while True:#永真循环,输入指定数字才会跳出
            show()#展示菜单
            n=input("请输入业务序号:")
            if n=='1':
                goods_show()
                x=input('请输入业务序号:')
                if x=='1':
                    gid=input('请输入要修改商品的id: ')
                    goods_change(gid)
                if x=='2':
                    gid=input('请输入要下架商品的id(可批量下架,用空格隔开): ').split()#可输入多个id
                    goods_delete(gid)
                if x=='3':
                    inventory_inquire()
                if x=='4':
                    sale_inquire()
                if x=='5':
                    continue
            if n=='2':
                sale_show()#显示子菜单
                x=input('请输入业务号:')
                if x=='1':
                    #可循环输入多组数据
                    sidls = []
                    gnumls = []
                    print("请输入出售商品的id和数量,以空格分开,0表示输入结束")
                    while True:
                        n = input()
                        if n == '0':#输入0表示结束输入
                            break
                        else:
                            nn = n.split()
                        sidls.append(nn[0])
                        gnumls.append(nn[1])
                    sale_goods(sidls, gnumls)
                if x=='2':
                    #循环输入多组数据
                    gidls = []
                    rnumls = []
                    print("请输入退货商品的id和数量,以空格分开,0表示输入结束")
                    while True:
                        n = input()
                        if n == '0':#输入0表示结束
                            break
                        else:
                            nn = n.split()
                        gidls.append(nn[0])
                        rnumls.append(nn[1])
                    return_goods(gidls, rnumls)
                if x=='3':
                    continue
            if n=='3':
                #循环输入
                print("请输入导入的商品信息(名称,类别,进价,售价,单位,库存量),以空格分开,0表示输入结束")
                while True:
                    n = input()
                    if n == '0':#结束条件
                        break
                    import_goods(n)
            if n=='4':
                transaction_inquire()
            if n=='5':
                out_file()
            if n=='6':
                user_show()#显示子菜单
                x=input('请输入业务号:')
                if x=='1':
                    new_password=input('请输入新密码:')
                    change_password(new_password)
                if x=='2':
                    #判断是否有权限操作
                    if user_power(user_id):#传入全局变量
                        n=input("请输入删除的用户id,空格隔开\n").split()
                        user_delete(n)
                    else:
                        print("无操作权限!!")
                if x=='3':
                    #判断是否有权限操作
                    if user_power(user_id):
                        print("请输入导入的用户信息(id,密码,权限),以空格分开,0表示输入结束")
                        while True:
                            n = input()
                            if n == '0':
                                break
                            import_user(n)
                    else:
                        print("无操作权限!!")
            if n=='7':
                break
    else:
        print('登录失败')
if __name__ == '__main__':
    #连接数据库
    db = pymysql.connect(host='localhost',
                         user='root',
                         password='*******',
                         db='GMS')
    main()#执行主函数
    db.close()#关闭数据库

五、总结

python新手非常适合拿此类项目练手,但是必须要对MySQL数据库相关的知识有一定了解。

如果需要项目报告可以留言或私信。

下一期文章将对大家在评论区留下的问题进行解决,希望大家能在评论区留下建议或者不同的观点吧!!

大家可以投一下票,如果有必要,下篇文章,将会简单讲一下MySQL已经SQL语法。