仿优酷1
一、需求分析
1.管理员:
注册、登录、上传视频、删除视频、发布公告
2.用户:
注册、登录、充会员、查看视频、下载免费视频、下载收费视频、查看下载记录、查看公告
二、程序的架构设计(三层架构)
1.用户视图层
与用户交互
2.接口层
处理核心业务逻辑
3.数据层
对数据进行存取
三、ORM(对象关系映射)
将对象映射成数据表中的一条条记录
类 ----> 表名
对象 ----> 记录
对象.属性 ----> 字段
# 问题1: 每一张表类,都必须定义一个__init__方法, # 假设有100张表,就得写100次,无法确定每张表的字段个数,以及字段的名字; # 解决1: 通过继承字典类,解决该问题; # 问题2: 继承的字典无法通过 对象.属性的 取值方式, 对象.属性 = 属性值 增加或修改属性的操作? # 解决2: __getattr__解决了取值方式, __setattr__解决了增加或修改属性的方式 class Models(dict, metaclass=OrmMetaClass): # 对象.属性, 属性没有时触发 def __getattr__(self, item): # print(self) return self.get(item) # {'name': 'tank', 'pwd': '123'}.get('name') ----> tank # 对象.属性 = 属性值 时触发 def __setattr__(self, key, value): # print(key, value) # print(self) # {'name': 'tank', 'pwd': '123'} # 给字典添加键值对的方式 self[key] = value
# 问题3: 一张表中只能有一个唯一的主键,在当前表类中,无法控制用户定义类的型? # 自定义元类: # 解决三件事情 ---》 # 1、保证一张表必须要有表名 2、保证一张表中只能有一个主键 # 3、将所有 “字段名” 与 “字段对象” 添加到一个独立的字典中(mappings), # 以key(字段名):value(字段对象) , 添加到类的名称空间中,方便后期使用 class OrmMetaClass(type): # __call__ ---> __new__ ----> type.__new__() ---> obj ---> Models # 只要定义类就会触发__new__, 因为类也是对象,OrmMetaClass() ---> Models类对象 、User类对象 def __new__(cls, class_name, class_bases, class_attr): # class_name, class_bases, class_attr = args # print(f'类名: {class_name}') # print(f'基类: {class_bases}') # print(f'类的名称空间: {class_attr}') # 1、过滤Models类 if class_name == 'Models': # 将models类的类名、基类、名称空间原路返回 return type.__new__(cls, class_name, class_bases, class_attr) # 2、获取 table 表名,若自定义则获取,没有则默认使用类名 # dict.get(key) ---> key若有则返回对应的值,若没有则返回默认值 class_name就是默认值 # 将类名当做表名 table_name = class_attr.get('table_name', class_name) # print(table_name) # 打印修改类名称空间前的 名称空间 # print(class_attr) # 主键值: 主键名为 字段名, 比如 主键是 id字段 ---》 id就是主键的名字 primary_key = None # 存放字段名与字段对象的字典 mappings = {} # 3、保证一张表只能有一个唯一的主键 # 循环遍历类的名称空间 for k, v in class_attr.items(): # print(k, v) # 将字段以外的属性过滤掉 # 判断当前的v是否是字段对象 if isinstance(v, Field): # print(k, v) # print(v.__dict__) # 4、将所有 “字段名” 与 “字段对象” 添加到一个独立的字典中(mappings) mappings[k] = v # 判断字段对象如果有 主键primary_key, 则为primary_key 变量赋值 if v.primary_key: # 若第二次进来,primary有值,证明有主键,抛出异常 if primary_key: raise TypeError('一张表只能有一个主键') # primary_key = k # print(k == v.name, 111111) # 给primary_key变量做一个赋值操作 primary_key = v.name # 5、过滤掉类名称空间中重复的字段属性 for key in mappings.keys(): class_attr.pop(key) if not primary_key: raise TypeError('必须要有一个主键!!!') # 6、给类的名称空间,添加table_name, primary_key,mappings属性; class_attr['table_name'] = table_name class_attr['primary_key'] = primary_key class_attr['mappings'] = mappings # print('*' * 100) # print(class_attr) return type.__new__(cls, class_name, class_bases, class_attr)
# 定义字段类型: 每个字段都应该有---> 字段名、字段类型、是否为主键、默认值 class Field: def __init__(self, name, column_type, primary_key, default): self.name = name self.column_type = column_type self.primary_key = primary_key self.default = default # varchar class StringField(Field): def __init__(self, name, column_type='varchar(64)', primary_key=False, default=None): super().__init__(name, column_type, primary_key, default) # int class IntegerField(Field): def __init__(self, name, column_type='int', primary_key=False, default=0): super().__init__(name, column_type, primary_key, default)
ORM需要处理的事情:
1.控制表类的创建 ---> 控制表创建的过程
2.给表类封装 增、查、改 方法
四、封装mysql方法类
import pymysql # MySQL连接类 class MySQL: __instance = None # 单例模式 @classmethod def singleton(cls): if not cls.__instance: cls.__instance = cls() return cls.__instance # 实例化MySQL类时,获取数据库链接对象,获取游标对象 def __init__(self): self.mysql_client = pymysql.connect( host='127.0.0.1', port=3306, user='root', password='123456', charset='utf8', database='orm_demo', autocommit=True ) self.cursor = self.mysql_client.cursor( pymysql.cursors.DictCursor ) # 自定义查询方法 def select(self, sql, args=None): # 1、先提交查询sql语句 # select * from table; # select * from table where id=%s; self.cursor.execute(sql, args) # 2、获取返回的查询结果 # res ---> [{}, {}] res = self.cursor.fetchall() return res # 自定义提交sql语句方法,比如: insert、update def execute(self, sql, args): # 1、提交sql语句 # insert into table(字段) values(%s); try: self.cursor.execute(sql, args) except Exception as e: print(e) def close(self): # 先关闭游标 self.cursor.close() # 再关闭数据库连接 self.mysql_client.close()