django全自动分库分表(横向)

django全自动分库分表

期待大神指点方法的不足之处或者其他更好的方法
由于当前项目目前只需要实现写,所以这里也没有考虑读写分离。如果要考虑读写分离,则在路由处进行设置即可。

分库原理

django在初始化的时候,从自己的数据库管理服务器获取当前的所有数据库,以及数据库的入库规则,在获取数据的时候,根据数据字段值动态生成Model,并动态保存到指定的数据库中。
动态生成的Model不仅能够实现分库,还能实现动态横向分表,牛逼吧。
一句话:通过动态路由分库,通过动态model分表

1 从数据库管理服务器获取设置

settings.py
#尽量放在靠前
GROUP_ID_TO_DATABASE={}#记录对应的动态model需要保存到哪个数据库
# # Application definition
def get_db_setting():
    """
    从数据库管理服务器获取数据库设置和数据标对应的数据库
    db_setting_url:连接数据库管理服务器的地址
    django_info:当前配置的服务器的信息,必须为一个不重复的固定值
    :return:
    """
    import requests
    django_info={
        'django_device_id':'',#该服务器的设备id,建议从配置文件读取,且各个django服务器之间不要重复

    }
    r=requests.post(url=db_setting_url,data=django_info)
    return r.json()

DATABASES,GROUP_ID_TO_DATABASE=get_db_setting()

2 创建数据库表

该model在collect这个app里面

#对应model.py里面的model
#MyModel是我自定义的一个抽象基类model,你可以直接使用models.Model
class BaseGetFaceRecord(MyModel):
    """
    入库记录
    基础的入库记录,该model为abstract=True的model,在之后会进行继承并创建真正需要的数据库
    
    """
    collected_face = models.ForeignKey(BaseCollectFace, on_delete=models.CASCADE, verbose_name="对应人脸信息")
    group_id = models.CharField(max_length=128, verbose_name="人脸库")
    camera_id = models.CharField(max_length=128, verbose_name="抓拍相机ID")
    arrived_time = models.DateTimeField(max_length=10, verbose_name="抓拍时间戳")
    person_id = models.CharField(max_length=128, verbose_name="人脸ID(faceToken)")
    first_arrive_time = models.DateTimeField(max_length=10, verbose_name="首次到访时间戳")

    class Meta:
        abstract = True
        verbose_name = "人脸照片库"
        verbose_name_plural = verbose_name

class BaseGetFaceRecord(MyModel):
    """
    入库记录
    基础的入库记录,该model为abstract=True的model,在之后会进行继承并创建真正需要的数据库

    """
    collected_face = models.ForeignKey(BaseCollectFace, on_delete=models.CASCADE, verbose_name="对应人脸信息")
    group_id = models.CharField(max_length=128, verbose_name="人脸库")
    camera_id = models.CharField(max_length=128, verbose_name="抓拍相机ID")
    arrived_time = models.DateTimeField(max_length=10, verbose_name="抓拍时间戳")
    person_id = models.CharField(max_length=128, verbose_name="人脸ID(faceToken)")
    first_arrive_time = models.DateTimeField(max_length=10, verbose_name="首次到访时间戳")

    class Meta:
        abstract = True
        verbose_name = "人脸照片库"
        verbose_name_plural = verbose_name


class CreateGetFaceRecordModel(object):
    """
    动态创建动态创建人脸库的类
    主要用来提供静态方法
    也可以不写成类
    """

    @staticmethod
    def __get_GetFaceRecord_model(group_id: str):
        """
        创建FaceRecord模型
        :param group_id:人脸库名称
        :return: FaceRecord
        """
        #要连接的数据表,
        table_name = 'getfacerecord_%s' % str(group_id)
        #这里会重新设置创建的model的名字,每个model名字都是动态的,不是GetFaceRecord
        class Metaclass(models.base.ModelBase):
            def __new__(cls, name, bases, attrs):
                name += group_id  # 这是Model的name.
                return models.base.ModelBase.__new__(cls, name, bases, attrs)

        # 注意继承的顺序
        class GetFaceRecord(BaseAlgoFace, metaclass=Metaclass):
            @staticmethod
            def is_exists(table_name1):
                """
                判断这个表是否已经在数据库
                :param table_name1:
                :return:
                """
                table_name1 = 'getfacerecord_' + table_name1
                return table_name1 in connection.introspection.table_names()

            class Meta:
                abstract = False
                db_table = table_name
                app_label="collect"

        return GetFaceRecord

    @staticmethod
    def create_GetFaceRecord(group_id: str):
        """
        注册GetFaceRecord模型
        :param group_id: 人脸库名称
        :return: GetFaceRecord
        """
        try:
            # cls = apps.get_model('__main__', 'ExcelData_%s' % project_name)
            # 获取模型对应的model,如果有的话,第一个参数为collect这个app,第二个参数为model的名字
            # 这里不用__main__是因为之后会考虑到分库,不同的app会到不同的库里
            cls = apps.get_model('collect', 'GetFaceRecord_%s' % group_id)
        except LookupError:
            cls = CreateGetFaceRecordModel.__get_GetFaceRecord_model(group_id)
        except Exception as e:
            raise e
        if not cls.is_exists(group_id):
            # 将数据表创建
            with connection.schema_editor() as schema_editor:
                # 创建模型,调用的应该是migrate和makemigrations里面的方法
                schema_editor.create_model(cls)
            return cls
        else:
            return cls


def get_GetFaceRecord(group_id: str):
    """
    获取注册人脸的人脸库对应model,但是请注意,如果没有会创建该表和该模型,如果有,返回该模型
    :param group_id:人脸库信息
    :return:
    """
    return CreateGetFaceRecordModel.create_GetFaceRecord(group_id)

3 创建数据库路由器

在对应的app里面创建自己的router.py,注意这部分内容也应该从数据库管理服务器获取

"""
@version: 1.0
@author: chise
@time : 2019/07/24 10:51
"""
from MultiAlgorithm.settings import GROUP_ID_TO_DATABASE


class BaseRouter():
    def db_for_read(self, model, **hints):
        """
        返回对应的数据库名称
        :param model:
        :param hints:
        :return:
        """
        if model._meta.label == "base.UserGroup" or model._meta.label == "base.FaceAggregation":
            return 'default'
        else:
            model_name = str(model.__class__.__name__)
            return GROUP_ID_TO_DATABASE[model_name]

    def db_for_write(self, model, **hints):
        if model._meta.label == "base.UserGroup" or model._meta.label == "base.FaceAggregation":
            return 'default'
        else:
            model_name = str(model.__class__.__name__)
            return GROUP_ID_TO_DATABASE[model_name]

    def allow_relation(self, obj1, obj2, **hints):
        """
        是否允许通过外键访问??
        如果应该允许obj1和obj2之间的关系,返回True;如果应该阻止这种关系,返回False;如果路由器没有意见,返回None。
        这纯粹是一个验证操作,由外键和许多对许多操作使用,以确定是否应该允许两个对象之间存在关系。
        :param obj1:
        :param obj2:
        :param hints:
        :return:
        """
        return True

    def allow_migrate(self, db, app_label, model_name=None, **hints):
        """
        是否允许migrate进行迁移的验证
        这里不允许通过migrate的方式将数据迁移,数据库表只能在数据库管理器里面进行创建
        :param db:
        :param app_label:
        :param model_name:
        :param hints:
        :return:
        """
        if app_label == 'base':
            return db == 'base.UserGroup' or db == 'base.UserGroup'
        return None

至此,就完成了动态的数据库配置

总结

通过这个方法,再结合nginx,就可完成django配置数据库。
注意,这里只是一个demo,还有很多东西没有进行实现,比如数据库DATABASE设置的热更新,如果配置,即可实现动态添加数据库和增加服务器配置。
创建路由的介绍可以看这,挺合适:https://www.jb51.net/article/141182.htm

  • 2
    点赞
  • 12
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值