valar 1.1.4__py3-none-any.whl → 1.2.1__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/apps.py +6 -9
- valar/channels/consumer.py +10 -12
- valar/channels/executer.py +7 -6
- valar/channels/sender.py +95 -43
- valar/channels/views.py +2 -3
- valar/classes/{auto_migration_mixin.py → app_mixins/auto_migration_mixin.py} +2 -2
- valar/classes/{auto_urlpatterns_mixin.py → app_mixins/auto_urlpatterns_mixin.py} +3 -3
- valar/classes/valar_minio.py +80 -0
- valar/classes/valar_response.py +2 -1
- valar/dao/__init__.py +45 -0
- valar/dao/abstract.py +106 -0
- valar/dao/defaults/__init__.py +0 -0
- valar/dao/defaults/field_keys_default.py +48 -0
- valar/dao/defaults/field_values_default.py +135 -0
- valar/dao/defaults/view_defaults.py +26 -0
- valar/dao/engine.py +80 -0
- valar/dao/frame.py +189 -0
- valar/dao/meta.py +128 -0
- valar/dao/mon_dao.py +113 -0
- valar/dao/mon_field.py +23 -0
- valar/dao/orm_dao.py +304 -0
- valar/dao/orm_field.py +142 -0
- valar/dao/query.py +36 -0
- valar/migrations/0001_initial.py +25 -17
- valar/models/core.py +16 -10
- valar/models/frame.py +4 -7
- valar/models/meta.py +19 -16
- valar/urls.py +22 -0
- valar/views/file.py +49 -0
- valar/views/handler.py +33 -0
- valar/views/meta.py +150 -0
- valar/views/rest.py +90 -0
- {valar-1.1.4.dist-info → valar-1.2.1.dist-info}/METADATA +9 -14
- valar-1.2.1.dist-info/RECORD +54 -0
- {valar-1.1.4.dist-info → valar-1.2.1.dist-info}/top_level.txt +1 -0
- vtest/__init__.py +0 -0
- vtest/apps.py +17 -0
- vtest/handlers.py +15 -0
- vtest/migrations/0001_initial.py +155 -0
- vtest/migrations/__init__.py +0 -0
- vtest/models.py +59 -0
- vtest/views.py +8 -0
- valar-1.1.4.dist-info/RECORD +0 -28
- /valar/{classes → channels}/counter.py +0 -0
- /valar/{frame → classes/app_mixins}/__init__.py +0 -0
- {valar-1.1.4.dist-info → valar-1.2.1.dist-info}/WHEEL +0 -0
- {valar-1.1.4.dist-info → valar-1.2.1.dist-info}/licenses/LICENSE +0 -0
valar/dao/mon_field.py
ADDED
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
from ..dao.abstract import AbstractField
|
|
2
|
+
from ..dao.frame import meta_field_tool_mapping
|
|
3
|
+
|
|
4
|
+
|
|
5
|
+
class MonField(AbstractField):
|
|
6
|
+
|
|
7
|
+
def __init__(self, entity, prop, domain, tool):
|
|
8
|
+
self.db = 'mon'
|
|
9
|
+
self.entity = entity
|
|
10
|
+
self.prop = prop
|
|
11
|
+
self.label = prop
|
|
12
|
+
self.domain = domain
|
|
13
|
+
self.tool = tool
|
|
14
|
+
|
|
15
|
+
def to_dict(self):
|
|
16
|
+
_field = {
|
|
17
|
+
"prop": self.prop,
|
|
18
|
+
"label": self.label,
|
|
19
|
+
"name": self.label,
|
|
20
|
+
"domain": self.domain,
|
|
21
|
+
'tool': self.tool,
|
|
22
|
+
}
|
|
23
|
+
return _field
|
valar/dao/orm_dao.py
ADDED
|
@@ -0,0 +1,304 @@
|
|
|
1
|
+
import datetime
|
|
2
|
+
import json
|
|
3
|
+
|
|
4
|
+
from django.core.paginator import Paginator
|
|
5
|
+
|
|
6
|
+
from .abstract import AbstractDao
|
|
7
|
+
from .query import Query
|
|
8
|
+
from django.db.models import ManyToOneRel, ForeignKey, ManyToManyRel, ManyToManyField, OneToOneField, OneToOneRel
|
|
9
|
+
from django.db.models import QuerySet
|
|
10
|
+
from django.db.models import Manager
|
|
11
|
+
from django.db.models.fields.files import FieldFile
|
|
12
|
+
from django.forms import FileField
|
|
13
|
+
|
|
14
|
+
from ..dao.orm_field import OrmField
|
|
15
|
+
from ..models.core import VTree, VModel
|
|
16
|
+
from ..models.meta import MetaField
|
|
17
|
+
|
|
18
|
+
|
|
19
|
+
class OrmDao(AbstractDao):
|
|
20
|
+
|
|
21
|
+
def __init__(self, entity):
|
|
22
|
+
model = self.engine.get_orm_model(entity)
|
|
23
|
+
meta = getattr(model, '_meta')
|
|
24
|
+
self.minio = self.engine.get_minio_bucket(entity)
|
|
25
|
+
self.db = 'orm'
|
|
26
|
+
self.entity = entity
|
|
27
|
+
self.name = meta.verbose_name
|
|
28
|
+
self.is_tree = issubclass(model, VTree)
|
|
29
|
+
self.manager: Manager = model.objects
|
|
30
|
+
fields = {}
|
|
31
|
+
model_fields = meta.get_fields()
|
|
32
|
+
for model_field in model_fields:
|
|
33
|
+
meta_field = OrmField(entity, model_field, self.is_tree)
|
|
34
|
+
fields[meta_field.prop] = meta_field
|
|
35
|
+
self.fields = fields
|
|
36
|
+
|
|
37
|
+
def save_many(self, array: list):
|
|
38
|
+
pass
|
|
39
|
+
|
|
40
|
+
def values(self, conditions, props: list):
|
|
41
|
+
query_set, _ = self.find(conditions)
|
|
42
|
+
if 'id' not in props:
|
|
43
|
+
props.append('id')
|
|
44
|
+
return list(query_set.values(*props))
|
|
45
|
+
|
|
46
|
+
def save_one(self, item, with_id=False):
|
|
47
|
+
oid, simple_item, complex_item = self.__detach_item__(item)
|
|
48
|
+
query_set = self.manager.filter(id=oid) if oid else []
|
|
49
|
+
if len(query_set):
|
|
50
|
+
simple_item['modify_time'] = datetime.datetime.now()
|
|
51
|
+
query_set.update(**simple_item)
|
|
52
|
+
bean = query_set.first()
|
|
53
|
+
else:
|
|
54
|
+
if oid:
|
|
55
|
+
simple_item.update({'id': oid})
|
|
56
|
+
bean = self.manager.create(**simple_item)
|
|
57
|
+
if simple_item.get('sort') is None:
|
|
58
|
+
bean.sort = bean.id
|
|
59
|
+
bean.save()
|
|
60
|
+
self.__save_complex_field__(complex_item, bean)
|
|
61
|
+
bean.save()
|
|
62
|
+
return bean
|
|
63
|
+
|
|
64
|
+
def delete_one(self, _id):
|
|
65
|
+
oid = self.object_id(_id)
|
|
66
|
+
flag = oid is not None
|
|
67
|
+
if flag:
|
|
68
|
+
query_set = self.manager.filter(id=oid)
|
|
69
|
+
paths = self.__get_file_paths__(query_set)
|
|
70
|
+
for path in paths:
|
|
71
|
+
self.minio.remove(path)
|
|
72
|
+
query_set.delete()
|
|
73
|
+
return flag
|
|
74
|
+
|
|
75
|
+
def find_one(self, _id):
|
|
76
|
+
oid = self.object_id(_id)
|
|
77
|
+
return self.manager.filter(id=oid).first() if oid is not None else None
|
|
78
|
+
|
|
79
|
+
def find(self, conditions=None, orders=None, size=0, page=1):
|
|
80
|
+
includes, excludes, orders = Query(conditions, orders).orm()
|
|
81
|
+
query_set = self.manager.filter(includes).exclude(excludes).order_by(*orders)
|
|
82
|
+
total = query_set.count()
|
|
83
|
+
if size:
|
|
84
|
+
paginator = Paginator(query_set, size)
|
|
85
|
+
query_set = paginator.page(page).object_list
|
|
86
|
+
return query_set, total
|
|
87
|
+
|
|
88
|
+
def update(self, template, conditions):
|
|
89
|
+
flag = template is not None and len(template.keys())
|
|
90
|
+
if flag:
|
|
91
|
+
oid, simple_item, complex_item = self.__detach_item__(template)
|
|
92
|
+
query_set, total = self.find(conditions)
|
|
93
|
+
query_set.update(**simple_item)
|
|
94
|
+
return flag
|
|
95
|
+
|
|
96
|
+
def delete(self, conditions=None) -> list:
|
|
97
|
+
query_set, total = self.find(conditions)
|
|
98
|
+
query_set.delete()
|
|
99
|
+
return self.__get_file_paths__(query_set)
|
|
100
|
+
|
|
101
|
+
def serialize(self, o, code=None):
|
|
102
|
+
return self.__to_dict__(o, code) if isinstance(o, QuerySet) else o.full()
|
|
103
|
+
|
|
104
|
+
def tree(self, root, conditions=None):
|
|
105
|
+
all_set, _ = self.find()
|
|
106
|
+
if Query(conditions).is_empty:
|
|
107
|
+
return all_set
|
|
108
|
+
values = all_set.values('id', 'pid')
|
|
109
|
+
mapping = {item['id']: item['pid'] for item in values}
|
|
110
|
+
results, _ = self.find(conditions)
|
|
111
|
+
id_set = {root}
|
|
112
|
+
for item in results:
|
|
113
|
+
_id = item.id
|
|
114
|
+
route = []
|
|
115
|
+
while _id is not None:
|
|
116
|
+
route.append(_id)
|
|
117
|
+
_id = mapping.get(_id)
|
|
118
|
+
if root in route:
|
|
119
|
+
id_set.update(route)
|
|
120
|
+
return all_set.filter(id__in=id_set).order_by('-sort')
|
|
121
|
+
|
|
122
|
+
""" 以下为私有方法 """
|
|
123
|
+
|
|
124
|
+
def __detach_item__(self, item):
|
|
125
|
+
_id = item.get('id')
|
|
126
|
+
if _id:
|
|
127
|
+
del item['id']
|
|
128
|
+
simple_item = {}
|
|
129
|
+
complex_item = {}
|
|
130
|
+
for prop in item:
|
|
131
|
+
meta_field = self.fields.get(prop)
|
|
132
|
+
if meta_field:
|
|
133
|
+
value = item.get(prop)
|
|
134
|
+
if meta_field.domain in ['ManyToOneRel', 'ManyToManyField', 'ManyToManyRel']:
|
|
135
|
+
complex_item[prop] = value
|
|
136
|
+
elif meta_field.domain in ['OneToOneRel', 'OneToOneField', 'FileField']:
|
|
137
|
+
complex_item[prop] = value
|
|
138
|
+
else:
|
|
139
|
+
simple_item[prop] = value
|
|
140
|
+
return self.object_id(_id), simple_item, complex_item
|
|
141
|
+
|
|
142
|
+
def __save_complex_field__(self, complex_item, bean):
|
|
143
|
+
for prop in complex_item:
|
|
144
|
+
value = complex_item[prop]
|
|
145
|
+
model_field = self.fields[prop].model_field
|
|
146
|
+
clazz = type(model_field)
|
|
147
|
+
if clazz == ManyToManyField:
|
|
148
|
+
m2m = getattr(bean, prop)
|
|
149
|
+
m2m.clear()
|
|
150
|
+
m2m.add(*value)
|
|
151
|
+
elif clazz == ManyToOneRel:
|
|
152
|
+
getattr(bean, model_field.get_accessor_name()).clear()
|
|
153
|
+
remote_model: VModel = model_field.related_model
|
|
154
|
+
new_set: QuerySet = remote_model.objects.filter(id__in=value)
|
|
155
|
+
remote_field: ForeignKey = model_field.remote_field
|
|
156
|
+
k = remote_field.get_attname()
|
|
157
|
+
new_set.update(**{k: bean.id})
|
|
158
|
+
elif clazz == ManyToManyRel:
|
|
159
|
+
getattr(bean, model_field.get_accessor_name()).clear()
|
|
160
|
+
remote_model: VModel = model_field.related_model
|
|
161
|
+
remote_items: QuerySet = remote_model.objects.filter(id__in=value)
|
|
162
|
+
remote_field: ManyToManyField = model_field.remote_field
|
|
163
|
+
remote_field_prop = remote_field.get_attname()
|
|
164
|
+
for _bean in remote_items:
|
|
165
|
+
bean_set = getattr(_bean, remote_field_prop)
|
|
166
|
+
bean_set.add(bean)
|
|
167
|
+
elif clazz == OneToOneRel and value is not None:
|
|
168
|
+
remote_model: VModel = model_field.related_model
|
|
169
|
+
remote_field: OneToOneField = model_field.remote_field
|
|
170
|
+
remote_field_prop = remote_field.get_attname()
|
|
171
|
+
_bean = remote_model.objects.get(id=value)
|
|
172
|
+
__bean = remote_model.objects.filter(**{remote_field_prop: bean.id}).first()
|
|
173
|
+
if __bean:
|
|
174
|
+
setattr(__bean, remote_field_prop, None)
|
|
175
|
+
__bean.save()
|
|
176
|
+
setattr(_bean, remote_field_prop, bean.id)
|
|
177
|
+
_bean.save()
|
|
178
|
+
elif clazz == OneToOneField and value is not None:
|
|
179
|
+
__bean = model_field.model.objects.filter(**{prop: value}).first()
|
|
180
|
+
if __bean:
|
|
181
|
+
setattr(__bean, prop, None)
|
|
182
|
+
__bean.save()
|
|
183
|
+
setattr(bean, prop, value)
|
|
184
|
+
elif clazz == FileField:
|
|
185
|
+
file_name, _bytes = value
|
|
186
|
+
field_file: FieldFile = getattr(bean, prop)
|
|
187
|
+
if field_file:
|
|
188
|
+
path = field_file.name
|
|
189
|
+
self.minio.remove(path)
|
|
190
|
+
object_name = self.minio.get_object_name(bean.id, prop, file_name)
|
|
191
|
+
path = self.minio.upload(object_name, _bytes) if _bytes else None
|
|
192
|
+
setattr(bean, prop, path)
|
|
193
|
+
|
|
194
|
+
def __get_file_paths__(self, query_set: QuerySet):
|
|
195
|
+
props = self.props('FileField')
|
|
196
|
+
if len(props):
|
|
197
|
+
items = query_set.values(*props)
|
|
198
|
+
paths = []
|
|
199
|
+
for item in items:
|
|
200
|
+
paths += [i for i in item.values() if i]
|
|
201
|
+
return [path for path in paths if path]
|
|
202
|
+
return []
|
|
203
|
+
|
|
204
|
+
def __to_dict__(self, query_set: QuerySet, code=None):
|
|
205
|
+
meta_fields = self.fields.values()
|
|
206
|
+
# 简单字段取值
|
|
207
|
+
simple_props = [field.prop for field in meta_fields if field.domain not in __referred_domains__]
|
|
208
|
+
custom_props = __get_custom_props__(self.entity, code)
|
|
209
|
+
results = list(query_set.filter().values(*[*simple_props, *custom_props]))
|
|
210
|
+
__set_simple_values__(meta_fields, results)
|
|
211
|
+
# 关系型字段取值
|
|
212
|
+
mapping = {row['id']: row for row in results}
|
|
213
|
+
referred_fields = [field for field in meta_fields if field.domain in __referred_domains__]
|
|
214
|
+
pks = mapping.keys()
|
|
215
|
+
for meta_field in referred_fields:
|
|
216
|
+
manager: Manager = query_set.model.objects
|
|
217
|
+
qs = manager.filter(id__in=pks)
|
|
218
|
+
__linkage__(meta_field, qs, mapping)
|
|
219
|
+
return results
|
|
220
|
+
|
|
221
|
+
|
|
222
|
+
""" 以下为静态方法和变量 """
|
|
223
|
+
|
|
224
|
+
__multiple_domains__ = ['ManyToOneRel', 'ManyToManyField', 'ManyToManyRel']
|
|
225
|
+
__referred_domains__ = [*__multiple_domains__, 'OneToOneRel', 'OneToOneField', 'ForeignKey']
|
|
226
|
+
__omit_field_props__ = ['create_time', 'modify_time', 'saved', 'sort']
|
|
227
|
+
__data_props_formatting__ = {'DateField': '%Y-%m-%d', 'DateTimeField': '%Y-%m-%d %H:%M:%S', 'TimeField': '%H:%M:%S'}
|
|
228
|
+
|
|
229
|
+
|
|
230
|
+
def __get_custom_props__(entity, code='default'):
|
|
231
|
+
field_set = MetaField.objects.filter(view__code=code, view__meta__entity=entity, domain='Custom').values('prop')
|
|
232
|
+
return [item['prop'] for item in field_set if item['prop'] is not None]
|
|
233
|
+
|
|
234
|
+
|
|
235
|
+
def __set_simple_values__(fields, values):
|
|
236
|
+
date_props_mapping = {}
|
|
237
|
+
json_props = []
|
|
238
|
+
for field in fields:
|
|
239
|
+
if isinstance(field, OrmField):
|
|
240
|
+
prop = field.prop
|
|
241
|
+
domain = field.domain
|
|
242
|
+
else:
|
|
243
|
+
prop = field.name
|
|
244
|
+
domain = type(field).__name__
|
|
245
|
+
if domain in __data_props_formatting__.keys():
|
|
246
|
+
date_props_mapping[prop] = __data_props_formatting__[domain]
|
|
247
|
+
elif domain == 'JSONField':
|
|
248
|
+
json_props.append(prop)
|
|
249
|
+
for row in values:
|
|
250
|
+
for prop, formating in date_props_mapping.items():
|
|
251
|
+
if row.get(prop):
|
|
252
|
+
row[prop] = row[prop].strftime(formating)
|
|
253
|
+
for prop in json_props:
|
|
254
|
+
row[prop] = json.loads(row[prop]) if type(row[prop]) is str else row[prop]
|
|
255
|
+
|
|
256
|
+
|
|
257
|
+
def __get_related_props__(fields):
|
|
258
|
+
def fun(field): return type(field).__name__ not in __referred_domains__ and field.name not in __omit_field_props__
|
|
259
|
+
|
|
260
|
+
return [field.name for field in fields if fun(field)]
|
|
261
|
+
|
|
262
|
+
|
|
263
|
+
def __linkage__(meta_field: OrmField, query_set: QuerySet, mapping):
|
|
264
|
+
model_field = meta_field.model_field
|
|
265
|
+
prop = model_field.name
|
|
266
|
+
multiple = meta_field.domain in __multiple_domains__
|
|
267
|
+
|
|
268
|
+
# 获取级联关系的键索引
|
|
269
|
+
ref_prop = f'{prop}__id'
|
|
270
|
+
edges = query_set.exclude(**{f'{ref_prop}__isnull': True}).values('id', ref_prop)
|
|
271
|
+
if multiple:
|
|
272
|
+
related_primary_keys = set()
|
|
273
|
+
results_mapping = {}
|
|
274
|
+
for edge in edges:
|
|
275
|
+
_id, rid = edge['id'], edge[ref_prop]
|
|
276
|
+
related_primary_keys.add(rid)
|
|
277
|
+
array = results_mapping.get(_id, [])
|
|
278
|
+
array.append(rid)
|
|
279
|
+
results_mapping[_id] = array
|
|
280
|
+
else:
|
|
281
|
+
results_mapping = {row['id']: row[ref_prop] for row in edges if row[ref_prop]}
|
|
282
|
+
related_primary_keys = set(results_mapping.values())
|
|
283
|
+
|
|
284
|
+
# 获取级联关系从属方的数据
|
|
285
|
+
related_model = model_field.related_model
|
|
286
|
+
related_fields = related_model._meta.get_fields()
|
|
287
|
+
related_props = __get_related_props__(related_fields)
|
|
288
|
+
related_values = list(related_model.objects.filter(id__in=related_primary_keys).values(*related_props))
|
|
289
|
+
__set_simple_values__(related_fields, related_values)
|
|
290
|
+
related_mapping = {item['id']: item for item in related_values}
|
|
291
|
+
|
|
292
|
+
# 将从属方的数据绑定在主数据上
|
|
293
|
+
for _id in mapping:
|
|
294
|
+
row = mapping[_id]
|
|
295
|
+
if multiple:
|
|
296
|
+
keys = results_mapping.get(_id, [])
|
|
297
|
+
items = [related_mapping[pid] for pid in keys]
|
|
298
|
+
row[prop] = keys
|
|
299
|
+
row[f'{prop}_set'] = items
|
|
300
|
+
else:
|
|
301
|
+
key = results_mapping.get(_id)
|
|
302
|
+
item = related_mapping.get(key) if key else None
|
|
303
|
+
row[prop] = item
|
|
304
|
+
row[f'{prop}_id'] = key
|
valar/dao/orm_field.py
ADDED
|
@@ -0,0 +1,142 @@
|
|
|
1
|
+
from deepmerge import always_merger
|
|
2
|
+
from django.db.models import ManyToOneRel, ForeignKey, ManyToManyRel, ManyToManyField, OneToOneField, OneToOneRel
|
|
3
|
+
from django.db.models import IntegerField, BooleanField, FloatField, TextField, CharField
|
|
4
|
+
from django.db.models import FileField, JSONField
|
|
5
|
+
from django.db.models import DateTimeField, TimeField, DateField
|
|
6
|
+
from django.db.models.options import Options
|
|
7
|
+
|
|
8
|
+
from ..dao.abstract import AbstractField
|
|
9
|
+
from ..dao.frame import meta_field_domain_mapping, meta_field_tool_mapping
|
|
10
|
+
from ..models.core import VTree
|
|
11
|
+
|
|
12
|
+
|
|
13
|
+
class OrmField(AbstractField):
|
|
14
|
+
|
|
15
|
+
def __init__(self, entity, model_field, is_tree):
|
|
16
|
+
self.db = 'orm'
|
|
17
|
+
self.entity = entity
|
|
18
|
+
self.model_field = model_field
|
|
19
|
+
self.is_tree = is_tree
|
|
20
|
+
self.not_null = not model_field.null
|
|
21
|
+
self.clazz = type(model_field)
|
|
22
|
+
self.multiple = self.clazz in [ManyToOneRel, ManyToManyField, ManyToManyRel]
|
|
23
|
+
self.prop = self.__prop__()
|
|
24
|
+
self.domain = self.__domain__()
|
|
25
|
+
self.model = self.__model__()
|
|
26
|
+
self.label = self.__label__()
|
|
27
|
+
self.tool = meta_field_domain_mapping.get(self.domain, 'none')
|
|
28
|
+
self.column_width = column_width(self.domain, self.tool)
|
|
29
|
+
self.refer = self.__refer__()
|
|
30
|
+
self.format = self.__formating__()
|
|
31
|
+
|
|
32
|
+
def to_dict(self):
|
|
33
|
+
_field = {
|
|
34
|
+
"prop": self.prop,
|
|
35
|
+
"label": self.label,
|
|
36
|
+
"name": self.label,
|
|
37
|
+
"domain": self.domain,
|
|
38
|
+
"refer": self.refer,
|
|
39
|
+
"format": self.format,
|
|
40
|
+
"not_null": self.not_null,
|
|
41
|
+
"column_width": self.column_width,
|
|
42
|
+
"tool": self.tool,
|
|
43
|
+
"disabled": False,
|
|
44
|
+
}
|
|
45
|
+
if self.is_tree:
|
|
46
|
+
if self.prop in ['pid', 'isLeaf']:
|
|
47
|
+
# _field['hide_on_table'] = True
|
|
48
|
+
_field['hide_on_form'] = True
|
|
49
|
+
_field['hide_on_form_branch'] = True
|
|
50
|
+
_field['hide_on_form_leaf'] = True
|
|
51
|
+
elif self.prop in ['icon']:
|
|
52
|
+
_field['tool'] = 'icon'
|
|
53
|
+
elif self.refer['isTree']:
|
|
54
|
+
_field['tool'] = 'tree'
|
|
55
|
+
return _field
|
|
56
|
+
|
|
57
|
+
def __label__(self):
|
|
58
|
+
return self.model._meta.verbose_name \
|
|
59
|
+
if self.clazz in [ManyToOneRel, ManyToManyField, ManyToManyRel, OneToOneRel, OneToOneField] \
|
|
60
|
+
else self.model_field.verbose_name
|
|
61
|
+
|
|
62
|
+
def __model__(self):
|
|
63
|
+
return self.model_field.related_model \
|
|
64
|
+
if self.clazz in [ManyToOneRel, ManyToManyField, ManyToManyRel, OneToOneRel, OneToOneField, ForeignKey] \
|
|
65
|
+
else None
|
|
66
|
+
|
|
67
|
+
def __domain__(self):
|
|
68
|
+
return self.clazz.__name__ \
|
|
69
|
+
if self.clazz in [ManyToOneRel, ManyToManyField, ManyToManyRel, OneToOneRel, OneToOneField] \
|
|
70
|
+
else self.model_field.get_internal_type()
|
|
71
|
+
|
|
72
|
+
def __prop__(self):
|
|
73
|
+
return self.model_field.name + "_id" \
|
|
74
|
+
if self.clazz in [ForeignKey, OneToOneRel, OneToOneField] \
|
|
75
|
+
else self.model_field.name
|
|
76
|
+
|
|
77
|
+
def __refer__(self):
|
|
78
|
+
refer = {
|
|
79
|
+
"entity": None, "db": self.db,
|
|
80
|
+
"value": "id", "label": 'name', "display": "id",
|
|
81
|
+
"multiple": self.multiple, "strict": False, "remote": False,
|
|
82
|
+
"includes": {}, "excludes": {},
|
|
83
|
+
"root": 0, "isTree": False
|
|
84
|
+
}
|
|
85
|
+
if self.model:
|
|
86
|
+
meta: Options = getattr(self.model, '_meta')
|
|
87
|
+
|
|
88
|
+
refer['entity'] = '%s.%s' % (meta.app_label, self.model.__name__)
|
|
89
|
+
refer['isTree'] = issubclass(self.model, VTree)
|
|
90
|
+
return refer
|
|
91
|
+
|
|
92
|
+
def __formating__(self):
|
|
93
|
+
_format = {
|
|
94
|
+
# 文本
|
|
95
|
+
"maxlength": None,
|
|
96
|
+
"type": 'text',
|
|
97
|
+
|
|
98
|
+
# 数值
|
|
99
|
+
"min": None,
|
|
100
|
+
"max": None,
|
|
101
|
+
"step": 1,
|
|
102
|
+
"precision": None,
|
|
103
|
+
"step_strictly": False,
|
|
104
|
+
|
|
105
|
+
# 日期
|
|
106
|
+
"frequency": "date",
|
|
107
|
+
|
|
108
|
+
# 文件
|
|
109
|
+
"maximum": 5,
|
|
110
|
+
"accept": [],
|
|
111
|
+
"width": 800,
|
|
112
|
+
"height": None,
|
|
113
|
+
"file_name_field": None,
|
|
114
|
+
"locked": False,
|
|
115
|
+
|
|
116
|
+
# 集合
|
|
117
|
+
"set": {},
|
|
118
|
+
"multiple": False
|
|
119
|
+
}
|
|
120
|
+
if self.clazz == CharField:
|
|
121
|
+
_format['maxlength'] = self.model_field.max_length
|
|
122
|
+
_format['$maxlength'] = self.model_field.max_length
|
|
123
|
+
if self.clazz == TextField:
|
|
124
|
+
_format['type'] = "textarea"
|
|
125
|
+
elif self.clazz == DateTimeField:
|
|
126
|
+
_format['frequency'] = "datetime"
|
|
127
|
+
elif self.clazz == IntegerField:
|
|
128
|
+
_format['precision'] = 0
|
|
129
|
+
_format['step_strictly'] = True
|
|
130
|
+
return _format
|
|
131
|
+
|
|
132
|
+
|
|
133
|
+
def column_width(domain, tool):
|
|
134
|
+
if domain in ['BooleanField', 'FileField', 'JSONField']:
|
|
135
|
+
return 80
|
|
136
|
+
elif domain in ['DateField', 'DateTimeField', 'TimeField']:
|
|
137
|
+
return 120
|
|
138
|
+
elif domain in ['ManyToManyRel', 'ManyToManyRel', 'ManyToOneRel']:
|
|
139
|
+
return 80
|
|
140
|
+
elif domain in ['TextField']:
|
|
141
|
+
return 80 if tool == 'rich' else 0
|
|
142
|
+
return 0
|
valar/dao/query.py
ADDED
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
from django.db.models import Q
|
|
2
|
+
from functools import reduce
|
|
3
|
+
|
|
4
|
+
|
|
5
|
+
class Query:
|
|
6
|
+
|
|
7
|
+
def __init__(self, conditions: list, orders=None):
|
|
8
|
+
self.is_empty = conditions is None or len(conditions) == 0
|
|
9
|
+
self.orders = orders or {'sort': -1}
|
|
10
|
+
self.conditions = [{'includes': {}, 'excludes': {}}] if self.is_empty else conditions
|
|
11
|
+
|
|
12
|
+
def orm(self):
|
|
13
|
+
orders = __translate_orders__(self.orders)
|
|
14
|
+
includes = __translate_orm_condition__(self.conditions, 'includes')
|
|
15
|
+
excludes = __translate_orm_condition__(self.conditions, 'excludes')
|
|
16
|
+
return includes, excludes, orders
|
|
17
|
+
|
|
18
|
+
def mon(self):
|
|
19
|
+
finder = {}
|
|
20
|
+
return finder, self.orders
|
|
21
|
+
|
|
22
|
+
|
|
23
|
+
def __fun__(x, y): return x | y
|
|
24
|
+
|
|
25
|
+
|
|
26
|
+
def __translate_orders__(orders):
|
|
27
|
+
array = []
|
|
28
|
+
for key in orders:
|
|
29
|
+
value = orders.get(key)
|
|
30
|
+
prefix = '-' if value == -1 else ''
|
|
31
|
+
array.append(f'{prefix}{key}')
|
|
32
|
+
return array
|
|
33
|
+
|
|
34
|
+
|
|
35
|
+
def __translate_orm_condition__(conditions, _type):
|
|
36
|
+
return reduce(__fun__, [Q(**cond.get(_type, {})) for cond in conditions])
|
valar/migrations/0001_initial.py
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
# Generated by Django 4.2.23 on 2025-
|
|
1
|
+
# Generated by Django 4.2.23 on 2025-08-28 10:15
|
|
2
2
|
|
|
3
3
|
from django.db import migrations, models
|
|
4
4
|
import django.db.models.deletion
|
|
@@ -19,10 +19,12 @@ class Migration(migrations.Migration):
|
|
|
19
19
|
('sort', models.BigIntegerField(null=True, verbose_name='序号')),
|
|
20
20
|
('create_time', models.DateTimeField(auto_now_add=True, null=True, verbose_name='创建时间')),
|
|
21
21
|
('modify_time', models.DateTimeField(auto_now=True, null=True, verbose_name='修改时间')),
|
|
22
|
-
('
|
|
22
|
+
('disabled', models.BooleanField(default=False, verbose_name='禁用')),
|
|
23
|
+
('saved', models.BooleanField(default=False, verbose_name='已保存')),
|
|
23
24
|
('db', models.CharField(max_length=100, null=True, verbose_name='数据库')),
|
|
24
25
|
('entity', models.CharField(max_length=100, null=True, verbose_name='数据源')),
|
|
25
26
|
('name', models.CharField(max_length=50, null=True, verbose_name='实体别名')),
|
|
27
|
+
('tree', models.BooleanField(default=False, verbose_name='是否树形')),
|
|
26
28
|
],
|
|
27
29
|
options={
|
|
28
30
|
'verbose_name': '数据实体',
|
|
@@ -36,12 +38,14 @@ class Migration(migrations.Migration):
|
|
|
36
38
|
('sort', models.BigIntegerField(null=True, verbose_name='序号')),
|
|
37
39
|
('create_time', models.DateTimeField(auto_now_add=True, null=True, verbose_name='创建时间')),
|
|
38
40
|
('modify_time', models.DateTimeField(auto_now=True, null=True, verbose_name='修改时间')),
|
|
39
|
-
('
|
|
41
|
+
('disabled', models.BooleanField(default=False, verbose_name='禁用')),
|
|
42
|
+
('saved', models.BooleanField(default=False, verbose_name='已保存')),
|
|
40
43
|
('pid', models.IntegerField(default=0, verbose_name='父节点')),
|
|
41
44
|
('isLeaf', models.BooleanField(default=False, verbose_name='叶子节点')),
|
|
42
45
|
('icon', models.CharField(max_length=255, null=True, verbose_name='图标')),
|
|
43
46
|
('name', models.CharField(max_length=255, null=True, verbose_name='名称')),
|
|
44
47
|
('code', models.CharField(max_length=100, null=True, unique=True, verbose_name='代码')),
|
|
48
|
+
('align', models.CharField(max_length=10, null=True, verbose_name='对齐方式')),
|
|
45
49
|
],
|
|
46
50
|
options={
|
|
47
51
|
'verbose_name': '元数据字段工具',
|
|
@@ -54,20 +58,24 @@ class Migration(migrations.Migration):
|
|
|
54
58
|
('sort', models.BigIntegerField(null=True, verbose_name='序号')),
|
|
55
59
|
('create_time', models.DateTimeField(auto_now_add=True, null=True, verbose_name='创建时间')),
|
|
56
60
|
('modify_time', models.DateTimeField(auto_now=True, null=True, verbose_name='修改时间')),
|
|
57
|
-
('
|
|
61
|
+
('disabled', models.BooleanField(default=False, verbose_name='禁用')),
|
|
62
|
+
('saved', models.BooleanField(default=False, verbose_name='已保存')),
|
|
63
|
+
('property', models.JSONField(default=dict, verbose_name='属性')),
|
|
58
64
|
('code', models.CharField(default='default ', max_length=50, verbose_name='类视图')),
|
|
59
65
|
('name', models.CharField(max_length=50, null=True, verbose_name='视图名称')),
|
|
60
|
-
('
|
|
61
|
-
('form_height', models.IntegerField(default=0, verbose_name='表单高度')),
|
|
62
|
-
('table_width', models.IntegerField(default=0, verbose_name='表格宽度')),
|
|
63
|
-
('table_height', models.IntegerField(default=0, verbose_name='表格高度')),
|
|
66
|
+
('lock', models.BooleanField(default=False, verbose_name='锁定元数据')),
|
|
64
67
|
('enable', models.BooleanField(default=True, verbose_name='是否启用')),
|
|
65
|
-
('
|
|
68
|
+
('form_width', models.IntegerField(null=True, verbose_name='表单宽度')),
|
|
69
|
+
('form_height', models.IntegerField(null=True, verbose_name='表单高度')),
|
|
70
|
+
('table_width', models.IntegerField(null=True, verbose_name='表格宽度')),
|
|
71
|
+
('table_height', models.IntegerField(null=True, verbose_name='表格高度')),
|
|
66
72
|
('allow_search', models.BooleanField(default=True, verbose_name='检索功能')),
|
|
67
|
-
('allow_sort', models.BooleanField(default=True, verbose_name='移动功能')),
|
|
68
73
|
('allow_order', models.BooleanField(default=True, verbose_name='排序功能')),
|
|
69
74
|
('allow_insert', models.BooleanField(default=True, verbose_name='新增功能')),
|
|
70
75
|
('allow_edit', models.BooleanField(default=True, verbose_name='编辑功能')),
|
|
76
|
+
('allow_edit_on_form', models.BooleanField(default=True, verbose_name='表单编辑')),
|
|
77
|
+
('allow_edit_on_cell', models.BooleanField(default=True, verbose_name='表内编辑')),
|
|
78
|
+
('allow_edit_on_sort', models.BooleanField(default=True, verbose_name='移动功能')),
|
|
71
79
|
('allow_remove', models.BooleanField(default=True, verbose_name='删除功能')),
|
|
72
80
|
('allow_download', models.BooleanField(default=True, verbose_name='下载功能')),
|
|
73
81
|
('allow_upload', models.BooleanField(default=True, verbose_name='上传功能')),
|
|
@@ -85,11 +93,10 @@ class Migration(migrations.Migration):
|
|
|
85
93
|
('sort', models.BigIntegerField(null=True, verbose_name='序号')),
|
|
86
94
|
('create_time', models.DateTimeField(auto_now_add=True, null=True, verbose_name='创建时间')),
|
|
87
95
|
('modify_time', models.DateTimeField(auto_now=True, null=True, verbose_name='修改时间')),
|
|
88
|
-
('
|
|
96
|
+
('disabled', models.BooleanField(default=False, verbose_name='禁用')),
|
|
97
|
+
('saved', models.BooleanField(default=False, verbose_name='已保存')),
|
|
89
98
|
('name', models.CharField(max_length=255, null=True, unique=True, verbose_name='名称')),
|
|
90
|
-
('align', models.CharField(max_length=10, null=True, verbose_name='对齐方式')),
|
|
91
99
|
('default', models.ForeignKey(null=True, on_delete=django.db.models.deletion.SET_NULL, related_name='+', to='valar.metafieldtool', verbose_name='默认工具')),
|
|
92
|
-
('search', models.ForeignKey(null=True, on_delete=django.db.models.deletion.SET_NULL, related_name='+', to='valar.metafieldtool', verbose_name='搜索工具')),
|
|
93
100
|
('tools', models.ManyToManyField(to='valar.metafieldtool', verbose_name='工具集')),
|
|
94
101
|
],
|
|
95
102
|
options={
|
|
@@ -103,7 +110,7 @@ class Migration(migrations.Migration):
|
|
|
103
110
|
('sort', models.BigIntegerField(null=True, verbose_name='序号')),
|
|
104
111
|
('create_time', models.DateTimeField(auto_now_add=True, null=True, verbose_name='创建时间')),
|
|
105
112
|
('modify_time', models.DateTimeField(auto_now=True, null=True, verbose_name='修改时间')),
|
|
106
|
-
('saved', models.BooleanField(default=False)),
|
|
113
|
+
('saved', models.BooleanField(default=False, verbose_name='已保存')),
|
|
107
114
|
('prop', models.CharField(max_length=100, verbose_name='字段名称')),
|
|
108
115
|
('label', models.CharField(max_length=100, verbose_name='字段标签')),
|
|
109
116
|
('name', models.CharField(max_length=100, verbose_name='字段别名')),
|
|
@@ -111,16 +118,17 @@ class Migration(migrations.Migration):
|
|
|
111
118
|
('tool', models.CharField(default='default', max_length=100, verbose_name='工具组件')),
|
|
112
119
|
('refer', models.JSONField(default=dict, verbose_name='索引')),
|
|
113
120
|
('format', models.JSONField(default=dict, verbose_name='格式')),
|
|
121
|
+
('disabled', models.BooleanField(default=False, verbose_name='禁用')),
|
|
114
122
|
('not_null', models.BooleanField(default=False, verbose_name='不为空')),
|
|
115
123
|
('allow_edit', models.BooleanField(default=True, verbose_name='可编辑')),
|
|
116
124
|
('allow_order', models.BooleanField(default=True, verbose_name='可排序')),
|
|
117
125
|
('allow_search', models.BooleanField(default=True, verbose_name='可搜索')),
|
|
118
126
|
('allow_download', models.BooleanField(default=True, verbose_name='可下载')),
|
|
119
|
-
('allow_upload', models.BooleanField(default=
|
|
120
|
-
('allow_update', models.BooleanField(default=
|
|
127
|
+
('allow_upload', models.BooleanField(default=True, verbose_name='可上传')),
|
|
128
|
+
('allow_update', models.BooleanField(default=True, verbose_name='可更新')),
|
|
121
129
|
('unit', models.CharField(max_length=55, null=True, verbose_name='单位符')),
|
|
122
130
|
('column_width', models.FloatField(default=0, verbose_name='表头宽度')),
|
|
123
|
-
('align', models.CharField(
|
|
131
|
+
('align', models.CharField(max_length=55, null=True, verbose_name='对齐方式')),
|
|
124
132
|
('fixed', models.CharField(max_length=100, null=True, verbose_name='固定位置')),
|
|
125
133
|
('header_color', models.CharField(max_length=55, null=True, verbose_name='表头颜色')),
|
|
126
134
|
('cell_color', models.CharField(max_length=55, null=True, verbose_name='单元颜色')),
|