Python技术实现一个完整的任务需求

简介

题目:经理管理上市菜品数据库

经理要进行登录验证,用户名和密码与数据库中匹配才可进入
登录成功后经理可进行三种操作:查询,增加,删除
查询:输入命令select,查询结果会写入文件,并且显示在客户端
增加:输入命令add,每条记录以逗号分隔,记录之间以空格分隔
删除:输入命令delete,输入要删除的菜品编号
文件有两个:
日志文件,记录何时进行何种操作,以及增加的内容和删除的菜品名称
查询结果文件,存放查询当前上市菜品的信息结果

线程写日志

详写

需求分析

背景

生活水平不断提高,大大小小的餐馆随处可见。每个餐馆都有自己的招牌菜,同时也有一些不太受欢迎的菜品,这就需要对店面的销售菜品种类及价格进行管理,比如将不受喜爱的菜品去除,调整一下价格,增加一些新品等等。对菜品进行实时动态的系统更新,可以方便餐馆运营。

需求

经理对菜品系统的操作包括select(查询),add(增加),delete(删除)。

实现该系统需要4项技术:多线程、文件操作、数据库操作、网络编程。

多线程:编写线程函数,进行写文件操作。

文件操作:包括两个文件,日志文件和查询结果文件。

            日志文件记录经理在何时进行了何种操作,如果进行增加操作,记录在何时增加了什么内容,若进行删除,记录在何时删除了哪个菜品。

数据库操作:包含两张表,manager和menu。

              manager(name,password)name表示用户名,password表示登录密码

              menu(mno,mname,price)mno菜品编号,mname菜品名称,price价格

网络编程:使用TCP传输,模拟client和server,经理在client端操作,菜品管理系统相当于server,反馈给client操作提示。

 

设计方案

功能

该系统实现的是经理对数据库中菜品表的查询、修改和删除。查询当前本餐馆销售哪些菜品,包括菜品的编号,名称和价格。如果餐馆增加了一些菜式,在数据库中对这些菜式进行添加。如果某些菜式不再上市,需要从系统中删除,给出要删除的菜品编号,在日志文件中记录被删除的菜品名称。

架构

采用TCP网络连接进行模拟,经理作为client,被管理的上市菜品数据库系统作为server。

client

首先进行登录者判断,用户名和密码和数据库中manager表中的信息一致,则和server建立连接,开始按照server的提示进行通信和操作,输入的指令为bye时,表示不再操作了,断开通信。如果不一致,直接断开连接,不进行进一步的操作。

server

client所有的操作规则全部在server中定义,包括日志文件和查询结果文件的写入。server接收client传过来的消息,首先将它转为str()型,再进行判断要执行的是哪一个操作。对于client发送的每一个操作请求,都会调用线程进行日志文件的写入,在执行完后,也会告诉client操作完毕。

操作1,查询。这个操作比较简单,直接执行sql语句,将查询得到的信息,通过调用线程,逐条写入到查询结果文件中,并在client中显示。

操作2,新增。得到这条指令后,发送给client一条提示信息,“请输入更新内容”,然后再对接收到更新信息进行写入数据库操作。

操作3,删除。基本流程类似操作2,反馈给client一条提示信息,“请输入删除的菜品编号”,接收到编号以后,先查询该编号的菜品名称,进行日志文件的写入,然后再执行删除操作。

若接收到的消息的小写格式为bye,则断开连接,表示操作已完成,同时关闭文件和数据库。

若接收到除以上内容外的信息,则默认为是错误信息,提示client重新输入。

逻辑流程

根据系统的功能,画出流程图如图2-1所示。

Python技术实现一个完整的任务需求_第1张图片
 

数据库设计

数据库中包含两个表。

一个是manager表,两列:name和password,分別表示用户名和密码。只有一条记录,即经理的用户名和对应的密码,用户名为jl,密码为python666。用作登录验证。

一个是menu表,三列:mno,mname,price,分别表示菜品编号,菜品名称和价格。初始插入两条记录:1,pork,15和2,chicken,20。

两个表结构如图2-2和2-3所示。

 Python技术实现一个完整的任务需求_第2张图片

系统实现

前期准备

首先创建一个数据库,python.db。在数据库中创建两个表,具体如上一节中的数据库设计所示。

client

用到三个模块:socket,sys,sqlite3。

对于登录验证,要求输入的格式是以逗号分隔用户名和密码,然后对输入的整个字符串进行拆分,拆分格式就是以逗号为分界符,放到数组中,代码实现为:

c = input('Input thecontent:')

c=c.split(',')

将从数据库manager表中查询到的用户名和密码分别存放到user和pwd中,如果列表第一个元素与user相同,且第二个元素与pwd相同,则登录成功,否则失败,断开连接,代码实现:

if c[0]==user andc[1]==pwd:   

    print("登录成功!")

        ……(继续操作)

else:

    print("登录失败!")

    (关闭连接)

server

用到的模块有socket,os.path中的 commonprefix,sqlite3,time,threading,re。

连接数据库,并且以追加写的格式打开两个文本文件,log.txt作为日志文件,cp.txt作为查询结果文件,代码实现:

con=sqlite3.connect("D://python程序设计/大作业/python.db")

f1=open('D://python程序设计/大作业/log.txt','a+')

f2=open('D://python程序设计/大作业/cp.txt','a+')

定义四个线程函数,其中三个是和日志文件相关,一个和查询结果文件相关。三个日志文件相关函数中,一个记录经理在何时进行了何种操作,含有一个参数,为操作类型;一个记录经理在何时新增了哪些内容,参数为增加的内容;还有一个记录经理删除了哪个菜品,参数是根据输入的菜品编号查询到的菜品名称。时间使用time模块的ctime()方法。

从client接收到的信息转化为str()类型,然后和规定的操作进行匹配。

查询。如果接收到的为select,先启动线程进行日志文件的写入,然后执行sql语句,进行数据库查询,对于每一个查询结果,调用一个线程进行查询结果文件写入,执行完后返回给client查询结果,代码实现:

t1=threading.Thread(target=w_log1,args=(data,))

t1.start()               

jl=con.execute("select* from menu")

j2=[]

for i in jl:

    t2=threading.Thread(target=w_cp,args=(i,))

    t2.start()

    j2.append(i)

 answers=str(j2)

 conn.sendall(answers.encode())

新增。如果接收到的是add,这个模块只进行日志文件写入,返给client一条“请输入新增内容”的提示信息,具体的增加在输入新增内容再进行操作。判断输入的内容是不是要新增的记录使用正则表达式,match()方法进行匹配,以数字开头,然后是字母,再数字,一条记录之间用逗号分隔,如果一次输入不止一条记录,记录之间用空格分隔,r'\b\d,[a-zA-Z]+,\d.*\b'。将输入的整个字符串以先以空格作为分隔符进行拆分,放到列表里。然后,遍历列表,对于该列表中的每一个元素再进行拆分,以逗号作为分隔符,放入另一个列表中,每个元素插入到对应的数据库表中。执行完后,发送给client“新增完毕”的提示信息,代码实现:

data=data.split(' ')

for i in data:

    i=i.split(',')

    t1=threading.Thread(target=w_log2,args=(i,))

    t1.start()                       

    con.execute("insert into menuvalues(?,?,?)",(int(i[0]),i[1],int(i[2])))

    con.commit()

删除。如果接收到的是delete,只进行日志文件写入,提示client“请输入删除的菜品编号”。具体的删除操作在输入编号后进行,判断输入的是否是要删除的菜品编号,使用正则表达式,条件是只含有数字,r'\b\d*\d\b'。如果直接把接收到的数字写在sql语句里,计算机会把它看作是一个常量,并不是变量,所以分两步执行,先把sql语句单独赋给一个变量,将菜品编号连接到语句中,再使用execute()执行。因为要将删除的菜品名称写在日志文件中,所以在删除之前,首先要根据菜品编号查询对应的菜品名称,再进行删除操作。执行完后,发送给client“删除完毕”的提示消息。代码实现:

s='select mname from menuwhere mno='+str(data)

result=con.execute(s)

for i in result:

   t1=threading.Thread(target=w_log3,args=(i,))

    t1.start()                                       

s='delete from menu wheremno='+str(data)

con.execute(s)

con.commit()

正常结束操作。如果接收到的信息的小写格式是bye,关闭两个文件,然后断开连接。

如果接收到除以上内容的其他信息,则提示client“输入的信息有误,请重输”。

运行效果

登录失败

Python技术实现一个完整的任务需求_第3张图片

登录成功并操作

登录成功,先进行查询操作,然后新增记录,再查询一次,输入其他内容,验证是否提示信息有误。接下来是删除操作,再进行查询。

如图4-2所示

 Python技术实现一个完整的任务需求_第4张图片

相应的日志文件内容如图4-3所示。

Python技术实现一个完整的任务需求_第5张图片
相应的查询结果文件如图4-4所示。

Python技术实现一个完整的任务需求_第6张图片

总结

在这次设计中,让我学会了如何综合利用学到的知识,如何将各项技术结合在一起,来完成一个比较大的系统。

虽然这次设计只是简单的模拟经理对餐馆上市菜品的管理,但是通过这个环节的设计,我体会到项目开发的困难和计算机编程技术对生产生活的重要作用,为社会带来极大的便利。

在设计过程中,遇到很多问题,比如,只能在client端输入一次信息,却无法得到设想的返回结果,之后查到原因是server未返回给client结果,消息的传递是相互的,一端发送,另一端必须也返回一个结果。印象最深的一个问题是,在关闭连接时,server总是提示一个属性错误,如图5-1所示。

Python技术实现一个完整的任务需求_第7张图片

原因是变量重名,将控制TCP连接的变量和一个字符串变量名字重复了,修改一下字符串的名字,错误就解决了。虽然这个错误很小,但是整个设计过程中,直到最后才发现错误的原因,让我体会到编程时一定要细心检查,即使这个错误对于整个系统并没有什么影响。python语言在使用变量时,虽然不用对变量进行定义,一个变量名可以代表不同的类型,但是也要进行区分,不同的变量类型最好不要相同的变量名,否则就会造成错误,不容易被发现。


代码

建数据库

import sqlite3
import re
con=sqlite3.connect("python.db")
con.execute("create table manager(name text,password text)")
con.execute("create table menu(mno int,mname text,price int)")
con.execute("insert into manager values('jl','python666')")
cd=[(1,'pork',15),(2,'chicken',20)]
con.executemany("insert into menu values(?,?,?)",cd)
con.commit()

client

#coding=utf-8
import socket
import sys
import sqlite3

HOST = '127.0.0.1'
PORT = 50007
s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
try:
    
    s.connect((HOST, PORT))
except Exception as e:
    print('Server not found or not open')
    sys.exit()
con=sqlite3.connect("D://python程序设计/大作业/python.db")
result=con.execute('select * from manager')
for i in result:
    user=i[0]
    pwd=i[1]    
c = input('Input the content:')
c=c.split(',')
if c[0]==user and c[1]==pwd:    
    print("登录成功!")
    while True:
        c = input('Input the content:')
        s.sendall(c.encode())
        data = s.recv(1024)
        data = data.decode()
        print('Received:', data)
        if data=='bye':
            s.close()
        if c.lower() == 'bye':
            break
else:
    print("登录失败!")
    s.close()

server

#coding=utf-8
import socket
from os.path import commonprefix
import sqlite3
import time
import threading
import re
HOST = ''
PORT = 50007
s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)

s.bind((HOST, PORT))

s.listen(1)
print('Listening on port:',PORT)
conn, addr = s.accept()
print('Connected by', addr)

con=sqlite3.connect("D://python程序设计/大作业/python.db")
f1=open('D://python程序设计/大作业/log.txt','a+')
f2=open('D://python程序设计/大作业/cp.txt','a+')

def w_log1(opt):
        f1.write('manager在'+time.ctime()+'进行了'+opt+'操作\n')
def w_log2(cp):
        f1.write('manager在'+time.ctime()+'新增了'+str(cp)+'\n')
def w_log3(mname):
        f1.write('manager在'+time.ctime()+'删除了'+str(mname)+'\n')
def w_cp(ss):
        f2.write(str(ss)+'\n')
while True:
        data=conn.recv(1024).decode()
        data=str(data)
        if data=='select':    
                t1=threading.Thread(target=w_log1,args=(data,))
                t1.start()                
                jl=con.execute("select * from menu")
                j2=[]
                for i in jl:
                        t2=threading.Thread(target=w_cp,args=(i,))
                        t2.start()
                        j2.append(i)
                        
                answers=str(j2)
                conn.sendall(answers.encode())
        elif data=='add':
                
                t1=threading.Thread(target=w_log1,args=(data,))
                t1.start()                
                              
                answers='请输入新增内容'
                conn.sendall(answers.encode())
        elif re.match(r'\b\d,[a-zA-Z]+,\d.*\b',data):
                data=data.split(' ')
                for i in data:
                        i=i.split(',')
                        t1=threading.Thread(target=w_log2,args=(i,))
                        t1.start()                        
                        con.execute("insert into menu values(?,?,?)",(int(i[0]),i[1],int(i[2])))
                        con.commit()
                answers='新增完毕'
                conn.sendall(answers.encode())
        elif data=='delete':
                
                t1=threading.Thread(target=w_log1,args=(data,))
                t1.start()                
                
                answers='请输入删除的菜品编号'
                conn.sendall(answers.encode())
        elif re.match(r'\b\d*\d\b',data):
                s1='select mname from menu where mno='+str(data)
                result=con.execute(s1)
                for i in result:
                        t1=threading.Thread(target=w_log3,args=(i,))
                        t1.start()                                        
                s2='delete from menu where mno='+str(data)
                con.execute(s2)
                con.commit()
                answers='删除完毕'
                conn.sendall(answers.encode())                
        elif data.lower()=='bye':
                f1.close()
                f2.close()                
                answers='bye'
                conn.sendall(answers.encode())
                break
        else:
                answers='输入的信息有误,请重输!'
                conn.sendall(answers.encode())                    
conn.close()
s.close()


你可能感兴趣的:(python)