drf-react-by-schema 0.27.0__tar.gz → 0.27.2__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.
- {drf_react_by_schema-0.27.0 → drf_react_by_schema-0.27.2}/PKG-INFO +31 -5
- {drf_react_by_schema-0.27.0 → drf_react_by_schema-0.27.2}/README.rst +30 -4
- {drf_react_by_schema-0.27.0 → drf_react_by_schema-0.27.2}/drf_react_by_schema/fields/array.py +5 -1
- {drf_react_by_schema-0.27.0 → drf_react_by_schema-0.27.2}/drf_react_by_schema/fields/boolean.py +5 -1
- {drf_react_by_schema-0.27.0 → drf_react_by_schema-0.27.2}/drf_react_by_schema/fields/date.py +14 -2
- {drf_react_by_schema-0.27.0 → drf_react_by_schema-0.27.2}/drf_react_by_schema/fields/file.py +6 -0
- {drf_react_by_schema-0.27.0 → drf_react_by_schema-0.27.2}/drf_react_by_schema/fields/geometry.py +5 -1
- {drf_react_by_schema-0.27.0 → drf_react_by_schema-0.27.2}/drf_react_by_schema/fields/number.py +6 -0
- {drf_react_by_schema-0.27.0 → drf_react_by_schema-0.27.2}/drf_react_by_schema/fields/relational.py +9 -0
- {drf_react_by_schema-0.27.0 → drf_react_by_schema-0.27.2}/drf_react_by_schema/fields/serializers.py +10 -1
- {drf_react_by_schema-0.27.0 → drf_react_by_schema-0.27.2}/drf_react_by_schema/fields/text.py +13 -2
- {drf_react_by_schema-0.27.0 → drf_react_by_schema-0.27.2}/drf_react_by_schema/schema_bundle.py +108 -3
- {drf_react_by_schema-0.27.0 → drf_react_by_schema-0.27.2}/drf_react_by_schema/tests.py +117 -0
- {drf_react_by_schema-0.27.0 → drf_react_by_schema-0.27.2}/drf_react_by_schema/views.py +5 -0
- {drf_react_by_schema-0.27.0 → drf_react_by_schema-0.27.2}/drf_react_by_schema.egg-info/PKG-INFO +31 -5
- {drf_react_by_schema-0.27.0 → drf_react_by_schema-0.27.2}/setup.cfg +1 -1
- {drf_react_by_schema-0.27.0 → drf_react_by_schema-0.27.2}/LICENSE +0 -0
- {drf_react_by_schema-0.27.0 → drf_react_by_schema-0.27.2}/MANIFEST.in +0 -0
- {drf_react_by_schema-0.27.0 → drf_react_by_schema-0.27.2}/drf_react_by_schema/__init__.py +0 -0
- {drf_react_by_schema-0.27.0 → drf_react_by_schema-0.27.2}/drf_react_by_schema/admin.py +0 -0
- {drf_react_by_schema-0.27.0 → drf_react_by_schema-0.27.2}/drf_react_by_schema/apps.py +0 -0
- {drf_react_by_schema-0.27.0 → drf_react_by_schema-0.27.2}/drf_react_by_schema/auth.py +0 -0
- {drf_react_by_schema-0.27.0 → drf_react_by_schema-0.27.2}/drf_react_by_schema/autocomplete_serializers.py +0 -0
- {drf_react_by_schema-0.27.0 → drf_react_by_schema-0.27.2}/drf_react_by_schema/autocomplete_viewsets.py +0 -0
- {drf_react_by_schema-0.27.0 → drf_react_by_schema-0.27.2}/drf_react_by_schema/fields/__init__.py +0 -0
- {drf_react_by_schema-0.27.0 → drf_react_by_schema-0.27.2}/drf_react_by_schema/filters.py +0 -0
- {drf_react_by_schema-0.27.0 → drf_react_by_schema-0.27.2}/drf_react_by_schema/management/__init__.py +0 -0
- {drf_react_by_schema-0.27.0 → drf_react_by_schema-0.27.2}/drf_react_by_schema/management/commands/__init__.py +0 -0
- {drf_react_by_schema-0.27.0 → drf_react_by_schema-0.27.2}/drf_react_by_schema/management/commands/sync_react.py +0 -0
- {drf_react_by_schema-0.27.0 → drf_react_by_schema-0.27.2}/drf_react_by_schema/metadata.py +0 -0
- {drf_react_by_schema-0.27.0 → drf_react_by_schema-0.27.2}/drf_react_by_schema/migrations/__init__.py +0 -0
- {drf_react_by_schema-0.27.0 → drf_react_by_schema-0.27.2}/drf_react_by_schema/mixins.py +0 -0
- {drf_react_by_schema-0.27.0 → drf_react_by_schema-0.27.2}/drf_react_by_schema/models.py +0 -0
- {drf_react_by_schema-0.27.0 → drf_react_by_schema-0.27.2}/drf_react_by_schema/pagination.py +0 -0
- {drf_react_by_schema-0.27.0 → drf_react_by_schema-0.27.2}/drf_react_by_schema/patches/__init__.py +0 -0
- {drf_react_by_schema-0.27.0 → drf_react_by_schema-0.27.2}/drf_react_by_schema/patches/drf_serializers.py +0 -0
- {drf_react_by_schema-0.27.0 → drf_react_by_schema-0.27.2}/drf_react_by_schema/renderers.py +0 -0
- {drf_react_by_schema-0.27.0 → drf_react_by_schema-0.27.2}/drf_react_by_schema/serializers.py +0 -0
- {drf_react_by_schema-0.27.0 → drf_react_by_schema-0.27.2}/drf_react_by_schema/typescript_generator.py +0 -0
- {drf_react_by_schema-0.27.0 → drf_react_by_schema-0.27.2}/drf_react_by_schema/urls.py +0 -0
- {drf_react_by_schema-0.27.0 → drf_react_by_schema-0.27.2}/drf_react_by_schema/utils.py +0 -0
- {drf_react_by_schema-0.27.0 → drf_react_by_schema-0.27.2}/drf_react_by_schema/utils_cache.py +0 -0
- {drf_react_by_schema-0.27.0 → drf_react_by_schema-0.27.2}/drf_react_by_schema/viewsets.py +0 -0
- {drf_react_by_schema-0.27.0 → drf_react_by_schema-0.27.2}/drf_react_by_schema.egg-info/SOURCES.txt +0 -0
- {drf_react_by_schema-0.27.0 → drf_react_by_schema-0.27.2}/drf_react_by_schema.egg-info/dependency_links.txt +0 -0
- {drf_react_by_schema-0.27.0 → drf_react_by_schema-0.27.2}/drf_react_by_schema.egg-info/requires.txt +0 -0
- {drf_react_by_schema-0.27.0 → drf_react_by_schema-0.27.2}/drf_react_by_schema.egg-info/top_level.txt +0 -0
- {drf_react_by_schema-0.27.0 → drf_react_by_schema-0.27.2}/setup.py +0 -0
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.1
|
|
2
2
|
Name: drf_react_by_schema
|
|
3
|
-
Version: 0.27.
|
|
3
|
+
Version: 0.27.2
|
|
4
4
|
Summary: A Django app to easily integrate with React from models.
|
|
5
5
|
Home-page: https://gitlab.com/eita/drf-react-by-schema/drf-react-by-schema-django
|
|
6
6
|
Author: Cooperativa EITA
|
|
@@ -317,16 +317,42 @@ object with:
|
|
|
317
317
|
version changes;
|
|
318
318
|
* ``drbs_version`` — the installed ``drf-react-by-schema`` version;
|
|
319
319
|
* ``system`` — apps, ``AUTH_USER_MODEL``, the ``DRF_REACT_BY_SCHEMA`` summary,
|
|
320
|
-
static permission hints (DRF defaults, global, per-model)
|
|
320
|
+
static permission hints (DRF defaults, global, per-model), request
|
|
321
321
|
``conventions`` (API base, pagination, ``ordering``/``search``/``sum_rows``
|
|
322
|
-
params, grid-filter operators)
|
|
322
|
+
params, grid-filter operators) and ``about`` (the project's ``ABOUT.md``,
|
|
323
|
+
trimmed — see below);
|
|
323
324
|
* ``endpoints`` — every generated route (list and nested) with its
|
|
324
325
|
``path``/``kind``/``verbose_name``/``parent_model``/``related_model``;
|
|
325
326
|
* ``models`` — each model's field ``schema`` (the same ``OPTIONS`` POST payload
|
|
326
|
-
the React app consumes), verbose names
|
|
327
|
-
``
|
|
327
|
+
the React app consumes), verbose names, generated TypeScript ``interface`` and
|
|
328
|
+
the model class ``docstring`` (trimmed);
|
|
328
329
|
* ``interfaces`` and ``enums`` — the generated TypeScript declarations.
|
|
329
330
|
|
|
331
|
+
Documentation in the bundle
|
|
332
|
+
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
|
333
|
+
Free-form, non-UI documentation is folded into the bundle so an assistant can
|
|
334
|
+
*understand* the system. It is kept out of the React artifacts on purpose:
|
|
335
|
+
``sync_react``'s ``schemas/*.json`` stay **byte-identical** to before.
|
|
336
|
+
|
|
337
|
+
* **Model docstrings** — a model class's *own* docstring (inherited docs are
|
|
338
|
+
ignored) is reported as ``models[<Model>].docstring``, trimmed at a word
|
|
339
|
+
boundary to ``schema_bundle.MODEL_DOCSTRING_MAX`` characters.
|
|
340
|
+
* **Field descriptions** — pass ``description="..."`` to any package field
|
|
341
|
+
(``CharField``, ``ForeignKey``, ``DecimalField``, ...); it is reported as
|
|
342
|
+
``models[<Model>].schema[<field>].description`` (trimmed to
|
|
343
|
+
``FIELD_DESCRIPTION_MAX``). This is **not** ``help_text``: ``help_text`` is
|
|
344
|
+
rendered to end users by ``drf-react-by-schema-js``, while ``description``
|
|
345
|
+
documents the field for the API client / assistant. Only values passed
|
|
346
|
+
explicitly are reported — the field type's own ``description`` is never.
|
|
347
|
+
* **Project ``ABOUT.md``** — a project-root ``ABOUT.md`` (``settings.BASE_DIR``)
|
|
348
|
+
is reported as ``system.about`` (trimmed to ``ABOUT_MAX``). Override the path
|
|
349
|
+
with ``DRF_REACT_BY_SCHEMA['ABOUT_PATH']``; a missing file yields ``null``.
|
|
350
|
+
Use it for *what the project is about* — not deploy/contribute instructions.
|
|
351
|
+
|
|
352
|
+
All three feed the ``stamp``, so changing them invalidates an MCP client's
|
|
353
|
+
cached copy; because the bundle is cached per process, a change also needs a
|
|
354
|
+
process restart to be picked up.
|
|
355
|
+
|
|
330
356
|
The endpoint sits behind the project's **existing auth** (the same permission
|
|
331
357
|
source used by the generated viewsets — ``DRF_REACT_BY_SCHEMA['PERMISSIONS']``,
|
|
332
358
|
defaulting to ``IsAuthenticated``; authentication comes from your DRF defaults,
|
|
@@ -290,16 +290,42 @@ object with:
|
|
|
290
290
|
version changes;
|
|
291
291
|
* ``drbs_version`` — the installed ``drf-react-by-schema`` version;
|
|
292
292
|
* ``system`` — apps, ``AUTH_USER_MODEL``, the ``DRF_REACT_BY_SCHEMA`` summary,
|
|
293
|
-
static permission hints (DRF defaults, global, per-model)
|
|
293
|
+
static permission hints (DRF defaults, global, per-model), request
|
|
294
294
|
``conventions`` (API base, pagination, ``ordering``/``search``/``sum_rows``
|
|
295
|
-
params, grid-filter operators)
|
|
295
|
+
params, grid-filter operators) and ``about`` (the project's ``ABOUT.md``,
|
|
296
|
+
trimmed — see below);
|
|
296
297
|
* ``endpoints`` — every generated route (list and nested) with its
|
|
297
298
|
``path``/``kind``/``verbose_name``/``parent_model``/``related_model``;
|
|
298
299
|
* ``models`` — each model's field ``schema`` (the same ``OPTIONS`` POST payload
|
|
299
|
-
the React app consumes), verbose names
|
|
300
|
-
``
|
|
300
|
+
the React app consumes), verbose names, generated TypeScript ``interface`` and
|
|
301
|
+
the model class ``docstring`` (trimmed);
|
|
301
302
|
* ``interfaces`` and ``enums`` — the generated TypeScript declarations.
|
|
302
303
|
|
|
304
|
+
Documentation in the bundle
|
|
305
|
+
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
|
306
|
+
Free-form, non-UI documentation is folded into the bundle so an assistant can
|
|
307
|
+
*understand* the system. It is kept out of the React artifacts on purpose:
|
|
308
|
+
``sync_react``'s ``schemas/*.json`` stay **byte-identical** to before.
|
|
309
|
+
|
|
310
|
+
* **Model docstrings** — a model class's *own* docstring (inherited docs are
|
|
311
|
+
ignored) is reported as ``models[<Model>].docstring``, trimmed at a word
|
|
312
|
+
boundary to ``schema_bundle.MODEL_DOCSTRING_MAX`` characters.
|
|
313
|
+
* **Field descriptions** — pass ``description="..."`` to any package field
|
|
314
|
+
(``CharField``, ``ForeignKey``, ``DecimalField``, ...); it is reported as
|
|
315
|
+
``models[<Model>].schema[<field>].description`` (trimmed to
|
|
316
|
+
``FIELD_DESCRIPTION_MAX``). This is **not** ``help_text``: ``help_text`` is
|
|
317
|
+
rendered to end users by ``drf-react-by-schema-js``, while ``description``
|
|
318
|
+
documents the field for the API client / assistant. Only values passed
|
|
319
|
+
explicitly are reported — the field type's own ``description`` is never.
|
|
320
|
+
* **Project ``ABOUT.md``** — a project-root ``ABOUT.md`` (``settings.BASE_DIR``)
|
|
321
|
+
is reported as ``system.about`` (trimmed to ``ABOUT_MAX``). Override the path
|
|
322
|
+
with ``DRF_REACT_BY_SCHEMA['ABOUT_PATH']``; a missing file yields ``null``.
|
|
323
|
+
Use it for *what the project is about* — not deploy/contribute instructions.
|
|
324
|
+
|
|
325
|
+
All three feed the ``stamp``, so changing them invalidates an MCP client's
|
|
326
|
+
cached copy; because the bundle is cached per process, a change also needs a
|
|
327
|
+
process restart to be picked up.
|
|
328
|
+
|
|
303
329
|
The endpoint sits behind the project's **existing auth** (the same permission
|
|
304
330
|
source used by the generated viewsets — ``DRF_REACT_BY_SCHEMA['PERMISSIONS']``,
|
|
305
331
|
defaulting to ``IsAuthenticated``; authentication comes from your DRF defaults,
|
{drf_react_by_schema-0.27.0 → drf_react_by_schema-0.27.2}/drf_react_by_schema/fields/array.py
RENAMED
|
@@ -4,7 +4,11 @@ from django.contrib.postgres.fields import ArrayField
|
|
|
4
4
|
class ArrayField(ArrayField):
|
|
5
5
|
description = "Extended PostgreSQL ArrayField"
|
|
6
6
|
|
|
7
|
-
def __init__(
|
|
7
|
+
def __init__(
|
|
8
|
+
self, *args, ui_required=False, conditional_visible=None, description=None, **kwargs
|
|
9
|
+
):
|
|
8
10
|
super().__init__(*args, **kwargs)
|
|
11
|
+
if description is not None:
|
|
12
|
+
self.description = description
|
|
9
13
|
self.ui_required = ui_required
|
|
10
14
|
self.conditional_visible = conditional_visible
|
{drf_react_by_schema-0.27.0 → drf_react_by_schema-0.27.2}/drf_react_by_schema/fields/boolean.py
RENAMED
|
@@ -4,7 +4,11 @@ from django.db import models
|
|
|
4
4
|
class BooleanField(models.BooleanField):
|
|
5
5
|
description = "Extended BooleanField"
|
|
6
6
|
|
|
7
|
-
def __init__(
|
|
7
|
+
def __init__(
|
|
8
|
+
self, *args, ui_required=False, conditional_visible=None, description=None, **kwargs
|
|
9
|
+
):
|
|
8
10
|
super().__init__(*args, **kwargs)
|
|
11
|
+
if description is not None:
|
|
12
|
+
self.description = description
|
|
9
13
|
self.ui_required = ui_required
|
|
10
14
|
self.conditional_visible = conditional_visible
|
{drf_react_by_schema-0.27.0 → drf_react_by_schema-0.27.2}/drf_react_by_schema/fields/date.py
RENAMED
|
@@ -5,9 +5,17 @@ class DateField(models.DateField):
|
|
|
5
5
|
description = "Extended DateField"
|
|
6
6
|
|
|
7
7
|
def __init__(
|
|
8
|
-
self,
|
|
8
|
+
self,
|
|
9
|
+
*args,
|
|
10
|
+
views=None,
|
|
11
|
+
ui_required=False,
|
|
12
|
+
conditional_visible=None,
|
|
13
|
+
description=None,
|
|
14
|
+
**kwargs
|
|
9
15
|
):
|
|
10
16
|
super().__init__(*args, **kwargs)
|
|
17
|
+
if description is not None:
|
|
18
|
+
self.description = description
|
|
11
19
|
if views:
|
|
12
20
|
self.views = views
|
|
13
21
|
self.ui_required = ui_required
|
|
@@ -17,7 +25,11 @@ class DateField(models.DateField):
|
|
|
17
25
|
class DateTimeField(models.DateTimeField):
|
|
18
26
|
description = "Extended DateTimeField"
|
|
19
27
|
|
|
20
|
-
def __init__(
|
|
28
|
+
def __init__(
|
|
29
|
+
self, *args, ui_required=False, conditional_visible=None, description=None, **kwargs
|
|
30
|
+
):
|
|
21
31
|
super().__init__(*args, **kwargs)
|
|
32
|
+
if description is not None:
|
|
33
|
+
self.description = description
|
|
22
34
|
self.ui_required = ui_required
|
|
23
35
|
self.conditional_visible = conditional_visible
|
{drf_react_by_schema-0.27.0 → drf_react_by_schema-0.27.2}/drf_react_by_schema/fields/file.py
RENAMED
|
@@ -12,9 +12,12 @@ class ImageField(models.ImageField):
|
|
|
12
12
|
max_file_size=None,
|
|
13
13
|
allowed_mime_types=None,
|
|
14
14
|
conditional_visible=None,
|
|
15
|
+
description=None,
|
|
15
16
|
**kwargs
|
|
16
17
|
):
|
|
17
18
|
super().__init__(*args, **kwargs)
|
|
19
|
+
if description is not None:
|
|
20
|
+
self.description = description
|
|
18
21
|
self.max_file_size = max_file_size
|
|
19
22
|
self.allowed_mime_types = allowed_mime_types
|
|
20
23
|
|
|
@@ -45,9 +48,12 @@ class FileField(models.FileField):
|
|
|
45
48
|
allowed_mime_types=None,
|
|
46
49
|
ui_required=False,
|
|
47
50
|
conditional_visible=None,
|
|
51
|
+
description=None,
|
|
48
52
|
**kwargs
|
|
49
53
|
):
|
|
50
54
|
super().__init__(*args, **kwargs)
|
|
55
|
+
if description is not None:
|
|
56
|
+
self.description = description
|
|
51
57
|
self.max_file_size = max_file_size
|
|
52
58
|
self.allowed_mime_types = allowed_mime_types
|
|
53
59
|
self.ui_required = ui_required
|
{drf_react_by_schema-0.27.0 → drf_react_by_schema-0.27.2}/drf_react_by_schema/fields/geometry.py
RENAMED
|
@@ -25,9 +25,13 @@ SRID = 4326
|
|
|
25
25
|
def _geometry_field(cls):
|
|
26
26
|
"""Build an extended GIS model field class adding ui_required support."""
|
|
27
27
|
|
|
28
|
-
def __init__(
|
|
28
|
+
def __init__(
|
|
29
|
+
self, *args, ui_required=False, conditional_visible=None, description=None, **kwargs
|
|
30
|
+
):
|
|
29
31
|
kwargs.setdefault("srid", SRID)
|
|
30
32
|
cls.__init__(self, *args, **kwargs)
|
|
33
|
+
if description is not None:
|
|
34
|
+
self.description = description
|
|
31
35
|
self.ui_required = ui_required
|
|
32
36
|
self.conditional_visible = conditional_visible
|
|
33
37
|
|
{drf_react_by_schema-0.27.0 → drf_react_by_schema-0.27.2}/drf_react_by_schema/fields/number.py
RENAMED
|
@@ -12,9 +12,12 @@ class DecimalField(models.DecimalField):
|
|
|
12
12
|
suffix="",
|
|
13
13
|
ui_required=False,
|
|
14
14
|
conditional_visible=None,
|
|
15
|
+
description=None,
|
|
15
16
|
**kwargs
|
|
16
17
|
):
|
|
17
18
|
super().__init__(*args, **kwargs)
|
|
19
|
+
if description is not None:
|
|
20
|
+
self.description = description
|
|
18
21
|
self.is_currency = is_currency and prefix == "" and suffix == ""
|
|
19
22
|
self.prefix = prefix
|
|
20
23
|
self.suffix = suffix
|
|
@@ -31,9 +34,12 @@ class IntegerField(models.IntegerField):
|
|
|
31
34
|
pattern_format=None,
|
|
32
35
|
ui_required=False,
|
|
33
36
|
conditional_visible=None,
|
|
37
|
+
description=None,
|
|
34
38
|
**kwargs
|
|
35
39
|
):
|
|
36
40
|
super().__init__(*args, **kwargs)
|
|
41
|
+
if description is not None:
|
|
42
|
+
self.description = description
|
|
37
43
|
self.pattern_format = pattern_format
|
|
38
44
|
self.ui_required = ui_required
|
|
39
45
|
self.conditional_visible = conditional_visible
|
{drf_react_by_schema-0.27.0 → drf_react_by_schema-0.27.2}/drf_react_by_schema/fields/relational.py
RENAMED
|
@@ -29,9 +29,12 @@ class ForeignKey(models.ForeignKey):
|
|
|
29
29
|
verbose_name="",
|
|
30
30
|
ui_required=False,
|
|
31
31
|
conditional_visible=None,
|
|
32
|
+
description=None,
|
|
32
33
|
**kwargs
|
|
33
34
|
):
|
|
34
35
|
super().__init__(*args, **kwargs)
|
|
36
|
+
if description is not None:
|
|
37
|
+
self.description = description
|
|
35
38
|
self.related_editable = related_editable
|
|
36
39
|
self.label = label # deprecated! Remove in jan2024
|
|
37
40
|
self.verbose_name = verbose_name
|
|
@@ -49,9 +52,12 @@ class ManyToManyField(models.ManyToManyField):
|
|
|
49
52
|
label="",
|
|
50
53
|
ui_required=False,
|
|
51
54
|
conditional_visible=None,
|
|
55
|
+
description=None,
|
|
52
56
|
**kwargs
|
|
53
57
|
):
|
|
54
58
|
super().__init__(*args, **kwargs)
|
|
59
|
+
if description is not None:
|
|
60
|
+
self.description = description
|
|
55
61
|
self.related_editable = related_editable
|
|
56
62
|
self.label = label # deprecated! Remove in jan2024
|
|
57
63
|
self.ui_required = ui_required
|
|
@@ -68,9 +74,12 @@ class OneToOneField(models.OneToOneField):
|
|
|
68
74
|
label="",
|
|
69
75
|
ui_required=False,
|
|
70
76
|
conditional_visible=None,
|
|
77
|
+
description=None,
|
|
71
78
|
**kwargs
|
|
72
79
|
):
|
|
73
80
|
super().__init__(*args, **kwargs)
|
|
81
|
+
if description is not None:
|
|
82
|
+
self.description = description
|
|
74
83
|
self.related_editable = related_editable
|
|
75
84
|
self.label = label # deprecated! Remove in jan2024
|
|
76
85
|
self.ui_required = ui_required
|
{drf_react_by_schema-0.27.0 → drf_react_by_schema-0.27.2}/drf_react_by_schema/fields/serializers.py
RENAMED
|
@@ -4,7 +4,16 @@ from rest_framework import serializers
|
|
|
4
4
|
class TypedSerializerMethodField(serializers.SerializerMethodField):
|
|
5
5
|
description = "Extended SerializerMethodField"
|
|
6
6
|
|
|
7
|
-
def __init__(
|
|
7
|
+
def __init__(
|
|
8
|
+
self,
|
|
9
|
+
*args,
|
|
10
|
+
return_type="string",
|
|
11
|
+
conditional_visible=None,
|
|
12
|
+
description=None,
|
|
13
|
+
**kwargs
|
|
14
|
+
):
|
|
8
15
|
super().__init__(*args, **kwargs)
|
|
16
|
+
if description is not None:
|
|
17
|
+
self.description = description
|
|
9
18
|
self.return_type = return_type
|
|
10
19
|
self.conditional_visible = conditional_visible
|
{drf_react_by_schema-0.27.0 → drf_react_by_schema-0.27.2}/drf_react_by_schema/fields/text.py
RENAMED
|
@@ -15,9 +15,12 @@ class CharField(models.CharField):
|
|
|
15
15
|
pattern_format=None,
|
|
16
16
|
ui_required=False,
|
|
17
17
|
conditional_visible=None,
|
|
18
|
+
description=None,
|
|
18
19
|
**kwargs,
|
|
19
20
|
):
|
|
20
21
|
super().__init__(*args, **kwargs)
|
|
22
|
+
if description is not None:
|
|
23
|
+
self.description = description
|
|
21
24
|
self.pattern_format = pattern_format
|
|
22
25
|
self.ui_required = ui_required
|
|
23
26
|
self.conditional_visible = conditional_visible
|
|
@@ -41,8 +44,12 @@ class CharField(models.CharField):
|
|
|
41
44
|
class TextField(models.TextField):
|
|
42
45
|
description = "Extended TextField allowing ui required"
|
|
43
46
|
|
|
44
|
-
def __init__(
|
|
47
|
+
def __init__(
|
|
48
|
+
self, *args, ui_required=False, conditional_visible=None, description=None, **kwargs
|
|
49
|
+
):
|
|
45
50
|
super().__init__(*args, **kwargs)
|
|
51
|
+
if description is not None:
|
|
52
|
+
self.description = description
|
|
46
53
|
self.ui_required = ui_required
|
|
47
54
|
self.conditional_visible = conditional_visible
|
|
48
55
|
|
|
@@ -50,7 +57,11 @@ class TextField(models.TextField):
|
|
|
50
57
|
class EmailField(models.EmailField):
|
|
51
58
|
description = "Extended EmailField allowing ui required"
|
|
52
59
|
|
|
53
|
-
def __init__(
|
|
60
|
+
def __init__(
|
|
61
|
+
self, *args, ui_required=False, conditional_visible=None, description=None, **kwargs
|
|
62
|
+
):
|
|
54
63
|
super().__init__(*args, **kwargs)
|
|
64
|
+
if description is not None:
|
|
65
|
+
self.description = description
|
|
55
66
|
self.ui_required = ui_required
|
|
56
67
|
self.conditional_visible = conditional_visible
|
{drf_react_by_schema-0.27.0 → drf_react_by_schema-0.27.2}/drf_react_by_schema/schema_bundle.py
RENAMED
|
@@ -25,6 +25,7 @@ from __future__ import annotations
|
|
|
25
25
|
|
|
26
26
|
import hashlib
|
|
27
27
|
import importlib
|
|
28
|
+
import inspect
|
|
28
29
|
import json
|
|
29
30
|
import os
|
|
30
31
|
import traceback
|
|
@@ -80,6 +81,14 @@ GRID_FILTER_OPERATORS = (
|
|
|
80
81
|
#: Number of hex characters kept from the bundle SHA-256.
|
|
81
82
|
STAMP_LENGTH = 16
|
|
82
83
|
|
|
84
|
+
#: Caps for free-form documentation folded into the MCP bundle (kept short so the
|
|
85
|
+
#: schema an assistant receives stays lean). Longer values are truncated at a
|
|
86
|
+
#: word boundary with an ellipsis. These docs are MCP-only: they are NOT written
|
|
87
|
+
#: to the React artifacts (``write_react_artifacts``), which stay byte-identical.
|
|
88
|
+
MODEL_DOCSTRING_MAX = 800
|
|
89
|
+
FIELD_DESCRIPTION_MAX = 300
|
|
90
|
+
ABOUT_MAX = 2000
|
|
91
|
+
|
|
83
92
|
|
|
84
93
|
def configured_apps() -> list[str]:
|
|
85
94
|
"""Return the apps to introspect (``DRF_REACT_BY_SCHEMA["APPS"]``)."""
|
|
@@ -100,6 +109,10 @@ class SchemaArtifacts:
|
|
|
100
109
|
* ``interfaces_by_model`` — the interface string keyed by model name.
|
|
101
110
|
* ``permissions`` — the static (class-level) DRF permission classes per model.
|
|
102
111
|
* ``index_exports`` — the ``index.ts`` export lines.
|
|
112
|
+
* ``model_docstrings`` — the model class's own docstring per model (trimmed),
|
|
113
|
+
for the MCP bundle only.
|
|
114
|
+
* ``field_descriptions`` — per model, the ``description=`` kwarg of each field
|
|
115
|
+
that carries one, for the MCP bundle only.
|
|
103
116
|
"""
|
|
104
117
|
|
|
105
118
|
models: dict = field(default_factory=dict)
|
|
@@ -109,6 +122,8 @@ class SchemaArtifacts:
|
|
|
109
122
|
interfaces_by_model: dict = field(default_factory=dict)
|
|
110
123
|
permissions: dict = field(default_factory=dict)
|
|
111
124
|
index_exports: list = field(default_factory=list)
|
|
125
|
+
model_docstrings: dict = field(default_factory=dict)
|
|
126
|
+
field_descriptions: dict = field(default_factory=dict)
|
|
112
127
|
|
|
113
128
|
|
|
114
129
|
def _mock_request() -> Request:
|
|
@@ -207,6 +222,11 @@ def generate_artifacts(apps_to_sync: list[str] | None = None) -> SchemaArtifacts
|
|
|
207
222
|
artifacts.permissions[model_name] = _describe_permissions(
|
|
208
223
|
getattr(viewset, "permission_classes", None)
|
|
209
224
|
)
|
|
225
|
+
model = getattr(view_instance, "model", None)
|
|
226
|
+
artifacts.model_docstrings[model_name] = _class_docstring(model)
|
|
227
|
+
artifacts.field_descriptions[model_name] = _field_descriptions(
|
|
228
|
+
model, view_instance
|
|
229
|
+
)
|
|
210
230
|
|
|
211
231
|
# B. Generate the TypeScript interface.
|
|
212
232
|
interface_code = artifacts.ts_gen.generate_interface(
|
|
@@ -244,14 +264,27 @@ def build_schema_bundle(
|
|
|
244
264
|
models = {}
|
|
245
265
|
for model_name in artifacts.model_order:
|
|
246
266
|
payload = artifacts.models[model_name]
|
|
247
|
-
schema =
|
|
248
|
-
|
|
249
|
-
|
|
267
|
+
schema = (
|
|
268
|
+
_strip_data_defaults(payload["schema"])
|
|
269
|
+
if structural
|
|
270
|
+
else dict(payload["schema"])
|
|
271
|
+
)
|
|
272
|
+
# Field ``description`` kwargs are documentation-only; fold them into the
|
|
273
|
+
# MCP bundle's copy of the schema (never the React artifact's).
|
|
274
|
+
for field_name, description in (
|
|
275
|
+
artifacts.field_descriptions.get(model_name) or {}
|
|
276
|
+
).items():
|
|
277
|
+
entry = schema.get(field_name)
|
|
278
|
+
if isinstance(entry, dict):
|
|
279
|
+
entry = dict(entry)
|
|
280
|
+
entry.setdefault("description", description)
|
|
281
|
+
schema[field_name] = entry
|
|
250
282
|
models[model_name] = {
|
|
251
283
|
"schema": schema,
|
|
252
284
|
"verbose_name": payload["verbose_name"],
|
|
253
285
|
"verbose_name_plural": payload["verbose_name_plural"],
|
|
254
286
|
"interface": artifacts.interfaces_by_model.get(model_name),
|
|
287
|
+
"docstring": artifacts.model_docstrings.get(model_name),
|
|
255
288
|
}
|
|
256
289
|
|
|
257
290
|
body = {
|
|
@@ -335,6 +368,7 @@ def _system_metadata(apps_to_sync, drf_settings, permissions) -> dict:
|
|
|
335
368
|
"per_model": permissions,
|
|
336
369
|
},
|
|
337
370
|
"conventions": _conventions(drf_settings),
|
|
371
|
+
"about": _project_about(drf_settings),
|
|
338
372
|
}
|
|
339
373
|
|
|
340
374
|
|
|
@@ -362,6 +396,77 @@ def _conventions(drf_settings) -> dict:
|
|
|
362
396
|
}
|
|
363
397
|
|
|
364
398
|
|
|
399
|
+
def _trim_text(text, limit: int) -> str | None:
|
|
400
|
+
"""Collapse a docstring's indentation and truncate it at a word boundary."""
|
|
401
|
+
if not text:
|
|
402
|
+
return None
|
|
403
|
+
cleaned = inspect.cleandoc(str(text)).strip()
|
|
404
|
+
if not cleaned:
|
|
405
|
+
return None
|
|
406
|
+
if len(cleaned) <= limit:
|
|
407
|
+
return cleaned
|
|
408
|
+
clipped = cleaned[:limit].rstrip()
|
|
409
|
+
space = clipped.rfind(" ")
|
|
410
|
+
if space > limit // 2:
|
|
411
|
+
clipped = clipped[:space].rstrip()
|
|
412
|
+
return clipped + "…"
|
|
413
|
+
|
|
414
|
+
|
|
415
|
+
def _class_docstring(model) -> str | None:
|
|
416
|
+
"""The model class's own docstring (trimmed). Inherited docs are ignored."""
|
|
417
|
+
if model is None:
|
|
418
|
+
return None
|
|
419
|
+
return _trim_text(
|
|
420
|
+
getattr(model, "__dict__", {}).get("__doc__"), MODEL_DOCSTRING_MAX
|
|
421
|
+
)
|
|
422
|
+
|
|
423
|
+
|
|
424
|
+
def _field_descriptions(model, view_instance) -> dict:
|
|
425
|
+
"""Map each field carrying a ``description=`` kwarg to its trimmed text.
|
|
426
|
+
|
|
427
|
+
Only ``description`` values the author passed explicitly are reported; a
|
|
428
|
+
field's class-level ``Field.description`` (the Django field-type label) is
|
|
429
|
+
not, because the lookup reads the instance ``__dict__``. Model fields are
|
|
430
|
+
read first, then declared serializer fields (e.g.
|
|
431
|
+
``TypedSerializerMethodField``) fill any name the model did not cover.
|
|
432
|
+
"""
|
|
433
|
+
descriptions: dict = {}
|
|
434
|
+
if model is not None:
|
|
435
|
+
for field in model._meta.get_fields():
|
|
436
|
+
name = getattr(field, "name", None)
|
|
437
|
+
value = getattr(field, "__dict__", {}).get("description")
|
|
438
|
+
if name and value:
|
|
439
|
+
descriptions[name] = _trim_text(value, FIELD_DESCRIPTION_MAX)
|
|
440
|
+
try:
|
|
441
|
+
serializer = view_instance.get_serializer()
|
|
442
|
+
except Exception:
|
|
443
|
+
serializer = None
|
|
444
|
+
if serializer is not None:
|
|
445
|
+
for name, field in serializer.fields.items():
|
|
446
|
+
value = getattr(field, "__dict__", {}).get("description")
|
|
447
|
+
if name not in descriptions and value:
|
|
448
|
+
descriptions[name] = _trim_text(value, FIELD_DESCRIPTION_MAX)
|
|
449
|
+
return descriptions
|
|
450
|
+
|
|
451
|
+
|
|
452
|
+
def _project_about(drf_settings) -> str | None:
|
|
453
|
+
"""The project's ``ABOUT.md`` — ``BASE_DIR/ABOUT.md`` or ``ABOUT_PATH``.
|
|
454
|
+
|
|
455
|
+
Returns ``None`` when neither path resolves or the file is missing/empty.
|
|
456
|
+
"""
|
|
457
|
+
path = drf_settings.get("ABOUT_PATH")
|
|
458
|
+
if not path:
|
|
459
|
+
base_dir = getattr(settings, "BASE_DIR", None)
|
|
460
|
+
if not base_dir:
|
|
461
|
+
return None
|
|
462
|
+
path = os.path.join(str(base_dir), "ABOUT.md")
|
|
463
|
+
try:
|
|
464
|
+
with open(path, encoding="utf-8") as handle:
|
|
465
|
+
return _trim_text(handle.read(), ABOUT_MAX)
|
|
466
|
+
except OSError:
|
|
467
|
+
return None
|
|
468
|
+
|
|
469
|
+
|
|
365
470
|
def _describe_permissions(permission_classes) -> list | None:
|
|
366
471
|
"""Render static permission classes as dotted paths (or ``None``)."""
|
|
367
472
|
if not permission_classes:
|
|
@@ -159,6 +159,17 @@ class SchemaBundleEndpointTest(TestCase):
|
|
|
159
159
|
):
|
|
160
160
|
self.assertIn(key, response.data)
|
|
161
161
|
|
|
162
|
+
def test_viewset_style_permission_reading_action(self):
|
|
163
|
+
"""A viewset permission branching on ``view.action`` must not crash."""
|
|
164
|
+
from rest_framework.permissions import BasePermission
|
|
165
|
+
|
|
166
|
+
class ActionAware(BasePermission):
|
|
167
|
+
def has_permission(self, request, view):
|
|
168
|
+
return view.action == "list"
|
|
169
|
+
|
|
170
|
+
with self.settings(DRF_REACT_BY_SCHEMA={"PERMISSIONS": [ActionAware]}):
|
|
171
|
+
self.assertEqual(self._get().status_code, 200)
|
|
172
|
+
|
|
162
173
|
def test_if_none_match_returns_304(self):
|
|
163
174
|
etag = self._get()["ETag"]
|
|
164
175
|
|
|
@@ -208,6 +219,112 @@ class SchemaBundleEndpointTest(TestCase):
|
|
|
208
219
|
self.assertTrue(schema_endpoint_enabled())
|
|
209
220
|
|
|
210
221
|
|
|
222
|
+
class SchemaDocumentationTest(TestCase):
|
|
223
|
+
"""Model docstrings, field ``description`` and ``ABOUT.md`` in the bundle."""
|
|
224
|
+
|
|
225
|
+
def tearDown(self):
|
|
226
|
+
from drf_react_by_schema.views import reset_schema_bundle_cache
|
|
227
|
+
|
|
228
|
+
reset_schema_bundle_cache()
|
|
229
|
+
|
|
230
|
+
def test_trim_text_collapses_and_truncates(self):
|
|
231
|
+
from drf_react_by_schema.schema_bundle import _trim_text
|
|
232
|
+
|
|
233
|
+
self.assertIsNone(_trim_text(None, 10))
|
|
234
|
+
self.assertIsNone(_trim_text(" ", 10))
|
|
235
|
+
self.assertEqual(_trim_text("short", 10), "short")
|
|
236
|
+
self.assertEqual(_trim_text("Title.\n\n body", 50), "Title.\n\nbody")
|
|
237
|
+
trimmed = _trim_text("palavra " * 50, 30)
|
|
238
|
+
self.assertLessEqual(len(trimmed), 31)
|
|
239
|
+
self.assertTrue(trimmed.endswith("…"))
|
|
240
|
+
|
|
241
|
+
def test_class_docstring_uses_own_docs_only(self):
|
|
242
|
+
from drf_react_by_schema.schema_bundle import _class_docstring
|
|
243
|
+
|
|
244
|
+
class Documented:
|
|
245
|
+
"""Real docs."""
|
|
246
|
+
|
|
247
|
+
class Inheriting(Documented):
|
|
248
|
+
pass
|
|
249
|
+
|
|
250
|
+
class ExplicitlyEmpty(Documented):
|
|
251
|
+
__doc__ = None
|
|
252
|
+
|
|
253
|
+
self.assertEqual(_class_docstring(Documented), "Real docs.")
|
|
254
|
+
self.assertIsNone(_class_docstring(Inheriting))
|
|
255
|
+
self.assertIsNone(_class_docstring(ExplicitlyEmpty))
|
|
256
|
+
self.assertIsNone(_class_docstring(None))
|
|
257
|
+
|
|
258
|
+
def test_field_descriptions_reads_explicit_kwarg_only(self):
|
|
259
|
+
from types import SimpleNamespace
|
|
260
|
+
|
|
261
|
+
from drf_react_by_schema import fields
|
|
262
|
+
from drf_react_by_schema.schema_bundle import _field_descriptions
|
|
263
|
+
|
|
264
|
+
described = fields.CharField("N", max_length=10, description="Do campo")
|
|
265
|
+
described.set_attributes_from_name("nome")
|
|
266
|
+
# A plain package field must not surface its class-level ``description``.
|
|
267
|
+
plain = fields.CharField("X", max_length=3)
|
|
268
|
+
plain.set_attributes_from_name("sem_descricao")
|
|
269
|
+
model = SimpleNamespace(
|
|
270
|
+
_meta=SimpleNamespace(get_fields=lambda: [described, plain])
|
|
271
|
+
)
|
|
272
|
+
no_serializer = SimpleNamespace(
|
|
273
|
+
get_serializer=lambda: SimpleNamespace(fields={})
|
|
274
|
+
)
|
|
275
|
+
self.assertEqual(
|
|
276
|
+
_field_descriptions(model, no_serializer), {"nome": "Do campo"}
|
|
277
|
+
)
|
|
278
|
+
|
|
279
|
+
# A declared serializer field (not a model field) is picked up too.
|
|
280
|
+
method = fields.TypedSerializerMethodField(description="Calculado")
|
|
281
|
+
with_serializer = SimpleNamespace(
|
|
282
|
+
get_serializer=lambda: SimpleNamespace(fields={"extra": method})
|
|
283
|
+
)
|
|
284
|
+
self.assertEqual(
|
|
285
|
+
_field_descriptions(None, with_serializer), {"extra": "Calculado"}
|
|
286
|
+
)
|
|
287
|
+
|
|
288
|
+
def test_project_about_from_base_dir_and_path(self):
|
|
289
|
+
import os
|
|
290
|
+
import tempfile
|
|
291
|
+
|
|
292
|
+
from django.test import override_settings
|
|
293
|
+
|
|
294
|
+
from drf_react_by_schema.schema_bundle import _project_about
|
|
295
|
+
|
|
296
|
+
with tempfile.TemporaryDirectory() as tmp:
|
|
297
|
+
with open(os.path.join(tmp, "ABOUT.md"), "w", encoding="utf-8") as handle:
|
|
298
|
+
handle.write("# Sobre\n\nProjeto de teste.")
|
|
299
|
+
with override_settings(BASE_DIR=tmp):
|
|
300
|
+
self.assertEqual(_project_about({}), "# Sobre\n\nProjeto de teste.")
|
|
301
|
+
self.assertIsNone(_project_about({"ABOUT_PATH": "/no/such/ABOUT.md"}))
|
|
302
|
+
|
|
303
|
+
def test_bundle_exposes_documentation(self):
|
|
304
|
+
import os
|
|
305
|
+
import tempfile
|
|
306
|
+
|
|
307
|
+
from django.test import override_settings
|
|
308
|
+
|
|
309
|
+
from drf_react_by_schema.views import (
|
|
310
|
+
get_schema_bundle,
|
|
311
|
+
reset_schema_bundle_cache,
|
|
312
|
+
)
|
|
313
|
+
|
|
314
|
+
with tempfile.TemporaryDirectory() as tmp:
|
|
315
|
+
with open(os.path.join(tmp, "ABOUT.md"), "w", encoding="utf-8") as handle:
|
|
316
|
+
handle.write("# Meu projeto\n\nDescrição curta.")
|
|
317
|
+
with override_settings(BASE_DIR=tmp):
|
|
318
|
+
reset_schema_bundle_cache()
|
|
319
|
+
bundle = get_schema_bundle()
|
|
320
|
+
|
|
321
|
+
self.assertEqual(
|
|
322
|
+
bundle["system"]["about"], "# Meu projeto\n\nDescrição curta."
|
|
323
|
+
)
|
|
324
|
+
for payload in bundle["models"].values():
|
|
325
|
+
self.assertIn("docstring", payload)
|
|
326
|
+
|
|
327
|
+
|
|
211
328
|
class PasswordResetFlowTest(TestCase):
|
|
212
329
|
"""End-to-end password reset (request -> confirm) and authenticated change."""
|
|
213
330
|
|
|
@@ -65,6 +65,11 @@ def _etag_matches(if_none_match: str | None, etag: str) -> bool:
|
|
|
65
65
|
class SchemaBundleView(APIView):
|
|
66
66
|
"""``GET /drbs/schema/`` — serve the cached bundle with ETag/304 handling."""
|
|
67
67
|
|
|
68
|
+
#: Project ``PERMISSIONS`` are written for the generated viewsets and may
|
|
69
|
+
#: branch on ``view.action``; this plain ``APIView`` exposes the read
|
|
70
|
+
#: action so such permission classes (e.g. CTA's) do not raise here.
|
|
71
|
+
action = "list"
|
|
72
|
+
|
|
68
73
|
def get_permissions(self):
|
|
69
74
|
# Re-read the setting per request so hosts can toggle PERMISSIONS in tests
|
|
70
75
|
# (mirrors the permission source used by the generated viewsets).
|
{drf_react_by_schema-0.27.0 → drf_react_by_schema-0.27.2}/drf_react_by_schema.egg-info/PKG-INFO
RENAMED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.1
|
|
2
2
|
Name: drf-react-by-schema
|
|
3
|
-
Version: 0.27.
|
|
3
|
+
Version: 0.27.2
|
|
4
4
|
Summary: A Django app to easily integrate with React from models.
|
|
5
5
|
Home-page: https://gitlab.com/eita/drf-react-by-schema/drf-react-by-schema-django
|
|
6
6
|
Author: Cooperativa EITA
|
|
@@ -317,16 +317,42 @@ object with:
|
|
|
317
317
|
version changes;
|
|
318
318
|
* ``drbs_version`` — the installed ``drf-react-by-schema`` version;
|
|
319
319
|
* ``system`` — apps, ``AUTH_USER_MODEL``, the ``DRF_REACT_BY_SCHEMA`` summary,
|
|
320
|
-
static permission hints (DRF defaults, global, per-model)
|
|
320
|
+
static permission hints (DRF defaults, global, per-model), request
|
|
321
321
|
``conventions`` (API base, pagination, ``ordering``/``search``/``sum_rows``
|
|
322
|
-
params, grid-filter operators)
|
|
322
|
+
params, grid-filter operators) and ``about`` (the project's ``ABOUT.md``,
|
|
323
|
+
trimmed — see below);
|
|
323
324
|
* ``endpoints`` — every generated route (list and nested) with its
|
|
324
325
|
``path``/``kind``/``verbose_name``/``parent_model``/``related_model``;
|
|
325
326
|
* ``models`` — each model's field ``schema`` (the same ``OPTIONS`` POST payload
|
|
326
|
-
the React app consumes), verbose names
|
|
327
|
-
``
|
|
327
|
+
the React app consumes), verbose names, generated TypeScript ``interface`` and
|
|
328
|
+
the model class ``docstring`` (trimmed);
|
|
328
329
|
* ``interfaces`` and ``enums`` — the generated TypeScript declarations.
|
|
329
330
|
|
|
331
|
+
Documentation in the bundle
|
|
332
|
+
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
|
333
|
+
Free-form, non-UI documentation is folded into the bundle so an assistant can
|
|
334
|
+
*understand* the system. It is kept out of the React artifacts on purpose:
|
|
335
|
+
``sync_react``'s ``schemas/*.json`` stay **byte-identical** to before.
|
|
336
|
+
|
|
337
|
+
* **Model docstrings** — a model class's *own* docstring (inherited docs are
|
|
338
|
+
ignored) is reported as ``models[<Model>].docstring``, trimmed at a word
|
|
339
|
+
boundary to ``schema_bundle.MODEL_DOCSTRING_MAX`` characters.
|
|
340
|
+
* **Field descriptions** — pass ``description="..."`` to any package field
|
|
341
|
+
(``CharField``, ``ForeignKey``, ``DecimalField``, ...); it is reported as
|
|
342
|
+
``models[<Model>].schema[<field>].description`` (trimmed to
|
|
343
|
+
``FIELD_DESCRIPTION_MAX``). This is **not** ``help_text``: ``help_text`` is
|
|
344
|
+
rendered to end users by ``drf-react-by-schema-js``, while ``description``
|
|
345
|
+
documents the field for the API client / assistant. Only values passed
|
|
346
|
+
explicitly are reported — the field type's own ``description`` is never.
|
|
347
|
+
* **Project ``ABOUT.md``** — a project-root ``ABOUT.md`` (``settings.BASE_DIR``)
|
|
348
|
+
is reported as ``system.about`` (trimmed to ``ABOUT_MAX``). Override the path
|
|
349
|
+
with ``DRF_REACT_BY_SCHEMA['ABOUT_PATH']``; a missing file yields ``null``.
|
|
350
|
+
Use it for *what the project is about* — not deploy/contribute instructions.
|
|
351
|
+
|
|
352
|
+
All three feed the ``stamp``, so changing them invalidates an MCP client's
|
|
353
|
+
cached copy; because the bundle is cached per process, a change also needs a
|
|
354
|
+
process restart to be picked up.
|
|
355
|
+
|
|
330
356
|
The endpoint sits behind the project's **existing auth** (the same permission
|
|
331
357
|
source used by the generated viewsets — ``DRF_REACT_BY_SCHEMA['PERMISSIONS']``,
|
|
332
358
|
defaulting to ``IsAuthenticated``; authentication comes from your DRF defaults,
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{drf_react_by_schema-0.27.0 → drf_react_by_schema-0.27.2}/drf_react_by_schema/fields/__init__.py
RENAMED
|
File without changes
|
|
File without changes
|
{drf_react_by_schema-0.27.0 → drf_react_by_schema-0.27.2}/drf_react_by_schema/management/__init__.py
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{drf_react_by_schema-0.27.0 → drf_react_by_schema-0.27.2}/drf_react_by_schema/migrations/__init__.py
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{drf_react_by_schema-0.27.0 → drf_react_by_schema-0.27.2}/drf_react_by_schema/patches/__init__.py
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
{drf_react_by_schema-0.27.0 → drf_react_by_schema-0.27.2}/drf_react_by_schema/serializers.py
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{drf_react_by_schema-0.27.0 → drf_react_by_schema-0.27.2}/drf_react_by_schema/utils_cache.py
RENAMED
|
File without changes
|
|
File without changes
|
{drf_react_by_schema-0.27.0 → drf_react_by_schema-0.27.2}/drf_react_by_schema.egg-info/SOURCES.txt
RENAMED
|
File without changes
|
|
File without changes
|
{drf_react_by_schema-0.27.0 → drf_react_by_schema-0.27.2}/drf_react_by_schema.egg-info/requires.txt
RENAMED
|
File without changes
|
{drf_react_by_schema-0.27.0 → drf_react_by_schema-0.27.2}/drf_react_by_schema.egg-info/top_level.txt
RENAMED
|
File without changes
|
|
File without changes
|