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_method
。MyConcreteClass
是 MyAbstractClass
的子类,它实现了 my_abstract_method
方法。
导入 ABCMeta
和 abstractmethod
的目的是为了使用它们来定义抽象类和抽象方法,从而实现面向对象编程中的多态性和继承性。
四、数据保存
如果前端传递的数据是简单的JSON格式,如 {"name": "kaiyi", "age": 11},后端可以通过Django的请求对象来获取这些参数,然后进行保存。通常情况下,处理JSON数据可以分为以下步骤:
1.获取JSON数据: 在Django视图函数中,可以通过request.body来获取原始的JSON数据字符串,然后将其解析为Python对象。
2.解析JSON数据: 使用Python内置的json模块或者Django的内置解析器来将JSON字符串解析为Python字典或其他适当的数据结构。
3.保存数据: 将解析后的数据保存到相应的模型中。
下面是一个示例的Django视图函数,演示如何处理从前端传来的简单JSON数据并保存到数据库:
import json
from django.http import JsonResponse
from .models import User
def create_user(request):
if request.method == 'POST':
try:
# 获取JSON数据
json_data = json.loads(request.body)
# 解析JSON数据中的参数
name = json_data.get('name')
age = json_data.get('age')
# 创建用户对象并保存
user = User.objects.create(name=name, age=age)
# 返回成功的响应
return JsonResponse({'message': 'User created successfully'}, status=201)
except json.JSONDecodeError as e:
return JsonResponse({'error': 'Invalid JSON format'}, status=400)
except KeyError as e:
return JsonResponse({'error': f'Missing key in JSON data: {e}'}, status=400)
# 如果请求不是POST方法,返回错误信息
return JsonResponse({'error': 'POST method required'}, status=405)
解释代码:
4.请求处理逻辑: 首先检查请求方法是否为POST,因为只有POST方法才能创建新用户。
5.JSON解析: 使用json.loads(request.body)将请求体中的JSON数据解析为Python字典。
6.参数获取: 从解析后的字典中获取name和age字段的值。
7.创建用户: 使用User.objects.create()方法创建用户对象并保存到数据库。
8.返回响应: 根据操作结果返回适当的JSON响应,包括成功消息或错误信息。
注意事项:
9.异常处理: 考虑到可能的JSON解析错误或关键字段丢失的情况,进行了相应的异常处理。
10.安全性考虑: 实际项目中,应该对传入的JSON数据进行更严格的验证和处理,确保数据的完整性和安全性。
通过这种方式,可以直接处理从前端传来的简单JSON数据,并将其保存到数据库中的User模型中。
除了直接使用 User.objects.create() 方法外,Django 还提供了其他几种常见的方式来保存数据到数据库,主要是使用模型实例的方法来保存数据。下面是一些常见的方法及其优缺点:
- 使用 save() 方法
可以先创建模型实例,然后调用其 save() 方法保存到数据库。user = User(name='kaiyi', age=11) user.save()
优点:
1.可以在创建模型实例后对其进行额外的操作,如设置其他字段的值或进行数据验证。
2.更灵活,可以在保存前进行一些自定义的逻辑处理。
缺点:
3.相比直接使用 create() 方法,代码稍微冗长。
- 使用 bulk_create() 方法
bulk_create() 方法允许一次性创建多个对象并将它们批量保存到数据库,这在需要大量创建对象时比较有用。users = [ User(name='user1', age=20), User(name='user2', age=25), # more User objects... ] User.objects.bulk_create(users)
优点:
4.提高了批量操作的效率,减少了与数据库的交互次数,从而提升性能。
缺点:
5.无法获取新创建的对象的主键值(除非设置了 return_id=True 参数),因为 bulk_create() 方法不会返回新对象的主键。
- 使用 update_or_create() 方法
update_or_create() 方法用于在数据库中查找匹配条件的记录,如果找到则更新,如果不存在则创建新记录。obj, created = User.objects.update_or_create( name='kaiyi', defaults={'age': 11}, )
优点:
6.可以在更新或创建时进行条件判断和额外的逻辑操作。
7.适合需要保证唯一性的操作,避免重复创建记录。
缺点:
8.相比较简单的创建操作,代码较为复杂,需要提供额外的条件和默认值。
综合比较
9.简单创建 (User.objects.create()):适用于一次性创建单个对象,并且不需要额外处理的情况,代码简洁。
10.使用 save() 方法:适用于需要在保存前进行一些逻辑处理或数据验证的场景,比较灵活。
11.使用 bulk_create() 方法:适用于大量数据的批量创建,可以提升性能,但不支持返回新对象的主键。
12.使用 update_or_create() 方法:适用于需要根据条件更新现有记录或创建新记录的场景,代码复杂度适中。
选择合适的方法取决于具体的需求和代码结构,通常情况下,User.objects.create() 是最常用且简洁的方法,而其他方法则根据需求在特定情况下进行选择。
实例代码:
def create_valid_scene(self, name, project_id, rule_type, rule_method, user_id, desc=None, is_visible=1):
try:
# 创建实例
valid_scene = DemoValidScene()
valid_scene.name = name
valid_scene.project_id = project_id
valid_scene.desc = desc
valid_scene.rule_type = rule_type
valid_scene.rule_method = rule_method
valid_scene.create_time = datetime.datetime.now()
valid_scene.user_id = user_id
valid_scene.is_visible = is_visible
valid_scene.save()
except Exception as e:
log_error(e)
raise Exception('create valid scene error: %s' % str(e))
return valid_scene
相关文章:
Python 常用语法整理 (二)
为者常成,行者常至
自由转载-非商用-非衍生-保持署名(创意共享3.0许可证)