slthcore 0.0.4__tar.gz → 0.0.6__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 slthcore might be problematic. Click here for more details.
- {slthcore-0.0.4/slthcore.egg-info → slthcore-0.0.6}/PKG-INFO +1 -1
- {slthcore-0.0.4 → slthcore-0.0.6}/setup.py +1 -1
- {slthcore-0.0.4 → slthcore-0.0.6}/slth/__init__.py +86 -1
- slthcore-0.0.6/slth/cmd/configure/__main__.py +88 -0
- {slthcore-0.0.4 → slthcore-0.0.6}/slth/components.py +3 -1
- slthcore-0.0.6/slth/db/generic.py +98 -0
- {slthcore-0.0.4 → slthcore-0.0.6}/slth/db/models.py +15 -0
- {slthcore-0.0.4 → slthcore-0.0.6}/slth/endpoints.py +165 -44
- {slthcore-0.0.4 → slthcore-0.0.6}/slth/factory.py +1 -1
- {slthcore-0.0.4 → slthcore-0.0.6}/slth/forms.py +8 -5
- slthcore-0.0.6/slth/migrations/0007_deletion_log.py +49 -0
- {slthcore-0.0.4 → slthcore-0.0.6}/slth/models.py +124 -1
- {slthcore-0.0.4 → slthcore-0.0.6}/slth/notifications.py +1 -0
- {slthcore-0.0.4 → slthcore-0.0.6}/slth/queryset.py +35 -25
- {slthcore-0.0.4 → slthcore-0.0.6}/slth/serializer.py +10 -4
- {slthcore-0.0.4 → slthcore-0.0.6}/slth/static/js/slth.min.js +52 -14
- slthcore-0.0.6/slth/tasks.py +70 -0
- {slthcore-0.0.4 → slthcore-0.0.6}/slth/templates/index.html +2 -0
- {slthcore-0.0.4 → slthcore-0.0.6}/slth/tests.py +4 -2
- slthcore-0.0.6/slth/threadlocal.py +3 -0
- {slthcore-0.0.4 → slthcore-0.0.6}/slth/utils.py +7 -1
- {slthcore-0.0.4 → slthcore-0.0.6}/slth/views.py +12 -3
- {slthcore-0.0.4 → slthcore-0.0.6/slthcore.egg-info}/PKG-INFO +1 -1
- {slthcore-0.0.4 → slthcore-0.0.6}/slthcore.egg-info/SOURCES.txt +5 -0
- {slthcore-0.0.4 → slthcore-0.0.6}/MANIFEST.in +0 -0
- {slthcore-0.0.4 → slthcore-0.0.6}/setup.cfg +0 -0
- {slthcore-0.0.4 → slthcore-0.0.6}/slth/cmd/init/__main__.py +0 -0
- {slthcore-0.0.4 → slthcore-0.0.6}/slth/cmd/init/boilerplate/.DS_Store +0 -0
- {slthcore-0.0.4 → slthcore-0.0.6}/slth/cmd/init/boilerplate/.gitignore +0 -0
- {slthcore-0.0.4 → slthcore-0.0.6}/slth/cmd/init/boilerplate/backend/api/__init__.py +0 -0
- {slthcore-0.0.4 → slthcore-0.0.6}/slth/cmd/init/boilerplate/backend/api/asgi.py +0 -0
- {slthcore-0.0.4 → slthcore-0.0.6}/slth/cmd/init/boilerplate/backend/api/endpoints.py +0 -0
- {slthcore-0.0.4 → slthcore-0.0.6}/slth/cmd/init/boilerplate/backend/api/models.py +0 -0
- {slthcore-0.0.4 → slthcore-0.0.6}/slth/cmd/init/boilerplate/backend/api/settings.py +0 -0
- {slthcore-0.0.4 → slthcore-0.0.6}/slth/cmd/init/boilerplate/backend/api/tests.py +0 -0
- {slthcore-0.0.4 → slthcore-0.0.6}/slth/cmd/init/boilerplate/backend/api/urls.py +0 -0
- {slthcore-0.0.4 → slthcore-0.0.6}/slth/cmd/init/boilerplate/backend/api/wsgi.py +0 -0
- {slthcore-0.0.4 → slthcore-0.0.6}/slth/cmd/init/boilerplate/backend/application.yml +0 -0
- {slthcore-0.0.4 → slthcore-0.0.6}/slth/cmd/init/boilerplate/backend/entrypoint.sh +0 -0
- {slthcore-0.0.4 → slthcore-0.0.6}/slth/cmd/init/boilerplate/backend/manage.py +0 -0
- {slthcore-0.0.4 → slthcore-0.0.6}/slth/cmd/init/boilerplate/backend/requirements.txt +0 -0
- {slthcore-0.0.4 → slthcore-0.0.6}/slth/cmd/init/boilerplate/base.env +0 -0
- {slthcore-0.0.4 → slthcore-0.0.6}/slth/cmd/init/boilerplate/docker-compose.yml +0 -0
- {slthcore-0.0.4 → slthcore-0.0.6}/slth/cmd/init/boilerplate/frontend/package.json +0 -0
- {slthcore-0.0.4 → slthcore-0.0.6}/slth/cmd/init/boilerplate/frontend/src/main.jsx +0 -0
- {slthcore-0.0.4 → slthcore-0.0.6}/slth/cmd/init/boilerplate/frontend/vite.config.js +0 -0
- {slthcore-0.0.4 → slthcore-0.0.6}/slth/cmd/init/boilerplate/local.env +0 -0
- {slthcore-0.0.4 → slthcore-0.0.6}/slth/cmd/init/boilerplate/run.sh +0 -0
- {slthcore-0.0.4 → slthcore-0.0.6}/slth/cmd/init/boilerplate/selenium/run.sh +0 -0
- {slthcore-0.0.4 → slthcore-0.0.6}/slth/cmd/init/boilerplate/test.sh +0 -0
- {slthcore-0.0.4 → slthcore-0.0.6}/slth/db/__init__.py +0 -0
- {slthcore-0.0.4 → slthcore-0.0.6}/slth/exceptions.py +0 -0
- {slthcore-0.0.4 → slthcore-0.0.6}/slth/management/__init__.py +0 -0
- {slthcore-0.0.4 → slthcore-0.0.6}/slth/management/commands/__init__.py +0 -0
- {slthcore-0.0.4 → slthcore-0.0.6}/slth/management/commands/integration_test.py +0 -0
- {slthcore-0.0.4 → slthcore-0.0.6}/slth/management/commands/sync.py +0 -0
- {slthcore-0.0.4 → slthcore-0.0.6}/slth/migrations/0001_initial.py +0 -0
- {slthcore-0.0.4 → slthcore-0.0.6}/slth/migrations/0002_email_role_pushsubscription_error.py +0 -0
- {slthcore-0.0.4 → slthcore-0.0.6}/slth/migrations/0003_rename_photo_profile_alter_profile_options.py +0 -0
- {slthcore-0.0.4 → slthcore-0.0.6}/slth/migrations/0004_alter_profile_photo.py +0 -0
- {slthcore-0.0.4 → slthcore-0.0.6}/slth/migrations/0005_alter_profile_photo.py +0 -0
- {slthcore-0.0.4 → slthcore-0.0.6}/slth/migrations/0006_user.py +0 -0
- {slthcore-0.0.4 → slthcore-0.0.6}/slth/migrations/__init__.py +0 -0
- {slthcore-0.0.4 → slthcore-0.0.6}/slth/oauth.py +0 -0
- {slthcore-0.0.4 → slthcore-0.0.6}/slth/permissions.py +0 -0
- {slthcore-0.0.4 → slthcore-0.0.6}/slth/roles.py +0 -0
- {slthcore-0.0.4 → slthcore-0.0.6}/slth/selenium/__init__.py +0 -0
- {slthcore-0.0.4 → slthcore-0.0.6}/slth/selenium/browser.py +0 -0
- {slthcore-0.0.4 → slthcore-0.0.6}/slth/static/.DS_Store +0 -0
- {slthcore-0.0.4 → slthcore-0.0.6}/slth/static/css/.DS_Store +0 -0
- {slthcore-0.0.4 → slthcore-0.0.6}/slth/static/css/slth.css +0 -0
- {slthcore-0.0.4 → slthcore-0.0.6}/slth/static/js/index.min.js +0 -0
- {slthcore-0.0.4 → slthcore-0.0.6}/slth/static/js/react.min.js +0 -0
- {slthcore-0.0.4 → slthcore-0.0.6}/slth/statistics.py +0 -0
- {slthcore-0.0.4 → slthcore-0.0.6}/slth/templates/service-worker.js +0 -0
- {slthcore-0.0.4 → slthcore-0.0.6}/slth/urls.py +0 -0
- {slthcore-0.0.4 → slthcore-0.0.6}/slthcore.egg-info/dependency_links.txt +0 -0
- {slthcore-0.0.4 → slthcore-0.0.6}/slthcore.egg-info/top_level.txt +0 -0
|
@@ -1,18 +1,23 @@
|
|
|
1
1
|
import re
|
|
2
2
|
import os
|
|
3
3
|
import yaml
|
|
4
|
+
import json
|
|
4
5
|
import warnings
|
|
5
6
|
from pathlib import Path
|
|
7
|
+
from datetime import datetime
|
|
6
8
|
from django.apps import apps
|
|
7
9
|
from django.db import models
|
|
8
10
|
from .queryset import QuerySet
|
|
9
11
|
from django.db.models import manager
|
|
10
|
-
from .serializer import Serializer
|
|
12
|
+
from .serializer import Serializer, serialize
|
|
13
|
+
from django.db.models.deletion import Collector
|
|
11
14
|
from .factory import FormFactory
|
|
12
15
|
import django.db.models.options as options
|
|
13
16
|
from django.db.models.base import ModelBase
|
|
14
17
|
from django.core.exceptions import FieldDoesNotExist
|
|
15
18
|
from django.utils.autoreload import autoreload_started
|
|
19
|
+
from django.core import serializers
|
|
20
|
+
from .threadlocal import tl
|
|
16
21
|
|
|
17
22
|
warnings.filterwarnings('ignore', module='urllib3')
|
|
18
23
|
|
|
@@ -89,6 +94,83 @@ class ModelMixin(object):
|
|
|
89
94
|
|
|
90
95
|
def formfactory(self) -> FormFactory:
|
|
91
96
|
return FormFactory(self)
|
|
97
|
+
|
|
98
|
+
def pre_save(self):
|
|
99
|
+
pass
|
|
100
|
+
|
|
101
|
+
def post_save(self):
|
|
102
|
+
pass
|
|
103
|
+
|
|
104
|
+
def safe_delete(self, username=None):
|
|
105
|
+
from .models import Deletion
|
|
106
|
+
order = []
|
|
107
|
+
objects = []
|
|
108
|
+
collector = Collector('default')
|
|
109
|
+
qs = type(self).objects.filter(pk=self.pk)
|
|
110
|
+
collector.collect(qs)
|
|
111
|
+
for instances in collector.data.values():
|
|
112
|
+
for instance in instances:
|
|
113
|
+
if instance.__class__.__name__ not in ['Log'] and instance not in objects:
|
|
114
|
+
objects.append(instance)
|
|
115
|
+
if instance.__class__.__name__ not in order:
|
|
116
|
+
order.append(instance.__class__.__name__)
|
|
117
|
+
for qs in collector.fast_deletes:
|
|
118
|
+
for instance in qs:
|
|
119
|
+
if instance.__class__.__name__ not in ['Log'] and instance not in objects:
|
|
120
|
+
objects.append(instance)
|
|
121
|
+
if instance.__class__.__name__ not in order:
|
|
122
|
+
order.append(instance.__class__.__name__)
|
|
123
|
+
backup = json.dumps(dict(order=order, objects=serializers.serialize("python", objects)))
|
|
124
|
+
instance = '{}.{}:{}'.format(self._meta.app_label, self._meta.model_name, self.pk)
|
|
125
|
+
Deletion.objects.create(username=username, datetime=datetime.now(), instance=instance, backup=backup)
|
|
126
|
+
return self.delete()
|
|
127
|
+
|
|
128
|
+
|
|
129
|
+
def save_decorator(func):
|
|
130
|
+
|
|
131
|
+
def decorate(self, *args, **kwargs):
|
|
132
|
+
diff = {}
|
|
133
|
+
if self.pk:
|
|
134
|
+
action = 'edit'
|
|
135
|
+
obj = type(self).objects.filter(pk=self.pk).first()
|
|
136
|
+
if obj:
|
|
137
|
+
for field in self._meta.fields:
|
|
138
|
+
a = getattr(obj, field.name)
|
|
139
|
+
b = getattr(self, field.name)
|
|
140
|
+
if a != b:
|
|
141
|
+
diff[field.verbose_name] = (serialize(a), serialize(b))
|
|
142
|
+
else:
|
|
143
|
+
action = 'add'
|
|
144
|
+
for field in self._meta.fields:
|
|
145
|
+
b = getattr(self, field.name)
|
|
146
|
+
if b is not None:
|
|
147
|
+
diff[field.verbose_name] = (None, serialize(b))
|
|
148
|
+
func(self, *args, **kwargs)
|
|
149
|
+
if diff:
|
|
150
|
+
model = '{}.{}'.format(self._meta.app_label, self._meta.model_name)
|
|
151
|
+
log = dict(model=model, pk=self.pk, action=action, diff=diff)
|
|
152
|
+
context = getattr(tl, 'context', None)
|
|
153
|
+
if context:
|
|
154
|
+
context['logs'].append(log)
|
|
155
|
+
|
|
156
|
+
return decorate
|
|
157
|
+
|
|
158
|
+
|
|
159
|
+
def delete_decorator(func):
|
|
160
|
+
def decorate(self, *args, **kwargs):
|
|
161
|
+
diff = {}
|
|
162
|
+
for field in self._meta.fields:
|
|
163
|
+
a = getattr(self, field.name)
|
|
164
|
+
diff[field.verbose_name] = (a, None)
|
|
165
|
+
log = dict(model='{}.{}'.format(
|
|
166
|
+
self._meta.app_label, self._meta.model_name), action='delete', diff=diff
|
|
167
|
+
)
|
|
168
|
+
context = getattr(tl, 'context', None)
|
|
169
|
+
if context:
|
|
170
|
+
context['logs'].append(log)
|
|
171
|
+
func(self, *args, **kwargs)
|
|
172
|
+
|
|
173
|
+
return decorate
|
|
92
174
|
|
|
93
175
|
|
|
94
176
|
___new___ = ModelBase.__new__
|
|
@@ -109,6 +191,9 @@ def __new__(mcs, name, bases, attrs, **kwargs):
|
|
|
109
191
|
cls = ___new___(mcs, name, bases, attrs, **kwargs)
|
|
110
192
|
if cls._meta.proxy_for_model:
|
|
111
193
|
PROXIED_MODELS.append(cls._meta.proxy_for_model)
|
|
194
|
+
if name == 'Model':
|
|
195
|
+
cls.save = save_decorator(cls.save)
|
|
196
|
+
cls.delete = delete_decorator(cls.delete)
|
|
112
197
|
return cls
|
|
113
198
|
|
|
114
199
|
|
|
@@ -0,0 +1,88 @@
|
|
|
1
|
+
import os
|
|
2
|
+
import sys
|
|
3
|
+
import pathlib
|
|
4
|
+
from slth.utils import parse_string_template
|
|
5
|
+
from subprocess import Popen, PIPE
|
|
6
|
+
|
|
7
|
+
|
|
8
|
+
USAGE = 'USAGE: python -m slth.cmd.configure [nginx|systemctl]'
|
|
9
|
+
|
|
10
|
+
NGINX = '''server {
|
|
11
|
+
listen 80;
|
|
12
|
+
server_name {{ server_name }};
|
|
13
|
+
{% if ssl %}
|
|
14
|
+
listen 443 ssl;
|
|
15
|
+
ssl_certificate /etc/letsencrypt/live/{{ server_name }}/fullchain.pem;
|
|
16
|
+
ssl_certificate_key /etc/letsencrypt/live/{{ server_name }}/privkey.pem;
|
|
17
|
+
if ($scheme = http) { return 301 https://$server_name$request_uri; }
|
|
18
|
+
{% endif %}
|
|
19
|
+
location / {
|
|
20
|
+
proxy_pass http://127.0.0.1:{{ port }};
|
|
21
|
+
}
|
|
22
|
+
}'''
|
|
23
|
+
|
|
24
|
+
SERVICE_SCRIPT = '''#!/bin/bash
|
|
25
|
+
source .venv/bin/activate
|
|
26
|
+
|
|
27
|
+
export POSTGRES_HOST=localhost
|
|
28
|
+
export POSTGRES_DB={{ name }}
|
|
29
|
+
|
|
30
|
+
python manage.py sync
|
|
31
|
+
gunicorn api.wsgi -b 0.0.0.0:8000 -w 3 --log-level info --reload --timeout 3600
|
|
32
|
+
'''
|
|
33
|
+
|
|
34
|
+
SERVICE_CONTENT = '''[Unit]
|
|
35
|
+
Description={{ server_name }}
|
|
36
|
+
|
|
37
|
+
[Service]
|
|
38
|
+
User=www-data
|
|
39
|
+
WorkingDirectory={{ dirname }}
|
|
40
|
+
ExecStart=bash service.sh
|
|
41
|
+
Restart=always
|
|
42
|
+
RestartSec=3
|
|
43
|
+
|
|
44
|
+
[Install]
|
|
45
|
+
WantedBy=multi-user.target
|
|
46
|
+
'''
|
|
47
|
+
|
|
48
|
+
def execute(command):
|
|
49
|
+
process = Popen(command.split(), stdout=PIPE, stderr=PIPE)
|
|
50
|
+
stdout, stderr = process.communicate()
|
|
51
|
+
#print(stdout, stderr)
|
|
52
|
+
|
|
53
|
+
def print_content(file_path, content):
|
|
54
|
+
print()
|
|
55
|
+
print(file_path)
|
|
56
|
+
print('--------------------------')
|
|
57
|
+
print(content)
|
|
58
|
+
print('--------------------------')
|
|
59
|
+
|
|
60
|
+
|
|
61
|
+
dirname = pathlib.Path().resolve()
|
|
62
|
+
server_name = input('DOMAIN: ')
|
|
63
|
+
name = server_name.split('.')[0]
|
|
64
|
+
|
|
65
|
+
execute('createdb -U postgres {};'.format(name))
|
|
66
|
+
|
|
67
|
+
ssl = input('SSL [y|N]: ').lower() == 'y'
|
|
68
|
+
port = input('PORT [8000]: ') or '8000'
|
|
69
|
+
nginx_file_path = f'/etc/nginx/conf.d/{server_name}.conf'
|
|
70
|
+
nginx_file_content = parse_string_template(NGINX, server_name=server_name, ssl=ssl, port=port)
|
|
71
|
+
print_content(nginx_file_path, nginx_file_content)
|
|
72
|
+
|
|
73
|
+
service_script_path = os.path.join(dirname, 'service.sh')
|
|
74
|
+
service_script_content = parse_string_template(SERVICE_SCRIPT, name=name)
|
|
75
|
+
service_file_content = parse_string_template(SERVICE_CONTENT, server_name=server_name, dirname=dirname)
|
|
76
|
+
service_file_path = f'/etc/systemd/system/{server_name}.service'
|
|
77
|
+
print_content(service_script_path, service_script_content)
|
|
78
|
+
print_content(service_file_path, service_file_content)
|
|
79
|
+
save = input('SAVE [y|N]: ').lower() == 'y'
|
|
80
|
+
if save:
|
|
81
|
+
with open(nginx_file_path, 'w') as file:
|
|
82
|
+
file.write(nginx_file_content)
|
|
83
|
+
with open(service_script_path, 'w') as file:
|
|
84
|
+
file.write(service_script_content)
|
|
85
|
+
with open(service_file_path, 'w') as file:
|
|
86
|
+
file.write(service_file_content)
|
|
87
|
+
|
|
88
|
+
execute('systemctl daemon-reload')
|
|
@@ -289,15 +289,17 @@ class Scheduler(dict):
|
|
|
289
289
|
def __init__(
|
|
290
290
|
self,
|
|
291
291
|
start_time=7,
|
|
292
|
-
end_time=
|
|
292
|
+
end_time=23,
|
|
293
293
|
chucks=2,
|
|
294
294
|
start_day=None,
|
|
295
295
|
days=14,
|
|
296
296
|
single_selection=False,
|
|
297
297
|
input_name="schedule",
|
|
298
298
|
readonly=False,
|
|
299
|
+
title=None
|
|
299
300
|
):
|
|
300
301
|
self["type"] = "scheduler"
|
|
302
|
+
self["title"] = title
|
|
301
303
|
self["single_selection"] = single_selection
|
|
302
304
|
self["input_name"] = input_name
|
|
303
305
|
self["readonly"] = readonly
|
|
@@ -0,0 +1,98 @@
|
|
|
1
|
+
# -*- coding: utf-8 -*-
|
|
2
|
+
import datetime
|
|
3
|
+
import json
|
|
4
|
+
from decimal import Decimal
|
|
5
|
+
|
|
6
|
+
from django.apps import apps
|
|
7
|
+
from django.db.models import *
|
|
8
|
+
from django.db.models.query_utils import DeferredAttribute
|
|
9
|
+
|
|
10
|
+
|
|
11
|
+
class GenericModelWrapper(object):
|
|
12
|
+
def __init__(self, obj):
|
|
13
|
+
self._wrapped_obj = obj
|
|
14
|
+
|
|
15
|
+
def __getattr__(self, attr):
|
|
16
|
+
if attr == 'prepare_database_save':
|
|
17
|
+
raise AttributeError()
|
|
18
|
+
return getattr(self._wrapped_obj, attr)
|
|
19
|
+
|
|
20
|
+
def __setattr__(self, attr, value):
|
|
21
|
+
if attr == '_wrapped_obj':
|
|
22
|
+
super().__setattr__(attr, value)
|
|
23
|
+
elif self._wrapped_obj is not None:
|
|
24
|
+
self._wrapped_obj.__setattr__(attr, value._wrapped_obj)
|
|
25
|
+
|
|
26
|
+
def __str__(self):
|
|
27
|
+
return self._wrapped_obj.__str__()
|
|
28
|
+
|
|
29
|
+
def __repr__(self):
|
|
30
|
+
return self._wrapped_obj.__repr__()
|
|
31
|
+
|
|
32
|
+
|
|
33
|
+
class GenericValue(object):
|
|
34
|
+
def __init__(self, value):
|
|
35
|
+
self.value = value
|
|
36
|
+
|
|
37
|
+
def get_value(self):
|
|
38
|
+
if isinstance(self.value, str) and '::' in self.value:
|
|
39
|
+
value_type, value = self.value.split('::')
|
|
40
|
+
if '.' in value_type:
|
|
41
|
+
self.value = apps.get_model(value_type).objects.get(pk=value)
|
|
42
|
+
elif value_type == 'str':
|
|
43
|
+
self.value = value
|
|
44
|
+
elif value_type == 'int':
|
|
45
|
+
self.value = int(value)
|
|
46
|
+
elif value_type == 'Decimal':
|
|
47
|
+
self.value = Decimal(value)
|
|
48
|
+
elif value_type in ('date', 'datetime'):
|
|
49
|
+
self.value = datetime.datetime.strptime(value[0:10], '%Y-%m-%d')
|
|
50
|
+
elif value_type == 'float':
|
|
51
|
+
self.value = float(value)
|
|
52
|
+
elif value_type == 'bool':
|
|
53
|
+
self.value = value == 'True'
|
|
54
|
+
elif value_type == 'list':
|
|
55
|
+
self.value = json.loads(value)
|
|
56
|
+
return self.value
|
|
57
|
+
|
|
58
|
+
def dumps(self):
|
|
59
|
+
value = self.value
|
|
60
|
+
if value is not None:
|
|
61
|
+
if isinstance(value, Model):
|
|
62
|
+
value = GenericModelWrapper(value)
|
|
63
|
+
if isinstance(value, GenericModelWrapper):
|
|
64
|
+
return '{}.{}::{}'.format(
|
|
65
|
+
value._meta.app_label, value._meta.model_name, value.pk
|
|
66
|
+
)
|
|
67
|
+
if hasattr(value, 'model'):
|
|
68
|
+
value = list(value.values_list('pk', flat=True))
|
|
69
|
+
if isinstance(value, list):
|
|
70
|
+
value = json.dumps(value)
|
|
71
|
+
return '{}::{}'.format(type(value).__name__, value)
|
|
72
|
+
return None
|
|
73
|
+
|
|
74
|
+
|
|
75
|
+
class GenericFieldDescriptor(DeferredAttribute):
|
|
76
|
+
def __get__(self, instance, cls=None):
|
|
77
|
+
obj = super().__get__(instance, cls=cls)
|
|
78
|
+
if isinstance(obj.value, Model):
|
|
79
|
+
return GenericModelWrapper(obj.value)
|
|
80
|
+
return obj.get_value()
|
|
81
|
+
|
|
82
|
+
def __set__(self, instance, value):
|
|
83
|
+
instance.__dict__[self.field.attname] = GenericValue(value)
|
|
84
|
+
|
|
85
|
+
|
|
86
|
+
class GenericField(CharField):
|
|
87
|
+
descriptor_class = GenericFieldDescriptor
|
|
88
|
+
|
|
89
|
+
def __init__(self, *args, max_length=255, null=True, **kwargs):
|
|
90
|
+
super().__init__(*args, max_length=max_length, null=null, **kwargs)
|
|
91
|
+
|
|
92
|
+
def get_prep_value(self, value):
|
|
93
|
+
if value is not None:
|
|
94
|
+
if isinstance(value, GenericValue):
|
|
95
|
+
value = value.dumps()
|
|
96
|
+
else:
|
|
97
|
+
value = GenericValue(value).dumps()
|
|
98
|
+
return value
|
|
@@ -2,27 +2,34 @@ from uuid import uuid1
|
|
|
2
2
|
from django.db.models import Model as DjangoModel
|
|
3
3
|
from django.db.models import *
|
|
4
4
|
from django.utils.translation import gettext_lazy as _
|
|
5
|
+
from . import generic
|
|
5
6
|
from .. import ModelMixin
|
|
6
7
|
|
|
8
|
+
GenericField = generic.GenericField
|
|
9
|
+
|
|
7
10
|
class CharField(CharField):
|
|
8
11
|
def __init__(self, *args, **kwargs):
|
|
9
12
|
self.mask = kwargs.pop('mask', None)
|
|
13
|
+
self.pick = kwargs.pop('pick', False)
|
|
10
14
|
kwargs.setdefault('max_length', 255)
|
|
11
15
|
super().__init__(*args, **kwargs)
|
|
12
16
|
|
|
13
17
|
def formfield(self, *args, **kwargs):
|
|
14
18
|
field = super().formfield(*args, **kwargs)
|
|
15
19
|
field.mask = self.mask
|
|
20
|
+
field.pick = self.pick
|
|
16
21
|
return field
|
|
17
22
|
|
|
18
23
|
class IntegerField(IntegerField):
|
|
19
24
|
def __init__(self, *args, **kwargs):
|
|
20
25
|
self.mask = kwargs.pop('mask', None)
|
|
26
|
+
self.pick = kwargs.pop('pick', False)
|
|
21
27
|
super().__init__(*args, **kwargs)
|
|
22
28
|
|
|
23
29
|
def formfield(self, *args, **kwargs):
|
|
24
30
|
field = super().formfield(*args, **kwargs)
|
|
25
31
|
field.mask = self.mask
|
|
32
|
+
field.pick = self.pick
|
|
26
33
|
return field
|
|
27
34
|
|
|
28
35
|
class ForeignKey(ForeignKey):
|
|
@@ -138,3 +145,11 @@ class ImageField(ImageField):
|
|
|
138
145
|
class Model(DjangoModel, ModelMixin):
|
|
139
146
|
class Meta:
|
|
140
147
|
abstract = True
|
|
148
|
+
|
|
149
|
+
class Filter:
|
|
150
|
+
|
|
151
|
+
def get_label(self):
|
|
152
|
+
return None
|
|
153
|
+
|
|
154
|
+
def choices(self, queryset):
|
|
155
|
+
return queryset
|