Python实现ORM

yizhihongxing

Python实现ORM

ORM全称Object-Relational Mapping,简单的理解就是通过代码的方式操作数据库。ORM 的出现让我们不用关心 SQL 而用自己熟悉的编程语言来操作数据库。在Python开发中,ORM框架也是非常常见的,比如Django自带的ORM,SQLAlchemy等。

ORM的基本概念

建立连接

在使用ORM之前,需要首先建立与数据库的连接。可以使用Python的标准库sqlite3进行测试。

import sqlite3

# 连接数据库
conn = sqlite3.connect('test.db')
print('Opened database successfully')

定义模型

在使用ORM时,需要先定义模型。模型是一个面向对象的类,对应数据库中的一张表。类中的属性对应着表中的列。很多ORM框架都支持简便的语法来定义模型,比如Django的models.py文件。

class User:
    def __init__(self, id, name, age):
        self.id = id
        self.name = name
        self.age = age

操作数据库

ORM提供了常用的CRUD操作(Create、Read、Update、Delete)。

创建表

需要为模型创建对应的数据库表。

c.execute('''CREATE TABLE USER
       (ID INT PRIMARY KEY     NOT NULL,
       NAME           TEXT    NOT NULL,
       AGE            INT     NOT NULL);''')

插入数据

可以通过ORM向表中插入数据。

user = User(1, 'Alice', 18)
conn.execute("INSERT INTO USER (ID,NAME,AGE) \
      VALUES (?, ?, ?)", (user.id, user.name, user.age))

查询数据

可以通过属性来查询数据。

cursor = conn.execute("SELECT id, name, age from USER")
for row in cursor:
  user = User(row[0], row[1], row[2])
  print(f"ID = {user.id}, NAME = {user.name}, AGE = {user.age}")

更新数据

可以修改已有的数据。

user.age = 20
conn.execute("UPDATE USER set AGE = ? where ID = ?", (user.age, user.id))

删除数据

可以删除指定的数据。

conn.execute("DELETE from USER where ID = ?", (user.id,))

实现ORM的基本框架

ORM的实现思路:

  1. 将每个类定义为python类,类中的属性对应着表中的列
  2. 定义一个基础的Model类,所有的类继承这个类
  3. 在基础的Model类中为类动态的添加增、删、改、查四个方法

下面我们来实现这个框架。

类的映射

我们可以为每一个表定义对应的类。

class User(Model):
    __table__ = 'USER'

    id = IntegerField('ID', primary_key=True)
    name = StringField('NAME')
    age = IntegerField('AGE')

其中,__table__表示对应的表名,__primary_key__表示主键,IntegerField和StringField是两个常用的属性类型。

创建连接

import sqlite3

conn = sqlite3.connect('test.db')

Field类

我们可以将每个字段抽象成一个类,包括字符型、数值型等不同的类型。

class Field:
    pass

class StringField(Field):
    def __init__(self, name):
        self.name = name

class IntegerField(Field):
    def __init__(self, name):
        self.name = name

Model类 & 增删改查

在基础的Model类中为类动态的添加新增、删除、查询和更新四个方法。

class Model:
    @classmethod
    def create_table(cls):
        fields = []
        for k, v in cls.__dict__.items():
            if isinstance(v, Field) and v.primary_key is True:
                field = k + " " + v.field_type + " PRIMARY KEY"
                fields.append(field)
            elif isinstance(v, Field):
                field = k + " " + v.field_type
                fields.append(field)
        field_str = ", ".join(fields)
        table_name = cls.__table__
        create_sql = f"CREATE TABLE {table_name} ({field_str})"
        conn.execute(create_sql)

    def save(self):
        fields = []
        values = []
        for k, v in self.__dict__.items():
            if isinstance(v, Field) and v.primary_key is not True:
                fields.append(k)
                values.append(v)
        field_str = ", ".join(fields)
        value_str = ", ".join(["?" for _ in range(len(fields))])
        table_name = self.__table__
        insert_sql = f"INSERT INTO {table_name} ({field_str}) VALUES ({value_str})"
        conn.execute(insert_sql, values)
        conn.commit()

    @classmethod
    def get_one(cls, pk):
        table_name = cls.__table__
        pk_field = None
        for k, v in cls.__dict__.items():
            if isinstance(v, Field) and v.primary_key is True:
                pk_field = k
        select_sql = f"SELECT * FROM {table_name} WHERE {pk_field}=?"
        cursor = conn.execute(select_sql, (pk, ))
        row = cursor.fetchone()
        if row is None:
            return None
        values = [row[i] for i in range(len(row))]
        obj = cls()
        for k, v in obj.__dict__.items():
            if isinstance(v, Field) and v.primary_key is True:
                setattr(obj, k, values.pop(0))
            elif isinstance(v, Field):
                setattr(obj, k, values.pop(0))
        return obj

    def update(self):
        fields = []
        values = []
        pk_field = None
        for k, v in self.__dict__.items():
            if isinstance(v, Field) and v.primary_key is not True:
                fields.append(k + "=?")
                values.append(v)
            elif isinstance(v, Field) and v.primary_key is True:
                pk_field = k
        values.append(getattr(self, pk_field))
        field_str = ", ".join(fields)
        table_name = self.__table__
        update_sql = f"UPDATE {table_name} SET {field_str} WHERE {pk_field}=?"
        conn.execute(update_sql, values)
        conn.commit()

    def delete(self):
        pk_field_name = None
        for k, v in self.__dict__.items():
            if isinstance(v, Field) and v.primary_key is True:
                pk_field_name = k
        pk_val = getattr(self, pk_field_name)
        table_name = self.__table__
        delete_sql = f"DELETE FROM {table_name} WHERE {pk_field_name}=?"
        conn.execute(delete_sql, (pk_val, ))
        conn.commit()

在使用的时候,可以通过继承Model类来实现增删改查的功能。

class User(Model):
    __table__ = 'USER'
    id = IntegerField('ID', primary_key=True)
    name = StringField('NAME')
    age = IntegerField('AGE')

    def __init__(self, id, name, age):
        self.id = id
        self.name = name
        self.age = age

User.create_table()

user1 = User(1, 'Alice', 18)
user2 = User(2, 'Bob', 20)
user3 = User(3, 'Cindy', 22)

# 插入
user1.save()
user2.save()
user3.save()

# 查询
user = User.get_one(1)
print(f"id: {user.id}, name: {user.name}, age: {user.age}")

# 更新
user.age = 20
user.update()

# 删除
user.delete()

总结

ORM是非常常用的技术,比如在Django、Flask、SQLAlchemy等框架中都有非常完善的ORM。实现一个简单的ORM可以帮助我们更好的理解ORM的底层实现原理,从而更好地应对一些较为复杂的业务场景。

本站文章如无特殊说明,均为本站原创,如若转载,请注明出处:Python实现ORM - Python技术站

(0)
上一篇 2023年3月28日
下一篇 2023年3月28日

相关文章

  • MySQL如何为表和字段取别名详解

    MySQL可以为表和字段设置别名,用于简化查询语句并增加可读性。以下是详细的攻略: 为表取别名 为表取别名,可以在查询语句中使用更短的表名代替完整的表名,同时也可以使查询语句更具可读性。为表取别名的语法如下: SELECT 列名 FROM 表名 AS 别名 其中,“AS”关键字并不是必需的,也可以直接使用“表名 别名”的方式为表取别名。以下是一个简单的示例:…

    other 2023年6月25日
    00
  • 7zip在dos命令行用法总结

    7zip在DOS命令行用法总结 7zip 是一款压缩/解压缩工具,安装完成后可以在命令行窗口中使用。本篇文章将详细讲解7zip在DOS命令行中的用法。 安装7zip 首先需要安装7zip,可以从官网下载最新版本的安装文件。 安装完成后,打开命令行窗口,输入“7z”命令,如果命令行窗口中出现7zip的说明,说明7zip已经安装成功。 常用命令 7zip最常用的…

    other 2023年6月27日
    00
  • java如何删除数组中的元素?

    Java如何删除数组中的元素? Java是一门十分流行的编程语言,在Java中经常需要对数组进行操作,而删除数组中的元素是一个常见的需求。本文将介绍如何在Java中删除数组中的元素。 方法一:使用System.arraycopy() 首先介绍一种使用System.arraycopy()方法的删除数组元素的方式。 public static int[] rem…

    其他 2023年3月29日
    00
  • 使用android studio开发工具编译GBK转换三方库iconv的方法

    下面详细讲解使用Android Studio开发工具编译GBK转换三方库iconv的方法。 简介 iconv是一个开源的转换库,它可以将不同编码格式之间的文本相互转换。在Android开发中,我们可能需要使用iconv将GBK编码的文本转换为UTF-8等其他编码格式,以方便显示和存储。但是由于Android Studio自带的编译工具并不支持GBK编码格式,…

    other 2023年6月26日
    00
  • nsattributedstring用法

    NSAttributedString用法 NSAttributedString是iOS平台上的一个类,用来显示富文本内容,即带有各种样式的文本。NSAttributedString提供了一些API,可以对文本的各种属性进行自定义设置。 创建NSAttributedString 创建NSAttributedString可以使用以下两种方式: 1. 直接创建 N…

    其他 2023年3月28日
    00
  • nginx支持cgi

    以下是关于“nginx支持cgi”的完整攻略: Nginx简介 Nginx是一款高性能的Web服务器和反向代理服务器,可以处理高并发的请求。Nginx支持多种模,包括HTTP、SMTP、POP3等,可以通过模块扩展来实现更多的功能。 Nginx支持CGI CGI( Gateway Interface)是一种Web服务器和应用程序之间的接口标准,可以让Web服…

    other 2023年5月9日
    00
  • 暗黑3 科普护盾的优先级与被动法能护体的刷新条件

    暗黑3中的科普护盾和被动法能护体是两种重要的防御手段,下面将详细介绍它们的优先级和刷新条件: 科普护盾的优先级 科普护盾是一种可以提供额外伤害减免的技能,其优先级如下: 拥有绝对优势的加强型护盾,如玻璃炮身护盾、碳纤维协议、回溯护盾等。 其他加强型护盾,如鸟类群集、交错护盾等。 标准型护盾,如神盾、石化护盾等。 轻型护盾,如粘附簇、追踪器等。 在选择护盾时,…

    other 2023年6月27日
    00
  • ps教程:如何批量处理图片

    PS教程:如何批量处理图片 如果你需要在Photoshop中批量处理大量的图片,可能你已经厌倦了一个一个操作。好在Photoshop为你提供了批量处理的功能!在本文中,我们将介绍如何快速、高效地批量处理图片。 步骤一:准备需要处理的图片 在开始批量处理之前,我们需要准备好需要处理的所有图片,并将它们存储在一个文件夹中。这里有几个小提示: 尽量将待处理的图片放…

    其他 2023年3月28日
    00
合作推广
合作推广
分享本页
返回顶部