├── 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 |
--------------------------------------------------------------------------------