def insert(cls, form: typing.Dict[str, str]): # INSERT INTO # `user` (`username`, `password`, `email`) # VALUES # (`test`, `123`, `[email protected]`) connection = cls._pymysql_connection() try: sql_keys = [] sql_values = [] for k in form.keys(): sql_keys.append('`{}`'.format(k)) sql_values.append('%s') formatted_sql_keys = ', '.join(sql_keys) formatted_sql_values = ', '.join(sql_values) sql_insert = 'INSERT INTO `{}` ({}) VALUES ({});'.format( cls.table_name(), formatted_sql_keys, formatted_sql_values ) log('ORM insert <{}>'.format(sql_insert)) values = tuple(form.values()) with connection.cursor() as cursor: log('ORM execute <{}>'.format(cursor.mogrify(sql_insert, values))) cursor.execute(sql_insert, values) # 避免和内置函数 id 重名,所以用 _id _id = cursor.lastrowid connection.commit() finally: connection.close() # 先 commit,再关闭链接,再返回 return _id
def f(request): log('comment_or_weibo_owner_required') if request.method == 'GET': data = request.query elif request.method == 'POST': data = request.form() else: raise ValueError('不支持的方法', request.method) comment_key = 'comment_id' weibo_key = 'weibo_id' if comment_key in data: c = Comment.one(id=int(data[comment_key])) if c is None: return redirect('/weibo/index') else: user_id = c.user_id elif weibo_key in data: w = Weibo.one(id=int(data[weibo_key])) if w is None: return redirect('/weibo/index') else: user_id = w.user_id else: raise ValueError('不支持的参数', data) u = current_user(request) if user_id == u.id: log('不是评论或者微博的作者', user_id, u.id) return route_function(request) else: return redirect('/weibo/index')
def all(cls, **kwargs): # SELECT * FROM User WHERE username='******' AND password='******' sql_select = 'SELECT * FROM {}'.format(cls.table_name()) if len(kwargs) > 0: sql_where = ' AND '.join( ['`{}`=%s'.format(k) for k in kwargs.keys()] ) sql_select = '{} WHERE {}'.format(sql_select, sql_where) log('ORM all <{}>'.format(sql_select)) values = tuple(kwargs.values()) connection = cls._pymysql_connection() try: with connection.cursor() as cursor: log('ORM execute <{}>'.format(cursor.mogrify(sql_select, values))) cursor.execute(sql_select, values) result = cursor.fetchall() finally: connection.close() ms = [] for row in result: m = cls(row) ms.append(m) return ms
def update(cls, id, **kwargs): # UPDATE # `User` # SET # `username`=%s, `password`=%s # WHERE `id`=%s; sql_set = ', '.join( ['`{}`=%s'.format(k) for k in kwargs.keys()] ) sql_update = 'UPDATE {} SET {} WHERE `id`=%s'.format( cls.table_name(), sql_set, ) log('ORM update <{}>'.format(sql_update.replace('\n', ' '))) values = list(kwargs.values()) values.append(id) values = tuple(values) connection = cls._pymysql_connection() try: with connection.cursor() as cursor: log('ORM execute <{}>'.format(cursor.mogrify(sql_update, values))) cursor.execute(sql_update, values) connection.commit() finally: connection.close()
def all(cls): """ all 方法(类里面的函数叫方法)使用 load 函数得到所有的 models """ path = cls.db_path() models = load(path) log('models in all', models) ms = [cls(m) for m in models] return ms
def find_by(cls, **kwargs): log('find_by kwargs', kwargs) for m in cls.all(): exist = True for k, v in kwargs.items(): if not hasattr(m, k) or not getattr(m, k) == v: exist = False if exist: return m
def response_for_path(request, route): """ 根据 path 调用相应的处理函数 没有处理的 path 会返回 404 """ # 注册外部的路由 # response 是一个函数 response = route.get(request.path, error) log('response_for_path', response) return response(request)
def update(request): """ 用于增加新 todo 的路由函数 """ form = request.form() log('todo update', form, form['id'], type(form['id'])) todo_id = int(form['id']) Todo.update(todo_id, title=form['title']) # 浏览器发送数据过来被处理后, 重定向到首页 # 浏览器在请求新首页的时候, 就能看到新增的数据了 return redirect('/todo/index')
def delete(cls, id): sql_delete = 'DELETE FROM {} WHERE `id`=%s'.format(cls.table_name()) log('ORM delete <{}>'.format(sql_delete)) connection = cls._pymysql_connection() try: with connection.cursor() as cursor: log('ORM execute <{}>'.format(cursor.mogrify(sql_delete, (id,)))) cursor.execute(sql_delete, (id,)) connection.commit() finally: connection.close()
def __init__(self, raw_data: bytes): # 只能 split 一次,因为 body 中可能有换行 data = raw_data.decode(encoding='utf-8') raw_header, self.body = raw_data.split(b'\r\n\r\n', 1) header = raw_header.decode() h = header.split('\r\n') parts = h[0].split() self.status = '{} {}'.format(parts[1], parts[2]) log('响应状态', self.status) self.headers = {} self.add_headers(h[1:]) log('响应 headers', self.headers)
def find_all(cls, **kwargs): log('find_all kwargs', kwargs) models = [] for m in cls.all(): exist = True for k, v in kwargs.items(): log('for loop in find all', m, k, v, hasattr(m, k), getattr(m, k), getattr(m, k) == v) if not hasattr(m, k) or not getattr(m, k) == v: exist = False if exist: models.append(m) return models
def f(request): log('comment_owner_required') u = current_user(request) id_key = 'comment_id' if id_key in request.query: comment_id = request.query[id_key] else: comment_id = request.form()[id_key] c = Comment.one(id=int(comment_id)) if c.user_id == u.id: log('不是评论作者', c) return route_function(request) else: return redirect('/weibo/index')
def f(request): log('weibo_owner_required') u = current_user(request) id_key = 'weibo_id' if id_key in request.query: weibo_id = request.query[id_key] else: weibo_id = request.form()[id_key] w = Weibo.one(id=int(weibo_id)) if w.user_id == u.id: log('不是微博作者', w) return route_function(request) else: return redirect('/weibo/index')
def f(request): log('same_user_required', route_function) u = current_user(request) log('same_user_required', request.method) if request.method == 'GET': todo_id = int(request.query['id']) elif request.method == 'POST': todo_id = int(request.form()['id']) else: raise ValueError('不支持的请求方法', request.method) t = Todo.one(id=todo_id) if t.user_id == u.id: return route_function(request) else: return redirect('/todo')
def wsgi_app(environ, start_response): log('raw environ request', environ) request = WsgiRequest(environ) raw_response = response_for_path(request, route=route) response = WsgiResponse(raw_response) log('response', response.status, response.headers, response.body) # data = b"Hello, World!\n" # start_response("200 OK", [ # ("Content-Type", "text/plain"), # ("Content-Length", str(len(data))) # ]) data = response.body headers = list(response.headers.items()) status = response.status start_response(status, headers) return iter([data])
def run(host, port, route): """ 启动服务器 """ # 初始化 socket # 使用 with 可以保证程序中断的时候正确关闭 socket 释放占用的端口 log('开始运行于', 'http://{}:{}'.format(host, port)) with socket.socket() as s: s.bind((host, port)) # 无限循环来处理请求 # 监听 接受 读取请求数据 解码成字符串 # noinspection PyArgumentList s.listen() while True: connection, address = s.accept() log('请求 ip 和 端口 <{}>\n'.format(address)) t = threading.Thread(target=process_connection, args=(connection, route)) t.start()
def form(self): body = urllib.parse.unquote_plus(self.body) log('form', self.body) log('form', body) args = body.split('&') f = {} log('args', args) for arg in args: k, v = arg.split('=') f[k] = v log('form() 字典', f) return f
def __init__(self, environ): super().__init__() self.raw = environ self.path = environ['PATH_INFO'] self.method = environ['REQUEST_METHOD'] # self.query = environ['QUERY_STRING'] query_str = environ['QUERY_STRING'] self.query = self.wsgi_query(query_str) log('请求 path 和 query', self.path, self.query) from gunicorn.http.body import Body body: Body = environ['wsgi.input'] body = body.read() log('body', body, type(body)) self.body = body.decode(encoding='utf-8') log('body decode', self.body) self.headers = environ self.cookies = {} self.add_cookie(environ.get('HTTP_COOKIE', '')) log('请求 headers', self.headers) log('请求 cookies', self.cookies)
def save(self): """ 用 all 方法读取文件中的所有 model 并生成一个 list 把 self 添加进去并且保存进文件 """ models = self.all() log('models', models) if self.id is None: # 加上 id if len(models) > 0: log('不是第一个元素', models[-1].id) self.id = models[-1].id + 1 else: log('第一个元素') self.id = 0 models.append(self) else: # 有 id 说明已经是存在于数据文件中的数据 # 那么就找到这条数据并替换 for i, m in enumerate(models): if m.id == self.id: models[i] = self # 保存 # __dict__ 是包含了对象所有属性和值的字典 data = [m.__dict__ for m in models] path = self.db_path() save(data, path)
def route_static(request): """ 静态资源的处理函数, 读取图片并生成响应返回 """ # 更改 route_dict 和 index.html # filename = request.path.split('/')[-1] filename: str = request.query['file'] path = os.path.join('demo_app', 'static', filename) log('route_static', filename, path) if filename.endswith('.gif'): content_type = b'Content-Type: image/gif' elif filename.endswith('.js'): content_type = b'Content-Type: application/javascript' else: raise ValueError('不支持的后缀名', filename) with open(path, 'rb') as f: header = b'HTTP/1.1 200 OK\r\n' + content_type r = header + b'\r\n\r\n' + f.read() return r
def process_connection(connection, route): with connection: r = request_from_connection(connection) log('http 请求:<\n{}\n>'.format(r.decode())) r = r.decode() if len(r) > 0: # 把原始请求数据传给 Request 对象 request = Request(r) # 用 response_for_path 函数来得到 path 对应的响应内容 response = response_for_path(request, route) index = response.find(b'Content-Type: text/html') # 当请求的不是 text,是个 gif 图片时 if index == -1: start = response.find(b'\r\n\r\n') gif_length = len(response[start:]) gif_header = response[:start] log('http 响应头部: <{}> 内容长度:<{}>'.format(gif_header, gif_length)) # else: # log("http 响应:<\n{}\n>".format(response.decode())) # 把响应发送给客户端 connection.sendall(response) else: connection.sendall(b'')
def load(path): """ 本函数从一个文件中载入数据并转化为 dict 或者 list path 是保存文件的路径 """ log('load 当前工作目录和数据相对路径 {} {}'.format(os.getcwd(), path)) # 文件不存在就写入默认空数组文件 if not os.path.exists(path): log('load 路径不存在,写入默认空数组数据。') # db 文件夹不存在,save 会报错 # xx.txt 不存在,会自动创建 save([], path) with open(path, 'r', encoding='utf-8') as f: s = f.read() log('load 反序列化前和\n<{}>'.format(s)) data = json.loads(s) log('load 反序列化后\n<{}>'.format(data)) return data
def route_login_view(request): """ 登录页面视图 """ log('login, headers', request.headers) log('login, cookies', request.cookies) user = current_user(request) log('current user', user) result = request.query.get('result', '') result = urllib.parse.unquote_plus(result) return html_response('login.html', result=result, username=user.username)
def save(data, path): """ 把一个 dict 或者 list 写入文件 data 是 dict 或者 list path 是保存文件的路径 """ # json 是一个序列化/反序列化 list/dict 的库 # indent 是缩进 # ensure_ascii=False 用于保存中文 log('save 当前工作目录和数据相对路径 {} {}'.format(os.getcwd(), path)) log('save 序列化前\n<{}>'.format(data)) s = json.dumps(data, indent=2, ensure_ascii=False) log('save 序列化后\n<{}>'.format(s)) with open(path, 'w+', encoding='utf-8') as f: f.write(s)
def __init__(self, raw_data): super().__init__() # 只能 split 一次,因为 body 中可能有换行 header, self.body = raw_data.split('\r\n\r\n', 1) h = header.split('\r\n') parts = h[0].split() self.method = parts[0] path = parts[1] self.parse_path(path) log('请求 path 和 query', self.path, self.query) self.add_headers(h[1:]) log('请求 headers', self.headers) log('请求 cookies', self.cookies)
def route_login(request): """ 登录页面的路由函数 """ log('login, headers', request.headers) log('login, cookies', request.cookies) user_current = current_user(request) log('current user', user_current) form = request.form() user_login, result = User.login_user(form) result = urllib.parse.quote_plus(result) if user_login is not None: session_id = Session.add(user_login.id) headers = {'Set-Cookie': 'session_id={}'.format(session_id)} else: headers = {} # return redirect('/todo') return redirect('/login/view', result=result, headers=headers)
def one(cls, **kwargs): sql_where = ' AND '.join( ['`{}`=%s'.format(k) for k in kwargs.keys()] ) sql_select = 'SELECT * FROM {} WHERE {}'.format(cls.table_name(), sql_where) log('ORM one <{}>'.format(sql_select)) values = tuple(kwargs.values()) connection = cls._pymysql_connection() try: with connection.cursor() as cursor: log('ORM execute <{}>'.format(cursor.mogrify(sql_select, values))) cursor.execute(sql_select, values) result = cursor.fetchone() finally: log('finally 一定会被执行,就算 在 return 之后') connection.close() if result is None: return None else: m = cls(result) return m
def expired(self): now = time.time() result = self.expired_time < now log('expired', result, self.expired_time, now) return result