2022-04-05 05:22:26 +00:00
# -*- coding: utf-8 -*-
"""
@author : 猿小天
@contact : QQ : 1638245306
@Created on : 2021 / 5 / 31 031 22 : 08
@Remark : 公共基础model类
"""
import uuid
2023-04-14 09:26:30 +00:00
from datetime import date , timedelta
2022-04-05 05:22:26 +00:00
from django . apps import apps
2023-04-14 09:26:30 +00:00
from django . db import models , connection , ProgrammingError
2022-08-01 17:12:04 +00:00
from django . db . models import QuerySet
2022-08-12 15:54:32 +00:00
2022-04-05 05:22:26 +00:00
from application import settings
2023-04-14 09:26:30 +00:00
from application . dispatch import is_tenants_mode
2022-04-05 05:22:26 +00:00
table_prefix = settings . TABLE_PREFIX # 数据库表名前缀
2022-08-01 17:12:04 +00:00
class SoftDeleteQuerySet ( QuerySet ) :
2022-11-05 16:27:02 +00:00
pass
2022-08-01 17:12:04 +00:00
class SoftDeleteManager ( models . Manager ) :
""" 支持软删除 """
2022-08-12 15:54:32 +00:00
def __init__ ( self , * args , * * kwargs ) :
self . __add_is_del_filter = False
super ( SoftDeleteManager , self ) . __init__ ( * args , * * kwargs )
def filter ( self , * args , * * kwargs ) :
# 考虑是否主动传入is_deleted
if not kwargs . get ( ' is_deleted ' ) is None :
self . __add_is_del_filter = True
return super ( SoftDeleteManager , self ) . filter ( * args , * * kwargs )
2022-08-01 17:12:04 +00:00
def get_queryset ( self ) :
2022-08-12 15:54:32 +00:00
if self . __add_is_del_filter :
return SoftDeleteQuerySet ( self . model , using = self . _db ) . exclude ( is_deleted = False )
return SoftDeleteQuerySet ( self . model ) . exclude ( is_deleted = True )
2023-04-14 09:26:30 +00:00
def get_by_natural_key ( self , name ) :
2022-08-12 15:54:32 +00:00
return SoftDeleteQuerySet ( self . model ) . get ( username = name )
2022-08-01 17:12:04 +00:00
2023-04-14 09:26:30 +00:00
def get_month_range ( start_day , end_day ) :
months = ( end_day . year - start_day . year ) * 12 + end_day . month - start_day . month
month_range = [ ' %s - %s -01 ' % ( start_day . year + mon / / 12 , str ( mon % 12 + 1 ) . zfill ( 2 ) )
for mon in range ( start_day . month - 1 , start_day . month + months ) ]
return month_range
2022-11-16 15:30:41 +00:00
class SoftDeleteModel ( models . Model ) :
"""
软删除模型
一旦继承 , 就将开启软删除
"""
is_deleted = models . BooleanField ( verbose_name = " 是否软删除 " , help_text = ' 是否软删除 ' , default = False , db_index = True )
objects = SoftDeleteManager ( )
class Meta :
abstract = True
verbose_name = ' 软删除模型 '
verbose_name_plural = verbose_name
def delete ( self , using = None , soft_delete = True , * args , * * kwargs ) :
"""
重写删除方法 , 直接开启软删除
"""
self . is_deleted = True
self . save ( using = using )
2022-04-05 05:22:26 +00:00
class CoreModel ( models . Model ) :
"""
核心标准抽象模型模型 , 可直接继承使用
增加审计字段 , 覆盖字段时 , 字段名称请勿修改 , 必须统一审计字段名称
"""
id = models . BigAutoField ( primary_key = True , help_text = " Id " , verbose_name = " Id " )
description = models . CharField ( max_length = 255 , verbose_name = " 描述 " , null = True , blank = True , help_text = " 描述 " )
creator = models . ForeignKey ( to = settings . AUTH_USER_MODEL , related_query_name = ' creator_query ' , null = True ,
2023-04-14 09:26:30 +00:00
verbose_name = ' 创建人 ' , help_text = " 创建人 " , on_delete = models . SET_NULL ,
db_constraint = False )
2022-04-05 05:22:26 +00:00
modifier = models . CharField ( max_length = 255 , null = True , blank = True , help_text = " 修改人 " , verbose_name = " 修改人 " )
2023-04-14 09:26:30 +00:00
dept_belong_id = models . CharField ( max_length = 255 , help_text = " 数据归属部门 " , null = True , blank = True ,
verbose_name = " 数据归属部门 " )
update_datetime = models . DateTimeField ( auto_now = True , null = True , blank = True , help_text = " 修改时间 " ,
verbose_name = " 修改时间 " )
2022-04-05 05:22:26 +00:00
create_datetime = models . DateTimeField ( auto_now_add = True , null = True , blank = True , help_text = " 创建时间 " ,
verbose_name = " 创建时间 " )
class Meta :
abstract = True
verbose_name = ' 核心模型 '
verbose_name_plural = verbose_name
2023-04-14 09:26:30 +00:00
class AddPostgresPartitionedBase :
"""
pgsql表分表基类
"""
@classmethod
def add_hash_partition ( cls , number = 36 ) :
"""
创建分区表
: return :
"""
if cls . PartitioningMeta . method != ' hash ' :
raise ProgrammingError ( " 表分区错误,无法进行分区 " )
schema_editor = connection . schema_editor ( )
if is_tenants_mode ( ) :
schema_editor . sql_add_hash_partition = f ' CREATE TABLE " { connection . tenant . schema_name } " .%s PARTITION OF " { connection . tenant . schema_name } " .%s FOR VALUES WITH (MODULUS %s, REMAINDER %s) '
for item in range ( number ) :
try :
schema_editor . add_hash_partition (
model = cls ,
name = " _ " + str ( item ) ,
modulus = number ,
remainder = item ,
)
except ProgrammingError as e :
print ( f " { cls . __name__ } 分表失败: " + str ( e ) . rstrip ( ' \n ' ) )
return
@classmethod
def add_range_day_partition ( cls , day = 7 ) :
"""
按照创建时间 " 天 " 分表
: return :
"""
if cls . PartitioningMeta . method != ' range ' :
raise ProgrammingError ( " 表分区错误,无法进行分区 " )
day_before = date . today ( ) . strftime ( " % Y- % m- %d " )
schema_editor = connection . schema_editor ( )
if is_tenants_mode ( ) :
schema_editor . sql_add_range_partition = (
f ' CREATE TABLE " { connection . tenant . schema_name } " .%s PARTITION OF " { connection . tenant . schema_name } " .%s FOR VALUES FROM (%s) TO (%s) '
)
for index in range ( day ) :
try :
day_following = ( date . today ( ) + timedelta ( days = index + 1 ) ) . strftime ( " % Y- % m- %d " )
schema_editor . add_range_partition (
model = cls ,
name = f " { day_before } _ { day_following } " ,
from_values = day_before ,
to_values = day_following ,
)
day_before = day_following
except ProgrammingError as e :
print ( f " { cls . __name__ } 分表失败: " + str ( e ) . rstrip ( ' \n ' ) )
return
@classmethod
def add_range_month_partition ( cls , start_date , end_date ) :
"""
按照创建时间 " 月 " 分表
: return :
"""
if cls . PartitioningMeta . method != ' range ' :
raise ProgrammingError ( " 表分区错误,无法进行分区 " )
range_month_partition_list = get_month_range ( start_date , end_date )
schema_editor = connection . schema_editor ( )
if is_tenants_mode ( ) :
schema_editor . sql_add_range_partition = (
f ' CREATE TABLE " { connection . tenant . schema_name } " .%s PARTITION OF " { connection . tenant . schema_name } " .%s FOR VALUES FROM (%s) TO (%s) '
)
for index , ele in enumerate ( range_month_partition_list ) :
if index == 0 :
continue
try :
schema_editor . add_range_partition (
model = cls ,
name = f " { range_month_partition_list [ index - 1 ] [ : - 3 ] } _ { ele [ : - 3 ] } " ,
from_values = range_month_partition_list [ index - 1 ] ,
to_values = ele ,
)
except ProgrammingError as e :
print ( f " { cls . __name__ } 分表失败: " + str ( e ) . rstrip ( ' \n ' ) )
return
@classmethod
def add_list_partition ( cls , unique_value ) :
"""
按照某个值进行分区
: param unique_value :
: return :
"""
if cls . PartitioningMeta . method != ' list ' :
raise ProgrammingError ( " 表分区错误,无法进行分区 " )
schema_editor = connection . schema_editor ( )
if is_tenants_mode ( ) :
schema_editor . sql_add_list_partition = (
f ' CREATE TABLE " { connection . tenant . schema_name } " .%s PARTITION OF " { connection . tenant . schema_name } " .%s FOR VALUES IN (%s) '
)
try :
schema_editor . add_list_partition (
model = cls ,
name = f " _ { unique_value } " ,
values = [ unique_value ] ,
)
except ProgrammingError as e :
print ( f " { cls . __name__ } 分表失败: " + str ( e ) . rstrip ( ' \n ' ) )
return
2022-08-01 17:12:04 +00:00
2022-04-05 05:22:26 +00:00
def get_all_models_objects ( model_name = None ) :
"""
获取所有 models 对象
: return : { }
"""
settings . ALL_MODELS_OBJECTS = { }
if not settings . ALL_MODELS_OBJECTS :
all_models = apps . get_models ( )
for item in list ( all_models ) :
table = {
" tableName " : item . _meta . verbose_name ,
" table " : item . __name__ ,
" tableFields " : [ ]
}
for field in item . _meta . fields :
fields = {
" title " : field . verbose_name ,
" field " : field . name
}
table [ ' tableFields ' ] . append ( fields )
settings . ALL_MODELS_OBJECTS . setdefault ( item . __name__ , { " table " : table , " object " : item } )
if model_name :
return settings . ALL_MODELS_OBJECTS [ model_name ] or { }
2023-04-14 09:26:30 +00:00
return settings . ALL_MODELS_OBJECTS or { }