Python 常用语法整理 (三)

一、Django多数据库配置

需求描述

项目开发中,部分业务功能的实现,需要跨数据库查询,并且想通过Django自带ORM来实现

解决方案

为Django配置多数据库,具体操作步骤如下:

1、修改项目settings.py DATABASES配置

打开settings.py ,修改DATABASES配置—-为需要连接的数据库新增配置(本例中以mysql数据库配置为例,假设需要链接两个数据库)

# ...略

DATABASES = {
     # 默认数据库配置
    'default': {
        'ENGINE': 'django.db.backends.mysql',
        'NAME': 'database_name', # 自定义数据库名称
        'USER': 'db_username',
        'PASSWORD': 'db_user_password',
        'HOST': '127.0.0.1',
        'PORT': '3306',
        'CONN_MAX_AGE': 30,
        'OPTION': {
            'init_command': 'SET default_storage_engine=INNODB'
        }
    },
    'secondDb': { #secondDb代表第二个数据库的配置#该名称可自定义
        'ENGINE': 'django.db.backends.mysql',
        'NAME': 'second_db_name',
        'USER': 'db_username ',
        'PASSWORD': 'db_user_password',
        'HOST': '127.0.0.1',
        'PORT': '3306',
        'CONN_MAX_AGE': 30,
        'OPTION': {
            'init_command': 'SET default_storage_engine=INNODB'
        },
    },
#...略
}

为了方便描述下文内容,这里暂且把上述的 default,secondDb等称为“数据库配置结点”

2、修改项目settings.py DATABASE_ROUTERS路由配置

打开settings.py ,修改DATABASE_ROUTERS配置(如果不存在,则新增该配置项)

DATABASES = {

#...略

}

DATABASE_ROUTERS = ['Package.database_routers.DatabaseRouters']

说明:

  • Package: 路由规则文件所在包(一般是项目根目录下,与项目同名的包目录,或者app根目录(包目录))
  • database_routers: 定义路由规则的.py文件名称,该文件名称可以自定义
  • DatabaseRouters:上述.py中,定义路由规则的类名称,该类名可自定义
  • DATABASE_ROUTERS为列表,所以,可以配置多个不同的路由

3、建立app应用和数据库的映射关系

在settings.py中新增app和数据库的映射关系(如果没有的话),即针对指定app,配置其需要连接的数据库

APP_DATABASE_MAPPING = { # 映射配置名称,可自定义

'mysite': ' defualt', # 格式说明 'app名称':'数据库配置结点' # 注意,这里的“app名称”,必须在settings.INSTALLED_APPS中已注册,“数据库配置结点”要同 settings.DATABASE 保持对应,两者皆不能随便自定义

'myblog': 'secondDb',

}

4、配置不允许执行migration操作的app(确切的说是app的model)

APPS_NOT_ALLOW_MIGRATE = ['myblog'] # 配置 app-label 为 myblog 的 model 不允许执行migration操作

5、创建数据库路由规则

在项目根目录下,与项目同名的目录下(与 settings.py 文件位于同一目录)创建路由规则.py文件(例中为 database_routers.py )

项目代码工程结构目录如下

TMP

|--TMP

    |--settings.py

    |--database_routers.py

|--manage.py

|--...略

|--mysite

|--myblog

database_routers.py 内容如下:

#!/usr/bin/env python
# -*- coding:utf-8 -*-

'''
@Author :shouke
'''

from django.conf import settings

DATABASE_MAPPING = settings.APP_DATABASE_MAPPING

DATABASES_NOT_ALLOW_MIGRATE = settings.APPS_NOT_ALLOW_MIGRATE

class DatabaseRouters(object):
    def db_for_read(self, model, **hints):
        """"指定mode进行读取操作时应使用的数据库, 如果返回None则表示使用默认数据库"""

        if model._meta.app_label in DATABASE_MAPPING:
            return DATABASE_MAPPING[model._meta.app_label]
        return None

    def db_for_write(self, model, **hints):
        """指定mode进行写入操作时应使用的数据库, 如果返回None则表示使用默认数据库"""

        if model._meta.app_label in DATABASE_MAPPING:
            return DATABASE_MAPPING[model._meta.app_label]
        return None

    def allow_relation(self, obj1, obj2, **hints):
        """控制是否允许obj1和obj2建立关联关系,供外键和多对多操作使用,如果返回True则表示允许,如果返回False则阻止建立关联关系,如果返回None则表示仅允许在相同数据库内的对象建立关联关系(备注:笔者亲测,执行save()保存包含关联外键对象,或者通过某个对象获取关联外键对象,该函数都不会被执行    
        """

        db_obj1 = DATABASE_MAPPING.get(obj1._meta.app_label)
        db_obj2 = DATABASE_MAPPING.get(obj2._meta.app_label)
        if db_obj1 and db_obj2:
            if db_obj1 == db_obj2:
                return True
            else:
                return False
         return None

    def allow_migrate(self, db, app_label, model=None, **hints):
        """指定是否允许在别名为db的数据库上运行迁移操作。如果允许运行,则返回True;否则返回False、None"""

        if app_labelin DATABASES_NOT_ALLOW_MIGRATE:
            return False
        else:
            return True

6、创建mode表

在对应app中,创建对应数据表的models,不过,需要注意的是,需要根据上述路由规则,及实际需求,考虑是否为model指定app_label,如果不指定,在默认数据库上执行相关操作。

以下为样例数据表 mode

class BugType(models.Model):
    id = models.AutoField(primary_key=True, verbose_name='自增id')
    bug_type = models.CharField(max_length=50, verbose_name='bug类型')

    class Meta:
        db_table = 'tb_bug_type'
        app_label = 'mysite'
        verbose_name = 'bug类型表'
        verbose_name_plural = verbose_name

class SprintBug(models.Model):
    id = models.AutoField(primary_key=True, verbose_name='自增id')
    name = models.CharField(max_length=10, verbose_name='bug名称')
    but_type_id = models.IntegerField()

    class Meta:
        db_table = 'tb_sprint_bug'
        app_label = 'myblog'
        verbose_name = '迭代bug表'
        verbose_name_plural = verbose_name

说明:

这里假设SprintBug Model对应数据表为项目中需要跨数据库查询的且已存在的数据表,所以,希望在当前项目中执行migrate操作操作时,不对它进行创建、或者修改其数据表,仅供ORM操作使用,为了达到这个目的,需要显示指定 db_table 为该据表在数据库中的表名,并且显示指定app_label值,并确保该 app_label 值存在上述settings.APPS_NOT_ALLOW_MIGRATE列表中(根据上述路由规则,app_label值存在settings.APPS_NOT_ALLOW_MIGRATE列表中的mode不允许执行migration操作)。

二、Queryset对象转为json

实战示例:

def get_info_service(request, task_id: int):
    """
    获取详细信息

    :param request:
    :param task_id:
    :return:
    """
    # obj_info = get_object_or_500(DagTaskOperatorInfo, task_id=task_id)
    # print(obj_info)

    info = DagTaskOperatorInfo.objects.filter(task_id=task_id).first()
    list_info = DagTaskOperatorInfo.objects.filter(task_id=task_id)

    # 第一种方式:将对象转为字典
    print(list(list_info.values()))
    print("===========")
    print(info, type(info))

    # 第二种方式
    new_info = model_to_dict(info)
    print(new_info)
    return http_response_ok(new_info)

三、queryset 的 values和values_list的区别

1、values()

departments = models.Department.objects.filter(dpm_status=1).values('dnp__name')
print(departments )
# queryset中是一个个字典。“departments”:[{"dnp__name":"运输部门"},{"dnp__name":"仓储部门"}]

2、values_list()

departments = models.Department.objects.filter(dpm_status=1).values_list('dnp__name')
print(departments )

# queryset中是一个个元组。“departments”:[("运输部门",),("仓储部门",)]

3.values_list(flat=True)

departments = models.Department.objects.filter(dpm_status=1).values_list('dpm_name', flat=True)
print(departments)
# queryset中是一个列表。 “departments”:["运输部门","仓储部门"]

子类继承父类

父类静态方法,子类继承进行调用父类静态方法:

class A:

    @staticmethod
    def add(x, y):
        return x + y

class B(A):
    @classmethod
    def bdd(cls, x, y):
        return 10 + super().add(x, y)  # 非要用super的话 好像只能这样写把

    @staticmethod
    def test(x, y):
        return 10 + B.add(x, y)  # 这两种方法都可以调用父类的方法

print(B().bdd(1, 2))

print(B().bdd(1, 3))

查询语句

1、objects.get(id=1)

该方法查询报错:

server_relation = models.DemoBlog.objects.get(server_id=server.server_id)
WARNING:root:DemoBlog matching query does not exist.

根据你提供的错误信息,报错的原因是查询方法未找到匹配的结果而抛出异常。

在Django中,get() 方法用于从数据库中获取单个对象。如果查询结果为空,即没有找到符合条件的对象,get() 方法会引发 DoesNotExist 异常。

使用 get() 方法的 first() 函数,该函数返回查询结果的第一条记录,如果结果为空则返回 None。

server_relation = models.DemoBlog.objects.filter(server_id=server.server_id).first()

或者使用 异常补货处理,使用 try-except 捕获 DoesNotExist 异常,并在异常处理块中进行相应的操作。

try:
    server_relation = models.DemoBlog.objects.get(server_id=server.server_id)
except models.DemoBlog.DoesNotExist:
    server_relation = None

ABC抽象基类

abc 模块是 Python 中的抽象基类库。它定义了一个抽象基类(Abstract Base Class,ABC)和相关的工具,用于实现面向对象编程中的多态性和可扩展性

使用 ABC 的主要目的是定义抽象类,抽象类是一种不能被直接实例化的类,它只能被继承。抽象类通常用于定义一个通用的接口,这个接口中定义了一些方法,但是这些方法没有具体的实现。子类可以继承抽象类,并实现抽象类中定义的方法,从而实现多态性。

ABC 模块中定义了 ABCMeta 类,它是抽象类的元类。每个抽象类都必须继承自 ABCMeta,并在其定义中使用 metaclass 关键字进行声明。ABCMeta 类定义了一些方法和属性,用于检查一个类是否是抽象类,以及管理抽象方法和抽象属性。

此外,abc 模块中还定义了一些其他的工具,例如 abstractmethod 函数装饰器,用于定义抽象方法;register 函数,用于将 ABC 注册到 ABC 系统中;isabstractmethod 函数,用于检查一个方法是否是抽象方法。

总之,abc 模块是 Python 中用于实现抽象类和多态性的重要工具,它可以帮助我们更好地组织和管理代码,提高代码的可读性和可维护性。

实践

"from abc import ABCMeta, abstractmethod" 是 Python 中的一句导入语句,它的作用是从 abc 模块中导入 ABCMeta 类和 abstractmethod 函数。

ABCMeta 是一个元类,用于定义抽象类。抽象类是一种不能被直接实例化的类,它只能被继承。ABCMeta 定义了抽象类的基本特性,例如如何检查一个类是否是抽象类,以及如何定义抽象方法。

abstractmethod 是一个函数装饰器,用于定义抽象方法。抽象方法是一种没有具体实现的方法,它只能在子类中被实现。abstractmethod 装饰器告诉 Python 解释器,这个方法是抽象方法,需要在子类中实现。

例如:

from abc import ABCMeta, abstractmethod

class MyAbstractClass(metaclass=ABCMeta):
    @abstractmethod
    def my_abstract_method(self):
        pass

class MyConcreteClass(MyAbstractClass):
    def my_abstract_method(self):
        print("Concrete implementation of my_abstract_method.")

在这个例子中,MyAbstractClass 是一个抽象类,它使用了 ABCMeta 作为元类,并定义了一个抽象方法 my_abstract_methodMyConcreteClassMyAbstractClass 的子类,它实现了 my_abstract_method 方法。

导入 ABCMetaabstractmethod 的目的是为了使用它们来定义抽象类和抽象方法,从而实现面向对象编程中的多态性和继承性。


相关文章:
Python 常用语法整理 (二)

为者常成,行者常至