用python写一个商城网页服务器并且实现数据库和网页交互

用python写一个服务器并且实现数据库和网页交互

  • 本文简介
    • 1.需要实现的功能逻辑
    • 2.设计想法
    • 3.数据库设计
    • 4.代码实现:
      • 服务器类代码
      • 应用程序框架代码
      • 商城类代码(实现数据库交互)
    • 5.运行

本文简介

开发环境是Ubuntu系统下python3.5,数据库为mysql,与数据库交互的包为pymysql。

1.需要实现的功能逻辑

用python写一个商城网页服务器并且实现数据库和网页交互_第1张图片

2.设计想法

先写一个动态资源服务器,详情见
动态资源服务器初级
然后更新应用程序框架用装饰器实现(模拟FLASK框架),再设计一个类shsp,类功能实现与数据库交互并返回想要的结果
之后是代码实现。

3.数据库设计

用python写一个商城网页服务器并且实现数据库和网页交互_第2张图片
数据库设计完然后写入要展示的商品数据,这里不展示。已打包上传

4.代码实现:

中间的网页内容具体不显示,具体已打包上传

服务器类代码

import gevent
from gevent import monkey
import sys
import socket
import chardet

'''这个程序实现的是一个动态资源服务器'''
gevent.monkey.patch_all()  # 给所有耗时操作打补丁


class Server(object):
    def __init__(self, port, app):
        '''初始化,实现逻辑'''
        self.app = app
        # 创建服务器的套接字
        self.server_socket = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
        self.server_socket.setsockopt(
            socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)
        self.server_socket.bind(('', port))
        self.response_data = None  # 这个是响应报文的数据(除了响应体之外)

    def start(self):
        # 服务器开始运行
        self.server_socket.listen()
        while True:
            handler_socket, address = self.server_socket.accept()
            # handler_prc = threading.Thread(
            #     target=self.handler_client, args=(handler_socket,))
            # handler_prc.start()
            gv = gevent.spawn(self.handler_client, handler_socket)
            gv.join()

    def start_response(self, status, header_list):
        '''
        这个函数实现的是拼接响应报文,遵循的还是响应报文的格式,请求动态资源调用
        :param status: 状态码,例如'200 ok'
        :param header_list: 列表 例如[(server,wsgisever),(name,xiaoming)]
        :return:
        HTTP/1.1 状态码 说明\r\n
        Headername1:header_value\r\n
        Headername2:header_value\r\n
        Headername3:header_value\r\n
        \r\n
        Response_body 响应体,就是数据,网页、图片、文本
        '''
        response_header_firstline = 'HTTP/1.1 %s\r\n' % status
        response_header = ''
        # 这是键值 直接拆包
        for header_key, header_value in header_list:
            response_header += ('%s:%s\r\n' % (header_key, header_value))
        self.response_data = response_header_firstline + response_header + '\r\n'

    def handler_client(self, handler_socket):
        # 人工客服处理客户端的请求
        data = handler_socket.recv(1024).decode('utf-8')  # 字节流文件--字符串
        request_datas_list = data.split('\r\n')
        for obj in request_datas_list:
            print(obj)
        # todo:开始拼接的响应报文
        # todo:截取请求的路径,获取请求的内容,根据路径不同返回不同数据

        first_line = request_datas_list[0]

        # ret = re.search('GET (/.*?) HTTP/1.1', first_line) #这个是正则
        # 获取第一行,然后路径
        firstline_list = first_line.split(' ')
        # 判断是否有ret值,如果没有,说明是一个非法请求
        if not firstline_list:
            handler_socket.close()
            return
        # 尝试没有就给他一个默认值
        try:
            request_path = firstline_list[1]
        except BaseException:
            request_path = '/'
        # request_path = ret.group(1)

        print('**********************')

        '''
        HTTP/1.1 状态码 说明\r\n
        Headername1:header_value\r\n
        Headername2:header_value\r\n
        Headername3:header_value\r\n
        \r\n
        Response_body 响应体,就是数据,网页、图片、文本
        '''

        environ = {'path': request_path}
        if request_path.endswith('.py'):
            # 交给动态资源处理框架取处理
            print('此时进入动态资源处理')

            user_info = request_datas_list[len(request_datas_list)-1]
            responsebody = self.app(environ, self.start_response, user_info)
            data = self.response_data + responsebody
            handler_socket.send(data.encode('UTF-8'))
            handler_socket.close()
        else:
            # 这里交给静态资源处理
            if request_path == '/':
                status_code = '200 ok'
                responsebody = 'hello world!'
                # 暂时不写静态
#             elif request_path == '/index.html':
#                 status_code = '200 ok'
#                 with open('index.html') as f:
#                     response_data = f.read()
#                 responsebody = response_data
#             elif request_path == '/login.html':
#                 status_code = '200 ok'
#                 with open('login.html') as f:
#                     responsebody = f.read()%('这是登录页面', '''
# # ''')
else: status_code = '404 not found' with open('err.html') as f: responsebody = f.read() responseHeader = 'HTTP/1.1' + status_code + ' 123\r\n' responseHeader += '\r\n' # 判断responsebody是否是二进制文件 # 需要知道发送数据大小,已经发送了多少 try: chardet.detect(responsebody) sent_count = 0 # 已经发送数据大小 reponse = responseHeader.encode('utf-8') + responsebody while sent_count < len(reponse): # send_size = int(sent_count + len(reponse) / 2) # 一次发送数据大小加上当前位置 # 现在发送的数据大小 now_size = handler_socket.send( reponse[sent_count:]) sent_count += now_size # handler_socket.send( # responseHeader.encode('utf-8') + responsebody) except BaseException: reponse = responseHeader + responsebody sent_count = 0 # 已经发送数据大小 while sent_count < len(reponse): # send_size = int(sent_count + len(reponse) / 2) # 一次发送数据大小加上当前位置 # 现在发送的数据大小 now_size = handler_socket.send( reponse[sent_count:].encode('utf-8')) # send有可能发送不完 sent_count += now_size # ret = handler_socket.send(reponse.encode('utf-8')) # print(ret) finally: # 最后关闭 handler_socket.close() def main(): '''实现主要逻辑''' try: port = sys.argv[1] frame_name = sys.argv[2] # 用来表示哪个框架的哪个接口 except: # 获取不到参数则给定默认参数 print('参数缺少,使用默认参数端口为5000') port = 5000 frame_name = 'application:app' return finally: # 获取接口名字切分,获取的是一个列表 frame_list = frame_name.split(':') modle_name = frame_list[0] func_name = frame_list[1] # 这是新的一种导包方式 # app是指接口的导用 # __import__的参数是字符串,文件名 frame_obj = __import__(modle_name) app = getattr(frame_obj, func_name) # 初始化我的服务器 my_server = Server(port=int(port), app=app) gv = gevent.spawn(my_server.start()) # 这个添加函数 gv.join() if __name__ == '__main__': main()

应用程序框架代码

from shopping import ShangHui

'''这是装饰器工厂函数来实现 类似于flask'''
# 定义一个路径字典
urlfuncdict = {}
# shsp尚汇优品对象
shsp = None
# 用户信息包括名字和密码
userinfo = None
# 验证是否登录
is_login = False
# 登陆后的账户名
user_name = ''
# 订单的货物信息
order_info = None

def route(url):
    # 装饰器工厂函数
    def wrapper(func):
        # 添加键值对,key是路径,value是函数的引用
        urlfuncdict[url] = func

        def inner():
            response_body = func()
            return response_body
        return inner
    return wrapper


@route('/signin.py')
def signin():
    with open('signin.html') as f:
        return f.read()


@route('/handler.py')
def handler():
    # 用来处理登录,返回的是布尔值
    if shsp.login(user_info=userinfo):
        html = '''


    
    Title


登录成功,按确认按钮跳转主页


'''
global is_login, user_name user_name = shsp.login_name is_login = True print('登陆后保存的:',user_name,is_login) else: html = ''' Title

账号密码错误,请重新登录


'''
return html @route('/change_psd.py') def change_psd(): # 用来修改密码 with open('change_psd.html') as f: return f.read() @route('/login.py') def login(): with open('login.html') as f: html_str = f.read() # 判断是否注册后进入这个界面 if 'reg' in userinfo: # 这是注册方法,返回布尔值判断注册成功与否 print('注册账号', userinfo) if shsp.signin(user_info=userinfo): html = html_str % ('注册成功请输入账号密码登录', '') else: html = html_str % ('注册失败,名字已存在请重试', '''
'''
) elif 'old' in userinfo: # 这是修改密码方法,返回布尔值 print('修改密码', userinfo) if shsp.change_password(user_info=userinfo): html = html_str % ('修改密码成功请重新登录', '') else: html = html_str % ('修改密码失败,账号或密码是输入错误', '''
'''
) else: html = html_str % ('这是登录页面', '''
'''
) return html @route('/exit.py') def exit(): # 用来关闭所有东西 global is_login is_login = False shsp.close() html = ''' Title

你已经退出商城,欢迎再来!

'''
return html @route('/index.py') def index(): # 用来显示网页内容 '''
'''
with open('index.html') as f: str_html = f.read() print('验证是否登录:', is_login) if is_login: # 登录状态则添加两个按钮,退出和更改密码 html = str_html % (shsp.get_data_str(), '''
'''
, '''
'''
) else: html = str_html % (shsp.get_data_str(), '''
'''
, '') return html @route('/order.py') def order_comm(): # 这是实现下订单 if is_login: if shsp.order_comm(order_info=userinfo): global order_info order_info = userinfo with open('order_comm.html') as f: str_html = f.read() html = str_html else: html = ''' Title

下单失败,检查输入的商品名字或数量是否出错

'''
else : html = ''' Title

账号没登录,请登录

'''
return html @route('/confirm_order.py') def confirm_order(): shsp.confirm_order(add_info=userinfo, order_info=order_info, sys_name=user_name) html = ''' Title

下单成功,确认返回主页面

'''
return html @route('/check_order.py') def check_order(): # 展示订单 # if is_login: # # else: # print('没登录,请登录') '''
'''
pass @route('/error.py') def error(): return '404 not found' def app(environ, start_response, user_info): ''' :param environ: 字典,传入的是请求头 :param start_response: 这是方法 :return: 返回网页内容 ''' request_path = environ['path'] print('app方法:', user_info) # 根据上面字典判断 staus_code = '' global shsp, userinfo shsp = ShangHui() userinfo = user_info # 判断方法 try: func = urlfuncdict[request_path] staus_code = '200 ok' except BaseException: staus_code = '404 NOT FOUND' func = error finally: start_response(staus_code, [('name', 'paul'), ('age', '18')]) return func()

商城类代码(实现数据库交互)

import pymysql
import time
'''这个程序实现数据库交互'''


class ShangHui(object):
    # _instance = None
    #
    # def __new__(cls, *args, **kwargs):
    #     if not cls._instance:
    #         cls._instance = super(ShangHui, cls).__new__(cls, *args, **kwargs)
    #         return cls._instance

    def __init__(self):
        '''
        初始化数据库'''
        self.connect_obj = pymysql.connect(
            host='127.0.0.1',
            port=3306,
            database='commodity',
            user='root',
            password='mysql',
            charset='utf8')
        self.cur = self.connect_obj.cursor()  # 操作
        self.login_name = ''  # 登录之后的用户名
        self.login_flag = 0  # 登陆验证

    def close(self):
        self.connect_obj.close()
        self.cur.close()
        print('再见')

    def login(self, user_info):
        '''这个是登录操作,返回一个布尔值'''
        #  username=zzz&password=123 取出名字和密码
        try:
            split_info = user_info.split('&')
            inname = split_info[0][9:]
            inpassword = split_info[1][9:]
        except :
            print('数据没有传输过来')
            return False

        # 不要用拼接sql字符串
        sql = 'SELECT * FROM user WHERE name=%s and password=%s'
        try:
            # 参数列表化
            ret = self.cur.execute(sql, [inname, inpassword])
        except Exception as e:
            ret = '数据库出错'
            print(e)
        if ret == 0:
            print('账号或密码出错')
            return False
        print('#########登录成功###########')
        self.login_name = inname
        self.login_flag = 1
        return True

    def get_data_str(self):
        print('商品展示')
        try:
            sql = '''SELECT commodity.name AS'商品名',commodity.price AS'价格',cate.name AS'商品类别',brand.name AS'商品品牌' FROM commodity INNER JOIN cate ON commodity.cate_id=cate.id INNER JOIN brand ON commodity.brand_id=brand.id'''
            ret = self.cur.execute(sql)
            if ret:
                data_str = '商品名  价格  商品类别  品牌
'
for i in self.cur.fetchall(): for j in i: str1 = str(j) data_str += str1 + ' ' data_str += '
'
print(data_str) return data_str except Exception as e: print(e) def get_order_id(self): # 这是读写文件,目的是为了保存并读取订单id,每次自增1 file_data = '' with open('order.txt', 'r') as f: for line in f: old_str = line new_str = str(int(old_str) + 1) file_data = new_str with open('order.txt', 'w') as f: f.write(file_data) # 最后异步读取 with open('order.txt', 'r') as f: for line in f: file_data = line return file_data def order_comm(self, order_info): '''下订单,判断可不可以''' # name=zzz&num=1 取出名字和密码 try: split_info = order_info.split('&') order_name = split_info[0][5:] # 这里编码问题 except: print('数据没有传输') return False print('商品下单', order_name) try: sql = 'SELECT price FROM commodity WHERE name=%s' ret = self.cur.execute(sql, [order_name, ]) if ret: print('下单成功,下面输入配送信息') return True else: print('输入错误,返回主界面') return False except Exception as e: print(e) def confirm_order(self, add_info, order_info, sys_name): # 订单确认,然后写入订单表,订单详情表,配送表, # name=zzz&phone=11132112&address=sdasda 取出名字和密码 try: # 切割用户信息 split_info = add_info.split('&') user_name = split_info[0][5:] user_phone = split_info[1][6:] user_addr = split_info[2][8:] except: print('数据没有传输') return # 切割订单 split_order = order_info.split('&') print('############') print(split_order) order_name = split_order[0][5:] # 商品名字 order_num = int(split_order[1][4:]) # 挑选并确认价格 sql = 'SELECT price FROM commodity WHERE name=%s' self.cur.execute(sql, [order_name, ]) pay_price = self.cur.fetchall()[0][0] payment = order_num * pay_price # 总价 # 获得用户id try: user_sql = 'SELECT id FROM user WHERE name=%s' self.cur.execute(user_sql, [sys_name, ]) user_id = self.cur.fetchone()[0] # 获得商品id comm_sql = 'SELECT id FROM commodity WHERE name=%s' self.cur.execute(comm_sql, [order_name]) comm_id = self.cur.fetchone()[0] print('写入订单表') # 写入订单表 id,payment,pay_time,user_id,send_time order_sql = 'INSERT INTO myorder VALUES(0, %s, %s, %s, %s)' self.cur.execute(order_sql, [payment, time.ctime(), user_id, '一天后']) # 获得订单id 用文件存储方法 order_id = int(self.get_order_id()) print('写入订单详情表') # 写入订单详情表 id,comm_id,order_id,num,price,total_fee items_sql = 'INSERT INTO order_items VALUES(0, %s, %s, %s, %s, %s)' self.cur.execute(items_sql, [comm_id, order_id, order_num, pay_price, payment]) # 写入配送表 id,order_id,user_id,name,phone,address print('写入配送表') ship_sql = 'INSERT INTO shipping VALUES(0, %s, %s, %s, %s, %s)' self.cur.execute(ship_sql, [order_id, user_id, user_name, user_phone, user_addr]) self.connect_obj.commit() # 写入 except Exception as e: print(e) def signin(self, user_info): '''注册注意数据库保存''' # reg_username=zzz&password=123 取出名字和密码 split_info = user_info.split('&') inname = split_info[0][13:] inpassword = split_info[1][9:] print('注册账号') try: sql = 'SELECT * FROM user WHERE name=%s' ret = self.cur.execute(sql, [inname, ]) if ret: print('用户名已经存在') return False sql_insert = 'INSERT INTO user VALUES(0, %s, %s)' ret1 = self.cur.execute(sql_insert, [inname, inpassword]) if ret1: print('注册成功') self.connect_obj.commit() # 提交 return True except Exception as e: print(e) def change_password(self, user_info): '''注册注意数据库保存''' # username=zzz&old_psd=123&new_psd=333 取出名字和密码 split_info = user_info.split('&') user_name = split_info[0][9:] old_password = split_info[1][8:] print('修改密码') new_password = split_info[2][8:] try: sql = 'SELECT * FROM user WHERE name=%s and password=%s' ret = self.cur.execute(sql, [user_name, old_password]) if ret == 0: print('旧密码输入错误') return False else: sql_update = 'UPDATE user SET password=%s WHERE name=%s' ret1 = self.cur.execute(sql_update, [new_password, user_name]) if ret1: print('修改成功') self.connect_obj.commit() # 提交 return True except Exception as e: print(e) def get_check_order(self): print('订单展示') try: sql = '''SELECT myorder.id,myorder.,cate.name AS'商品类别',brand.name AS'商品品牌' FROM commodity INNER JOIN cate ON commodity.cate_id=cate.id INNER JOIN brand ON commodity.brand_id=brand.id''' ret = self.cur.execute(sql) if ret: data_str = '商品名 价格 商品类别 品牌
'
for i in self.cur.fetchall(): for j in i: str1 = str(j) data_str += str1 + ' ' data_str += '
'
print(data_str) return data_str except Exception as e: print(e)

5.运行

这里使用了sys模块,使用终端启动py文件并且可以传输数据,先进入当前文件夹,然后启动,后面是端口和调用的框架及方法
sys启动服务器
相关代码截取出来:
用python写一个商城网页服务器并且实现数据库和网页交互_第3张图片
启动服务器后,打开localhost或者本地回环地址127.0.0.1即可,这里自定义的逻辑是后缀为py

用python写一个商城网页服务器并且实现数据库和网页交互_第4张图片
以上功能已经都实现了,自己手动下了个订单然后查看数据库,订单数据库有三个,用一下三表联查:
用python写一个商城网页服务器并且实现数据库和网页交互_第5张图片

你可能感兴趣的:(Python)