Skip to content

Commit 922b02e

Browse files
authored
Maintenance (#36)
* Updated dependencies, added support for Python 3.11 and Django 4.1, dropped support for Django 2. * Flake 8 doesn't support Python < 3.8. * CI action to create GH release. * Updated example app, docker-compose, Dockerfile, added black, some small fixes. * Dropped Python 3.7 support.
1 parent c260c3e commit 922b02e

File tree

23 files changed

+418
-300
lines changed

23 files changed

+418
-300
lines changed

.github/workflows/ci.yml

Lines changed: 20 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -15,12 +15,14 @@ jobs:
1515
strategy:
1616
fail-fast: false
1717
matrix:
18-
python: [ "3.7", "3.8", "3.9", "3.10" ]
19-
django: [ "2.2.26", "3.2.11", "4.0.1" ]
18+
python: ["3.8", "3.9", "3.10", "3.11"]
19+
django: ["3.2.16", "4.0.8", "4.1.4" ]
2020
exclude:
21-
# excludes Django 4 on Python 3.7
22-
- python: 3.7
23-
django: 4.0.1
21+
# Excludes Python 3.11 for Django < 4.1
22+
- python: 3.11
23+
django: 3.2.16
24+
- python: 3.11
25+
django: 4.0.8
2426

2527
services:
2628
postgres:
@@ -58,7 +60,7 @@ jobs:
5860
- uses: actions/checkout@master
5961
- uses: actions/setup-python@v2
6062
with:
61-
python-version: 3.9
63+
python-version: 3.10
6264
architecture: x64
6365
- run: pip install --upgrade pip
6466
- run: pip install poetry
@@ -70,3 +72,15 @@ jobs:
7072
- uses: pypa/gh-action-pypi-publish@master
7173
with:
7274
password: ${{ secrets.pypi_password }}
75+
- uses: actions/create-release@v1
76+
env:
77+
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
78+
with:
79+
tag_name: ${{ github.ref }}
80+
release_name: ${{ github.ref }}
81+
body: |
82+
Changes:
83+
- ...
84+
- ...
85+
draft: true
86+
prerelease: false

.gitignore

Lines changed: 0 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -25,9 +25,6 @@ output/*/index.html
2525
# Sphinx
2626
docs/_build
2727

28-
# Docker-compose
29-
/data
30-
3128
# Poetry
3229
poetry.lock
3330
/dist

Dockerfile

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,9 @@
1-
FROM python:3.7
1+
FROM python:3.10
22

33
ENV PYTHONUNBUFFERED 1
44

55
RUN mkdir /code
66
WORKDIR /code
7-
COPY . /code/
87

98
RUN pip install poetry
109
RUN poetry config virtualenvs.create false

README.rst

Lines changed: 23 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -17,24 +17,25 @@ in Django projects.
1717
Motivation
1818
----------
1919

20-
`JSON types <https://www.postgresql.org/docs/10/datatype-json.html>`_ is a cool feature of
21-
Postgres that allows combining both relational and non-relational approaches to storing data. In most cases
22-
it leads to a simpler database design.
23-
24-
The `JSONField <https://docs.djangoproject.com/en/3.1/ref/contrib/postgres/fields/#jsonfield>`_ in Django provides a
25-
nice way of using **json** and **jsonb** Postgres types in the model classes. ORM can even allow you to perform queries
26-
against the data stored inside the JSON structure. Moreover, it is possible to use
27-
`GIN indexes <https://www.postgresql.org/docs/12/datatype-json.html#JSON-INDEXING>`_ with **jsonb** types in Postgres, what
28-
makes it a useful tool in the application design and opens a wide range of possibilities such as polymorphic behaviour,
29-
storing complex hierarchies and lists of related entities, etc.
30-
31-
However, the main limitation of JSONField in Django is a lack of good support of UI for JSON structures as defining JSON objects
20+
`JSON types <https://www.postgresql.org/docs/10/datatype-json.html>`_ in Postgres allow combining both relational
21+
and non-relational approaches to storing data. That can lead to a simpler database design in the most cases.
22+
23+
Django provides ORM support for JSON types in Postgres and other databases via the
24+
`JSONField model field <https://docs.djangoproject.com/en/4.1/ref/models/fields/#django.db.models.JSONField>`_. Also the
25+
`JSONField form field <https://docs.djangoproject.com/en/4.1/ref/forms/fields/#jsonfield>`_ allows basic support of JSON in forms.
26+
Django ORM even `allows querying <https://docs.djangoproject.com/en/4.1/topics/db/queries/#querying-jsonfield>`_ against the data stored
27+
inside the JSON structures. Moreover, it is possible to improve performance of these queries using
28+
`GIN indexes <https://www.postgresql.org/docs/15/datatype-json.html#JSON-INDEXING>`_ with **jsonb** types
29+
`in Django <https://docs.djangoproject.com/en/4.1/ref/contrib/postgres/indexes/#ginindex>`_, which
30+
makes opens up a wide range of possibilities for simplifying application design, such as polymorphic collections, storing complex hierarchies in JSON, lists of related entities, etc.
31+
32+
However, the main limitation of JSONField in Django is the lack of good support of UI for JSON structures as defining JSON objects
3233
inside the textarea inputs is not practical for most use cases. django-reactive tries to address this problem by offering an
3334
integration between JSONField and the awesome `react-jsonschema-form <https://github.com/mozilla-services/react-jsonschema-form>`_
3435
(RJSF) JavaScript library.
3536

3637
django-reactive also uses Python `jsonschema <https://github.com/Julian/jsonschema>` library for backend validation. Such integration
37-
can significantly reduce the amount of work you need to contribute into building custom forms for JSONField types.
38+
can significantly reduce the amount of work needed for building custom forms for JSONField types.
3839

3940
In most cases it only requires a JSON schema configuration for such field and optionally a UI schema
4041
to modify some representation parameters.
@@ -142,7 +143,7 @@ Running the example
142143

143144
Build the docker image for the Django application in `example/`:
144145

145-
* Run `docker-compose up -d`
146+
* Run `docker compose up -d`
146147

147148
This will automatically create the database, run migrations, import the default superuser, and run the Django development server on `http://127.0.0.1:8000`.
148149

@@ -254,19 +255,21 @@ Features
254255
* React, RJSF and other JS assets are bundled with the package.
255256
* Integration with default Django admin theme.
256257
* Backend and frontend validation.
257-
* Configurable static media assets
258-
* Dynamic schema mutation in widget renders
258+
* Configurable static media assets.
259+
* Dynamic schema mutation in widget renders.
259260

260261
Limitations
261262
-----------
262263

263264
* `Additional properties <https://github.com/mozilla-services/react-jsonschema-form#expandable-option>`_ ( a feature of RJSF) is not supported.
264265

265-
To implement this behaviour you can define an array schema with one property serving as a key of the object and do
266-
transformation in your JSON class. An example will be provided later.
266+
To implement this behavior you can define an array schema with one property serving as a key of the object and do
267+
transformation in the Django form.
268+
269+
* An outdated version (1.8) of RJSF is used in this project. Not all features of RJSF 1.8 are compatible with JSON Schema 4.0. Please, refer to the documentation if any issues.
267270

268271
Future development
269272
------------------
270273

271-
* Display description as tooltips
272-
* Polish styles and HTML generated by **RJSF**
274+
* At the moment there is no plans to add new features or support a newer version of RJSF.
275+
* Probably, it is a good idea to replace RJSF with a more Django-friendly solution. It would require significant development effort though, that's why the idea is put on back burner at the moment.

bin/wait-for

Lines changed: 0 additions & 79 deletions
This file was deleted.

bin/wait_for_it.py

Lines changed: 82 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,82 @@
1+
#!/bin/usr/env python
2+
3+
from optparse import OptionParser
4+
import socket
5+
import time
6+
import sys
7+
8+
9+
class OptionException(Exception):
10+
def __init__(self, value):
11+
self.value = value
12+
13+
14+
class wait_for_app:
15+
def log(self, loginfo):
16+
if self.options.quiet is not False:
17+
print(loginfo)
18+
19+
def build_log(self, type, app, time=0):
20+
# 1=enable_timeout,2=disable_timeout,3=success_msg,4=unavailable,5=timeout_msg
21+
loginfo = {
22+
1: "%s: waiting %d seconds for %s" % (sys.argv[0], time, app),
23+
2: "%s: waiting for %s without a timeout" % (sys.argv[0], app),
24+
3: "%s: %s is available after %d seconds" % (sys.argv[0], app, time),
25+
4: "%s: %s is unavailable" % (sys.argv[0], app),
26+
5: "%s: timeout occurred after waiting %d seconds for %s" % (sys.argv[0], time, app),
27+
}.get(type)
28+
return loginfo
29+
30+
def wait_for(self, host, port, timeout):
31+
self.app = ("%s:%d") % (host, port)
32+
sk = socket.socket()
33+
logmsg = self.build_log(2, self.app, timeout)
34+
if timeout != 0:
35+
logmsg = self.build_log(1, self.app, timeout)
36+
sk.settimeout(timeout)
37+
self.log(logmsg)
38+
start_ts = int(time.time())
39+
sk.connect((host, port))
40+
end_ts = int(time.time())
41+
diff_ts = end_ts - start_ts
42+
logmsg = self.build_log(3, self.app, diff_ts)
43+
self.log(logmsg)
44+
45+
def get_parser(self):
46+
parser = OptionParser()
47+
parser.add_option("-a", "--address", dest="address", help="Host or IP under test")
48+
parser.add_option("-p", "--port", dest="port", help="TCP port under test")
49+
parser.add_option(
50+
"-t", "--timeout", dest="timeout", default="15", help="Timeout in seconds, zero for no timeout"
51+
)
52+
parser.add_option("-q", "--quiet", dest="quiet", action="store_false", help="Don't output any status messages")
53+
return parser
54+
55+
def verify_options(self):
56+
if self.options.address is None:
57+
raise OptionException("The address must be set!")
58+
elif self.options.port is None:
59+
raise OptionException("The port must be set!")
60+
elif str(self.options.port).isnumeric() is False:
61+
raise OptionException("The value of port must be number!")
62+
63+
def start_up(self):
64+
try:
65+
parser = self.get_parser()
66+
self.options, self.args = parser.parse_args()
67+
self.verify_options()
68+
self.wait_for(self.options.address, int(self.options.port), int(self.options.timeout))
69+
except OptionException as err:
70+
print(err)
71+
parser.print_help()
72+
except socket.timeout as err:
73+
logmsg = self.build_log(5, self.app, int(self.options.timeout))
74+
self.log(logmsg)
75+
except ConnectionRefusedError as err:
76+
logmsg = self.build_log(4, self.app)
77+
self.log(logmsg)
78+
79+
80+
if __name__ == "__main__":
81+
w = wait_for_app()
82+
w.start_up()

django_reactive/__init__.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1 +1 @@
1-
default_app_config = 'django_reactive.apps.DjangoReactJsonSchemaFormConfig'
1+
default_app_config = "django_reactive.apps.DjangoReactJsonSchemaFormConfig"

django_reactive/apps.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3,4 +3,4 @@
33

44

55
class DjangoReactJsonSchemaFormConfig(AppConfig):
6-
name = 'django_reactive'
6+
name = "django_reactive"

django_reactive/fields.py

Lines changed: 1 addition & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -12,15 +12,7 @@
1212

1313

1414
class ReactJSONSchemaField(BaseJSONField):
15-
def __init__(
16-
self,
17-
schema=None,
18-
ui_schema=None,
19-
on_render=None,
20-
extra_css=None,
21-
extra_js=None,
22-
**kwargs
23-
):
15+
def __init__(self, schema=None, ui_schema=None, on_render=None, extra_css=None, extra_js=None, **kwargs):
2416
kwargs.setdefault("default", dict)
2517
super(ReactJSONSchemaField, self).__init__(**kwargs)
2618
self.schema = schema

docker-compose.yaml

Lines changed: 6 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -3,10 +3,10 @@ version: "3.5"
33
services:
44

55
db:
6-
image: postgres:12-alpine
6+
image: postgres:15-alpine
77
restart: always
88
volumes:
9-
- "./data/pgdata:/var/lib/postgresql/data"
9+
- db_vol:/var/lib/postgresql/data
1010
ports:
1111
- 5432:5432
1212
environment:
@@ -24,7 +24,7 @@ services:
2424
web:
2525
build: .
2626
command: >
27-
sh -c './wait-for db:5432 --
27+
sh -c 'python /code/bin/wait_for_it.py -a db -p 5432 --
2828
python example/manage.py makemigrations &&
2929
python example/manage.py migrate &&
3030
python example/manage.py loaddata admin &&
@@ -35,3 +35,6 @@ services:
3535
- "8000:8000"
3636
depends_on:
3737
- db
38+
39+
volumes:
40+
db_vol: ~

0 commit comments

Comments
 (0)