valar 1.0.18__py3-none-any.whl → 1.0.19__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/data/file/__init__.py +91 -0
- valar/data/migrations/0001_initial.py +172 -516
- valar/data/models.py +15 -6
- valar/data/orm/__init__.py +33 -8
- valar/data/orm/meta.py +59 -6
- valar/data/orm/meta_frame.py +49 -0
- valar/data/orm/meta_loader.py +70 -19
- valar/data/orm/values.py +11 -2
- valar/data/urls.py +3 -0
- valar/data/views.py +56 -7
- {valar-1.0.18.dist-info → valar-1.0.19.dist-info}/METADATA +5 -1
- valar-1.0.19.dist-info/RECORD +27 -0
- {valar-1.0.18.dist-info → valar-1.0.19.dist-info}/WHEEL +1 -1
- valar/data/migrations/0002_valatree_alter_metafield_allow_sort.py +0 -36
- valar-1.0.18.dist-info/RECORD +0 -26
- {valar-1.0.18.dist-info → valar-1.0.19.dist-info}/licenses/LICENSE +0 -0
- {valar-1.0.18.dist-info → valar-1.0.19.dist-info}/top_level.txt +0 -0
valar/data/models.py
CHANGED
|
@@ -4,6 +4,7 @@ from django.db.models.fields.files import FieldFile
|
|
|
4
4
|
|
|
5
5
|
|
|
6
6
|
class VModel(models.Model):
|
|
7
|
+
objects = models.Manager()
|
|
7
8
|
sort = models.BigIntegerField(null=True, verbose_name='序号')
|
|
8
9
|
name = models.CharField(max_length=50, null=True)
|
|
9
10
|
create_time = models.DateTimeField(auto_now_add=True, null=True, verbose_name='创建时间')
|
|
@@ -180,15 +181,23 @@ class MetaFieldTool(VTree):
|
|
|
180
181
|
verbose_name = '元数据字段工具'
|
|
181
182
|
#
|
|
182
183
|
#
|
|
184
|
+
class TestM(models.Model):
|
|
185
|
+
name = models.CharField(max_length=100, verbose_name='<UNK>')
|
|
186
|
+
|
|
183
187
|
class MetaFieldDomain(VModel):
|
|
184
188
|
name = models.CharField(max_length=255, unique=True, null=True, verbose_name='名称')
|
|
185
189
|
tools = models.ManyToManyField(to=MetaFieldTool, verbose_name='工具集')
|
|
186
|
-
default = models.ForeignKey(
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
|
|
190
|
+
default = models.ForeignKey(
|
|
191
|
+
to=MetaFieldTool, null=True,
|
|
192
|
+
on_delete=models.SET_NULL,
|
|
193
|
+
related_name='+',
|
|
194
|
+
verbose_name='默认工具')
|
|
195
|
+
search = models.ForeignKey(
|
|
196
|
+
to=MetaFieldTool, null=True,
|
|
197
|
+
on_delete=models.SET_NULL,
|
|
198
|
+
related_name='+',
|
|
199
|
+
verbose_name='搜索工具')
|
|
190
200
|
align = models.CharField(max_length=10, null=True, verbose_name='对齐方式')
|
|
191
|
-
|
|
192
201
|
class Meta:
|
|
193
202
|
verbose_name = '元数据字段类型'
|
|
194
203
|
|
|
@@ -222,6 +231,6 @@ class O2O(VModel):
|
|
|
222
231
|
name = models.CharField(max_length=100, null=True, verbose_name='name')
|
|
223
232
|
|
|
224
233
|
|
|
225
|
-
class M2M(
|
|
234
|
+
class M2M(VTree):
|
|
226
235
|
valas = models.ManyToManyField(to=Vala, verbose_name='valas')
|
|
227
236
|
name = models.CharField(max_length=100, null=True, verbose_name='name')
|
valar/data/orm/__init__.py
CHANGED
|
@@ -1,14 +1,18 @@
|
|
|
1
1
|
import datetime
|
|
2
2
|
from django.apps import apps
|
|
3
3
|
from django.core.paginator import Paginator
|
|
4
|
-
from django.db.models import Manager, QuerySet
|
|
4
|
+
from django.db.models import Manager, QuerySet, FileField
|
|
5
5
|
|
|
6
|
+
from .meta_frame import load_meta_frame
|
|
7
|
+
from ..file import minio_remove_path
|
|
6
8
|
from ..orm.detacher import detach_props, save_detached
|
|
7
9
|
from ..orm.meta_loader import load_meta, load_view, load_meta_field
|
|
8
|
-
from ..models import VModel
|
|
10
|
+
from ..models import VModel, VTree
|
|
9
11
|
from ..query import Query
|
|
10
12
|
|
|
11
13
|
|
|
14
|
+
load_meta_frame()
|
|
15
|
+
|
|
12
16
|
def load_model(entity=None):
|
|
13
17
|
mapping = {}
|
|
14
18
|
for mod in apps.get_models():
|
|
@@ -31,12 +35,13 @@ class OrmDao:
|
|
|
31
35
|
if param is None:
|
|
32
36
|
raise Exception('no entity named %s' % entity)
|
|
33
37
|
self.model = param[0]
|
|
38
|
+
self.isTree = issubclass(self.model, VTree)
|
|
34
39
|
self.name: str = param[1]
|
|
35
40
|
self.manager: Manager = self.model.objects
|
|
36
41
|
self.meta_fields = {}
|
|
37
42
|
self.model_fields = {}
|
|
38
43
|
for field in self.model._meta.get_fields():
|
|
39
|
-
_field = load_meta_field(field)
|
|
44
|
+
_field = load_meta_field(field, self.isTree)
|
|
40
45
|
prop = _field['prop']
|
|
41
46
|
self.model_fields[prop] = field
|
|
42
47
|
self.meta_fields[prop] = _field
|
|
@@ -45,7 +50,6 @@ class OrmDao:
|
|
|
45
50
|
def tree(self, query: Query, root_id = 0):
|
|
46
51
|
all_set, _ = self.find_many(Query())
|
|
47
52
|
includes, excludes = query.orm_conditions()
|
|
48
|
-
print(root_id)
|
|
49
53
|
if not len(includes) + len(excludes) + root_id:
|
|
50
54
|
return all_set
|
|
51
55
|
values = all_set.values('id','pid')
|
|
@@ -62,6 +66,19 @@ class OrmDao:
|
|
|
62
66
|
id_set.update(route)
|
|
63
67
|
return all_set.filter(id__in=id_set).order_by('-sort')
|
|
64
68
|
|
|
69
|
+
def __check_remove_file__(self, query_set, template:dict = None):
|
|
70
|
+
props = [key for key in self.model_fields if type(self.model_fields[key]) == FileField]
|
|
71
|
+
keys = [ key for key in props if template.get(key, 1) is None] if template else props
|
|
72
|
+
if len(keys):
|
|
73
|
+
values = query_set.values(*props)
|
|
74
|
+
for row in values:
|
|
75
|
+
for path in row.values():
|
|
76
|
+
if path:
|
|
77
|
+
print(path)
|
|
78
|
+
minio_remove_path(path)
|
|
79
|
+
|
|
80
|
+
|
|
81
|
+
|
|
65
82
|
def save_one(self, item):
|
|
66
83
|
_item = detach_props(item, self.meta_fields.values())
|
|
67
84
|
_id = item.get('id',0)
|
|
@@ -69,6 +86,7 @@ class OrmDao:
|
|
|
69
86
|
if len(query_set):
|
|
70
87
|
del item['id']
|
|
71
88
|
item['modify_time'] = datetime.datetime.now()
|
|
89
|
+
self.__check_remove_file__(query_set,item)
|
|
72
90
|
query_set.update(**item)
|
|
73
91
|
bean = query_set.first()
|
|
74
92
|
else:
|
|
@@ -79,13 +97,18 @@ class OrmDao:
|
|
|
79
97
|
return bean
|
|
80
98
|
|
|
81
99
|
def update_many(self, query: Query, template):
|
|
82
|
-
self.find_many(query)
|
|
100
|
+
query_set, total = self.find_many(query)
|
|
101
|
+
query_set.update(**template)
|
|
83
102
|
|
|
84
103
|
def delete_one(self, _id):
|
|
85
|
-
self.manager.filter(id=_id)
|
|
104
|
+
query_set = self.manager.filter(id=_id)
|
|
105
|
+
self.__check_remove_file__(query_set)
|
|
106
|
+
query_set.delete()
|
|
86
107
|
|
|
87
108
|
def delete_many(self, query: Query):
|
|
88
|
-
self.find_many(query)
|
|
109
|
+
query_set, total = self.find_many(query)
|
|
110
|
+
self.__check_remove_file__(query_set)
|
|
111
|
+
query_set.delete()
|
|
89
112
|
|
|
90
113
|
def find_one(self, _id):
|
|
91
114
|
return self.manager.filter(id=_id).first()
|
|
@@ -104,7 +127,9 @@ class OrmDao:
|
|
|
104
127
|
omit = [ 'id', 'saved', 'sort', 'create_time', 'modify_time']
|
|
105
128
|
fields = [ self.meta_fields[prop] for prop in self.meta_fields if prop not in omit]
|
|
106
129
|
view = load_view(self.entity, code, self.name, fields)
|
|
107
|
-
|
|
130
|
+
_view = load_meta(view)
|
|
131
|
+
_view['isTree'] = self.isTree
|
|
132
|
+
return _view
|
|
108
133
|
|
|
109
134
|
|
|
110
135
|
|
valar/data/orm/meta.py
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
mf_common = ['prop','name'
|
|
1
|
+
mf_common = ['prop','name']
|
|
2
2
|
|
|
3
3
|
meta_props = {
|
|
4
4
|
'data.Meta': {
|
|
@@ -8,8 +8,8 @@ meta_props = {
|
|
|
8
8
|
'list': ('pick', ['meta_id','code','view_name']),
|
|
9
9
|
},
|
|
10
10
|
'data.MetaField': {
|
|
11
|
-
'add': ('pick',[
|
|
12
|
-
'tool': ('pick',[*mf_common,'tool','refer','format']),
|
|
11
|
+
'add': ('pick',['prop','domain','name']),
|
|
12
|
+
'tool': ('pick',[*mf_common,'domain','tool','refer','format']),
|
|
13
13
|
'rest': ('pick',[*mf_common,'not_null','allow_edit','allow_sort','allow_search','allow_download','allow_upload','allow_update']),
|
|
14
14
|
'table': ('pick',[*mf_common,'unit','column_width','fixed','align','edit_on_table','hide_on_table','header_color','cell_color']),
|
|
15
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']),
|
|
@@ -18,17 +18,70 @@ meta_props = {
|
|
|
18
18
|
|
|
19
19
|
|
|
20
20
|
meta_defaults = {
|
|
21
|
+
'data.MetaFieldDomain':{
|
|
22
|
+
"default_id":{
|
|
23
|
+
"tool":"tree"
|
|
24
|
+
},
|
|
25
|
+
"search_id":{
|
|
26
|
+
"tool":"tree"
|
|
27
|
+
},
|
|
28
|
+
"tools":{
|
|
29
|
+
"tool":"tree",
|
|
30
|
+
"refer": {
|
|
31
|
+
"display":"code"
|
|
32
|
+
}
|
|
33
|
+
},
|
|
34
|
+
"align":{
|
|
35
|
+
"tool":"set",
|
|
36
|
+
"format":{
|
|
37
|
+
"set": {
|
|
38
|
+
'left':'左对齐',
|
|
39
|
+
'right':'右对齐',
|
|
40
|
+
'center':'剧中对齐',
|
|
41
|
+
}
|
|
42
|
+
}
|
|
43
|
+
}
|
|
44
|
+
},
|
|
21
45
|
'data.MetaField':{
|
|
46
|
+
"column_width":{
|
|
47
|
+
'unit':'px'
|
|
48
|
+
},
|
|
49
|
+
"fixed":{
|
|
50
|
+
"tool":"set",
|
|
51
|
+
"format":{
|
|
52
|
+
"set": {
|
|
53
|
+
'left':'左侧固定',
|
|
54
|
+
'right':'右侧固定',
|
|
55
|
+
}
|
|
56
|
+
}
|
|
57
|
+
},
|
|
58
|
+
"align":{
|
|
59
|
+
"tool":"set",
|
|
60
|
+
"format":{
|
|
61
|
+
"set": {
|
|
62
|
+
'left':'左对齐',
|
|
63
|
+
'right':'右对齐',
|
|
64
|
+
'center':'剧中对齐',
|
|
65
|
+
}
|
|
66
|
+
}
|
|
67
|
+
},
|
|
22
68
|
"prop":{
|
|
23
69
|
'allow_edit': False,
|
|
24
|
-
'column_width':
|
|
70
|
+
'column_width': 120
|
|
25
71
|
},
|
|
26
72
|
"domain":{
|
|
27
73
|
'allow_edit': False,
|
|
28
|
-
'column_width':
|
|
74
|
+
'column_width': 120,
|
|
29
75
|
},
|
|
30
76
|
"tool":{
|
|
31
|
-
'column_width': 100
|
|
77
|
+
'column_width': 100,
|
|
78
|
+
'tool': 'tree',
|
|
79
|
+
'refer': {
|
|
80
|
+
'entity':'data.MetaFieldTool',
|
|
81
|
+
'includes': {'metafielddomain__name':'$domain'},
|
|
82
|
+
'value': 'code'
|
|
83
|
+
}
|
|
84
|
+
|
|
32
85
|
},
|
|
33
86
|
"span":{
|
|
34
87
|
'column_width': 100,
|
|
@@ -0,0 +1,49 @@
|
|
|
1
|
+
import os
|
|
2
|
+
|
|
3
|
+
from django.db import OperationalError
|
|
4
|
+
|
|
5
|
+
from src.valar.data.models import MetaFieldDomain, MetaFieldTool
|
|
6
|
+
import pandas as pd
|
|
7
|
+
|
|
8
|
+
|
|
9
|
+
def load_meta_frame():
|
|
10
|
+
try:
|
|
11
|
+
MetaFieldTool.objects.all().delete()
|
|
12
|
+
MetaFieldDomain.objects.all().delete()
|
|
13
|
+
project_root = os.path.dirname(os.path.abspath(__file__))
|
|
14
|
+
file_path = os.path.join(project_root, 'meta_frame.xlsx')
|
|
15
|
+
array = pd.read_excel(file_path, sheet_name='tool').to_dict("records")
|
|
16
|
+
for row in array:
|
|
17
|
+
row['saved'] = True
|
|
18
|
+
MetaFieldTool.objects.create(**row)
|
|
19
|
+
array = pd.read_excel(file_path, sheet_name='domain').to_dict("records")
|
|
20
|
+
for row in array:
|
|
21
|
+
row['saved'] = True
|
|
22
|
+
tools = row.get('tools', '').split(';')
|
|
23
|
+
del row['tools']
|
|
24
|
+
item = MetaFieldDomain.objects.create(**row)
|
|
25
|
+
for tk in tools:
|
|
26
|
+
item.tools.add(tk)
|
|
27
|
+
except OperationalError as e:
|
|
28
|
+
print('initialization')
|
|
29
|
+
|
|
30
|
+
|
|
31
|
+
def convert_meta_fields(fields, entity):
|
|
32
|
+
values = MetaFieldDomain.objects.all().values('name', 'default__code', 'align')
|
|
33
|
+
mapping = {vs['name']: {
|
|
34
|
+
"tool": vs['default__code'],
|
|
35
|
+
"align": vs['align']
|
|
36
|
+
} for vs in values}
|
|
37
|
+
array = []
|
|
38
|
+
for field in fields:
|
|
39
|
+
node = mapping[field.domain]
|
|
40
|
+
_field = field.json
|
|
41
|
+
if field.tool == 'default':
|
|
42
|
+
_field['tool'] = node['tool']
|
|
43
|
+
_field['align'] = node['align']
|
|
44
|
+
_field['entity'] = entity
|
|
45
|
+
array.append(_field)
|
|
46
|
+
return array
|
|
47
|
+
|
|
48
|
+
|
|
49
|
+
|
valar/data/orm/meta_loader.py
CHANGED
|
@@ -2,16 +2,17 @@ from django.db.models import (ManyToOneRel, ForeignKey, ManyToManyRel, ManyToMan
|
|
|
2
2
|
OneToOneRel, IntegerField, BooleanField, FloatField, FileField, JSONField, DateField,
|
|
3
3
|
TextField,DateTimeField, TimeField)
|
|
4
4
|
|
|
5
|
+
from .meta_frame import convert_meta_fields
|
|
5
6
|
from ..orm.meta import meta_props, meta_defaults
|
|
6
|
-
from ..models import Meta, MetaView, VModel, MetaField
|
|
7
|
-
|
|
8
|
-
|
|
7
|
+
from ..models import Meta, MetaView, VModel, MetaField, VTree
|
|
9
8
|
|
|
9
|
+
from deepmerge import always_merger
|
|
10
10
|
|
|
11
11
|
|
|
12
12
|
def __save_model(model):
|
|
13
13
|
model.save()
|
|
14
14
|
model.sort = model.id
|
|
15
|
+
model.saved = True
|
|
15
16
|
model.save()
|
|
16
17
|
|
|
17
18
|
|
|
@@ -31,8 +32,7 @@ def load_view(entity, code, name, fields):
|
|
|
31
32
|
defaults = meta_defaults.get(entity,{})
|
|
32
33
|
for _field in _fields:
|
|
33
34
|
prop = _field['prop']
|
|
34
|
-
_field.
|
|
35
|
-
|
|
35
|
+
_field = always_merger.merge(_field,defaults.get(prop,{}))
|
|
36
36
|
_fields.reverse()
|
|
37
37
|
for f in _fields:
|
|
38
38
|
f['view'] = view
|
|
@@ -44,7 +44,8 @@ def load_meta(view):
|
|
|
44
44
|
_view = view.full
|
|
45
45
|
_meta = _view['meta']
|
|
46
46
|
fields = view.metafield_set.all().order_by('-sort')
|
|
47
|
-
_fields = [
|
|
47
|
+
_fields = convert_meta_fields(fields,_meta['entity'])
|
|
48
|
+
# _fields = [f.json for f in fields]
|
|
48
49
|
clear_item(_view, 'meta_id', 'metafield', 'metafield_set', 'meta')
|
|
49
50
|
_view['meta_name'] = _meta['name']
|
|
50
51
|
_view['entity'] = _meta['entity']
|
|
@@ -66,6 +67,15 @@ def clear_item(item, *keys):
|
|
|
66
67
|
del item[key]
|
|
67
68
|
|
|
68
69
|
|
|
70
|
+
def get_default_refer():
|
|
71
|
+
return {
|
|
72
|
+
"entity": None,
|
|
73
|
+
"value": "name", "label": 'name', "display": "id",
|
|
74
|
+
"strict": False, "remote": False, "multiple": False,
|
|
75
|
+
"includes": {}, "excludes": {}, "root": 0, "isTree": False
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
|
|
69
79
|
def get_refer(model, multiple = False):
|
|
70
80
|
module, name = model.__module__, model.__name__
|
|
71
81
|
entity = '%s.%s' % (module.replace('.models', '').split('.')[-1], name)
|
|
@@ -73,30 +83,61 @@ def get_refer(model, multiple = False):
|
|
|
73
83
|
"entity": entity,
|
|
74
84
|
"value": "id", "label": 'name', "display": "id",
|
|
75
85
|
"strict": False, "remote": False, "multiple": multiple,
|
|
76
|
-
"includes": {}, "excludes": {}, "root": 0,
|
|
86
|
+
"includes": {}, "excludes": {}, "root": 0, "isTree": issubclass(model, VTree)
|
|
77
87
|
}
|
|
78
88
|
|
|
79
89
|
def get_align(clazz):
|
|
80
|
-
if clazz in [FloatField, IntegerField
|
|
90
|
+
if clazz in [FloatField, IntegerField]: #, ManyToManyRel, ManyToManyField, ManyToOneRel
|
|
81
91
|
return 'right'
|
|
82
92
|
elif clazz in [BooleanField,FileField,JSONField,DateField,DateTimeField,TimeField]:
|
|
83
93
|
return 'center'
|
|
84
94
|
return 'left'
|
|
85
95
|
|
|
96
|
+
def get_default_format():
|
|
97
|
+
return {
|
|
98
|
+
# 文本
|
|
99
|
+
"maxlength": 0,
|
|
100
|
+
"type": 'text',
|
|
101
|
+
|
|
102
|
+
# 数值
|
|
103
|
+
"min": None,
|
|
104
|
+
"max": None,
|
|
105
|
+
"step": 1,
|
|
106
|
+
"precision": None,
|
|
107
|
+
"step_strictly": False,
|
|
108
|
+
|
|
109
|
+
# 日期
|
|
110
|
+
"frequency": "date",
|
|
111
|
+
|
|
112
|
+
# 文件
|
|
113
|
+
"maximum": 5,
|
|
114
|
+
"width": 800,
|
|
115
|
+
"height": 0,
|
|
116
|
+
"accept": [],
|
|
117
|
+
"file_name_field":None,
|
|
118
|
+
"locked": False,
|
|
119
|
+
|
|
120
|
+
#集合
|
|
121
|
+
"set": {}
|
|
122
|
+
}
|
|
123
|
+
|
|
124
|
+
|
|
125
|
+
|
|
86
126
|
def get_format(field):
|
|
87
127
|
clazz = type(field)
|
|
128
|
+
_format = get_default_format()
|
|
88
129
|
if clazz == CharField:
|
|
89
|
-
|
|
130
|
+
_format['maxlength'] = field.max_length
|
|
90
131
|
if clazz == TextField:
|
|
91
|
-
|
|
92
|
-
elif clazz ==
|
|
93
|
-
|
|
132
|
+
_format['type'] = "textarea"
|
|
133
|
+
elif clazz == DateTimeField:
|
|
134
|
+
_format['frequency'] = "datetime"
|
|
94
135
|
elif clazz == IntegerField:
|
|
95
|
-
|
|
136
|
+
_format['precision'] = 0
|
|
137
|
+
_format['step_strictly'] = True
|
|
96
138
|
elif clazz == FileField:
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
return {}
|
|
139
|
+
pass
|
|
140
|
+
return _format
|
|
100
141
|
|
|
101
142
|
def get_field_column_width(field,clazz):
|
|
102
143
|
if clazz in [BooleanField, FileField, JSONField]:
|
|
@@ -107,7 +148,7 @@ def get_field_column_width(field,clazz):
|
|
|
107
148
|
|
|
108
149
|
|
|
109
150
|
|
|
110
|
-
def load_meta_field(field):
|
|
151
|
+
def load_meta_field(field, isTree):
|
|
111
152
|
clazz = type(field)
|
|
112
153
|
if clazz in [ManyToOneRel, ManyToManyField, ManyToManyRel]:
|
|
113
154
|
prop = field.name
|
|
@@ -131,12 +172,12 @@ def load_meta_field(field):
|
|
|
131
172
|
prop = field.name
|
|
132
173
|
domain = field.get_internal_type()
|
|
133
174
|
label = field.verbose_name
|
|
134
|
-
refer =
|
|
175
|
+
refer = get_default_refer()
|
|
135
176
|
not_null = not field.null
|
|
136
177
|
align = get_align(clazz)
|
|
137
178
|
_format = get_format(field)
|
|
138
179
|
column_width = get_field_column_width(field,clazz)
|
|
139
|
-
|
|
180
|
+
_field = {
|
|
140
181
|
"prop": prop,
|
|
141
182
|
"label":label,
|
|
142
183
|
"name":label,
|
|
@@ -147,3 +188,13 @@ def load_meta_field(field):
|
|
|
147
188
|
"align":align,
|
|
148
189
|
"column_width":column_width
|
|
149
190
|
}
|
|
191
|
+
|
|
192
|
+
if isTree:
|
|
193
|
+
if prop in ['pid','isLeaf']:
|
|
194
|
+
_field['hide_on_table'] = True
|
|
195
|
+
_field['hide_on_form'] = True
|
|
196
|
+
_field['hide_on_form_branch'] = True
|
|
197
|
+
_field['hide_on_form_leaf'] = True
|
|
198
|
+
elif prop in ['icon']:
|
|
199
|
+
_field['tool'] = 'icon'
|
|
200
|
+
return _field
|
valar/data/orm/values.py
CHANGED
|
@@ -43,6 +43,13 @@ def get_props(model:VModel, code):
|
|
|
43
43
|
return simple_props, referred_fields, date_props
|
|
44
44
|
|
|
45
45
|
|
|
46
|
+
def __get_ref_keys__(related_model):
|
|
47
|
+
"""新增功能"""
|
|
48
|
+
return [f.name for f in related_model._meta.get_fields()
|
|
49
|
+
if type(f) not in [ManyToManyField, ManyToOneRel, ManyToManyRel, ForeignKey, OneToOneField, OneToOneRel]
|
|
50
|
+
and f.name not in ['create_time', 'modify_time', 'saved', 'sort']
|
|
51
|
+
]
|
|
52
|
+
|
|
46
53
|
def to_dict(query_set: QuerySet, code=None):
|
|
47
54
|
model = query_set.model
|
|
48
55
|
simple_props, referred_fields, date_props = get_props(model, code)
|
|
@@ -62,8 +69,9 @@ def to_dict(query_set: QuerySet, code=None):
|
|
|
62
69
|
keys = {'id', value, display, label}
|
|
63
70
|
if clazz in [ForeignKey, OneToOneField, OneToOneRel]:
|
|
64
71
|
related_model = field.related_model
|
|
72
|
+
__keys = __get_ref_keys__(related_model)
|
|
65
73
|
related_pks = set([row.get(prop) for row in results if row.get(prop)])
|
|
66
|
-
related_items = related_model.objects.filter(id__in=related_pks).values(*
|
|
74
|
+
related_items = related_model.objects.filter(id__in=related_pks).values(*__keys)
|
|
67
75
|
mapping = {item['id']: item for item in related_items}
|
|
68
76
|
for row in results:
|
|
69
77
|
value = row.get(prop)
|
|
@@ -82,7 +90,8 @@ def to_dict(query_set: QuerySet, code=None):
|
|
|
82
90
|
array.append(_pk)
|
|
83
91
|
row_mapping[_id] = array
|
|
84
92
|
related_model = field.related_model
|
|
85
|
-
|
|
93
|
+
__keys = __get_ref_keys__(related_model)
|
|
94
|
+
related_items = related_model.objects.filter(id__in=_pks).values(*__keys)
|
|
86
95
|
mapping = {item['id']: item for item in related_items}
|
|
87
96
|
for row in results:
|
|
88
97
|
_id = row.get('id')
|
valar/data/urls.py
CHANGED
|
@@ -5,11 +5,14 @@ from ..data import views
|
|
|
5
5
|
urlpatterns = [
|
|
6
6
|
path('<str:db>/<str:entity>/save_one', views.save_one),
|
|
7
7
|
path('<str:db>/<str:entity>/save_many', views.save_many),
|
|
8
|
+
path('<str:db>/<str:entity>/save_file', views.save_file),
|
|
9
|
+
|
|
8
10
|
path('<str:db>/<str:entity>/update_many', views.update_many),
|
|
9
11
|
path('<str:db>/<str:entity>/delete_one', views.delete_one),
|
|
10
12
|
path('<str:db>/<str:entity>/delete_many', views.delete_many),
|
|
11
13
|
path('<str:db>/<str:entity>/find_one', views.find_one),
|
|
12
14
|
path('<str:db>/<str:entity>/find_many', views.find_many),
|
|
15
|
+
path('find_file/<str:bucket_name>/<str:object_name>', views.find_file),
|
|
13
16
|
path('<str:db>/<str:entity>/tree', views.tree),
|
|
14
17
|
|
|
15
18
|
# path('data/<str:db>/<str:entity>/', include('src.valar.data.urls')),
|
valar/data/views.py
CHANGED
|
@@ -1,7 +1,14 @@
|
|
|
1
1
|
import json
|
|
2
|
+
from io import BytesIO
|
|
2
3
|
|
|
4
|
+
from django.core.files.uploadedfile import InMemoryUploadedFile
|
|
5
|
+
from django.http import QueryDict, HttpResponse
|
|
6
|
+
from django.utils.encoding import escape_uri_path
|
|
7
|
+
from urllib3 import HTTPResponse
|
|
3
8
|
|
|
4
|
-
from .
|
|
9
|
+
from .file import get_minio_object_name, get_minio_bucket_name, minio_upload_object, minio_remove_path, \
|
|
10
|
+
minio_read_object
|
|
11
|
+
from .models import MetaField, VModel, VTree
|
|
5
12
|
from .orm import load_model
|
|
6
13
|
from .query import Query
|
|
7
14
|
from .. import ValarResponse
|
|
@@ -59,10 +66,8 @@ def find_many(request, db, entity):
|
|
|
59
66
|
body = json.loads(request.body)
|
|
60
67
|
query = Query(body.get('query'))
|
|
61
68
|
dao = get_dao(db, entity)
|
|
62
|
-
page = body.get('page', 1)
|
|
63
|
-
size = body.get('size', 0)
|
|
64
69
|
code = body.get('code')
|
|
65
|
-
results, total = dao.find_many(query,
|
|
70
|
+
results, total = dao.find_many(query, query.size, query.page)
|
|
66
71
|
return ValarResponse({
|
|
67
72
|
'results': transform(db, results, code),
|
|
68
73
|
'total': total
|
|
@@ -71,7 +76,6 @@ def find_many(request, db, entity):
|
|
|
71
76
|
|
|
72
77
|
def tree(request, db, entity):
|
|
73
78
|
body = json.loads(request.body)
|
|
74
|
-
code = body.get('code','default')
|
|
75
79
|
root = body.get('root', 0)
|
|
76
80
|
query = Query(body)
|
|
77
81
|
dao = get_dao(db, entity)
|
|
@@ -117,8 +121,53 @@ def metas(request):
|
|
|
117
121
|
for entity in mapping:
|
|
118
122
|
_, name = mapping[entity]
|
|
119
123
|
app, model = entity.split('.')
|
|
120
|
-
node = {'label': name, 'value': model}
|
|
124
|
+
node = {'label': name, 'value': model, 'isTree': issubclass(_, VTree)}
|
|
121
125
|
root = tree.get(app, {'label': app, 'value': app, 'children': []})
|
|
122
126
|
root['children'].append(node)
|
|
123
127
|
tree[app] = root
|
|
124
|
-
return ValarResponse(list(tree.values()))
|
|
128
|
+
return ValarResponse(list(tree.values()))
|
|
129
|
+
|
|
130
|
+
def find_file(request, bucket_name, object_name):
|
|
131
|
+
print(bucket_name, object_name)
|
|
132
|
+
ret: HTTPResponse = minio_read_object(bucket_name, object_name)
|
|
133
|
+
file_bytes = BytesIO(ret.read())
|
|
134
|
+
content = file_bytes
|
|
135
|
+
response = HttpResponse(content)
|
|
136
|
+
response['Content-Type'] = 'application/octet-stream;charset=utf-8'
|
|
137
|
+
response['Content-Disposition'] = "attachment; filename={}".format(escape_uri_path(object_name))
|
|
138
|
+
|
|
139
|
+
return response
|
|
140
|
+
|
|
141
|
+
def save_file(request, db, entity):
|
|
142
|
+
params: QueryDict = request.POST.dict()
|
|
143
|
+
_id, prop, field = (params.get(key) for key in ['id','prop','field'])
|
|
144
|
+
file: InMemoryUploadedFile = request.FILES['file']
|
|
145
|
+
dao = get_dao(db, entity)
|
|
146
|
+
item = dao.find_one(params.get('id',0))
|
|
147
|
+
if item:
|
|
148
|
+
"""删除已有文件"""
|
|
149
|
+
old_value = getattr(item, prop)
|
|
150
|
+
if old_value:
|
|
151
|
+
minio_remove_path(old_value.name)
|
|
152
|
+
"""上传新文件"""
|
|
153
|
+
bucket_name = get_minio_bucket_name(entity)
|
|
154
|
+
object_name = get_minio_object_name(_id, prop, file.name)
|
|
155
|
+
path = minio_upload_object(bucket_name, object_name, file.read())
|
|
156
|
+
"""更新数据"""
|
|
157
|
+
setattr(item, prop, path)
|
|
158
|
+
if field:
|
|
159
|
+
setattr(item, field, file.name)
|
|
160
|
+
item.save()
|
|
161
|
+
return ValarResponse({
|
|
162
|
+
'uploaded': True,
|
|
163
|
+
"url": f'/files/{path}',
|
|
164
|
+
'fileName': path,
|
|
165
|
+
"name": file.name
|
|
166
|
+
})
|
|
167
|
+
else:
|
|
168
|
+
return ValarResponse(False)
|
|
169
|
+
|
|
170
|
+
# path = service.save_file(domain, entity, item, prop, file)
|
|
171
|
+
# dfs.remove_path(old_path)
|
|
172
|
+
|
|
173
|
+
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: valar
|
|
3
|
-
Version: 1.0.
|
|
3
|
+
Version: 1.0.19
|
|
4
4
|
Summary: valar for morghulis
|
|
5
5
|
Author: LYP
|
|
6
6
|
Author-email: liuyinpeng@buaa.edu.cn
|
|
@@ -11,6 +11,10 @@ Requires-Dist: channels==3.0.3
|
|
|
11
11
|
Requires-Dist: pymongo~=4.11.2
|
|
12
12
|
Requires-Dist: asgiref~=3.8.1
|
|
13
13
|
Requires-Dist: django-cors-headers==4.2.0
|
|
14
|
+
Requires-Dist: pandas==2.2.3
|
|
15
|
+
Requires-Dist: openpyxl==3.1.5
|
|
16
|
+
Requires-Dist: deepmerge~=2.0
|
|
17
|
+
Requires-Dist: minio==7.2.2
|
|
14
18
|
Dynamic: author
|
|
15
19
|
Dynamic: author-email
|
|
16
20
|
Dynamic: description
|
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
valar/__init__.py,sha256=ekQ2XVpfTAc9RUYjriUY2On5dZBIjZGnew__TeKVWws,858
|
|
2
|
+
valar/channels/__init__.py,sha256=scvypQLiH-gp-Y0gZfSBAP5eoXFEyYgY7TSafzqP5Wk,3432
|
|
3
|
+
valar/channels/utils.py,sha256=lQQZp6XJhTHJSBvfsxgZ7PcDKYGLo5rMpWH5wtteHww,1298
|
|
4
|
+
valar/channels/views.py,sha256=GjcFw_WswaJnlSrv2tFx12jLk6RtzxSjW1D2kBskImY,413
|
|
5
|
+
valar/data/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
6
|
+
valar/data/handlers.py,sha256=2WQfJ5PJuGLPFqBpYg2my6Mh6RqAeI6jHEj4tRAsSXw,858
|
|
7
|
+
valar/data/models.py,sha256=XXKT1DXcAQCv8mTKWB8AP7zO6Bt0RB9QQosmJ7vUT6Q,10621
|
|
8
|
+
valar/data/query.py,sha256=Cx-DDJQmMP8Qc9Sv5dvQ-QFE-aRVeYI6ccE8zUhFsVQ,1585
|
|
9
|
+
valar/data/urls.py,sha256=32EqAi4A2zR-_-FE3Ued8gVetHuY3hdgausP7eIFL3w,929
|
|
10
|
+
valar/data/utils.py,sha256=d5mdx_hV6Vs-SoAZLBdzQCvrbeuoXCozFzrCT-njCn0,2061
|
|
11
|
+
valar/data/views.py,sha256=wiKBRsrBDT4arusLh9NKBgZFK7lwaTgsTZiuTnNsJFQ,5292
|
|
12
|
+
valar/data/file/__init__.py,sha256=YwBQ0Vhfg3wbcCa6k57c9vL-AnpOgnDO6lInERlIyPc,2880
|
|
13
|
+
valar/data/migrations/0001_initial.py,sha256=jBWKQwSclN8NctNIctqied1ibVYMEXUyimzQICLMJEA,15681
|
|
14
|
+
valar/data/migrations/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
15
|
+
valar/data/mon/__init__.py,sha256=99b04h-j9HbmKlVB2qNl2ZVyUMC9pH0sofu7yXAi_Zg,3818
|
|
16
|
+
valar/data/mon/query_translator.py,sha256=g5rurgUOATarrO_hCuImr2VV6U1axwVSKAZx8zGeOuw,4646
|
|
17
|
+
valar/data/orm/__init__.py,sha256=beQOUIDMEx83sMERr4NtZikgGxjhEDUJReYz4x8Zf7E,4730
|
|
18
|
+
valar/data/orm/detacher.py,sha256=coZtVFzOVSl8AsoX2zLy7ZOkKVBLeg1HIKyKkstU1Tc,2660
|
|
19
|
+
valar/data/orm/meta.py,sha256=gzrqBp6VEryIApu1Ab3WxLRtXXcnGJ7O9UlCSjElYcw,2719
|
|
20
|
+
valar/data/orm/meta_frame.py,sha256=63mKMHw6lGVAq_R2Ml-KG3Xstd8s1Rxv2e9xDAazeRg,1538
|
|
21
|
+
valar/data/orm/meta_loader.py,sha256=aAOtZSkIONT85tld9UxK2FFClltnyZ0zJZAeyrTU1-A,6031
|
|
22
|
+
valar/data/orm/values.py,sha256=5B4zpxhW9tplz4qPaNx5ONzYt5JosS0_K4XfDUQKD38,4716
|
|
23
|
+
valar-1.0.19.dist-info/licenses/LICENSE,sha256=2bm9uFabQZ3Ykb_SaSU_uUbAj2-htc6WJQmS_65qD00,1073
|
|
24
|
+
valar-1.0.19.dist-info/METADATA,sha256=cauwp8rZdtCogK5NpMyy2ZuZTctGHegNubueoJv4JsY,2902
|
|
25
|
+
valar-1.0.19.dist-info/WHEEL,sha256=zaaOINJESkSfm_4HQVc5ssNzHCPXhJm0kEUakpsEHaU,91
|
|
26
|
+
valar-1.0.19.dist-info/top_level.txt,sha256=TVi6VcvvYfVYZ_WnUVwT4psI8p6inaP3KfmQEWrrvYg,6
|
|
27
|
+
valar-1.0.19.dist-info/RECORD,,
|