easyapi-django 1.1.2__tar.gz → 1.1.4__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.
Files changed (99) hide show
  1. {easyapi_django-1.1.2/easyapi_django.egg-info → easyapi_django-1.1.4}/PKG-INFO +1 -1
  2. {easyapi_django-1.1.2 → easyapi_django-1.1.4}/easyapi/base.py +4 -1
  3. {easyapi_django-1.1.2 → easyapi_django-1.1.4}/easyapi/filters.py +6 -2
  4. {easyapi_django-1.1.2 → easyapi_django-1.1.4}/easyapi/helpers.py +61 -3
  5. {easyapi_django-1.1.2 → easyapi_django-1.1.4/easyapi_django.egg-info}/PKG-INFO +1 -1
  6. {easyapi_django-1.1.2 → easyapi_django-1.1.4}/pyproject.toml +1 -1
  7. {easyapi_django-1.1.2 → easyapi_django-1.1.4}/LICENSE +0 -0
  8. {easyapi_django-1.1.2 → easyapi_django-1.1.4}/MANIFEST.in +0 -0
  9. {easyapi_django-1.1.2 → easyapi_django-1.1.4}/README.md +0 -0
  10. {easyapi_django-1.1.2 → easyapi_django-1.1.4}/easyapi/__init__.py +0 -0
  11. {easyapi_django-1.1.2 → easyapi_django-1.1.4}/easyapi/auth.py +0 -0
  12. {easyapi_django-1.1.2 → easyapi_django-1.1.4}/easyapi/bearer.py +0 -0
  13. {easyapi_django-1.1.2 → easyapi_django-1.1.4}/easyapi/blocking.py +0 -0
  14. {easyapi_django-1.1.2 → easyapi_django-1.1.4}/easyapi/calc.py +0 -0
  15. {easyapi_django-1.1.2 → easyapi_django-1.1.4}/easyapi/calc_resource.py +0 -0
  16. {easyapi_django-1.1.2 → easyapi_django-1.1.4}/easyapi/client_ip.py +0 -0
  17. {easyapi_django-1.1.2 → easyapi_django-1.1.4}/easyapi/constants.py +0 -0
  18. {easyapi_django-1.1.2 → easyapi_django-1.1.4}/easyapi/core/__init__.py +0 -0
  19. {easyapi_django-1.1.2 → easyapi_django-1.1.4}/easyapi/core/endpoint.py +0 -0
  20. {easyapi_django-1.1.2 → easyapi_django-1.1.4}/easyapi/core/resource.py +0 -0
  21. {easyapi_django-1.1.2 → easyapi_django-1.1.4}/easyapi/dates.py +0 -0
  22. {easyapi_django-1.1.2 → easyapi_django-1.1.4}/easyapi/exception.py +0 -0
  23. {easyapi_django-1.1.2 → easyapi_django-1.1.4}/easyapi/gen/__init__.py +0 -0
  24. {easyapi_django-1.1.2 → easyapi_django-1.1.4}/easyapi/gen/cli.py +0 -0
  25. {easyapi_django-1.1.2 → easyapi_django-1.1.4}/easyapi/gen/config.py +0 -0
  26. {easyapi_django-1.1.2 → easyapi_django-1.1.4}/easyapi/gen/generator.py +0 -0
  27. {easyapi_django-1.1.2 → easyapi_django-1.1.4}/easyapi/gen/inspector/__init__.py +0 -0
  28. {easyapi_django-1.1.2 → easyapi_django-1.1.4}/easyapi/gen/inspector/base.py +0 -0
  29. {easyapi_django-1.1.2 → easyapi_django-1.1.4}/easyapi/gen/inspector/mysql.py +0 -0
  30. {easyapi_django-1.1.2 → easyapi_django-1.1.4}/easyapi/gen/inspector/postgres.py +0 -0
  31. {easyapi_django-1.1.2 → easyapi_django-1.1.4}/easyapi/gen/templates/README.md.j2 +0 -0
  32. {easyapi_django-1.1.2 → easyapi_django-1.1.4}/easyapi/gen/templates/asgi.py.j2 +0 -0
  33. {easyapi_django-1.1.2 → easyapi_django-1.1.4}/easyapi/gen/templates/env.example.j2 +0 -0
  34. {easyapi_django-1.1.2 → easyapi_django-1.1.4}/easyapi/gen/templates/gitignore.j2 +0 -0
  35. {easyapi_django-1.1.2 → easyapi_django-1.1.4}/easyapi/gen/templates/manage.py.j2 +0 -0
  36. {easyapi_django-1.1.2 → easyapi_django-1.1.4}/easyapi/gen/templates/module/models.py.j2 +0 -0
  37. {easyapi_django-1.1.2 → easyapi_django-1.1.4}/easyapi/gen/templates/module/resources.py.j2 +0 -0
  38. {easyapi_django-1.1.2 → easyapi_django-1.1.4}/easyapi/gen/templates/requirements.txt.j2 +0 -0
  39. {easyapi_django-1.1.2 → easyapi_django-1.1.4}/easyapi/gen/templates/router/endpoints.py.j2 +0 -0
  40. {easyapi_django-1.1.2 → easyapi_django-1.1.4}/easyapi/gen/templates/router/urls.py.j2 +0 -0
  41. {easyapi_django-1.1.2 → easyapi_django-1.1.4}/easyapi/gen/templates/run.sh.j2 +0 -0
  42. {easyapi_django-1.1.2 → easyapi_django-1.1.4}/easyapi/gen/templates/settings/env.py.j2 +0 -0
  43. {easyapi_django-1.1.2 → easyapi_django-1.1.4}/easyapi/gen/templates/settings/settings.py.j2 +0 -0
  44. {easyapi_django-1.1.2 → easyapi_django-1.1.4}/easyapi/management/__init__.py +0 -0
  45. {easyapi_django-1.1.2 → easyapi_django-1.1.4}/easyapi/management/commands/__init__.py +0 -0
  46. {easyapi_django-1.1.2 → easyapi_django-1.1.4}/easyapi/management/commands/mcp_serve.py +0 -0
  47. {easyapi_django-1.1.2 → easyapi_django-1.1.4}/easyapi/mcp/__init__.py +0 -0
  48. {easyapi_django-1.1.2 → easyapi_django-1.1.4}/easyapi/mcp/protocol.py +0 -0
  49. {easyapi_django-1.1.2 → easyapi_django-1.1.4}/easyapi/mcp/schema.py +0 -0
  50. {easyapi_django-1.1.2 → easyapi_django-1.1.4}/easyapi/mcp/server.py +0 -0
  51. {easyapi_django-1.1.2 → easyapi_django-1.1.4}/easyapi/mcp/testing.py +0 -0
  52. {easyapi_django-1.1.2 → easyapi_django-1.1.4}/easyapi/mcp/tool.py +0 -0
  53. {easyapi_django-1.1.2 → easyapi_django-1.1.4}/easyapi/mcp/tools.py +0 -0
  54. {easyapi_django-1.1.2 → easyapi_django-1.1.4}/easyapi/middleware.py +0 -0
  55. {easyapi_django-1.1.2 → easyapi_django-1.1.4}/easyapi/oauth/__init__.py +0 -0
  56. {easyapi_django-1.1.2 → easyapi_django-1.1.4}/easyapi/oauth/_common.py +0 -0
  57. {easyapi_django-1.1.2 → easyapi_django-1.1.4}/easyapi/oauth/apps.py +0 -0
  58. {easyapi_django-1.1.2 → easyapi_django-1.1.4}/easyapi/oauth/clients.py +0 -0
  59. {easyapi_django-1.1.2 → easyapi_django-1.1.4}/easyapi/oauth/codes.py +0 -0
  60. {easyapi_django-1.1.2 → easyapi_django-1.1.4}/easyapi/oauth/constants.py +0 -0
  61. {easyapi_django-1.1.2 → easyapi_django-1.1.4}/easyapi/oauth/devices.py +0 -0
  62. {easyapi_django-1.1.2 → easyapi_django-1.1.4}/easyapi/oauth/discovery.py +0 -0
  63. {easyapi_django-1.1.2 → easyapi_django-1.1.4}/easyapi/oauth/migrations/0001_initial.py +0 -0
  64. {easyapi_django-1.1.2 → easyapi_django-1.1.4}/easyapi/oauth/migrations/__init__.py +0 -0
  65. {easyapi_django-1.1.2 → easyapi_django-1.1.4}/easyapi/oauth/models.py +0 -0
  66. {easyapi_django-1.1.2 → easyapi_django-1.1.4}/easyapi/oauth/resolver.py +0 -0
  67. {easyapi_django-1.1.2 → easyapi_django-1.1.4}/easyapi/oauth/resources/__init__.py +0 -0
  68. {easyapi_django-1.1.2 → easyapi_django-1.1.4}/easyapi/oauth/resources/authorize.py +0 -0
  69. {easyapi_django-1.1.2 → easyapi_django-1.1.4}/easyapi/oauth/resources/device.py +0 -0
  70. {easyapi_django-1.1.2 → easyapi_django-1.1.4}/easyapi/oauth/resources/device_authorization.py +0 -0
  71. {easyapi_django-1.1.2 → easyapi_django-1.1.4}/easyapi/oauth/resources/register.py +0 -0
  72. {easyapi_django-1.1.2 → easyapi_django-1.1.4}/easyapi/oauth/resources/revoke.py +0 -0
  73. {easyapi_django-1.1.2 → easyapi_django-1.1.4}/easyapi/oauth/resources/token.py +0 -0
  74. {easyapi_django-1.1.2 → easyapi_django-1.1.4}/easyapi/oauth/routes.py +0 -0
  75. {easyapi_django-1.1.2 → easyapi_django-1.1.4}/easyapi/oauth/session.py +0 -0
  76. {easyapi_django-1.1.2 → easyapi_django-1.1.4}/easyapi/oauth/settings_helper.py +0 -0
  77. {easyapi_django-1.1.2 → easyapi_django-1.1.4}/easyapi/oauth/templates/zeromcp_oauth/consent.html +0 -0
  78. {easyapi_django-1.1.2 → easyapi_django-1.1.4}/easyapi/oauth/templates/zeromcp_oauth/device.html +0 -0
  79. {easyapi_django-1.1.2 → easyapi_django-1.1.4}/easyapi/oauth/tokens.py +0 -0
  80. {easyapi_django-1.1.2 → easyapi_django-1.1.4}/easyapi/openapi.py +0 -0
  81. {easyapi_django-1.1.2 → easyapi_django-1.1.4}/easyapi/orm/__init__.py +0 -0
  82. {easyapi_django-1.1.2 → easyapi_django-1.1.4}/easyapi/rate_limit.py +0 -0
  83. {easyapi_django-1.1.2 → easyapi_django-1.1.4}/easyapi/redis_config.py +0 -0
  84. {easyapi_django-1.1.2 → easyapi_django-1.1.4}/easyapi/routes.py +0 -0
  85. {easyapi_django-1.1.2 → easyapi_django-1.1.4}/easyapi/schemas.py +0 -0
  86. {easyapi_django-1.1.2 → easyapi_django-1.1.4}/easyapi/security.py +0 -0
  87. {easyapi_django-1.1.2 → easyapi_django-1.1.4}/easyapi/serializer.py +0 -0
  88. {easyapi_django-1.1.2 → easyapi_django-1.1.4}/easyapi/settings_helper.py +0 -0
  89. {easyapi_django-1.1.2 → easyapi_django-1.1.4}/easyapi/tenant/__init__.py +0 -0
  90. {easyapi_django-1.1.2 → easyapi_django-1.1.4}/easyapi/tenant/db_router.py +0 -0
  91. {easyapi_django-1.1.2 → easyapi_django-1.1.4}/easyapi/tenant/registry.py +0 -0
  92. {easyapi_django-1.1.2 → easyapi_django-1.1.4}/easyapi/tenant/tenant.py +0 -0
  93. {easyapi_django-1.1.2 → easyapi_django-1.1.4}/easyapi/util.py +0 -0
  94. {easyapi_django-1.1.2 → easyapi_django-1.1.4}/easyapi_django.egg-info/SOURCES.txt +0 -0
  95. {easyapi_django-1.1.2 → easyapi_django-1.1.4}/easyapi_django.egg-info/dependency_links.txt +0 -0
  96. {easyapi_django-1.1.2 → easyapi_django-1.1.4}/easyapi_django.egg-info/entry_points.txt +0 -0
  97. {easyapi_django-1.1.2 → easyapi_django-1.1.4}/easyapi_django.egg-info/requires.txt +0 -0
  98. {easyapi_django-1.1.2 → easyapi_django-1.1.4}/easyapi_django.egg-info/top_level.txt +0 -0
  99. {easyapi_django-1.1.2 → easyapi_django-1.1.4}/setup.cfg +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: easyapi-django
3
- Version: 1.1.2
3
+ Version: 1.1.4
4
4
  Summary: Zero-effort MCP server (and REST API) generator from Django models.
5
5
  Author-email: Stamatios Stamou Jr <bushier.outsets.0c@icloud.com>
6
6
  License-Expression: MIT
@@ -29,6 +29,7 @@ from .exception import HTTPException
29
29
  from .helpers import (
30
30
  LOCAL_HOST,
31
31
  get_edit_related,
32
+ get_list_related_selects,
32
33
  get_related_objects,
33
34
  get_tz,
34
35
  method_not_allowed,
@@ -1243,7 +1244,9 @@ class BaseResource(CoreResource):
1243
1244
  start = 0
1244
1245
 
1245
1246
  if self.list_related_fields:
1246
- self.queryset = self.queryset.select_related(*self.list_related_fields.keys())
1247
+ self.queryset = self.queryset.select_related(
1248
+ *get_list_related_selects(self.list_related_fields)
1249
+ )
1247
1250
 
1248
1251
  prefetch_fields = self.list_prefetch_related.keys() if self.list_prefetch_related else []
1249
1252
  if prefetch_fields:
@@ -446,7 +446,8 @@ class Filter(object):
446
446
  value = timezone.now() - timedelta(days=int(value))
447
447
  except ValueError:
448
448
  if len(value) > 10:
449
- value = datetime.strptime(value, '%Y-%m-%d')
449
+ from django.utils.dateparse import parse_datetime as _parse_dt
450
+ value = _parse_dt(value) or datetime.strptime(value[:10], '%Y-%m-%d')
450
451
  else:
451
452
  value = datetime.strptime(value, '%Y-%m-%d').date()
452
453
 
@@ -592,7 +593,10 @@ class Filter(object):
592
593
 
593
594
  # Decide whether to compare against empty values
594
595
  elif operator in ['isnull', 'isnotnull'] and coalesce:
595
- boolean = True if operator == 'isnull' else False
596
+ if operator == 'isnull':
597
+ boolean = value if isinstance(value, bool) else True
598
+ else:
599
+ boolean = not value if isinstance(value, bool) else False
596
600
  if _type in ['123', 'date', 'fk']:
597
601
  _filter = Q(**{'{}__isnull'.format(field): boolean})
598
602
  else:
@@ -2,6 +2,8 @@ import re
2
2
  import zoneinfo
3
3
  from functools import reduce
4
4
 
5
+ from django.core.exceptions import ObjectDoesNotExist
6
+
5
7
  from .exception import HTTPException
6
8
 
7
9
 
@@ -95,6 +97,59 @@ def get_edit_related(args, field):
95
97
  return args
96
98
 
97
99
 
100
+ def safe_related_getattr(obj, attr):
101
+ if obj is None:
102
+ return None
103
+ try:
104
+ return getattr(obj, attr, None)
105
+ except ObjectDoesNotExist:
106
+ return None
107
+
108
+
109
+ def set_nested_related_field(result, obj, field):
110
+ current_obj = obj
111
+ current_result = result
112
+ parts = field.split('__')
113
+
114
+ for index, part in enumerate(parts):
115
+ value = safe_related_getattr(current_obj, part)
116
+ last = index == len(parts) - 1
117
+
118
+ if last:
119
+ current_result[part] = value
120
+ return
121
+
122
+ if value is None:
123
+ current_result[part] = None
124
+ return
125
+
126
+ existing = current_result.get(part)
127
+ if not isinstance(existing, dict):
128
+ existing = {}
129
+ current_result[part] = existing
130
+
131
+ current_obj = value
132
+ current_result = existing
133
+
134
+
135
+ def get_list_related_selects(list_related_fields):
136
+ selects = []
137
+
138
+ def add(path):
139
+ if path and path not in selects:
140
+ selects.append(path)
141
+
142
+ for key, fields in list_related_fields.items():
143
+ add(key)
144
+ for field in fields:
145
+ if not isinstance(field, str) or '__' not in field:
146
+ continue
147
+ related_path = '__'.join(field.split('__')[:-1])
148
+ add(f'{key}__{related_path}')
149
+
150
+ return selects
151
+
152
+
98
153
  def get_related_objects(args, model):
99
154
  obj = args[0]
100
155
  result = args[1]
@@ -102,16 +157,19 @@ def get_related_objects(args, model):
102
157
  parent = args[3]
103
158
  related_models = args[4]
104
159
  related_fields = args[5]
105
- model_obj = getattr(obj, model, None)
160
+ model_obj = safe_related_getattr(obj, model)
106
161
 
107
162
  if count == 0:
108
163
  if model not in related_models:
109
164
  related_models[model] = []
110
165
 
111
- if model_obj:
166
+ if model_obj is not None:
112
167
  result[model] = {}
113
168
  for field in related_fields[parent]:
114
- result[model][field] = getattr(model_obj, field, None)
169
+ if isinstance(field, str) and '__' in field:
170
+ set_nested_related_field(result[model], model_obj, field)
171
+ else:
172
+ result[model][field] = safe_related_getattr(model_obj, field)
115
173
  related_models[model].append(field)
116
174
  else:
117
175
  result[model] = None
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: easyapi-django
3
- Version: 1.1.2
3
+ Version: 1.1.4
4
4
  Summary: Zero-effort MCP server (and REST API) generator from Django models.
5
5
  Author-email: Stamatios Stamou Jr <bushier.outsets.0c@icloud.com>
6
6
  License-Expression: MIT
@@ -4,7 +4,7 @@ build-backend = "setuptools.build_meta"
4
4
 
5
5
  [project]
6
6
  name = "easyapi-django"
7
- version = "1.1.2"
7
+ version = "1.1.4"
8
8
  authors = [
9
9
  { name="Stamatios Stamou Jr", email="bushier.outsets.0c@icloud.com" },
10
10
  ]
File without changes
File without changes
File without changes