├── README.md └── main.py /README.md: -------------------------------------------------------------------------------- 1 | django-dynamic-tables 2 | ===================== 3 | 4 | 动态创建table, 并通过 Django ORM 操作. 5 | 6 | 7 | ## 动态的创建表 8 | 9 | 动态的创建模型其实就是在运行时生成 Model 类, 这个可以通过函数实现, 通过传参(今天的日期, 如: 20181211),然后生成新的模型类, Meta 中的 db\_table 为log\_20181211. 10 | 11 | ```python 12 | def get_log_model(prefix): 13 | table_name = 'log_%s' % str(prefix) 14 | 15 | LOG_LEVELS = ( 16 | (0, 'DEBUG'), 17 | (10, 'INFO'), 18 | (20, 'WARNING'), 19 | ) 20 | 21 | class LogMetaclass(models.base.ModelBase): 22 | def __new__(cls, name, bases, attrs): 23 | name += '_' + prefix # 这是Model的name. 24 | return models.base.ModelBase.__new__(cls, name, bases, attrs) 25 | 26 | class Log(models.Model): 27 | __metaclass__ = LogMetaclass 28 | level = models.IntegerField(choices=LOG_LEVELS) 29 | msg = models.TextField() 30 | time = models.DateTimeField(auto_now=True, auto_now_add=True) 31 | 32 | @staticmethod 33 | def is_exists(): 34 | return table_name in connection.introspection.table_names() 35 | 36 | class Meta: 37 | db_table = table_name 38 | 39 | return Log 40 | ``` 41 | 42 | 可以看到, 通过函数生成不同的 Log Class. 注意LogMetaclass和\_\_metaclass\_\_ , 元类可以在运行时改变模型的名字,table 的名称我们可以通过db\_table定义, 类的名称可以通过覆盖元类的方法定义。 43 | 44 | ```python 45 | print cls.__name__ 46 | Log_20181211 47 | print cls._meta.db_table 48 | log_20181211 49 | ``` 50 | 51 | ## 使用 52 | 53 | 使用直接通过函数, 获取当前日期的 Log 模型, 然后通过is\_exists判读表是否创建, 没有创建则创建对应的表. 54 | 55 | 56 | ```python 57 | def index(request): 58 | today = date.today().strftime("%Y%m%d") 59 | 60 | # RuntimeWarning: Model '__main__.logclasslog_' was already registered. 61 | # Reloading models is not advised as it can lead to inconsistencies 62 | # most notably with related models. 63 | # 如上述警告所述, Django 不建议重复加载 Model 的定义. 64 | # 作为 demo 可以直接通过get_log_model获取,无视警告. 65 | # 所以这里先通过 all_models 获取已经注册的 Model, 66 | # 如果获取不到, 再生成新的模型. 67 | try: 68 | cls = apps.get_model('__main__', 'Log_%s' % today) 69 | except LookupError: 70 | cls = get_log_model(today) 71 | 72 | if not cls.is_exists(): 73 | with connection.schema_editor() as schema_editor: 74 | schema_editor.create_model(cls) 75 | 76 | log = cls(level=10, msg="Hello") 77 | log.save() 78 | 79 | return HttpResponse('

%s

' % cls._meta.db_table) 80 | ``` 81 | 82 | 上面获取 cls 部分, 这里的代码先通过apps的已经注册的 all_models 获取, 否则一个模型的第二次执行定义代码就会抛出RuntimeWarning警告, 在模型的初始化函数都会注册此模型, 最好不要重复注册. 先通过 apps.get_model 获取这个模型, 如果没有获取到则通过get_log_model初始化新的模型. 这样做更加稳妥一点. 83 | 84 | -------------------------------------------------------------------------------- /main.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | import sys, os 3 | 4 | from datetime import date 5 | from django.db import models 6 | from django.db import connection 7 | from django.apps import apps 8 | from django.conf import settings 9 | from django.conf.urls import url 10 | from django.core.management import execute_from_command_line 11 | from django.http import HttpResponse 12 | 13 | settings.configure( 14 | DEBUG=True, 15 | SECRET_KEY='A-random-secret-key!', 16 | ROOT_URLCONF=sys.modules[__name__], 17 | INSTALLED_APPS=['__main__', ], 18 | DATABASES= { 19 | 'default': { 20 | 'ENGINE': 'django.db.backends.sqlite3', 21 | 'NAME': os.path.join('./', 'db.sqlite3'), 22 | } 23 | } 24 | ) 25 | 26 | 27 | def get_log_model(prefix): 28 | table_name = 'log_%s' % str(prefix) 29 | 30 | LOG_LEVELS = ( 31 | (0, 'DEBUG'), 32 | (10, 'INFO'), 33 | (20, 'WARNING'), 34 | ) 35 | 36 | class LogMetaclass(models.base.ModelBase): 37 | def __new__(cls, name, bases, attrs): 38 | name += '_' + prefix # 这是Model的name. 39 | return models.base.ModelBase.__new__(cls, name, bases, attrs) 40 | 41 | class Log(models.Model): 42 | __metaclass__ = LogMetaclass 43 | level = models.IntegerField(choices=LOG_LEVELS) 44 | msg = models.TextField() 45 | time = models.DateTimeField(auto_now=True, auto_now_add=True) 46 | 47 | @staticmethod 48 | def is_exists(): 49 | return table_name in connection.introspection.table_names() 50 | 51 | class Meta: 52 | db_table = table_name 53 | 54 | return Log 55 | 56 | 57 | def index(request): 58 | today = date.today().strftime("%Y%m%d") 59 | 60 | # RuntimeWarning: Model '__main__.logclasslog_' was already registered. 61 | # Reloading models is not advised as it can lead to inconsistencies 62 | # most notably with related models. 63 | # 如上述错误所述, Django 不建议重复加载 Model 的定义. 64 | # 所以这里先通过 all_models 获取已经注册的 Model, 65 | # 如果获取不到, 再生成新的模型. 66 | try: 67 | cls = apps.get_model('__main__', 'Log_%s' % today) 68 | except LookupError: 69 | cls = get_log_model(today) 70 | 71 | if not cls.is_exists(): 72 | with connection.schema_editor() as schema_editor: 73 | schema_editor.create_model(cls) 74 | 75 | log = cls(level=10, msg="Hello") 76 | log.save() 77 | 78 | return HttpResponse('

%s

' % cls._meta.db_table) 79 | 80 | 81 | urlpatterns = [ 82 | url(r'^$', index), 83 | ] 84 | 85 | 86 | if __name__ == '__main__': 87 | execute_from_command_line(sys.argv) 88 | --------------------------------------------------------------------------------