valar 0.0.11__py3-none-any.whl → 0.0.13__py3-none-any.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Potentially problematic release.
This version of valar might be problematic. Click here for more details.
- valar/__init__.py +11 -6
- valar/channels/__init__.py +25 -23
- valar/channels/utils.py +43 -0
- valar/channels/views.py +5 -35
- valar/data/handlers.py +28 -0
- valar/data/migrations/0001_initial.py +210 -0
- valar/data/models.py +223 -0
- valar/data/mon/__init__.py +84 -0
- valar/data/orm/__init__.py +87 -0
- valar/data/orm/detacher.py +61 -0
- valar/data/orm/meta.py +24 -0
- valar/data/orm/meta_loader.py +135 -0
- valar/data/orm/values.py +93 -0
- valar/data/query.py +48 -0
- valar/data/urls.py +15 -0
- valar/data/utils.py +70 -0
- valar/data/views.py +76 -0
- {valar-0.0.11.dist-info → valar-0.0.13.dist-info}/METADATA +4 -3
- valar-0.0.13.dist-info/RECORD +24 -0
- {valar-0.0.11.dist-info → valar-0.0.13.dist-info}/WHEEL +1 -1
- valar/dao/migrations/0001_initial.py +0 -22
- valar/dao/models.py +0 -7
- valar/dao/views.py +0 -3
- valar-0.0.11.dist-info/RECORD +0 -13
- /valar/{dao → data}/__init__.py +0 -0
- /valar/{dao → data}/migrations/__init__.py +0 -0
- {valar-0.0.11.dist-info → valar-0.0.13.dist-info/licenses}/LICENSE +0 -0
- {valar-0.0.11.dist-info → valar-0.0.13.dist-info}/top_level.txt +0 -0
valar/data/models.py
ADDED
|
@@ -0,0 +1,223 @@
|
|
|
1
|
+
from django.db import models
|
|
2
|
+
from django.db.models.fields.files import FieldFile
|
|
3
|
+
|
|
4
|
+
|
|
5
|
+
class VModel(models.Model):
|
|
6
|
+
sort = models.BigIntegerField(null=True, verbose_name='序号')
|
|
7
|
+
name = models.CharField(max_length=50, null=True)
|
|
8
|
+
create_time = models.DateTimeField(auto_now_add=True, null=True, verbose_name='创建时间')
|
|
9
|
+
modify_time = models.DateTimeField(auto_now=True, null=True, verbose_name='修改时间')
|
|
10
|
+
saved = models.BooleanField(default=False)
|
|
11
|
+
|
|
12
|
+
class Meta:
|
|
13
|
+
abstract = True
|
|
14
|
+
|
|
15
|
+
@property
|
|
16
|
+
def mapping(self):
|
|
17
|
+
mapping = {}
|
|
18
|
+
for field in self._meta.get_fields():
|
|
19
|
+
prop = field.name
|
|
20
|
+
domain = type(field).__name__
|
|
21
|
+
mapping[prop] = {'prop': prop, 'domain': domain, 'field': field}
|
|
22
|
+
return mapping
|
|
23
|
+
|
|
24
|
+
"""
|
|
25
|
+
只序列化基础字段:能用value_from_object直接取出的字段。与values等价
|
|
26
|
+
"""
|
|
27
|
+
|
|
28
|
+
def __str__(self):
|
|
29
|
+
return str(self.json)
|
|
30
|
+
|
|
31
|
+
@property
|
|
32
|
+
def json(self):
|
|
33
|
+
mapping = self.mapping
|
|
34
|
+
excludes = ['ManyToOneRel', 'OneToOneRel', 'ManyToManyRel', 'ManyToManyField', 'UUIDField']
|
|
35
|
+
mapping = {prop: mapping[prop] for prop in mapping if mapping[prop]['domain'] not in excludes}
|
|
36
|
+
data = {}
|
|
37
|
+
for prop in mapping:
|
|
38
|
+
field = mapping[prop]['field']
|
|
39
|
+
domain = mapping[prop]['domain']
|
|
40
|
+
value = field.value_from_object(self)
|
|
41
|
+
if domain in ['ForeignKey', 'OneToOneField']:
|
|
42
|
+
prop = prop + '_id'
|
|
43
|
+
elif domain in ['DateField']:
|
|
44
|
+
value = value.strftime('%Y-%m-%d') if value else None
|
|
45
|
+
elif domain in ['DateTimeField']:
|
|
46
|
+
value = value.strftime('%Y-%m-%d %H:%M:%S') if value else None
|
|
47
|
+
elif domain in ['FileField']:
|
|
48
|
+
file: FieldFile = value
|
|
49
|
+
value = file.name
|
|
50
|
+
# elif domain in ['BigAutoField']:
|
|
51
|
+
# value = str(value)
|
|
52
|
+
data[prop] = value
|
|
53
|
+
return data
|
|
54
|
+
|
|
55
|
+
@property
|
|
56
|
+
def full(self):
|
|
57
|
+
data = self.json
|
|
58
|
+
mapping = self.mapping
|
|
59
|
+
excludes = ['ManyToManyField', 'ManyToManyRel', 'ForeignKey', 'ManyToOneRel', 'OneToOneField', 'OneToOneRel']
|
|
60
|
+
mapping = {prop: mapping[prop] for prop in mapping if mapping[prop]['domain'] in excludes}
|
|
61
|
+
for prop in mapping:
|
|
62
|
+
field = mapping[prop]['field']
|
|
63
|
+
domain = mapping[prop]['domain']
|
|
64
|
+
if domain in ['ForeignKey', 'OneToOneField', 'OneToOneRel']:
|
|
65
|
+
if hasattr(self, prop):
|
|
66
|
+
bean: VModel = getattr(self, prop)
|
|
67
|
+
data[prop] = bean.json if bean else None
|
|
68
|
+
data['%s_id'%prop] = bean.id if bean else None
|
|
69
|
+
elif domain in ['ManyToManyField', 'ManyToManyRel', 'ManyToOneRel']:
|
|
70
|
+
accessor = prop if domain == 'ManyToManyField' else field.get_accessor_name()
|
|
71
|
+
try:
|
|
72
|
+
_set = getattr(self, accessor).all()
|
|
73
|
+
data[prop] = [item.id for item in _set]
|
|
74
|
+
data[f'{prop}_set'] = [item.json for item in _set]
|
|
75
|
+
except Exception as e:
|
|
76
|
+
print(e)
|
|
77
|
+
pass
|
|
78
|
+
return data
|
|
79
|
+
|
|
80
|
+
|
|
81
|
+
class VTree(VModel):
|
|
82
|
+
pid = models.IntegerField(null=False, default=0, verbose_name='父节点')
|
|
83
|
+
isLeaf = models.BooleanField( default=False, verbose_name='叶子节点')
|
|
84
|
+
icon = models.CharField(max_length=255, null=True, verbose_name='图标')
|
|
85
|
+
class Meta:
|
|
86
|
+
abstract = True
|
|
87
|
+
|
|
88
|
+
|
|
89
|
+
class Meta(VModel):
|
|
90
|
+
entity = models.CharField(max_length=100, verbose_name='数据源', null=True, unique=True)
|
|
91
|
+
name = models.CharField(max_length=50, verbose_name='实体别名', null=True)
|
|
92
|
+
|
|
93
|
+
class Meta:
|
|
94
|
+
verbose_name = '数据实体'
|
|
95
|
+
# unique_together = ('domain', 'entity')
|
|
96
|
+
|
|
97
|
+
|
|
98
|
+
class MetaView(VModel):
|
|
99
|
+
meta = models.ForeignKey('Meta', on_delete=models.CASCADE, verbose_name='元数据')
|
|
100
|
+
|
|
101
|
+
code = models.CharField(max_length=50, verbose_name='类视图', default='default ')
|
|
102
|
+
view_name = models.CharField(max_length=50, verbose_name='视图名称', null=True)
|
|
103
|
+
|
|
104
|
+
form_width = models.IntegerField(default=0, verbose_name='表单宽度')
|
|
105
|
+
form_height = models.IntegerField(default=0, verbose_name='表单高度')
|
|
106
|
+
table_width = models.IntegerField(default=0, verbose_name='表格宽度')
|
|
107
|
+
table_height = models.IntegerField(default=0, verbose_name='表格高度')
|
|
108
|
+
|
|
109
|
+
enable = models.BooleanField(default=True, verbose_name='是否启用')
|
|
110
|
+
show_header = models.BooleanField(default=True, verbose_name='展示头部')
|
|
111
|
+
allow_batch = models.BooleanField(default=True, verbose_name='批处理')
|
|
112
|
+
allow_search = models.BooleanField(default=True, verbose_name='检索功能')
|
|
113
|
+
allow_sort = models.BooleanField(default=True, verbose_name='排序功能')
|
|
114
|
+
allow_pop = models.BooleanField(default=True, verbose_name='移动功能')
|
|
115
|
+
allow_insert = models.BooleanField(default=True, verbose_name='新增功能')
|
|
116
|
+
allow_edit = models.BooleanField(default=True, verbose_name='编辑功能')
|
|
117
|
+
allow_remove = models.BooleanField(default=True, verbose_name='删除功能')
|
|
118
|
+
allow_download = models.BooleanField(default=True, verbose_name='下载功能')
|
|
119
|
+
allow_upload = models.BooleanField(default=True, verbose_name='上传功能')
|
|
120
|
+
|
|
121
|
+
class Meta:
|
|
122
|
+
verbose_name = '数据视图'
|
|
123
|
+
unique_together = ('meta', 'code')
|
|
124
|
+
|
|
125
|
+
|
|
126
|
+
class MetaField(VModel):
|
|
127
|
+
# 标识
|
|
128
|
+
view = models.ForeignKey('MetaView', on_delete=models.CASCADE, verbose_name='数据视图')
|
|
129
|
+
prop = models.CharField(max_length=100, verbose_name='字段名称') #
|
|
130
|
+
label = models.CharField(max_length=100, verbose_name='字段标签') #
|
|
131
|
+
name = models.CharField(max_length=100, verbose_name='字段别名') #
|
|
132
|
+
|
|
133
|
+
"""tool"""
|
|
134
|
+
domain = models.CharField(max_length=100, verbose_name='字段类型') #
|
|
135
|
+
tool = models.CharField(max_length=100, default='default', verbose_name='工具组件')
|
|
136
|
+
refer = models.JSONField(default=dict, verbose_name='索引') #
|
|
137
|
+
format = models.JSONField(default=dict, verbose_name='格式') #
|
|
138
|
+
|
|
139
|
+
|
|
140
|
+
"""rest"""
|
|
141
|
+
unit = models.CharField(max_length=55, verbose_name='单位符', null=True)
|
|
142
|
+
not_null = models.BooleanField(default=False, verbose_name='不为空') #
|
|
143
|
+
read_only = models.BooleanField(default=False, verbose_name='只读')
|
|
144
|
+
sortable = models.BooleanField(default=False, verbose_name='可排序')
|
|
145
|
+
allow_search = models.BooleanField(default=True, verbose_name='可搜索')
|
|
146
|
+
allow_download = models.BooleanField(default=True, verbose_name='可下载')
|
|
147
|
+
allow_upload = models.BooleanField(default=False, verbose_name='可上传')
|
|
148
|
+
allow_update = models.BooleanField(default=False, verbose_name='可更新')
|
|
149
|
+
|
|
150
|
+
"""table"""
|
|
151
|
+
column_width = models.FloatField(default=0, verbose_name='表头宽度')
|
|
152
|
+
align = models.CharField(max_length=55, default='left', verbose_name='对齐方式') #
|
|
153
|
+
fixed = models.CharField(max_length=100, verbose_name='固定位置', null=True)
|
|
154
|
+
header_color = models.CharField(max_length=55, verbose_name='表头颜色', null=True)
|
|
155
|
+
cell_color = models.CharField(max_length=55, verbose_name='单元颜色', null=True)
|
|
156
|
+
edit_on_table = models.BooleanField(default=False, verbose_name='表格编辑')
|
|
157
|
+
hide_on_table = models.BooleanField(default=False, verbose_name='表内隐藏')
|
|
158
|
+
|
|
159
|
+
"""form"""
|
|
160
|
+
span = models.IntegerField(default=0, verbose_name='表单占位')
|
|
161
|
+
hide_on_form = models.BooleanField(default=False, verbose_name='表单隐藏')
|
|
162
|
+
hide_on_form_edit = models.BooleanField(default=False, verbose_name='编辑隐藏')
|
|
163
|
+
hide_on_form_insert = models.BooleanField(default=False, verbose_name='新增隐藏')
|
|
164
|
+
hide_on_form_branch = models.BooleanField(default=False, verbose_name='分支隐藏')
|
|
165
|
+
hide_on_form_leaf = models.BooleanField(default=False, verbose_name='叶子隐藏')
|
|
166
|
+
|
|
167
|
+
class Meta:
|
|
168
|
+
verbose_name = '视图字段'
|
|
169
|
+
|
|
170
|
+
|
|
171
|
+
|
|
172
|
+
class MetaFieldTool(VTree):
|
|
173
|
+
name = models.CharField(max_length=255, null=True, verbose_name='名称')
|
|
174
|
+
code = models.CharField(max_length=100, unique=True, null=True, verbose_name='代码') #
|
|
175
|
+
format = models.JSONField(default=dict, verbose_name='格式参数') #
|
|
176
|
+
|
|
177
|
+
class Meta:
|
|
178
|
+
verbose_name = '元数据字段工具'
|
|
179
|
+
#
|
|
180
|
+
#
|
|
181
|
+
class MetaFieldDomain(VModel):
|
|
182
|
+
name = models.CharField(max_length=255, unique=True, null=True, verbose_name='名称')
|
|
183
|
+
tools = models.ManyToManyField(to=MetaFieldTool, verbose_name='工具集')
|
|
184
|
+
default = models.ForeignKey(to=MetaFieldTool, null=True, on_delete=models.SET_NULL, related_name='+',
|
|
185
|
+
verbose_name='默认工具')
|
|
186
|
+
search = models.ForeignKey(to=MetaFieldTool, null=True, on_delete=models.SET_NULL, related_name='+',
|
|
187
|
+
verbose_name='搜索工具')
|
|
188
|
+
align = models.CharField(max_length=10, null=True, verbose_name='对齐方式')
|
|
189
|
+
|
|
190
|
+
class Meta:
|
|
191
|
+
verbose_name = '元数据字段类型'
|
|
192
|
+
|
|
193
|
+
|
|
194
|
+
|
|
195
|
+
|
|
196
|
+
|
|
197
|
+
class Vala(VModel):
|
|
198
|
+
text_field = models.TextField(null=True, verbose_name='text')
|
|
199
|
+
boolean_field = models.BooleanField(null=True, verbose_name='boolean')
|
|
200
|
+
integer_field = models.IntegerField(null=True, verbose_name='integer')
|
|
201
|
+
float_field = models.FloatField(null=True, verbose_name='float')
|
|
202
|
+
date_field = models.DateField(null=True, verbose_name='date')
|
|
203
|
+
datetime_field = models.DateTimeField(null=True, verbose_name='datetime')
|
|
204
|
+
time_field = models.TimeField(null=True, verbose_name='time')
|
|
205
|
+
json_field = models.JSONField(null=True, verbose_name='json')
|
|
206
|
+
file = models.FileField(null=True, verbose_name='File')
|
|
207
|
+
# menus = models.ManyToManyField(to=MetaField, related_name='+')
|
|
208
|
+
# menu = models.ForeignKey(to=MetaField, null=True, on_delete=models.CASCADE, verbose_name='vala')
|
|
209
|
+
|
|
210
|
+
|
|
211
|
+
class M2O(VModel):
|
|
212
|
+
vala = models.ForeignKey(to=Vala, null=True, on_delete=models.CASCADE, verbose_name='vala')
|
|
213
|
+
name = models.CharField(max_length=100, null=True, verbose_name='name')
|
|
214
|
+
|
|
215
|
+
|
|
216
|
+
class O2O(VModel):
|
|
217
|
+
vala = models.OneToOneField(to=Vala, null=True, on_delete=models.CASCADE, verbose_name='vala')
|
|
218
|
+
name = models.CharField(max_length=100, null=True, verbose_name='name')
|
|
219
|
+
|
|
220
|
+
|
|
221
|
+
class M2M(VModel):
|
|
222
|
+
valas = models.ManyToManyField(to=Vala, verbose_name='valas')
|
|
223
|
+
name = models.CharField(max_length=100, null=True, verbose_name='name')
|
|
@@ -0,0 +1,84 @@
|
|
|
1
|
+
from bson import ObjectId
|
|
2
|
+
from django.db.models import QuerySet
|
|
3
|
+
from pymongo.results import InsertOneResult, UpdateResult
|
|
4
|
+
|
|
5
|
+
from django.conf import settings
|
|
6
|
+
import pymongo
|
|
7
|
+
|
|
8
|
+
from ..query import Query
|
|
9
|
+
|
|
10
|
+
try:
|
|
11
|
+
MONGO = settings.MONGO_SETTINGS
|
|
12
|
+
except AttributeError:
|
|
13
|
+
MONGO = {
|
|
14
|
+
'host': 'localhost',
|
|
15
|
+
'port': 27017,
|
|
16
|
+
}
|
|
17
|
+
host, port, username, password = MONGO.get('host'), MONGO.get('port'), MONGO.get('username'), MONGO.get('password')
|
|
18
|
+
|
|
19
|
+
if username and password:
|
|
20
|
+
uri = f'mongodb://{username}:{password}@{host}:{port}/'
|
|
21
|
+
else:
|
|
22
|
+
uri = f'mongodb://{host}:{port}/'
|
|
23
|
+
mongo_params = {
|
|
24
|
+
'maxPoolSize': 10,
|
|
25
|
+
'minPoolSize': 0,
|
|
26
|
+
'maxIdleTimeMS': 10000,
|
|
27
|
+
'connectTimeoutMS': 10000,
|
|
28
|
+
'socketTimeoutMS': 10000,
|
|
29
|
+
'serverSelectionTimeoutMS': 10000,
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
def get_mongo_client():
|
|
33
|
+
client = pymongo.MongoClient(uri, **mongo_params)
|
|
34
|
+
client['admin'].command('ping')
|
|
35
|
+
return client
|
|
36
|
+
|
|
37
|
+
|
|
38
|
+
|
|
39
|
+
class MongoDao:
|
|
40
|
+
def __init__(self, ref):
|
|
41
|
+
self.ref = ref
|
|
42
|
+
db_name = settings.BASE_APP
|
|
43
|
+
col_name = ref.replace('.', '_')
|
|
44
|
+
self.client = get_mongo_client()
|
|
45
|
+
self.collection = self.client[db_name][col_name]
|
|
46
|
+
def save_one(self, item):
|
|
47
|
+
_id = item.get('id', None)
|
|
48
|
+
_id = None if isinstance(_id, int) else _id
|
|
49
|
+
if _id is None:
|
|
50
|
+
bean:InsertOneResult = self.collection.insert_one(item)
|
|
51
|
+
_id = bean.inserted_id
|
|
52
|
+
self.collection.update_one({'_id': _id}, {'$set': {'sort':str(_id)}})
|
|
53
|
+
else:
|
|
54
|
+
del item['id']
|
|
55
|
+
_id = ObjectId(_id)
|
|
56
|
+
self.collection.update_one({'_id': _id}, {'$set': item})
|
|
57
|
+
return self.collection.find_one({'_id': _id})
|
|
58
|
+
|
|
59
|
+
|
|
60
|
+
def update_many(self, query, template):
|
|
61
|
+
self.collection.update_many(query.mon_conditions(), {'$set': template})
|
|
62
|
+
|
|
63
|
+
|
|
64
|
+
def delete_one(self, _id):
|
|
65
|
+
self.collection.delete_one({'_id': ObjectId(_id)})
|
|
66
|
+
|
|
67
|
+
def delete_many(self, query):
|
|
68
|
+
self.collection.delete_many(query.mon_conditions())
|
|
69
|
+
|
|
70
|
+
|
|
71
|
+
def find_one(self, _id):
|
|
72
|
+
return self.collection.find_one({'_id': ObjectId(_id)})
|
|
73
|
+
|
|
74
|
+
def find_many(self, query: Query, size=0, page=1) -> [QuerySet, int]:
|
|
75
|
+
skip = (page - 1) * size
|
|
76
|
+
condition = query.mon_conditions()
|
|
77
|
+
total = self.collection.count_documents(condition)
|
|
78
|
+
cursor = self.collection.find(condition).sort(query.orders).skip(skip).limit(size)
|
|
79
|
+
return [cursor, total]
|
|
80
|
+
|
|
81
|
+
|
|
82
|
+
def meta(self):
|
|
83
|
+
one = self.collection.find_one()
|
|
84
|
+
return one
|
|
@@ -0,0 +1,87 @@
|
|
|
1
|
+
import datetime
|
|
2
|
+
from django.apps import apps
|
|
3
|
+
from django.core.paginator import Paginator
|
|
4
|
+
from django.db.models import Manager, QuerySet
|
|
5
|
+
|
|
6
|
+
from ..orm.detacher import detach_props, save_detached
|
|
7
|
+
from ..orm.meta_loader import load_meta, load_view, load_meta_field
|
|
8
|
+
from ..models import VModel
|
|
9
|
+
from ..query import Query
|
|
10
|
+
|
|
11
|
+
|
|
12
|
+
def load_model(entity=None):
|
|
13
|
+
mapping = {}
|
|
14
|
+
for mod in apps.get_models():
|
|
15
|
+
if issubclass(mod, VModel):
|
|
16
|
+
path, name = mod.__module__, mod.__name__
|
|
17
|
+
print(path)
|
|
18
|
+
app = path.replace('.models', '').replace('src.valar.','')
|
|
19
|
+
key = '%s.%s' % (app, name)
|
|
20
|
+
verbose_name = mod._meta.verbose_name
|
|
21
|
+
mapping[key] = [mod, verbose_name]
|
|
22
|
+
return mapping.get(entity) if entity else mapping
|
|
23
|
+
|
|
24
|
+
class OrmDao:
|
|
25
|
+
def __init__(self, entity):
|
|
26
|
+
self.entity = entity
|
|
27
|
+
param = load_model(entity)
|
|
28
|
+
if param is None:
|
|
29
|
+
raise Exception('no entity named %s' % entity)
|
|
30
|
+
self.model = param[0]
|
|
31
|
+
self.name: str = param[1]
|
|
32
|
+
self.manager: Manager = self.model.objects
|
|
33
|
+
self.meta_fields = {}
|
|
34
|
+
self.model_fields = {}
|
|
35
|
+
for field in self.model._meta.get_fields():
|
|
36
|
+
_field = load_meta_field(field)
|
|
37
|
+
prop = _field['prop']
|
|
38
|
+
self.model_fields[prop] = field
|
|
39
|
+
self.meta_fields[prop] = _field
|
|
40
|
+
|
|
41
|
+
|
|
42
|
+
def save_one(self, item):
|
|
43
|
+
_item = detach_props(item, self.meta_fields.values())
|
|
44
|
+
_id = item.get('id',0)
|
|
45
|
+
query_set = self.manager.filter(id=_id)
|
|
46
|
+
if len(query_set):
|
|
47
|
+
del item['id']
|
|
48
|
+
item['modify_time'] = datetime.datetime.now()
|
|
49
|
+
query_set.update(**item)
|
|
50
|
+
bean = query_set.first()
|
|
51
|
+
else:
|
|
52
|
+
bean = self.manager.create(**item)
|
|
53
|
+
bean.sort = bean.id
|
|
54
|
+
bean.save()
|
|
55
|
+
save_detached(bean, _item, self.model_fields)
|
|
56
|
+
return bean
|
|
57
|
+
|
|
58
|
+
def update_many(self, query: Query, template):
|
|
59
|
+
self.find_many(query).update(**template)
|
|
60
|
+
|
|
61
|
+
def delete_one(self, _id):
|
|
62
|
+
self.manager.filter(id=_id).delete()
|
|
63
|
+
|
|
64
|
+
def delete_many(self, query: Query):
|
|
65
|
+
self.find_many(query)[0].delete()
|
|
66
|
+
|
|
67
|
+
def find_one(self, _id):
|
|
68
|
+
return self.manager.filter(id=_id).first()
|
|
69
|
+
|
|
70
|
+
def find_many(self, query: Query, size=0, page=1)->[QuerySet, int]:
|
|
71
|
+
includes, excludes = query.orm_conditions()
|
|
72
|
+
query_set = self.manager.filter(includes).exclude(excludes).order_by(*query.orm_orders())
|
|
73
|
+
total = query_set.count()
|
|
74
|
+
if size:
|
|
75
|
+
paginator = Paginator(query_set, size)
|
|
76
|
+
query_set = paginator.page(page).object_list
|
|
77
|
+
return query_set, total
|
|
78
|
+
|
|
79
|
+
|
|
80
|
+
def meta(self, code:str = 'default'):
|
|
81
|
+
omit = [ 'sort', 'create_time', 'modify_time', 'saved']
|
|
82
|
+
fields = [ self.meta_fields[prop] for prop in self.meta_fields if prop not in omit]
|
|
83
|
+
view = load_view(self.entity, code, self.name, fields)
|
|
84
|
+
return load_meta(view)
|
|
85
|
+
|
|
86
|
+
|
|
87
|
+
|
|
@@ -0,0 +1,61 @@
|
|
|
1
|
+
from django.db.models import (ManyToOneRel, ForeignKey, ManyToManyRel, ManyToManyField, OneToOneField, CharField,
|
|
2
|
+
OneToOneRel, IntegerField, BooleanField, FloatField, FileField, JSONField, DateField,
|
|
3
|
+
DateTimeField, TimeField, QuerySet)
|
|
4
|
+
|
|
5
|
+
from ..models import VModel
|
|
6
|
+
|
|
7
|
+
|
|
8
|
+
def detach_props(item, fields):
|
|
9
|
+
keys = [field['prop'] for field in fields if
|
|
10
|
+
field['domain'] in ['ManyToOneRel', 'ManyToManyField', 'ManyToManyRel', 'OneToOneRel', 'OneToOneField']]
|
|
11
|
+
_item = {}
|
|
12
|
+
for key in keys:
|
|
13
|
+
value = item.get(key)
|
|
14
|
+
if value is not None:
|
|
15
|
+
_item[key] = value
|
|
16
|
+
del item[key]
|
|
17
|
+
return _item
|
|
18
|
+
|
|
19
|
+
def save_detached(bean, _item, model_fields ):
|
|
20
|
+
for prop in _item:
|
|
21
|
+
value = _item[prop]
|
|
22
|
+
field = model_fields.get(prop)
|
|
23
|
+
clazz = type(field)
|
|
24
|
+
if clazz == ManyToManyField:
|
|
25
|
+
m2m = getattr(bean, prop)
|
|
26
|
+
m2m.clear()
|
|
27
|
+
m2m.add(*value)
|
|
28
|
+
elif clazz == ManyToOneRel:
|
|
29
|
+
getattr(bean, field.get_accessor_name()).clear()
|
|
30
|
+
remote_model: VModel = field.related_model
|
|
31
|
+
new_set: QuerySet = remote_model.objects.filter(id__in=value)
|
|
32
|
+
remote_field: ForeignKey = field.remote_field
|
|
33
|
+
k = remote_field.get_attname()
|
|
34
|
+
new_set.update(**{k: bean.id})
|
|
35
|
+
elif clazz == ManyToManyRel:
|
|
36
|
+
getattr(bean, field.get_accessor_name()).clear()
|
|
37
|
+
remote_model: VModel = field.related_model
|
|
38
|
+
remote_items: QuerySet = remote_model.objects.filter(id__in=value)
|
|
39
|
+
remote_field: ManyToManyField = field.remote_field
|
|
40
|
+
remote_field_prop = remote_field.get_attname()
|
|
41
|
+
for _bean in remote_items:
|
|
42
|
+
bean_set = getattr(_bean, remote_field_prop)
|
|
43
|
+
bean_set.add(bean)
|
|
44
|
+
elif clazz == OneToOneRel:
|
|
45
|
+
remote_model: VModel = field.related_model
|
|
46
|
+
remote_field: OneToOneField = field.remote_field
|
|
47
|
+
remote_field_prop = remote_field.get_attname()
|
|
48
|
+
_bean = remote_model.objects.get(id=value)
|
|
49
|
+
__bean = remote_model.objects.filter(**{remote_field_prop: bean.id}).first()
|
|
50
|
+
if __bean:
|
|
51
|
+
setattr(__bean, remote_field_prop, None)
|
|
52
|
+
__bean.save()
|
|
53
|
+
setattr(_bean, remote_field_prop, bean.id)
|
|
54
|
+
_bean.save()
|
|
55
|
+
elif clazz == OneToOneField:
|
|
56
|
+
__bean = field.model.objects.filter(**{prop: value}).first()
|
|
57
|
+
if __bean:
|
|
58
|
+
setattr(__bean, prop, None)
|
|
59
|
+
__bean.save()
|
|
60
|
+
setattr(bean, prop, value)
|
|
61
|
+
bean.save()
|
valar/data/orm/meta.py
ADDED
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
mf_common = ['prop','name','domain']
|
|
2
|
+
|
|
3
|
+
field_props = {
|
|
4
|
+
'data.Meta': {
|
|
5
|
+
'default': ('pick', ['entity','name']),
|
|
6
|
+
},
|
|
7
|
+
'data.MetaView': {
|
|
8
|
+
'list': ('pick', ['meta_id','code','view_name']),
|
|
9
|
+
'control':('omit', ['name','meta_id', 'code', 'view_name', 'metafield'])
|
|
10
|
+
},
|
|
11
|
+
'data.MetaField': {
|
|
12
|
+
'tool': ('pick',[*mf_common,'tool','refer','format']),
|
|
13
|
+
'rest': ('pick',[*mf_common,'not_null','read_only','unit','sortable','allow_search','allow_download','allow_upload','allow_update']),
|
|
14
|
+
'table': ('pick',[*mf_common,'column_width','fixed','align','edit_on_table','hide_on_table','header_color','cell_color']),
|
|
15
|
+
'form': ('pick',[*mf_common,'hide_on_form','hide_on_form_insert','hide_on_form_edit','hide_on_form_branch','hide_on_form_leaf','span']),
|
|
16
|
+
}
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
|
|
20
|
+
field_default = {
|
|
21
|
+
'data.MetaField':{
|
|
22
|
+
|
|
23
|
+
}
|
|
24
|
+
}
|
|
@@ -0,0 +1,135 @@
|
|
|
1
|
+
from django.db.models import (ManyToOneRel, ForeignKey, ManyToManyRel, ManyToManyField, OneToOneField, CharField,
|
|
2
|
+
OneToOneRel, IntegerField, BooleanField, FloatField, FileField, JSONField, DateField,
|
|
3
|
+
TextField,DateTimeField, TimeField)
|
|
4
|
+
|
|
5
|
+
from ..orm.meta import field_props
|
|
6
|
+
from ..models import Meta, MetaView, VModel, MetaField
|
|
7
|
+
|
|
8
|
+
|
|
9
|
+
|
|
10
|
+
|
|
11
|
+
|
|
12
|
+
def __save_model(model):
|
|
13
|
+
model.save()
|
|
14
|
+
model.sort = model.id
|
|
15
|
+
model.save()
|
|
16
|
+
|
|
17
|
+
|
|
18
|
+
|
|
19
|
+
def load_view(entity, code, name, fields):
|
|
20
|
+
meta = Meta.objects.filter(entity=entity).first()
|
|
21
|
+
if meta is None:
|
|
22
|
+
meta = Meta(entity=entity, name=name)
|
|
23
|
+
__save_model(meta)
|
|
24
|
+
view = MetaView.objects.filter(meta__entity=entity, code=code).first()
|
|
25
|
+
if view is None:
|
|
26
|
+
view = MetaView(meta=meta, code=code, view_name=code.upper())
|
|
27
|
+
__save_model(view)
|
|
28
|
+
if view.metafield_set.count() == 0:
|
|
29
|
+
t, p = field_props.get(entity, {}).get(code,('omit',[]))
|
|
30
|
+
_fields = [f for f in fields if f['prop'] not in p] if t=='omit' else [f for f in fields if f['prop'] in p]
|
|
31
|
+
for f in _fields:
|
|
32
|
+
f['view'] = view
|
|
33
|
+
field = MetaField.objects.create(**f)
|
|
34
|
+
__save_model(field)
|
|
35
|
+
return view
|
|
36
|
+
|
|
37
|
+
def load_meta(view):
|
|
38
|
+
_view = view.full
|
|
39
|
+
_meta = _view['meta']
|
|
40
|
+
_fields = _view['metafield_set']
|
|
41
|
+
clear_item(_view, 'meta_id', 'metafield', 'metafield_set', 'meta')
|
|
42
|
+
_view['meta_name'] = _meta['name']
|
|
43
|
+
_view['entity'] = _meta['entity']
|
|
44
|
+
_view['fields'] = {}
|
|
45
|
+
for _field in _fields:
|
|
46
|
+
clear_item(_field, 'view_id')
|
|
47
|
+
prop = _field['prop']
|
|
48
|
+
_view['fields'][prop] = _field
|
|
49
|
+
return _view
|
|
50
|
+
|
|
51
|
+
|
|
52
|
+
|
|
53
|
+
def clear_item(item, *keys):
|
|
54
|
+
del item['saved']
|
|
55
|
+
del item['sort']
|
|
56
|
+
del item['id']
|
|
57
|
+
del item['create_time']
|
|
58
|
+
del item['modify_time']
|
|
59
|
+
for key in keys:
|
|
60
|
+
del item[key]
|
|
61
|
+
|
|
62
|
+
|
|
63
|
+
|
|
64
|
+
def get_refer(model, multiple = False):
|
|
65
|
+
module, name = model.__module__, model.__name__
|
|
66
|
+
entity = '%s.%s' % (module.replace('.models', '').split('.')[-1], name)
|
|
67
|
+
return {
|
|
68
|
+
"entity": entity,
|
|
69
|
+
"value": "id", "label": 'name', "display": "id",
|
|
70
|
+
"strict": False, "remote": False, "multiple": multiple,
|
|
71
|
+
"includes": {}, "excludes": {}, "root": 0,
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
def get_align(clazz):
|
|
75
|
+
if clazz in [FloatField, IntegerField, ManyToManyRel, ManyToManyField, ManyToOneRel]:
|
|
76
|
+
return 'right'
|
|
77
|
+
elif clazz in [BooleanField,FileField,JSONField,DateField,DateTimeField,TimeField]:
|
|
78
|
+
return 'center'
|
|
79
|
+
return 'left'
|
|
80
|
+
|
|
81
|
+
def get_format(field):
|
|
82
|
+
clazz = type(field)
|
|
83
|
+
if clazz == CharField:
|
|
84
|
+
return {'maxlength': field.max_length, "type": "text"}
|
|
85
|
+
if clazz == TextField:
|
|
86
|
+
return {'maxlength': None, "type": "textarea"}
|
|
87
|
+
elif clazz == FloatField:
|
|
88
|
+
return {'min': None, 'max': None, 'step': 1, 'precision': None, 'step_strictly': False}
|
|
89
|
+
elif clazz == IntegerField:
|
|
90
|
+
return {'min': None, 'max': None, 'step': 1, 'precision': 0, 'step_strictly': True}
|
|
91
|
+
elif clazz == FileField:
|
|
92
|
+
return {'max': 5, 'accept': "*", "width": "800px", "height": "auto", 'locked': False}
|
|
93
|
+
else:
|
|
94
|
+
return {}
|
|
95
|
+
|
|
96
|
+
|
|
97
|
+
|
|
98
|
+
def load_meta_field(field):
|
|
99
|
+
clazz = type(field)
|
|
100
|
+
if clazz in [ManyToOneRel, ManyToManyField, ManyToManyRel]:
|
|
101
|
+
prop = field.name
|
|
102
|
+
domain = clazz.__name__
|
|
103
|
+
model: VModel= field.related_model
|
|
104
|
+
label = model._meta.verbose_name
|
|
105
|
+
refer = get_refer(model, True)
|
|
106
|
+
elif clazz in [ForeignKey]:
|
|
107
|
+
prop = field.name + "_id"
|
|
108
|
+
domain = field.get_internal_type()
|
|
109
|
+
model: VModel = field.related_model
|
|
110
|
+
label = field.verbose_name
|
|
111
|
+
refer = get_refer(model)
|
|
112
|
+
elif clazz in [OneToOneRel, OneToOneField]:
|
|
113
|
+
prop = field.name + "_id"
|
|
114
|
+
domain = clazz.__name__
|
|
115
|
+
model: VModel = field.related_model
|
|
116
|
+
label = model._meta.verbose_name
|
|
117
|
+
refer = get_refer(model)
|
|
118
|
+
else:
|
|
119
|
+
prop = field.name
|
|
120
|
+
domain = field.get_internal_type()
|
|
121
|
+
label = field.verbose_name
|
|
122
|
+
refer = {}
|
|
123
|
+
not_null = not field.null
|
|
124
|
+
align = get_align(clazz)
|
|
125
|
+
_format = get_format(field)
|
|
126
|
+
return {
|
|
127
|
+
"prop": prop,
|
|
128
|
+
"label":label,
|
|
129
|
+
"name":label,
|
|
130
|
+
"domain":domain,
|
|
131
|
+
"refer":refer,
|
|
132
|
+
"format":_format,
|
|
133
|
+
"not_null":not_null,
|
|
134
|
+
"align":align,
|
|
135
|
+
}
|