valar 1.0.17__py3-none-any.whl → 1.0.18__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/channels/__init__.py +7 -3
- valar/channels/views.py +3 -2
- valar/data/migrations/0001_initial.py +517 -145
- valar/data/migrations/0002_valatree_alter_metafield_allow_sort.py +36 -0
- valar/data/models.py +11 -7
- valar/data/mon/__init__.py +44 -5
- valar/data/mon/query_translator.py +91 -0
- valar/data/orm/__init__.py +25 -2
- valar/data/orm/meta.py +28 -6
- valar/data/orm/meta_loader.py +19 -5
- valar/data/query.py +14 -14
- valar/data/urls.py +15 -9
- valar/data/views.py +56 -8
- {valar-1.0.17.dist-info → valar-1.0.18.dist-info}/METADATA +1 -1
- valar-1.0.18.dist-info/RECORD +26 -0
- {valar-1.0.17.dist-info → valar-1.0.18.dist-info}/WHEEL +1 -1
- valar-1.0.17.dist-info/RECORD +0 -24
- {valar-1.0.17.dist-info → valar-1.0.18.dist-info}/licenses/LICENSE +0 -0
- {valar-1.0.17.dist-info → valar-1.0.18.dist-info}/top_level.txt +0 -0
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
# Generated by Django 4.2.20 on 2025-05-05 06:50
|
|
2
|
+
|
|
3
|
+
from django.db import migrations, models
|
|
4
|
+
|
|
5
|
+
|
|
6
|
+
class Migration(migrations.Migration):
|
|
7
|
+
|
|
8
|
+
dependencies = [
|
|
9
|
+
('data', '0001_initial'),
|
|
10
|
+
]
|
|
11
|
+
|
|
12
|
+
operations = [
|
|
13
|
+
migrations.CreateModel(
|
|
14
|
+
name='ValaTree',
|
|
15
|
+
fields=[
|
|
16
|
+
('id', models.BigAutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
|
|
17
|
+
('sort', models.BigIntegerField(null=True, verbose_name='序号')),
|
|
18
|
+
('name', models.CharField(max_length=50, null=True)),
|
|
19
|
+
('create_time', models.DateTimeField(auto_now_add=True, null=True, verbose_name='创建时间')),
|
|
20
|
+
('modify_time', models.DateTimeField(auto_now=True, null=True, verbose_name='修改时间')),
|
|
21
|
+
('saved', models.BooleanField(default=False)),
|
|
22
|
+
('pid', models.IntegerField(default=0, verbose_name='父节点')),
|
|
23
|
+
('isLeaf', models.BooleanField(default=False, verbose_name='叶子节点')),
|
|
24
|
+
('icon', models.CharField(max_length=255, null=True, verbose_name='图标')),
|
|
25
|
+
('text', models.TextField(null=True, verbose_name='text')),
|
|
26
|
+
],
|
|
27
|
+
options={
|
|
28
|
+
'verbose_name': '树形测试',
|
|
29
|
+
},
|
|
30
|
+
),
|
|
31
|
+
migrations.AlterField(
|
|
32
|
+
model_name='metafield',
|
|
33
|
+
name='allow_sort',
|
|
34
|
+
field=models.BooleanField(default=True, verbose_name='可排序'),
|
|
35
|
+
),
|
|
36
|
+
]
|
valar/data/models.py
CHANGED
|
@@ -2,6 +2,7 @@ from django.db import models
|
|
|
2
2
|
from django.db.models.fields.files import FieldFile
|
|
3
3
|
|
|
4
4
|
|
|
5
|
+
|
|
5
6
|
class VModel(models.Model):
|
|
6
7
|
sort = models.BigIntegerField(null=True, verbose_name='序号')
|
|
7
8
|
name = models.CharField(max_length=50, null=True)
|
|
@@ -69,7 +70,7 @@ class VModel(models.Model):
|
|
|
69
70
|
elif domain in ['ManyToManyField', 'ManyToManyRel', 'ManyToOneRel']:
|
|
70
71
|
accessor = prop if domain == 'ManyToManyField' else field.get_accessor_name()
|
|
71
72
|
try:
|
|
72
|
-
_set = getattr(self, accessor).all()
|
|
73
|
+
_set = getattr(self, accessor).all().order_by('sort')
|
|
73
74
|
data[prop] = [item.id for item in _set]
|
|
74
75
|
data[f'{prop}_set'] = [item.json for item in _set]
|
|
75
76
|
except Exception as e:
|
|
@@ -138,22 +139,22 @@ class MetaField(VModel):
|
|
|
138
139
|
|
|
139
140
|
|
|
140
141
|
"""rest"""
|
|
141
|
-
unit = models.CharField(max_length=55, verbose_name='单位符', null=True)
|
|
142
142
|
not_null = models.BooleanField(default=False, verbose_name='不为空') #
|
|
143
|
-
|
|
144
|
-
|
|
143
|
+
allow_edit = models.BooleanField(default=True, verbose_name='可编辑')
|
|
144
|
+
allow_sort = models.BooleanField(default=True, verbose_name='可排序')
|
|
145
145
|
allow_search = models.BooleanField(default=True, verbose_name='可搜索')
|
|
146
146
|
allow_download = models.BooleanField(default=True, verbose_name='可下载')
|
|
147
147
|
allow_upload = models.BooleanField(default=False, verbose_name='可上传')
|
|
148
148
|
allow_update = models.BooleanField(default=False, verbose_name='可更新')
|
|
149
149
|
|
|
150
150
|
"""table"""
|
|
151
|
+
unit = models.CharField(max_length=55, verbose_name='单位符', null=True)
|
|
151
152
|
column_width = models.FloatField(default=0, verbose_name='表头宽度')
|
|
152
153
|
align = models.CharField(max_length=55, default='left', verbose_name='对齐方式') #
|
|
153
154
|
fixed = models.CharField(max_length=100, verbose_name='固定位置', null=True)
|
|
154
155
|
header_color = models.CharField(max_length=55, verbose_name='表头颜色', null=True)
|
|
155
156
|
cell_color = models.CharField(max_length=55, verbose_name='单元颜色', null=True)
|
|
156
|
-
edit_on_table = models.BooleanField(default=
|
|
157
|
+
edit_on_table = models.BooleanField(default=True, verbose_name='表格编辑')
|
|
157
158
|
hide_on_table = models.BooleanField(default=False, verbose_name='表内隐藏')
|
|
158
159
|
|
|
159
160
|
"""form"""
|
|
@@ -164,6 +165,7 @@ class MetaField(VModel):
|
|
|
164
165
|
hide_on_form_branch = models.BooleanField(default=False, verbose_name='分支隐藏')
|
|
165
166
|
hide_on_form_leaf = models.BooleanField(default=False, verbose_name='叶子隐藏')
|
|
166
167
|
|
|
168
|
+
|
|
167
169
|
class Meta:
|
|
168
170
|
verbose_name = '视图字段'
|
|
169
171
|
|
|
@@ -191,8 +193,10 @@ class MetaFieldDomain(VModel):
|
|
|
191
193
|
verbose_name = '元数据字段类型'
|
|
192
194
|
|
|
193
195
|
|
|
194
|
-
|
|
195
|
-
|
|
196
|
+
class ValaTree(VTree):
|
|
197
|
+
text = models.TextField(null=True, verbose_name='text')
|
|
198
|
+
class Meta:
|
|
199
|
+
verbose_name = '树形测试'
|
|
196
200
|
|
|
197
201
|
class Vala(VModel):
|
|
198
202
|
text_field = models.TextField(null=True, verbose_name='text')
|
valar/data/mon/__init__.py
CHANGED
|
@@ -1,12 +1,14 @@
|
|
|
1
1
|
from bson import ObjectId
|
|
2
|
-
from
|
|
3
|
-
from pymongo.results import InsertOneResult, UpdateResult
|
|
2
|
+
from pymongo.results import InsertOneResult
|
|
4
3
|
|
|
5
4
|
from django.conf import settings
|
|
6
5
|
import pymongo
|
|
7
6
|
|
|
8
7
|
from ..query import Query
|
|
9
8
|
|
|
9
|
+
from pymongo import MongoClient
|
|
10
|
+
from typing import Dict, Any, List, Optional
|
|
11
|
+
|
|
10
12
|
try:
|
|
11
13
|
MONGO = settings.MONGO_SETTINGS
|
|
12
14
|
except AttributeError:
|
|
@@ -71,14 +73,51 @@ class MongoDao:
|
|
|
71
73
|
def find_one(self, _id):
|
|
72
74
|
return self.collection.find_one({'_id': ObjectId(_id)})
|
|
73
75
|
|
|
74
|
-
def find_many(self, query: Query, size=0, page=1)
|
|
76
|
+
def find_many(self, query: Query, size=0, page=1) :
|
|
75
77
|
skip = (page - 1) * size
|
|
76
78
|
condition = query.mon_conditions()
|
|
77
79
|
total = self.collection.count_documents(condition)
|
|
78
|
-
cursor = self.collection.find(condition)
|
|
79
|
-
|
|
80
|
+
cursor = self.collection.find(condition)
|
|
81
|
+
|
|
82
|
+
sort_fields = []
|
|
83
|
+
for key, value in query.orders.items():
|
|
84
|
+
sort_direction = pymongo.DESCENDING if value == -1 else pymongo.ASCENDING
|
|
85
|
+
sort_fields.append((key, sort_direction))
|
|
80
86
|
|
|
87
|
+
if sort_fields:
|
|
88
|
+
cursor = cursor.sort(sort_fields)
|
|
89
|
+
|
|
90
|
+
if size:
|
|
91
|
+
cursor = cursor.skip(skip).limit(size)
|
|
92
|
+
|
|
93
|
+
return cursor, total
|
|
81
94
|
|
|
82
95
|
def meta(self):
|
|
83
96
|
one = self.collection.find_one()
|
|
84
97
|
return one
|
|
98
|
+
|
|
99
|
+
class MongoDBQuery:
|
|
100
|
+
#Mongo的查询
|
|
101
|
+
def __init__(self, connection_string: str, database_name: str):
|
|
102
|
+
self.client = MongoClient(connection_string)
|
|
103
|
+
self.db = self.client[database_name]
|
|
104
|
+
|
|
105
|
+
def find(self, collection: str, query: Dict[str, Any],
|
|
106
|
+
projection: Optional[Dict[str, Any]] = None,
|
|
107
|
+
sort: Optional[List[tuple]] = None,
|
|
108
|
+
limit: Optional[int] = None,
|
|
109
|
+
skip: Optional[int] = None) -> List[Dict[str, Any]]:
|
|
110
|
+
cursor = self.db[collection].find(query, projection)
|
|
111
|
+
if sort:
|
|
112
|
+
cursor = cursor.sort(sort) #好像有警告……但是能跑
|
|
113
|
+
if skip:
|
|
114
|
+
cursor = cursor.skip(skip)
|
|
115
|
+
if limit:
|
|
116
|
+
cursor = cursor.limit(limit)
|
|
117
|
+
return list(cursor)
|
|
118
|
+
|
|
119
|
+
def count(self, collection: str, query: Dict[str, Any]) -> int:
|
|
120
|
+
return self.db[collection].count_documents(query)
|
|
121
|
+
|
|
122
|
+
def close(self):
|
|
123
|
+
self.client.close()
|
|
@@ -0,0 +1,91 @@
|
|
|
1
|
+
class MongoQueryTranslator:
|
|
2
|
+
# 将Django ORM查询操作符映射到MongoDB操作符的字典
|
|
3
|
+
MONGO_OPERATORS = {
|
|
4
|
+
'exact': lambda v, e: {'$ne': v} if e else v,
|
|
5
|
+
'iexact': lambda v, e: {'$not': {'$regex': f'^{v}$', '$options': 'i'}} if e else {'$regex': f'^{v}$', '$options': 'i'},
|
|
6
|
+
'contains': lambda v, e: {'$not': {'$regex': f'{v}'}} if e else {'$regex': f'{v}'},
|
|
7
|
+
'icontains': lambda v, e: {'$not': {'$regex': f'{v}', '$options': 'i'}} if e else {'$regex': f'{v}', '$options': 'i'},
|
|
8
|
+
'startswith': lambda v, e: {'$not': {'$regex': f'^{v}'}} if e else {'$regex': f'^{v}'},
|
|
9
|
+
'istartswith': lambda v, e: {'$not': {'$regex': f'^{v}', '$options': 'i'}} if e else {'$regex': f'^{v}', '$options': 'i'},
|
|
10
|
+
'endswith': lambda v, e: {'$not': {'$regex': f'{v}$'}} if e else {'$regex': f'{v}$'},
|
|
11
|
+
'iendswith': lambda v, e: {'$not': {'$regex': f'{v}$', '$options': 'i'}} if e else {'$regex': f'{v}$', '$options': 'i'},
|
|
12
|
+
'gt': lambda v, e: {'$not': {'$gt': v}} if e else {'$gt': v},
|
|
13
|
+
'gte': lambda v, e: {'$not': {'$gte': v}} if e else {'$gte': v},
|
|
14
|
+
'lt': lambda v, e: {'$not': {'$lt': v}} if e else {'$lt': v},
|
|
15
|
+
'lte': lambda v, e: {'$not': {'$lte': v}} if e else {'$lte': v},
|
|
16
|
+
'in': lambda v, e: {'$nin': v} if e else {'$in': v},
|
|
17
|
+
'exists': lambda v, e: {'$eq': None} if (e and bool(v)) or (not e and not bool(v)) else {'$ne': None},
|
|
18
|
+
'isnull': lambda v, e: {'$ne': None} if e else {'$eq': None},
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
@classmethod
|
|
22
|
+
def process_field_query(cls, key, value, is_exclude=False):
|
|
23
|
+
"""处理单个字段查询"""
|
|
24
|
+
if '__' in key:
|
|
25
|
+
field, op = key.split('__', 1)
|
|
26
|
+
if op in cls.MONGO_OPERATORS:
|
|
27
|
+
return field, cls.MONGO_OPERATORS[op](value, is_exclude)
|
|
28
|
+
return key, {'$ne': value} if is_exclude else value
|
|
29
|
+
|
|
30
|
+
@classmethod
|
|
31
|
+
def process_condition(cls, condition_dict, is_exclude=False):
|
|
32
|
+
"""处理条件字典,返回MongoDB查询条件"""
|
|
33
|
+
return {field: query for field, query in
|
|
34
|
+
[cls.process_field_query(k, v, is_exclude) for k, v in condition_dict.items()]}
|
|
35
|
+
|
|
36
|
+
@classmethod
|
|
37
|
+
def process_finder(cls, finder):
|
|
38
|
+
"""处理finder字段,转换为MongoDB查询条件"""
|
|
39
|
+
if not finder:
|
|
40
|
+
return {}
|
|
41
|
+
mongo_query = {}
|
|
42
|
+
if 'term' in finder and finder['term'] and isinstance(finder['term'], str):
|
|
43
|
+
term = finder['term']
|
|
44
|
+
fields = finder.get('fields', [])
|
|
45
|
+
if fields:
|
|
46
|
+
mongo_query['$or'] = [{field: {'$regex': term, '$options': 'i'}} for field in fields]
|
|
47
|
+
else:
|
|
48
|
+
mongo_query['$text'] = {'$search': term}
|
|
49
|
+
if 'range' in finder:
|
|
50
|
+
for field, ranges in finder['range'].items():
|
|
51
|
+
field_query = {f'${op}': val for op, val in ranges.items() if op in ['gt', 'gte', 'lt', 'lte']}
|
|
52
|
+
if field_query:
|
|
53
|
+
mongo_query[field] = field_query
|
|
54
|
+
for query_type, operator in [('match', None), ('exists', '$ne')]:
|
|
55
|
+
if query_type in finder:
|
|
56
|
+
for field, value in finder[query_type].items():
|
|
57
|
+
if operator == '$ne':
|
|
58
|
+
# 处理exists查询,检查字段是否为空
|
|
59
|
+
mongo_query[field] = {'$ne': None} if bool(value) else {'$eq': None}
|
|
60
|
+
else:
|
|
61
|
+
mongo_query[field] = value
|
|
62
|
+
return mongo_query
|
|
63
|
+
|
|
64
|
+
@classmethod
|
|
65
|
+
def translate_query(cls, query_obj):
|
|
66
|
+
"""
|
|
67
|
+
将Query对象转换为MongoDB查询
|
|
68
|
+
Args:
|
|
69
|
+
query_obj: Query对象,包含condition, search和finder
|
|
70
|
+
Returns:
|
|
71
|
+
转换后的MongoDB查询条件
|
|
72
|
+
"""
|
|
73
|
+
query = {}
|
|
74
|
+
query.update(cls.process_condition(query_obj.condition.includes))
|
|
75
|
+
query.update(cls.process_condition(query_obj.condition.excludes, True))
|
|
76
|
+
# 处理搜索条件
|
|
77
|
+
if query_obj.search:
|
|
78
|
+
or_conditions = []
|
|
79
|
+
for sea in query_obj.search:
|
|
80
|
+
search_query = {}
|
|
81
|
+
search_query.update(cls.process_condition(sea.includes))
|
|
82
|
+
search_query.update(cls.process_condition(sea.excludes, True))
|
|
83
|
+
if search_query:
|
|
84
|
+
or_conditions.append(search_query)
|
|
85
|
+
if or_conditions:
|
|
86
|
+
query["$or"] = or_conditions
|
|
87
|
+
# 处理和合并finder条件
|
|
88
|
+
finder_query = cls.process_finder(query_obj.finder)
|
|
89
|
+
if finder_query and query:
|
|
90
|
+
return {"$and": [query, finder_query]}
|
|
91
|
+
return finder_query or query
|
valar/data/orm/__init__.py
CHANGED
|
@@ -21,6 +21,9 @@ def load_model(entity=None):
|
|
|
21
21
|
mapping[key] = [mod, verbose_name]
|
|
22
22
|
return mapping.get(entity) if entity else mapping
|
|
23
23
|
|
|
24
|
+
|
|
25
|
+
|
|
26
|
+
|
|
24
27
|
class OrmDao:
|
|
25
28
|
def __init__(self, entity):
|
|
26
29
|
self.entity = entity
|
|
@@ -39,6 +42,26 @@ class OrmDao:
|
|
|
39
42
|
self.meta_fields[prop] = _field
|
|
40
43
|
|
|
41
44
|
|
|
45
|
+
def tree(self, query: Query, root_id = 0):
|
|
46
|
+
all_set, _ = self.find_many(Query())
|
|
47
|
+
includes, excludes = query.orm_conditions()
|
|
48
|
+
print(root_id)
|
|
49
|
+
if not len(includes) + len(excludes) + root_id:
|
|
50
|
+
return all_set
|
|
51
|
+
values = all_set.values('id','pid')
|
|
52
|
+
mapping = {item['id']: item['pid'] for item in values}
|
|
53
|
+
results, _ = self.find_many(query)
|
|
54
|
+
id_set = {root_id}
|
|
55
|
+
for item in results:
|
|
56
|
+
_id = item.id
|
|
57
|
+
route = []
|
|
58
|
+
while _id is not None:
|
|
59
|
+
route.append(_id)
|
|
60
|
+
_id = mapping.get(_id)
|
|
61
|
+
if root_id in route:
|
|
62
|
+
id_set.update(route)
|
|
63
|
+
return all_set.filter(id__in=id_set).order_by('-sort')
|
|
64
|
+
|
|
42
65
|
def save_one(self, item):
|
|
43
66
|
_item = detach_props(item, self.meta_fields.values())
|
|
44
67
|
_id = item.get('id',0)
|
|
@@ -67,7 +90,7 @@ class OrmDao:
|
|
|
67
90
|
def find_one(self, _id):
|
|
68
91
|
return self.manager.filter(id=_id).first()
|
|
69
92
|
|
|
70
|
-
def find_many(self, query: Query, size=0, page=1)
|
|
93
|
+
def find_many(self, query: Query, size=0, page=1):
|
|
71
94
|
includes, excludes = query.orm_conditions()
|
|
72
95
|
query_set = self.manager.filter(includes).exclude(excludes).order_by(*query.orm_orders())
|
|
73
96
|
total = query_set.count()
|
|
@@ -78,7 +101,7 @@ class OrmDao:
|
|
|
78
101
|
|
|
79
102
|
|
|
80
103
|
def meta(self, code:str = 'default'):
|
|
81
|
-
omit = [ 'sort', 'create_time', 'modify_time']
|
|
104
|
+
omit = [ 'id', 'saved', 'sort', 'create_time', 'modify_time']
|
|
82
105
|
fields = [ self.meta_fields[prop] for prop in self.meta_fields if prop not in omit]
|
|
83
106
|
view = load_view(self.entity, code, self.name, fields)
|
|
84
107
|
return load_meta(view)
|
valar/data/orm/meta.py
CHANGED
|
@@ -1,24 +1,46 @@
|
|
|
1
1
|
mf_common = ['prop','name','domain']
|
|
2
2
|
|
|
3
|
-
|
|
3
|
+
meta_props = {
|
|
4
4
|
'data.Meta': {
|
|
5
5
|
'default': ('pick', ['entity','name']),
|
|
6
6
|
},
|
|
7
7
|
'data.MetaView': {
|
|
8
8
|
'list': ('pick', ['meta_id','code','view_name']),
|
|
9
|
-
'control':('omit', ['name','meta_id', 'code', 'view_name', 'metafield'])
|
|
10
9
|
},
|
|
11
10
|
'data.MetaField': {
|
|
11
|
+
'add': ('pick',[*mf_common]),
|
|
12
12
|
'tool': ('pick',[*mf_common,'tool','refer','format']),
|
|
13
|
-
'rest': ('pick',[*mf_common,'not_null','
|
|
14
|
-
'table': ('pick',[*mf_common,'column_width','fixed','align','edit_on_table','hide_on_table','header_color','cell_color']),
|
|
13
|
+
'rest': ('pick',[*mf_common,'not_null','allow_edit','allow_sort','allow_search','allow_download','allow_upload','allow_update']),
|
|
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']),
|
|
16
16
|
}
|
|
17
17
|
}
|
|
18
18
|
|
|
19
19
|
|
|
20
|
-
|
|
20
|
+
meta_defaults = {
|
|
21
21
|
'data.MetaField':{
|
|
22
|
-
|
|
22
|
+
"prop":{
|
|
23
|
+
'allow_edit': False,
|
|
24
|
+
'column_width': 100
|
|
25
|
+
},
|
|
26
|
+
"domain":{
|
|
27
|
+
'allow_edit': False,
|
|
28
|
+
'column_width': 100
|
|
29
|
+
},
|
|
30
|
+
"tool":{
|
|
31
|
+
'column_width': 100
|
|
32
|
+
},
|
|
33
|
+
"span":{
|
|
34
|
+
'column_width': 100,
|
|
35
|
+
"format": { "min": 0, "max": 24, "step": 1, "precision": 0, "step_strictly": True }
|
|
36
|
+
},
|
|
37
|
+
"refer":{
|
|
38
|
+
'allow_edit': False,
|
|
39
|
+
'column_width': 80
|
|
40
|
+
},
|
|
41
|
+
"format":{
|
|
42
|
+
'allow_edit': False,
|
|
43
|
+
'column_width': 80
|
|
44
|
+
},
|
|
23
45
|
}
|
|
24
46
|
}
|
valar/data/orm/meta_loader.py
CHANGED
|
@@ -2,7 +2,7 @@ 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 ..orm.meta import
|
|
5
|
+
from ..orm.meta import meta_props, meta_defaults
|
|
6
6
|
from ..models import Meta, MetaView, VModel, MetaField
|
|
7
7
|
|
|
8
8
|
|
|
@@ -26,8 +26,14 @@ def load_view(entity, code, name, fields):
|
|
|
26
26
|
view = MetaView(meta=meta, code=code, view_name=code.upper())
|
|
27
27
|
__save_model(view)
|
|
28
28
|
if view.metafield_set.count() == 0:
|
|
29
|
-
t, p =
|
|
29
|
+
t, p = meta_props.get(entity, {}).get(code,('omit',[]))
|
|
30
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
|
+
defaults = meta_defaults.get(entity,{})
|
|
32
|
+
for _field in _fields:
|
|
33
|
+
prop = _field['prop']
|
|
34
|
+
_field.update(defaults.get(prop,{}))
|
|
35
|
+
|
|
36
|
+
_fields.reverse()
|
|
31
37
|
for f in _fields:
|
|
32
38
|
f['view'] = view
|
|
33
39
|
field = MetaField.objects.create(**f)
|
|
@@ -37,7 +43,8 @@ def load_view(entity, code, name, fields):
|
|
|
37
43
|
def load_meta(view):
|
|
38
44
|
_view = view.full
|
|
39
45
|
_meta = _view['meta']
|
|
40
|
-
|
|
46
|
+
fields = view.metafield_set.all().order_by('-sort')
|
|
47
|
+
_fields = [f.json for f in fields]
|
|
41
48
|
clear_item(_view, 'meta_id', 'metafield', 'metafield_set', 'meta')
|
|
42
49
|
_view['meta_name'] = _meta['name']
|
|
43
50
|
_view['entity'] = _meta['entity']
|
|
@@ -53,14 +60,12 @@ def load_meta(view):
|
|
|
53
60
|
def clear_item(item, *keys):
|
|
54
61
|
del item['saved']
|
|
55
62
|
del item['sort']
|
|
56
|
-
del item['id']
|
|
57
63
|
del item['create_time']
|
|
58
64
|
del item['modify_time']
|
|
59
65
|
for key in keys:
|
|
60
66
|
del item[key]
|
|
61
67
|
|
|
62
68
|
|
|
63
|
-
|
|
64
69
|
def get_refer(model, multiple = False):
|
|
65
70
|
module, name = model.__module__, model.__name__
|
|
66
71
|
entity = '%s.%s' % (module.replace('.models', '').split('.')[-1], name)
|
|
@@ -93,6 +98,13 @@ def get_format(field):
|
|
|
93
98
|
else:
|
|
94
99
|
return {}
|
|
95
100
|
|
|
101
|
+
def get_field_column_width(field,clazz):
|
|
102
|
+
if clazz in [BooleanField, FileField, JSONField]:
|
|
103
|
+
return 100
|
|
104
|
+
elif clazz in [ DateField, DateTimeField, TimeField]:
|
|
105
|
+
return 120
|
|
106
|
+
return 0
|
|
107
|
+
|
|
96
108
|
|
|
97
109
|
|
|
98
110
|
def load_meta_field(field):
|
|
@@ -123,6 +135,7 @@ def load_meta_field(field):
|
|
|
123
135
|
not_null = not field.null
|
|
124
136
|
align = get_align(clazz)
|
|
125
137
|
_format = get_format(field)
|
|
138
|
+
column_width = get_field_column_width(field,clazz)
|
|
126
139
|
return {
|
|
127
140
|
"prop": prop,
|
|
128
141
|
"label":label,
|
|
@@ -132,4 +145,5 @@ def load_meta_field(field):
|
|
|
132
145
|
"format":_format,
|
|
133
146
|
"not_null":not_null,
|
|
134
147
|
"align":align,
|
|
148
|
+
"column_width":column_width
|
|
135
149
|
}
|
valar/data/query.py
CHANGED
|
@@ -1,6 +1,4 @@
|
|
|
1
1
|
from functools import reduce
|
|
2
|
-
|
|
3
|
-
|
|
4
2
|
from django.db.models import Q
|
|
5
3
|
|
|
6
4
|
|
|
@@ -17,15 +15,18 @@ class Query:
|
|
|
17
15
|
def __init__(self, body=None):
|
|
18
16
|
if body is None:
|
|
19
17
|
body = {}
|
|
20
|
-
self.template = body.get('template',{})
|
|
21
|
-
self.condition = Condition(body.get('condition',{}))
|
|
22
|
-
self.search = [
|
|
23
|
-
self.orders = body.get('orders',{})
|
|
18
|
+
self.template = body.get('template', {})
|
|
19
|
+
self.condition = Condition(body.get('condition', {}))
|
|
20
|
+
self.search = [Condition(condition) for condition in body.get('search', [])]
|
|
21
|
+
self.orders = body.get('orders', {})
|
|
22
|
+
self.finder = body.get('finder', {})
|
|
23
|
+
self.page = body.get('page',1)
|
|
24
|
+
self.size = body.get('size', 0)
|
|
24
25
|
|
|
25
26
|
def orm_orders(self):
|
|
26
27
|
array = []
|
|
27
|
-
for key in self.orders
|
|
28
|
-
value = self.orders
|
|
28
|
+
for key in self.orders:
|
|
29
|
+
value = self.orders.get(key)
|
|
29
30
|
prefix = '-' if value == -1 else ''
|
|
30
31
|
array.append(f'{prefix}{key}')
|
|
31
32
|
return array
|
|
@@ -33,16 +34,15 @@ class Query:
|
|
|
33
34
|
def orm_conditions(self):
|
|
34
35
|
includes, excludes = self.condition.includes, self.condition.excludes
|
|
35
36
|
if len(self.search):
|
|
36
|
-
inc = [
|
|
37
|
-
exc = [
|
|
38
|
-
def fun(x, y):return x | y
|
|
37
|
+
inc = [Q(**{**includes, **sea.includes}) for sea in self.search]
|
|
38
|
+
exc = [Q(**{**excludes, **sea.excludes}) for sea in self.search]
|
|
39
|
+
def fun(x, y): return x | y
|
|
39
40
|
return [reduce(fun, inc), reduce(fun, exc)]
|
|
40
41
|
else:
|
|
41
42
|
return [Q(**self.condition.includes), Q(**self.condition.excludes)]
|
|
42
43
|
|
|
43
44
|
def mon_conditions(self):
|
|
44
|
-
|
|
45
|
-
return
|
|
46
|
-
|
|
45
|
+
from .mon.query_translator import MongoQueryTranslator
|
|
46
|
+
return MongoQueryTranslator.translate_query(self)
|
|
47
47
|
|
|
48
48
|
# def _mon_conditions(self):
|
valar/data/urls.py
CHANGED
|
@@ -1,15 +1,21 @@
|
|
|
1
|
-
from django.urls import path
|
|
1
|
+
from django.urls import path, include
|
|
2
2
|
|
|
3
3
|
from ..data import views
|
|
4
4
|
|
|
5
5
|
urlpatterns = [
|
|
6
|
-
path('save_one', views.save_one),
|
|
7
|
-
path('save_many', views.save_many),
|
|
8
|
-
path('update_many', views.update_many),
|
|
9
|
-
path('delete_one', views.delete_one),
|
|
10
|
-
path('delete_many', views.delete_many),
|
|
11
|
-
path('find_one', views.find_one),
|
|
12
|
-
path('find_many', views.find_many),
|
|
13
|
-
path('
|
|
6
|
+
path('<str:db>/<str:entity>/save_one', views.save_one),
|
|
7
|
+
path('<str:db>/<str:entity>/save_many', views.save_many),
|
|
8
|
+
path('<str:db>/<str:entity>/update_many', views.update_many),
|
|
9
|
+
path('<str:db>/<str:entity>/delete_one', views.delete_one),
|
|
10
|
+
path('<str:db>/<str:entity>/delete_many', views.delete_many),
|
|
11
|
+
path('<str:db>/<str:entity>/find_one', views.find_one),
|
|
12
|
+
path('<str:db>/<str:entity>/find_many', views.find_many),
|
|
13
|
+
path('<str:db>/<str:entity>/tree', views.tree),
|
|
14
|
+
|
|
15
|
+
# path('data/<str:db>/<str:entity>/', include('src.valar.data.urls')),
|
|
14
16
|
|
|
17
|
+
path('add_fields', views.add_fields),
|
|
18
|
+
path('fields', views.fields),
|
|
19
|
+
path('meta', views.meta),
|
|
20
|
+
path('metas', views.metas),
|
|
15
21
|
]
|
valar/data/views.py
CHANGED
|
@@ -1,5 +1,8 @@
|
|
|
1
1
|
import json
|
|
2
2
|
|
|
3
|
+
|
|
4
|
+
from .models import MetaField, VModel
|
|
5
|
+
from .orm import load_model
|
|
3
6
|
from .query import Query
|
|
4
7
|
from .. import ValarResponse
|
|
5
8
|
from ..channels import ValarSocketSender
|
|
@@ -10,12 +13,10 @@ from ..data.utils import get_dao, transform
|
|
|
10
13
|
|
|
11
14
|
|
|
12
15
|
async def save_many(request,db, entity):
|
|
13
|
-
|
|
14
|
-
data =
|
|
15
|
-
|
|
16
|
-
sender = ValarSocketSender(handler, request)
|
|
16
|
+
body = json.loads(request.body)
|
|
17
|
+
data = body.get('data')
|
|
18
|
+
sender = ValarSocketSender(request)
|
|
17
19
|
await execute_channel(save_many_handler, data, sender)
|
|
18
|
-
|
|
19
20
|
return ValarResponse(True)
|
|
20
21
|
|
|
21
22
|
def save_one (request,db, entity):
|
|
@@ -56,7 +57,7 @@ def find_one(request, db, entity):
|
|
|
56
57
|
|
|
57
58
|
def find_many(request, db, entity):
|
|
58
59
|
body = json.loads(request.body)
|
|
59
|
-
query = Query(body)
|
|
60
|
+
query = Query(body.get('query'))
|
|
60
61
|
dao = get_dao(db, entity)
|
|
61
62
|
page = body.get('page', 1)
|
|
62
63
|
size = body.get('size', 0)
|
|
@@ -67,10 +68,57 @@ def find_many(request, db, entity):
|
|
|
67
68
|
'total': total
|
|
68
69
|
})
|
|
69
70
|
|
|
70
|
-
|
|
71
|
+
|
|
72
|
+
def tree(request, db, entity):
|
|
71
73
|
body = json.loads(request.body)
|
|
72
|
-
code = body.get('code')
|
|
74
|
+
code = body.get('code','default')
|
|
75
|
+
root = body.get('root', 0)
|
|
76
|
+
query = Query(body)
|
|
73
77
|
dao = get_dao(db, entity)
|
|
78
|
+
results = [n.json for n in dao.tree(query,root)]
|
|
79
|
+
return ValarResponse(results)
|
|
80
|
+
|
|
81
|
+
|
|
82
|
+
def meta(request):
|
|
83
|
+
body = json.loads(request.body)
|
|
84
|
+
code = body.get('code')
|
|
85
|
+
entity = body.get('entity')
|
|
86
|
+
dao = get_dao('orm', entity)
|
|
74
87
|
view = dao.meta(code)
|
|
75
88
|
return ValarResponse(view)
|
|
76
89
|
|
|
90
|
+
def fields(request ):
|
|
91
|
+
body = json.loads(request.body)
|
|
92
|
+
entity = body.get('entity')
|
|
93
|
+
dao = get_dao('orm', entity)
|
|
94
|
+
_fields =dao.meta_fields
|
|
95
|
+
return ValarResponse(_fields)
|
|
96
|
+
|
|
97
|
+
|
|
98
|
+
|
|
99
|
+
def add_fields(request):
|
|
100
|
+
body = json.loads(request.body)
|
|
101
|
+
entity = body.get('entity')
|
|
102
|
+
view_id = body.get('view_id')
|
|
103
|
+
props = body.get('props')
|
|
104
|
+
dao = get_dao('orm', entity)
|
|
105
|
+
field_dao = get_dao('orm','data.MetaField')
|
|
106
|
+
_fields = dao.meta_fields
|
|
107
|
+
for prop in props:
|
|
108
|
+
field = _fields.get(prop)
|
|
109
|
+
if field:
|
|
110
|
+
field['view_id'] = view_id
|
|
111
|
+
field_dao.save_one(field)
|
|
112
|
+
return ValarResponse(True)
|
|
113
|
+
|
|
114
|
+
def metas(request):
|
|
115
|
+
mapping = load_model()
|
|
116
|
+
tree = {}
|
|
117
|
+
for entity in mapping:
|
|
118
|
+
_, name = mapping[entity]
|
|
119
|
+
app, model = entity.split('.')
|
|
120
|
+
node = {'label': name, 'value': model}
|
|
121
|
+
root = tree.get(app, {'label': app, 'value': app, 'children': []})
|
|
122
|
+
root['children'].append(node)
|
|
123
|
+
tree[app] = root
|
|
124
|
+
return ValarResponse(list(tree.values()))
|
|
@@ -0,0 +1,26 @@
|
|
|
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=jkKzxU6dqbwda_YmxiO6W7Mro_f_8lgWd3jekH1Q_FY,10495
|
|
8
|
+
valar/data/query.py,sha256=Cx-DDJQmMP8Qc9Sv5dvQ-QFE-aRVeYI6ccE8zUhFsVQ,1585
|
|
9
|
+
valar/data/urls.py,sha256=yj4BJlb0AJn6pvWSHlk-iIpDHPC0tBQzbpSop5y0on0,790
|
|
10
|
+
valar/data/utils.py,sha256=d5mdx_hV6Vs-SoAZLBdzQCvrbeuoXCozFzrCT-njCn0,2061
|
|
11
|
+
valar/data/views.py,sha256=XxbgeOX8XuENuyD4qidYlVgZyf1L4pLj52V1SNgXXDk,3426
|
|
12
|
+
valar/data/migrations/0001_initial.py,sha256=_990niJ2N1API1NLwffAYH_1jszdeGg0BmCAhYuEzYo,21823
|
|
13
|
+
valar/data/migrations/0002_valatree_alter_metafield_allow_sort.py,sha256=kmfSq_QVRAWuox9BH-vhdoO4qBEDKzHmY23dCUlrnbc,1506
|
|
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=tJxz6_oGmGoJpKNmTgDMaSf5qKO1G14j1ME0U0Lxxwk,3748
|
|
18
|
+
valar/data/orm/detacher.py,sha256=coZtVFzOVSl8AsoX2zLy7ZOkKVBLeg1HIKyKkstU1Tc,2660
|
|
19
|
+
valar/data/orm/meta.py,sha256=_difVxW-aU2kmgd3FDiuwp0Sm-T7rSwcd9STz7QLmZc,1411
|
|
20
|
+
valar/data/orm/meta_loader.py,sha256=LQhhMYjB1830Ee6TSRJcZtTHYmhVPGR6fxB7kwGMbtk,4863
|
|
21
|
+
valar/data/orm/values.py,sha256=A0g2G2_ccsIu4YjCBHV_MMeYBDvgFAmh3gKCEJ8Q9bU,4295
|
|
22
|
+
valar-1.0.18.dist-info/licenses/LICENSE,sha256=2bm9uFabQZ3Ykb_SaSU_uUbAj2-htc6WJQmS_65qD00,1073
|
|
23
|
+
valar-1.0.18.dist-info/METADATA,sha256=TXlQ2p79k-2NrRcD40fhaMdOmRXyG261_Cn_G7oLZlU,2784
|
|
24
|
+
valar-1.0.18.dist-info/WHEEL,sha256=DnLRTWE75wApRYVsjgc6wsVswC54sMSJhAEd4xhDpBk,91
|
|
25
|
+
valar-1.0.18.dist-info/top_level.txt,sha256=TVi6VcvvYfVYZ_WnUVwT4psI8p6inaP3KfmQEWrrvYg,6
|
|
26
|
+
valar-1.0.18.dist-info/RECORD,,
|