valar 1.0.17__tar.gz → 1.0.19__tar.gz
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-1.0.17/src/valar.egg-info → valar-1.0.19}/PKG-INFO +5 -1
- {valar-1.0.17 → valar-1.0.19}/setup.py +11 -2
- {valar-1.0.17 → valar-1.0.19}/src/valar/channels/__init__.py +7 -3
- {valar-1.0.17 → valar-1.0.19}/src/valar/channels/views.py +3 -2
- valar-1.0.19/src/valar/data/file/__init__.py +91 -0
- {valar-1.0.17 → valar-1.0.19}/src/valar/data/migrations/0001_initial.py +56 -28
- {valar-1.0.17 → valar-1.0.19}/src/valar/data/models.py +26 -13
- {valar-1.0.17 → valar-1.0.19}/src/valar/data/mon/__init__.py +44 -5
- valar-1.0.19/src/valar/data/mon/query_translator.py +91 -0
- {valar-1.0.17 → valar-1.0.19}/src/valar/data/orm/__init__.py +57 -9
- valar-1.0.19/src/valar/data/orm/meta.py +99 -0
- valar-1.0.19/src/valar/data/orm/meta_frame.py +49 -0
- {valar-1.0.17 → valar-1.0.19}/src/valar/data/orm/meta_loader.py +85 -20
- {valar-1.0.17 → valar-1.0.19}/src/valar/data/orm/values.py +11 -2
- {valar-1.0.17 → valar-1.0.19}/src/valar/data/query.py +14 -14
- valar-1.0.19/src/valar/data/urls.py +24 -0
- valar-1.0.19/src/valar/data/views.py +173 -0
- {valar-1.0.17 → valar-1.0.19/src/valar.egg-info}/PKG-INFO +5 -1
- {valar-1.0.17 → valar-1.0.19}/src/valar.egg-info/SOURCES.txt +3 -0
- {valar-1.0.17 → valar-1.0.19}/src/valar.egg-info/requires.txt +4 -0
- valar-1.0.17/src/valar/data/orm/meta.py +0 -24
- valar-1.0.17/src/valar/data/urls.py +0 -15
- valar-1.0.17/src/valar/data/views.py +0 -76
- {valar-1.0.17 → valar-1.0.19}/LICENSE +0 -0
- {valar-1.0.17 → valar-1.0.19}/README.md +0 -0
- {valar-1.0.17 → valar-1.0.19}/setup.cfg +0 -0
- {valar-1.0.17 → valar-1.0.19}/src/valar/__init__.py +0 -0
- {valar-1.0.17 → valar-1.0.19}/src/valar/channels/utils.py +0 -0
- {valar-1.0.17 → valar-1.0.19}/src/valar/data/__init__.py +0 -0
- {valar-1.0.17 → valar-1.0.19}/src/valar/data/handlers.py +0 -0
- {valar-1.0.17 → valar-1.0.19}/src/valar/data/migrations/__init__.py +0 -0
- {valar-1.0.17 → valar-1.0.19}/src/valar/data/orm/detacher.py +0 -0
- {valar-1.0.17 → valar-1.0.19}/src/valar/data/utils.py +0 -0
- {valar-1.0.17 → valar-1.0.19}/src/valar.egg-info/dependency_links.txt +0 -0
- {valar-1.0.17 → valar-1.0.19}/src/valar.egg-info/top_level.txt +0 -0
|
@@ -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
|
|
@@ -6,11 +6,20 @@ with open("README.md", "r", encoding="utf-8") as f:
|
|
|
6
6
|
# with open('requirements.txt', "r", encoding="utf-8") as f:
|
|
7
7
|
# required = f.read().splitlines()
|
|
8
8
|
|
|
9
|
-
requires = [
|
|
9
|
+
requires = [
|
|
10
|
+
'channels==3.0.3',
|
|
11
|
+
'pymongo~=4.11.2',
|
|
12
|
+
'asgiref~=3.8.1',
|
|
13
|
+
'django-cors-headers==4.2.0',
|
|
14
|
+
'pandas==2.2.3',
|
|
15
|
+
'openpyxl==3.1.5',
|
|
16
|
+
'deepmerge~=2.0',
|
|
17
|
+
'minio==7.2.2'
|
|
18
|
+
]
|
|
10
19
|
|
|
11
20
|
setup(
|
|
12
21
|
name="valar", # 包名
|
|
13
|
-
version="1.0.
|
|
22
|
+
version="1.0.19", # 版本号
|
|
14
23
|
author="LYP", # 作者
|
|
15
24
|
author_email="liuyinpeng@buaa.edu.cn", # 邮箱
|
|
16
25
|
description="valar for morghulis", # 简短描述
|
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import json
|
|
1
2
|
from datetime import datetime
|
|
2
3
|
|
|
3
4
|
from asgiref.sync import async_to_sync
|
|
@@ -13,7 +14,8 @@ except AttributeError:
|
|
|
13
14
|
GROUP = 'VALAR'
|
|
14
15
|
|
|
15
16
|
class ValarSocketSender:
|
|
16
|
-
def __init__(self,
|
|
17
|
+
def __init__(self, request: HttpRequest):
|
|
18
|
+
body = json.loads(request.body)
|
|
17
19
|
client = request.headers.get('CLIENT')
|
|
18
20
|
auth = request.headers.get('AUTH')
|
|
19
21
|
uid = request.session.get('UID')
|
|
@@ -21,7 +23,8 @@ class ValarSocketSender:
|
|
|
21
23
|
raise Exception('Unauthorized!')
|
|
22
24
|
self.client = client
|
|
23
25
|
self.uid = uid
|
|
24
|
-
self.
|
|
26
|
+
self.handlerKey = body.get('handlerKey')
|
|
27
|
+
self.channelKey = body.get('channelKey', 'default')
|
|
25
28
|
self.send = get_channel_layer().group_send
|
|
26
29
|
|
|
27
30
|
|
|
@@ -31,7 +34,8 @@ class ValarSocketSender:
|
|
|
31
34
|
'type': emit,
|
|
32
35
|
'data': {
|
|
33
36
|
'status': status,
|
|
34
|
-
'
|
|
37
|
+
'handlerKey': self.handlerKey,
|
|
38
|
+
'channelKey': self.channelKey,
|
|
35
39
|
'payload': data,
|
|
36
40
|
'timestamp': datetime.now().timestamp()
|
|
37
41
|
},
|
|
@@ -7,8 +7,9 @@ from ..channels import ValarSocketSender
|
|
|
7
7
|
|
|
8
8
|
async def handel_channel(request, handler):
|
|
9
9
|
method = get_channel_handler(handler)
|
|
10
|
-
|
|
11
|
-
|
|
10
|
+
body = json.loads(request.body)
|
|
11
|
+
data = body.get('data')
|
|
12
|
+
sender = ValarSocketSender(request)
|
|
12
13
|
await execute_channel(method, data, sender)
|
|
13
14
|
return ValarResponse(True)
|
|
14
15
|
|
|
@@ -0,0 +1,91 @@
|
|
|
1
|
+
import json
|
|
2
|
+
from io import BytesIO
|
|
3
|
+
|
|
4
|
+
from django.conf import settings
|
|
5
|
+
from minio import Minio
|
|
6
|
+
from urllib3 import HTTPResponse
|
|
7
|
+
|
|
8
|
+
def minio_upload_object(bucket_name, object_name, _bytes):
|
|
9
|
+
client = __get_minio_client__()
|
|
10
|
+
__create_bucket__(bucket_name, client)
|
|
11
|
+
file_data = BytesIO(_bytes)
|
|
12
|
+
file_size = len(_bytes) # file.siz
|
|
13
|
+
client.put_object(bucket_name=bucket_name, object_name=object_name, data=file_data, length=file_size)
|
|
14
|
+
return f'{bucket_name}/{object_name}'
|
|
15
|
+
|
|
16
|
+
def minio_remove_object(bucket_name, object_name):
|
|
17
|
+
client = __get_minio_client__()
|
|
18
|
+
client.remove_object(bucket_name=bucket_name, object_name=object_name)
|
|
19
|
+
|
|
20
|
+
def minio_remove_path(path):
|
|
21
|
+
[bucket_name, object_name] = path.split('/')
|
|
22
|
+
client = __get_minio_client__()
|
|
23
|
+
client.remove_object(bucket_name=bucket_name, object_name=object_name)
|
|
24
|
+
|
|
25
|
+
|
|
26
|
+
def minio_read_object(bucket_name, object_name) -> HTTPResponse:
|
|
27
|
+
client = __get_minio_client__()
|
|
28
|
+
return client.get_object(bucket_name=bucket_name, object_name=object_name)
|
|
29
|
+
|
|
30
|
+
|
|
31
|
+
|
|
32
|
+
|
|
33
|
+
|
|
34
|
+
def __get_minio_client__(bucket_name=None):
|
|
35
|
+
options = settings.MINIO_SETTINGS
|
|
36
|
+
client = Minio(**options)
|
|
37
|
+
if bucket_name:
|
|
38
|
+
__create_bucket__(bucket_name, client)
|
|
39
|
+
return client
|
|
40
|
+
|
|
41
|
+
def __create_bucket__(bucket_name, client=None):
|
|
42
|
+
client = client or __get_minio_client__()
|
|
43
|
+
exists = client.bucket_exists(bucket_name)
|
|
44
|
+
if not exists:
|
|
45
|
+
client.make_bucket(bucket_name)
|
|
46
|
+
policy = generate_policy(bucket_name)
|
|
47
|
+
client.set_bucket_policy(bucket_name, policy)
|
|
48
|
+
|
|
49
|
+
|
|
50
|
+
def get_minio_bucket_name(entity):
|
|
51
|
+
value = f'{settings.BASE_DIR.name}.{entity}'
|
|
52
|
+
bucket_name = value.replace('_','-').lower()
|
|
53
|
+
__create_bucket__(bucket_name)
|
|
54
|
+
return bucket_name
|
|
55
|
+
|
|
56
|
+
def get_minio_object_name(_id, prop, file_name):
|
|
57
|
+
return f"{_id}-{prop}-{file_name}"
|
|
58
|
+
|
|
59
|
+
def generate_policy(bucket_name):
|
|
60
|
+
return json.dumps({
|
|
61
|
+
"Version": "2012-10-17",
|
|
62
|
+
"Statement": [
|
|
63
|
+
{
|
|
64
|
+
"Sid": "",
|
|
65
|
+
"Effect": "Allow",
|
|
66
|
+
"Principal": {"AWS": "*"},
|
|
67
|
+
"Action": "s3:GetBucketLocation",
|
|
68
|
+
"Resource": f"arn:aws:s3:::{bucket_name}"
|
|
69
|
+
},
|
|
70
|
+
{
|
|
71
|
+
"Sid": "",
|
|
72
|
+
"Effect": "Allow",
|
|
73
|
+
"Principal": {"AWS": "*"},
|
|
74
|
+
"Action": "s3:ListBucket",
|
|
75
|
+
"Resource": f"arn:aws:s3:::{bucket_name}"
|
|
76
|
+
},
|
|
77
|
+
{
|
|
78
|
+
"Sid": "",
|
|
79
|
+
"Effect": "Allow",
|
|
80
|
+
"Principal": {"AWS": "*"},
|
|
81
|
+
"Action": "s3:GetObject",
|
|
82
|
+
"Resource": f"arn:aws:s3:::{bucket_name}/*"
|
|
83
|
+
},
|
|
84
|
+
{
|
|
85
|
+
"Sid": "",
|
|
86
|
+
"Effect": "Allow",
|
|
87
|
+
"Principal": {"AWS": "*"},
|
|
88
|
+
"Action": "s3:PutObject",
|
|
89
|
+
"Resource": f"arn:aws:s3:::{bucket_name}/*"
|
|
90
|
+
}
|
|
91
|
+
]})
|
|
@@ -1,7 +1,7 @@
|
|
|
1
|
-
# Generated by Django
|
|
1
|
+
# Generated by Django 4.2.21 on 2025-05-17 11:07
|
|
2
2
|
|
|
3
|
-
import django.db.models.deletion
|
|
4
3
|
from django.db import migrations, models
|
|
4
|
+
import django.db.models.deletion
|
|
5
5
|
|
|
6
6
|
|
|
7
7
|
class Migration(migrations.Migration):
|
|
@@ -46,6 +46,13 @@ class Migration(migrations.Migration):
|
|
|
46
46
|
'verbose_name': '元数据字段工具',
|
|
47
47
|
},
|
|
48
48
|
),
|
|
49
|
+
migrations.CreateModel(
|
|
50
|
+
name='TestM',
|
|
51
|
+
fields=[
|
|
52
|
+
('id', models.BigAutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
|
|
53
|
+
('name', models.CharField(max_length=100, verbose_name='<UNK>')),
|
|
54
|
+
],
|
|
55
|
+
),
|
|
49
56
|
migrations.CreateModel(
|
|
50
57
|
name='Vala',
|
|
51
58
|
fields=[
|
|
@@ -70,21 +77,36 @@ class Migration(migrations.Migration):
|
|
|
70
77
|
},
|
|
71
78
|
),
|
|
72
79
|
migrations.CreateModel(
|
|
73
|
-
name='
|
|
80
|
+
name='ValaTree',
|
|
74
81
|
fields=[
|
|
75
82
|
('id', models.BigAutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
|
|
76
83
|
('sort', models.BigIntegerField(null=True, verbose_name='序号')),
|
|
84
|
+
('name', models.CharField(max_length=50, null=True)),
|
|
77
85
|
('create_time', models.DateTimeField(auto_now_add=True, null=True, verbose_name='创建时间')),
|
|
78
86
|
('modify_time', models.DateTimeField(auto_now=True, null=True, verbose_name='修改时间')),
|
|
79
87
|
('saved', models.BooleanField(default=False)),
|
|
80
|
-
('
|
|
81
|
-
('
|
|
82
|
-
('
|
|
83
|
-
('
|
|
84
|
-
('tools', models.ManyToManyField(to='data.metafieldtool', verbose_name='工具集')),
|
|
88
|
+
('pid', models.IntegerField(default=0, verbose_name='父节点')),
|
|
89
|
+
('isLeaf', models.BooleanField(default=False, verbose_name='叶子节点')),
|
|
90
|
+
('icon', models.CharField(max_length=255, null=True, verbose_name='图标')),
|
|
91
|
+
('text', models.TextField(null=True, verbose_name='text')),
|
|
85
92
|
],
|
|
86
93
|
options={
|
|
87
|
-
'verbose_name': '
|
|
94
|
+
'verbose_name': '树形测试',
|
|
95
|
+
},
|
|
96
|
+
),
|
|
97
|
+
migrations.CreateModel(
|
|
98
|
+
name='O2O',
|
|
99
|
+
fields=[
|
|
100
|
+
('id', models.BigAutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
|
|
101
|
+
('sort', models.BigIntegerField(null=True, verbose_name='序号')),
|
|
102
|
+
('create_time', models.DateTimeField(auto_now_add=True, null=True, verbose_name='创建时间')),
|
|
103
|
+
('modify_time', models.DateTimeField(auto_now=True, null=True, verbose_name='修改时间')),
|
|
104
|
+
('saved', models.BooleanField(default=False)),
|
|
105
|
+
('name', models.CharField(max_length=100, null=True, verbose_name='name')),
|
|
106
|
+
('vala', models.OneToOneField(null=True, on_delete=django.db.models.deletion.CASCADE, to='data.vala', verbose_name='vala')),
|
|
107
|
+
],
|
|
108
|
+
options={
|
|
109
|
+
'abstract': False,
|
|
88
110
|
},
|
|
89
111
|
),
|
|
90
112
|
migrations.CreateModel(
|
|
@@ -120,6 +142,24 @@ class Migration(migrations.Migration):
|
|
|
120
142
|
'unique_together': {('meta', 'code')},
|
|
121
143
|
},
|
|
122
144
|
),
|
|
145
|
+
migrations.CreateModel(
|
|
146
|
+
name='MetaFieldDomain',
|
|
147
|
+
fields=[
|
|
148
|
+
('id', models.BigAutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
|
|
149
|
+
('sort', models.BigIntegerField(null=True, verbose_name='序号')),
|
|
150
|
+
('create_time', models.DateTimeField(auto_now_add=True, null=True, verbose_name='创建时间')),
|
|
151
|
+
('modify_time', models.DateTimeField(auto_now=True, null=True, verbose_name='修改时间')),
|
|
152
|
+
('saved', models.BooleanField(default=False)),
|
|
153
|
+
('name', models.CharField(max_length=255, null=True, unique=True, verbose_name='名称')),
|
|
154
|
+
('align', models.CharField(max_length=10, null=True, verbose_name='对齐方式')),
|
|
155
|
+
('default', models.ForeignKey(null=True, on_delete=django.db.models.deletion.SET_NULL, related_name='+', to='data.metafieldtool', verbose_name='默认工具')),
|
|
156
|
+
('search', models.ForeignKey(null=True, on_delete=django.db.models.deletion.SET_NULL, related_name='+', to='data.metafieldtool', verbose_name='搜索工具')),
|
|
157
|
+
('tools', models.ManyToManyField(to='data.metafieldtool', verbose_name='工具集')),
|
|
158
|
+
],
|
|
159
|
+
options={
|
|
160
|
+
'verbose_name': '元数据字段类型',
|
|
161
|
+
},
|
|
162
|
+
),
|
|
123
163
|
migrations.CreateModel(
|
|
124
164
|
name='MetaField',
|
|
125
165
|
fields=[
|
|
@@ -135,20 +175,20 @@ class Migration(migrations.Migration):
|
|
|
135
175
|
('tool', models.CharField(default='default', max_length=100, verbose_name='工具组件')),
|
|
136
176
|
('refer', models.JSONField(default=dict, verbose_name='索引')),
|
|
137
177
|
('format', models.JSONField(default=dict, verbose_name='格式')),
|
|
138
|
-
('unit', models.CharField(max_length=55, null=True, verbose_name='单位符')),
|
|
139
178
|
('not_null', models.BooleanField(default=False, verbose_name='不为空')),
|
|
140
|
-
('
|
|
141
|
-
('
|
|
179
|
+
('allow_edit', models.BooleanField(default=True, verbose_name='可编辑')),
|
|
180
|
+
('allow_sort', models.BooleanField(default=True, verbose_name='可排序')),
|
|
142
181
|
('allow_search', models.BooleanField(default=True, verbose_name='可搜索')),
|
|
143
182
|
('allow_download', models.BooleanField(default=True, verbose_name='可下载')),
|
|
144
183
|
('allow_upload', models.BooleanField(default=False, verbose_name='可上传')),
|
|
145
184
|
('allow_update', models.BooleanField(default=False, verbose_name='可更新')),
|
|
185
|
+
('unit', models.CharField(max_length=55, null=True, verbose_name='单位符')),
|
|
146
186
|
('column_width', models.FloatField(default=0, verbose_name='表头宽度')),
|
|
147
187
|
('align', models.CharField(default='left', max_length=55, verbose_name='对齐方式')),
|
|
148
188
|
('fixed', models.CharField(max_length=100, null=True, verbose_name='固定位置')),
|
|
149
189
|
('header_color', models.CharField(max_length=55, null=True, verbose_name='表头颜色')),
|
|
150
190
|
('cell_color', models.CharField(max_length=55, null=True, verbose_name='单元颜色')),
|
|
151
|
-
('edit_on_table', models.BooleanField(default=
|
|
191
|
+
('edit_on_table', models.BooleanField(default=True, verbose_name='表格编辑')),
|
|
152
192
|
('hide_on_table', models.BooleanField(default=False, verbose_name='表内隐藏')),
|
|
153
193
|
('span', models.IntegerField(default=0, verbose_name='表单占位')),
|
|
154
194
|
('hide_on_form', models.BooleanField(default=False, verbose_name='表单隐藏')),
|
|
@@ -162,21 +202,6 @@ class Migration(migrations.Migration):
|
|
|
162
202
|
'verbose_name': '视图字段',
|
|
163
203
|
},
|
|
164
204
|
),
|
|
165
|
-
migrations.CreateModel(
|
|
166
|
-
name='O2O',
|
|
167
|
-
fields=[
|
|
168
|
-
('id', models.BigAutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
|
|
169
|
-
('sort', models.BigIntegerField(null=True, verbose_name='序号')),
|
|
170
|
-
('create_time', models.DateTimeField(auto_now_add=True, null=True, verbose_name='创建时间')),
|
|
171
|
-
('modify_time', models.DateTimeField(auto_now=True, null=True, verbose_name='修改时间')),
|
|
172
|
-
('saved', models.BooleanField(default=False)),
|
|
173
|
-
('name', models.CharField(max_length=100, null=True, verbose_name='name')),
|
|
174
|
-
('vala', models.OneToOneField(null=True, on_delete=django.db.models.deletion.CASCADE, to='data.vala', verbose_name='vala')),
|
|
175
|
-
],
|
|
176
|
-
options={
|
|
177
|
-
'abstract': False,
|
|
178
|
-
},
|
|
179
|
-
),
|
|
180
205
|
migrations.CreateModel(
|
|
181
206
|
name='M2O',
|
|
182
207
|
fields=[
|
|
@@ -200,6 +225,9 @@ class Migration(migrations.Migration):
|
|
|
200
225
|
('create_time', models.DateTimeField(auto_now_add=True, null=True, verbose_name='创建时间')),
|
|
201
226
|
('modify_time', models.DateTimeField(auto_now=True, null=True, verbose_name='修改时间')),
|
|
202
227
|
('saved', models.BooleanField(default=False)),
|
|
228
|
+
('pid', models.IntegerField(default=0, verbose_name='父节点')),
|
|
229
|
+
('isLeaf', models.BooleanField(default=False, verbose_name='叶子节点')),
|
|
230
|
+
('icon', models.CharField(max_length=255, null=True, verbose_name='图标')),
|
|
203
231
|
('name', models.CharField(max_length=100, null=True, verbose_name='name')),
|
|
204
232
|
('valas', models.ManyToManyField(to='data.vala', verbose_name='valas')),
|
|
205
233
|
],
|
|
@@ -2,7 +2,9 @@ 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):
|
|
7
|
+
objects = models.Manager()
|
|
6
8
|
sort = models.BigIntegerField(null=True, verbose_name='序号')
|
|
7
9
|
name = models.CharField(max_length=50, null=True)
|
|
8
10
|
create_time = models.DateTimeField(auto_now_add=True, null=True, verbose_name='创建时间')
|
|
@@ -69,7 +71,7 @@ class VModel(models.Model):
|
|
|
69
71
|
elif domain in ['ManyToManyField', 'ManyToManyRel', 'ManyToOneRel']:
|
|
70
72
|
accessor = prop if domain == 'ManyToManyField' else field.get_accessor_name()
|
|
71
73
|
try:
|
|
72
|
-
_set = getattr(self, accessor).all()
|
|
74
|
+
_set = getattr(self, accessor).all().order_by('sort')
|
|
73
75
|
data[prop] = [item.id for item in _set]
|
|
74
76
|
data[f'{prop}_set'] = [item.json for item in _set]
|
|
75
77
|
except Exception as e:
|
|
@@ -138,22 +140,22 @@ class MetaField(VModel):
|
|
|
138
140
|
|
|
139
141
|
|
|
140
142
|
"""rest"""
|
|
141
|
-
unit = models.CharField(max_length=55, verbose_name='单位符', null=True)
|
|
142
143
|
not_null = models.BooleanField(default=False, verbose_name='不为空') #
|
|
143
|
-
|
|
144
|
-
|
|
144
|
+
allow_edit = models.BooleanField(default=True, verbose_name='可编辑')
|
|
145
|
+
allow_sort = models.BooleanField(default=True, verbose_name='可排序')
|
|
145
146
|
allow_search = models.BooleanField(default=True, verbose_name='可搜索')
|
|
146
147
|
allow_download = models.BooleanField(default=True, verbose_name='可下载')
|
|
147
148
|
allow_upload = models.BooleanField(default=False, verbose_name='可上传')
|
|
148
149
|
allow_update = models.BooleanField(default=False, verbose_name='可更新')
|
|
149
150
|
|
|
150
151
|
"""table"""
|
|
152
|
+
unit = models.CharField(max_length=55, verbose_name='单位符', null=True)
|
|
151
153
|
column_width = models.FloatField(default=0, verbose_name='表头宽度')
|
|
152
154
|
align = models.CharField(max_length=55, default='left', verbose_name='对齐方式') #
|
|
153
155
|
fixed = models.CharField(max_length=100, verbose_name='固定位置', null=True)
|
|
154
156
|
header_color = models.CharField(max_length=55, verbose_name='表头颜色', null=True)
|
|
155
157
|
cell_color = models.CharField(max_length=55, verbose_name='单元颜色', null=True)
|
|
156
|
-
edit_on_table = models.BooleanField(default=
|
|
158
|
+
edit_on_table = models.BooleanField(default=True, verbose_name='表格编辑')
|
|
157
159
|
hide_on_table = models.BooleanField(default=False, verbose_name='表内隐藏')
|
|
158
160
|
|
|
159
161
|
"""form"""
|
|
@@ -164,6 +166,7 @@ class MetaField(VModel):
|
|
|
164
166
|
hide_on_form_branch = models.BooleanField(default=False, verbose_name='分支隐藏')
|
|
165
167
|
hide_on_form_leaf = models.BooleanField(default=False, verbose_name='叶子隐藏')
|
|
166
168
|
|
|
169
|
+
|
|
167
170
|
class Meta:
|
|
168
171
|
verbose_name = '视图字段'
|
|
169
172
|
|
|
@@ -178,21 +181,31 @@ class MetaFieldTool(VTree):
|
|
|
178
181
|
verbose_name = '元数据字段工具'
|
|
179
182
|
#
|
|
180
183
|
#
|
|
184
|
+
class TestM(models.Model):
|
|
185
|
+
name = models.CharField(max_length=100, verbose_name='<UNK>')
|
|
186
|
+
|
|
181
187
|
class MetaFieldDomain(VModel):
|
|
182
188
|
name = models.CharField(max_length=255, unique=True, null=True, verbose_name='名称')
|
|
183
189
|
tools = models.ManyToManyField(to=MetaFieldTool, verbose_name='工具集')
|
|
184
|
-
default = models.ForeignKey(
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
|
|
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='搜索工具')
|
|
188
200
|
align = models.CharField(max_length=10, null=True, verbose_name='对齐方式')
|
|
189
|
-
|
|
190
201
|
class Meta:
|
|
191
202
|
verbose_name = '元数据字段类型'
|
|
192
203
|
|
|
193
204
|
|
|
194
|
-
|
|
195
|
-
|
|
205
|
+
class ValaTree(VTree):
|
|
206
|
+
text = models.TextField(null=True, verbose_name='text')
|
|
207
|
+
class Meta:
|
|
208
|
+
verbose_name = '树形测试'
|
|
196
209
|
|
|
197
210
|
class Vala(VModel):
|
|
198
211
|
text_field = models.TextField(null=True, verbose_name='text')
|
|
@@ -218,6 +231,6 @@ class O2O(VModel):
|
|
|
218
231
|
name = models.CharField(max_length=100, null=True, verbose_name='name')
|
|
219
232
|
|
|
220
233
|
|
|
221
|
-
class M2M(
|
|
234
|
+
class M2M(VTree):
|
|
222
235
|
valas = models.ManyToManyField(to=Vala, verbose_name='valas')
|
|
223
236
|
name = models.CharField(max_length=100, null=True, verbose_name='name')
|
|
@@ -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
|