slthcore 0.0.3__tar.gz → 0.0.5__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.

Files changed (82) hide show
  1. {slthcore-0.0.3/slthcore.egg-info → slthcore-0.0.5}/PKG-INFO +1 -1
  2. {slthcore-0.0.3 → slthcore-0.0.5}/setup.py +1 -1
  3. {slthcore-0.0.3 → slthcore-0.0.5}/slth/__init__.py +86 -1
  4. slthcore-0.0.5/slth/cmd/configure/__main__.py +88 -0
  5. {slthcore-0.0.3/slth/cmd/init/boilerplate/django/project → slthcore-0.0.5/slth/cmd/init/boilerplate/backend/api}/asgi.py +1 -1
  6. slthcore-0.0.5/slth/cmd/init/boilerplate/backend/api/endpoints.py +1 -0
  7. {slthcore-0.0.3/slth/cmd/init/boilerplate/django/project → slthcore-0.0.5/slth/cmd/init/boilerplate/backend/api}/settings.py +4 -16
  8. {slthcore-0.0.3/slth/cmd/init/boilerplate/django/project → slthcore-0.0.5/slth/cmd/init/boilerplate/backend/api}/wsgi.py +1 -1
  9. {slthcore-0.0.3/slth/cmd/init/boilerplate/django → slthcore-0.0.5/slth/cmd/init/boilerplate/backend}/application.yml +1 -1
  10. slthcore-0.0.5/slth/cmd/init/boilerplate/backend/entrypoint.sh +3 -0
  11. {slthcore-0.0.3/slth/cmd/init/boilerplate/django → slthcore-0.0.5/slth/cmd/init/boilerplate/backend}/manage.py +1 -1
  12. slthcore-0.0.5/slth/cmd/init/boilerplate/backend/requirements.txt +2 -0
  13. {slthcore-0.0.3 → slthcore-0.0.5}/slth/cmd/init/boilerplate/docker-compose.yml +1 -1
  14. {slthcore-0.0.3/slth/cmd/init/boilerplate/reactjs → slthcore-0.0.5/slth/cmd/init/boilerplate/frontend}/vite.config.js +1 -1
  15. {slthcore-0.0.3 → slthcore-0.0.5}/slth/components.py +1 -1
  16. slthcore-0.0.5/slth/db/generic.py +98 -0
  17. {slthcore-0.0.3 → slthcore-0.0.5}/slth/db/models.py +11 -0
  18. {slthcore-0.0.3 → slthcore-0.0.5}/slth/endpoints.py +140 -44
  19. {slthcore-0.0.3 → slthcore-0.0.5}/slth/factory.py +1 -1
  20. {slthcore-0.0.3 → slthcore-0.0.5}/slth/forms.py +3 -2
  21. slthcore-0.0.5/slth/migrations/0007_deletion_log.py +49 -0
  22. {slthcore-0.0.3 → slthcore-0.0.5}/slth/models.py +123 -0
  23. {slthcore-0.0.3 → slthcore-0.0.5}/slth/queryset.py +33 -25
  24. {slthcore-0.0.3 → slthcore-0.0.5}/slth/serializer.py +9 -3
  25. {slthcore-0.0.3 → slthcore-0.0.5}/slth/static/js/slth.min.js +54 -16
  26. slthcore-0.0.5/slth/tasks.py +70 -0
  27. {slthcore-0.0.3 → slthcore-0.0.5}/slth/tests.py +3 -0
  28. slthcore-0.0.5/slth/threadlocal.py +3 -0
  29. {slthcore-0.0.3 → slthcore-0.0.5}/slth/utils.py +7 -1
  30. {slthcore-0.0.3 → slthcore-0.0.5}/slth/views.py +12 -3
  31. {slthcore-0.0.3 → slthcore-0.0.5/slthcore.egg-info}/PKG-INFO +1 -1
  32. {slthcore-0.0.3 → slthcore-0.0.5}/slthcore.egg-info/SOURCES.txt +20 -16
  33. slthcore-0.0.3/slth/cmd/init/boilerplate/django/entrypoint.sh +0 -3
  34. slthcore-0.0.3/slth/cmd/init/boilerplate/django/project/app/endpoints.py +0 -1
  35. slthcore-0.0.3/slth/cmd/init/boilerplate/django/requirements.txt +0 -1
  36. slthcore-0.0.3/slth/migrations/__init__.py +0 -0
  37. {slthcore-0.0.3 → slthcore-0.0.5}/MANIFEST.in +0 -0
  38. {slthcore-0.0.3 → slthcore-0.0.5}/setup.cfg +0 -0
  39. {slthcore-0.0.3 → slthcore-0.0.5}/slth/cmd/init/__main__.py +0 -0
  40. {slthcore-0.0.3 → slthcore-0.0.5}/slth/cmd/init/boilerplate/.DS_Store +0 -0
  41. {slthcore-0.0.3 → slthcore-0.0.5}/slth/cmd/init/boilerplate/.gitignore +0 -0
  42. {slthcore-0.0.3/slth/cmd/init/boilerplate/django/project → slthcore-0.0.5/slth/cmd/init/boilerplate/backend/api}/__init__.py +0 -0
  43. {slthcore-0.0.3/slth/cmd/init/boilerplate/django/project/app → slthcore-0.0.5/slth/cmd/init/boilerplate/backend/api}/models.py +0 -0
  44. {slthcore-0.0.3/slth/cmd/init/boilerplate/django/project/app → slthcore-0.0.5/slth/cmd/init/boilerplate/backend/api}/tests.py +0 -0
  45. {slthcore-0.0.3/slth/cmd/init/boilerplate/django/project → slthcore-0.0.5/slth/cmd/init/boilerplate/backend/api}/urls.py +0 -0
  46. {slthcore-0.0.3 → slthcore-0.0.5}/slth/cmd/init/boilerplate/base.env +0 -0
  47. {slthcore-0.0.3/slth/cmd/init/boilerplate/reactjs → slthcore-0.0.5/slth/cmd/init/boilerplate/frontend}/package.json +0 -0
  48. {slthcore-0.0.3/slth/cmd/init/boilerplate/reactjs → slthcore-0.0.5/slth/cmd/init/boilerplate/frontend}/src/main.jsx +0 -0
  49. {slthcore-0.0.3 → slthcore-0.0.5}/slth/cmd/init/boilerplate/local.env +0 -0
  50. {slthcore-0.0.3 → slthcore-0.0.5}/slth/cmd/init/boilerplate/run.sh +0 -0
  51. {slthcore-0.0.3 → slthcore-0.0.5}/slth/cmd/init/boilerplate/selenium/run.sh +0 -0
  52. {slthcore-0.0.3 → slthcore-0.0.5}/slth/cmd/init/boilerplate/test.sh +0 -0
  53. {slthcore-0.0.3 → slthcore-0.0.5}/slth/db/__init__.py +0 -0
  54. {slthcore-0.0.3 → slthcore-0.0.5}/slth/exceptions.py +0 -0
  55. {slthcore-0.0.3/slth/cmd/init/boilerplate/django/project/app → slthcore-0.0.5/slth/management}/__init__.py +0 -0
  56. {slthcore-0.0.3/slth/management → slthcore-0.0.5/slth/management/commands}/__init__.py +0 -0
  57. {slthcore-0.0.3 → slthcore-0.0.5}/slth/management/commands/integration_test.py +0 -0
  58. {slthcore-0.0.3 → slthcore-0.0.5}/slth/management/commands/sync.py +0 -0
  59. {slthcore-0.0.3 → slthcore-0.0.5}/slth/migrations/0001_initial.py +0 -0
  60. {slthcore-0.0.3 → slthcore-0.0.5}/slth/migrations/0002_email_role_pushsubscription_error.py +0 -0
  61. {slthcore-0.0.3 → slthcore-0.0.5}/slth/migrations/0003_rename_photo_profile_alter_profile_options.py +0 -0
  62. {slthcore-0.0.3 → slthcore-0.0.5}/slth/migrations/0004_alter_profile_photo.py +0 -0
  63. {slthcore-0.0.3 → slthcore-0.0.5}/slth/migrations/0005_alter_profile_photo.py +0 -0
  64. {slthcore-0.0.3 → slthcore-0.0.5}/slth/migrations/0006_user.py +0 -0
  65. {slthcore-0.0.3/slth/management/commands → slthcore-0.0.5/slth/migrations}/__init__.py +0 -0
  66. {slthcore-0.0.3 → slthcore-0.0.5}/slth/notifications.py +0 -0
  67. {slthcore-0.0.3 → slthcore-0.0.5}/slth/oauth.py +0 -0
  68. {slthcore-0.0.3 → slthcore-0.0.5}/slth/permissions.py +0 -0
  69. {slthcore-0.0.3 → slthcore-0.0.5}/slth/roles.py +0 -0
  70. {slthcore-0.0.3 → slthcore-0.0.5}/slth/selenium/__init__.py +0 -0
  71. {slthcore-0.0.3 → slthcore-0.0.5}/slth/selenium/browser.py +0 -0
  72. {slthcore-0.0.3 → slthcore-0.0.5}/slth/static/.DS_Store +0 -0
  73. {slthcore-0.0.3 → slthcore-0.0.5}/slth/static/css/.DS_Store +0 -0
  74. {slthcore-0.0.3 → slthcore-0.0.5}/slth/static/css/slth.css +0 -0
  75. {slthcore-0.0.3 → slthcore-0.0.5}/slth/static/js/index.min.js +0 -0
  76. {slthcore-0.0.3 → slthcore-0.0.5}/slth/static/js/react.min.js +0 -0
  77. {slthcore-0.0.3 → slthcore-0.0.5}/slth/statistics.py +0 -0
  78. {slthcore-0.0.3 → slthcore-0.0.5}/slth/templates/index.html +0 -0
  79. {slthcore-0.0.3 → slthcore-0.0.5}/slth/templates/service-worker.js +0 -0
  80. {slthcore-0.0.3 → slthcore-0.0.5}/slth/urls.py +0 -0
  81. {slthcore-0.0.3 → slthcore-0.0.5}/slthcore.egg-info/dependency_links.txt +0 -0
  82. {slthcore-0.0.3 → slthcore-0.0.5}/slthcore.egg-info/top_level.txt +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.1
2
2
  Name: slthcore
3
- Version: 0.0.3
3
+ Version: 0.0.5
4
4
  Summary: API generator based on yml file
5
5
  Home-page: https://github.com/brenokcc
6
6
  Author: Breno Silva
@@ -5,7 +5,7 @@ install_requires = []
5
5
 
6
6
  setup(
7
7
  name='slthcore',
8
- version='0.0.3',
8
+ version='0.0.5',
9
9
  packages=find_packages(),
10
10
  install_requires=install_requires,
11
11
  include_package_data=True,
@@ -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')
@@ -11,6 +11,6 @@ import os
11
11
 
12
12
  from django.core.asgi import get_asgi_application
13
13
 
14
- os.environ.setdefault('DJANGO_SETTINGS_MODULE', 'project.settings')
14
+ os.environ.setdefault('DJANGO_SETTINGS_MODULE', 'api.settings')
15
15
 
16
16
  application = get_asgi_application()
@@ -0,0 +1 @@
1
+ from slth import endpoints
@@ -1,17 +1,5 @@
1
1
  import os
2
2
  import sys
3
- """
4
- Django settings for app project.
5
-
6
- Generated by 'django-admin startproject' using Django 4.1.
7
-
8
- For more information on this file, see
9
- https://docs.djangoproject.com/en/4.1/topics/settings/
10
-
11
- For the full list of settings and their values, see
12
- https://docs.djangoproject.com/en/4.1/ref/settings/
13
- """
14
-
15
3
  from pathlib import Path
16
4
 
17
5
  # Build paths inside the project like this: BASE_DIR / 'subdir'.
@@ -31,9 +19,9 @@ DEBUG = True
31
19
  # Application definition
32
20
 
33
21
  INSTALLED_APPS = [
22
+ 'api',
23
+ 'slth',
34
24
  'slthlib',
35
- 'slthcore',
36
- 'project.app',
37
25
  'whitenoise.runserver_nostatic',
38
26
  'django.contrib.auth',
39
27
  'django.contrib.contenttypes',
@@ -53,7 +41,7 @@ MIDDLEWARE = [
53
41
  'django.middleware.clickjacking.XFrameOptionsMiddleware',
54
42
  ]
55
43
 
56
- ROOT_URLCONF = 'project.urls'
44
+ ROOT_URLCONF = 'api.urls'
57
45
 
58
46
  TEMPLATES = [
59
47
  {
@@ -71,7 +59,7 @@ TEMPLATES = [
71
59
  },
72
60
  ]
73
61
 
74
- WSGI_APPLICATION = 'project.wsgi.application'
62
+ WSGI_APPLICATION = 'api.wsgi.application'
75
63
 
76
64
 
77
65
  # Database
@@ -11,6 +11,6 @@ import os
11
11
 
12
12
  from django.core.wsgi import get_wsgi_application
13
13
 
14
- os.environ.setdefault('DJANGO_SETTINGS_MODULE', 'project.settings')
14
+ os.environ.setdefault('DJANGO_SETTINGS_MODULE', 'api.settings')
15
15
 
16
16
  application = get_wsgi_application()
@@ -4,7 +4,7 @@ application:
4
4
  subtitle: Take your time!
5
5
  icon: /static/images/logo.png
6
6
  logo: /static/images/logo.png
7
- version: 1.0.5
7
+ version: 0.0.1
8
8
  oauth:
9
9
  govbr:
10
10
  name: gov.br
@@ -0,0 +1,3 @@
1
+ #!/bin/sh
2
+ python manage.py sync
3
+ gunicorn api.wsgi -b 0.0.0.0:8000 -w 3 --log-level info --reload --timeout 3600
@@ -6,7 +6,7 @@ import sys
6
6
 
7
7
  def main():
8
8
  """Run administrative tasks."""
9
- os.environ.setdefault('DJANGO_SETTINGS_MODULE', 'project.settings')
9
+ os.environ.setdefault('DJANGO_SETTINGS_MODULE', 'api.settings')
10
10
  try:
11
11
  from django.core.management import execute_from_command_line
12
12
  except ImportError as exc:
@@ -0,0 +1,2 @@
1
+ slthlib
2
+ slthcore
@@ -7,7 +7,7 @@ services:
7
7
  ports:
8
8
  - "8000:8000"
9
9
  build:
10
- context: django
10
+ context: backend
11
11
  dockerfile: Dockerfile
12
12
  restart: always
13
13
  volumes:
@@ -12,7 +12,7 @@ export default defineConfig({
12
12
  if (id.includes("slth/src/reactjs/src/")) return "slth";
13
13
  if (id.includes("service-worker")) return "service-worker";
14
14
  },
15
- dir: "../django/project/app/static",
15
+ dir: "../backend/api/static",
16
16
  entryFileNames: `[name].js`,
17
17
  chunkFileNames: `[name].js`,
18
18
  assetFileNames: `[name].[ext]`,
@@ -289,7 +289,7 @@ class Scheduler(dict):
289
289
  def __init__(
290
290
  self,
291
291
  start_time=7,
292
- end_time=20,
292
+ end_time=23,
293
293
  chucks=2,
294
294
  start_day=None,
295
295
  days=14,
@@ -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,8 +2,11 @@ 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)
@@ -138,3 +141,11 @@ class ImageField(ImageField):
138
141
  class Model(DjangoModel, ModelMixin):
139
142
  class Meta:
140
143
  abstract = True
144
+
145
+ class Filter:
146
+
147
+ def get_label(self):
148
+ return None
149
+
150
+ def choices(self, queryset):
151
+ return queryset