django-tortoise-objects 0.1.0__py3-none-any.whl

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.
@@ -0,0 +1,184 @@
1
+ """
2
+ TortoiseObjects descriptor for the ``tortoise_objects`` attribute.
3
+
4
+ Provides a Django-model-attached descriptor that lazily initializes
5
+ Tortoise ORM and returns an async-capable queryset interface.
6
+ """
7
+
8
+ import logging
9
+
10
+ logger = logging.getLogger("django_tortoise")
11
+
12
+
13
+ class TortoiseObjects:
14
+ """
15
+ Descriptor/proxy that provides access to Tortoise QuerySet for a Django model.
16
+
17
+ Usage: ``MyDjangoModel.tortoise_objects.filter(name="foo")``
18
+
19
+ All async QuerySet operations automatically trigger lazy initialization
20
+ of Tortoise ORM connections on first use.
21
+ """
22
+
23
+ def __init__(self, tortoise_model_class):
24
+ self._tortoise_model = tortoise_model_class
25
+
26
+ def __get__(self, obj, objtype=None):
27
+ """
28
+ When accessed as class attribute, return self (the manager).
29
+ When accessed as instance attribute, also return self.
30
+ """
31
+ return self
32
+
33
+ @property
34
+ def model(self):
35
+ """The underlying Tortoise model class."""
36
+ return self._tortoise_model
37
+
38
+ def _get_queryset(self):
39
+ """Get a fresh Tortoise QuerySet for the model."""
40
+ return self._tortoise_model.all()
41
+
42
+ # --- QuerySet proxy methods ---
43
+ # Each method returns a _LazyQuerySet that ensures init before execution.
44
+
45
+ def all(self):
46
+ return _LazyQuerySet(self._tortoise_model, "all")
47
+
48
+ def filter(self, *args, **kwargs):
49
+ return _LazyQuerySet(self._tortoise_model, "filter", *args, **kwargs)
50
+
51
+ def exclude(self, *args, **kwargs):
52
+ return _LazyQuerySet(self._tortoise_model, "exclude", *args, **kwargs)
53
+
54
+ def get(self, *args, **kwargs):
55
+ return _LazyQuerySet(self._tortoise_model, "get", *args, **kwargs)
56
+
57
+ def create(self, **kwargs):
58
+ return _LazyQuerySet(self._tortoise_model, "create", **kwargs)
59
+
60
+ def first(self):
61
+ return _LazyQuerySet(self._tortoise_model, "first")
62
+
63
+ def count(self):
64
+ return _LazyQuerySet(self._tortoise_model, "count")
65
+
66
+ def exists(self):
67
+ return _LazyQuerySet(self._tortoise_model, "exists")
68
+
69
+ def values(self, *args, **kwargs):
70
+ return _LazyQuerySet(self._tortoise_model, "values", *args, **kwargs)
71
+
72
+ def values_list(self, *args, **kwargs):
73
+ return _LazyQuerySet(self._tortoise_model, "values_list", *args, **kwargs)
74
+
75
+ def delete(self):
76
+ return _LazyQuerySet(self._tortoise_model, "delete")
77
+
78
+ def update(self, **kwargs):
79
+ return _LazyQuerySet(self._tortoise_model, "update", **kwargs)
80
+
81
+ def get_or_create(self, **kwargs):
82
+ return _LazyQuerySet(self._tortoise_model, "get_or_create", **kwargs)
83
+
84
+ def bulk_create(self, objects, **kwargs):
85
+ return _LazyQuerySet(self._tortoise_model, "bulk_create", objects, **kwargs)
86
+
87
+
88
+ class _LazyQuerySet:
89
+ """
90
+ A lazy wrapper around a Tortoise QuerySet method call.
91
+
92
+ Ensures Tortoise is initialized before executing the query.
93
+ Supports both ``__await__`` (for direct ``await``) and chaining.
94
+ """
95
+
96
+ def __init__(self, tortoise_model, method_name, *args, **kwargs):
97
+ self._tortoise_model = tortoise_model
98
+ self._method_name = method_name
99
+ self._args = args
100
+ self._kwargs = kwargs
101
+ self._chain = [] # List of (method_name, args, kwargs) for chaining
102
+
103
+ def filter(self, *args, **kwargs):
104
+ self._chain.append(("filter", args, kwargs))
105
+ return self
106
+
107
+ def exclude(self, *args, **kwargs):
108
+ self._chain.append(("exclude", args, kwargs))
109
+ return self
110
+
111
+ def order_by(self, *args):
112
+ self._chain.append(("order_by", args, {}))
113
+ return self
114
+
115
+ def limit(self, limit):
116
+ self._chain.append(("limit", (limit,), {}))
117
+ return self
118
+
119
+ def offset(self, offset):
120
+ self._chain.append(("offset", (offset,), {}))
121
+ return self
122
+
123
+ def values(self, *args, **kwargs):
124
+ self._chain.append(("values", args, kwargs))
125
+ return self
126
+
127
+ def values_list(self, *args, **kwargs):
128
+ self._chain.append(("values_list", args, kwargs))
129
+ return self
130
+
131
+ def count(self):
132
+ self._chain.append(("count", (), {}))
133
+ return self
134
+
135
+ def first(self):
136
+ self._chain.append(("first", (), {}))
137
+ return self
138
+
139
+ def exists(self):
140
+ self._chain.append(("exists", (), {}))
141
+ return self
142
+
143
+ def delete(self):
144
+ self._chain.append(("delete", (), {}))
145
+ return self
146
+
147
+ def update(self, **kwargs):
148
+ self._chain.append(("update", (), kwargs))
149
+ return self
150
+
151
+ async def _execute(self):
152
+ """Execute the query chain with lazy initialization."""
153
+ from django_tortoise.initialization import ensure_initialized
154
+
155
+ await ensure_initialized()
156
+
157
+ # Start with the initial method call
158
+ qs = getattr(self._tortoise_model, self._method_name)(*self._args, **self._kwargs)
159
+
160
+ # Apply chain
161
+ for method_name, args, kwargs in self._chain:
162
+ qs = getattr(qs, method_name)(*args, **kwargs)
163
+
164
+ # If the result is awaitable (queryset), await it
165
+ if hasattr(qs, "__await__"):
166
+ return await qs
167
+ return qs
168
+
169
+ def __await__(self):
170
+ return self._execute().__await__()
171
+
172
+ def __aiter__(self):
173
+ return self._async_iter()
174
+
175
+ async def _async_iter(self):
176
+ result = await self._execute()
177
+ if hasattr(result, "__aiter__"):
178
+ async for item in result:
179
+ yield item
180
+ elif hasattr(result, "__iter__"):
181
+ for item in result:
182
+ yield item
183
+ else:
184
+ yield result
@@ -0,0 +1,123 @@
1
+ """
2
+ Central registry of Django-to-Tortoise model mappings.
3
+
4
+ Maintains a bidirectional mapping between Django model classes and their
5
+ dynamically generated Tortoise ORM counterparts. Provides lookup by
6
+ Django model class, Tortoise model class, or ``"app_label.ModelName"``
7
+ label string.
8
+
9
+ A module-level ``model_registry`` singleton is the single source of truth.
10
+ The public ``get_tortoise_model()`` function is a convenience wrapper
11
+ re-exported by ``django_tortoise.__init__``.
12
+ """
13
+
14
+ from __future__ import annotations
15
+
16
+ import logging
17
+ from typing import TYPE_CHECKING
18
+
19
+ if TYPE_CHECKING:
20
+ from tortoise.models import Model as TortoiseModel
21
+
22
+ logger = logging.getLogger("django_tortoise")
23
+
24
+
25
+ class ModelRegistry:
26
+ """Central registry mapping Django models to Tortoise models."""
27
+
28
+ def __init__(self) -> None:
29
+ self._django_to_tortoise: dict[type, type[TortoiseModel]] = {}
30
+ self._tortoise_to_django: dict[type[TortoiseModel], type] = {}
31
+ self._by_label: dict[str, type[TortoiseModel]] = {} # "app_label.ModelName" -> Tortoise
32
+ self._tortoise_models: list[type[TortoiseModel]] = []
33
+
34
+ def register(
35
+ self,
36
+ django_model: type,
37
+ tortoise_model: type[TortoiseModel],
38
+ label: str,
39
+ ) -> None:
40
+ """Register a Django <-> Tortoise model pair."""
41
+ self._django_to_tortoise[django_model] = tortoise_model
42
+ self._tortoise_to_django[tortoise_model] = django_model
43
+ self._by_label[label] = tortoise_model
44
+ self._tortoise_models.append(tortoise_model)
45
+ logger.debug("Registered: %s -> %s", label, tortoise_model.__name__)
46
+
47
+ def get_tortoise_model(self, django_model: type) -> type[TortoiseModel] | None:
48
+ """Get the Tortoise model for a Django model."""
49
+ return self._django_to_tortoise.get(django_model)
50
+
51
+ def get_django_model(self, tortoise_model: type[TortoiseModel]) -> type | None:
52
+ """Get the Django model for a Tortoise model."""
53
+ return self._tortoise_to_django.get(tortoise_model)
54
+
55
+ def get_by_label(self, label: str) -> type[TortoiseModel] | None:
56
+ """Get Tortoise model by ``'app_label.ModelName'`` string."""
57
+ return self._by_label.get(label)
58
+
59
+ def get_all_tortoise_models(self) -> list[type[TortoiseModel]]:
60
+ """Get all registered Tortoise models."""
61
+ return list(self._tortoise_models)
62
+
63
+ def get_all_mappings(self) -> dict[type, type[TortoiseModel]]:
64
+ """Return a copy of the full Django -> Tortoise model registry."""
65
+ return dict(self._django_to_tortoise)
66
+
67
+ def is_registered(self, django_model: type) -> bool:
68
+ """Check if a Django model has a Tortoise counterpart."""
69
+ return django_model in self._django_to_tortoise
70
+
71
+ def clear(self) -> None:
72
+ """Clear all registrations. Primarily used in testing."""
73
+ self._django_to_tortoise.clear()
74
+ self._tortoise_to_django.clear()
75
+ self._by_label.clear()
76
+ self._tortoise_models.clear()
77
+
78
+
79
+ # Global singleton instance
80
+ model_registry = ModelRegistry()
81
+
82
+
83
+ # ---------------------------------------------------------------------------
84
+ # Public convenience functions (re-exported by django_tortoise.__init__)
85
+ # ---------------------------------------------------------------------------
86
+
87
+
88
+ def register_model(
89
+ django_model: type,
90
+ tortoise_model: type[TortoiseModel],
91
+ label: str | None = None,
92
+ ) -> None:
93
+ """
94
+ Register a Django -> Tortoise model mapping.
95
+
96
+ If *label* is not provided, it is derived from the Django model's
97
+ ``_meta.app_label`` and class name.
98
+ """
99
+ if label is None:
100
+ try:
101
+ label = f"{django_model._meta.app_label}.{django_model.__name__}" # type: ignore[attr-defined]
102
+ except AttributeError:
103
+ label = django_model.__name__
104
+ model_registry.register(django_model, tortoise_model, label)
105
+
106
+
107
+ def get_tortoise_model(django_model: type) -> type[TortoiseModel] | None:
108
+ """
109
+ Retrieve the Tortoise ORM model class generated for the given Django model.
110
+
111
+ Returns ``None`` if no mapping has been registered for this model.
112
+ """
113
+ return model_registry.get_tortoise_model(django_model)
114
+
115
+
116
+ def get_all_mappings() -> dict[type, type[TortoiseModel]]:
117
+ """Return a copy of the full Django -> Tortoise model registry."""
118
+ return model_registry.get_all_mappings()
119
+
120
+
121
+ def clear_registry() -> None:
122
+ """Clear all registered mappings. Primarily used in testing."""
123
+ model_registry.clear()
@@ -0,0 +1,302 @@
1
+ Metadata-Version: 2.4
2
+ Name: django-tortoise-objects
3
+ Version: 0.1.0
4
+ Summary: Bridge Django ORM models to Tortoise ORM for truly async database access
5
+ Project-URL: Homepage, https://github.com/tortoise/django-tortoise-objects
6
+ Project-URL: Repository, https://github.com/tortoise/django-tortoise-objects
7
+ Project-URL: Issues, https://github.com/tortoise/django-tortoise-objects/issues
8
+ Author: Andrei Bondar
9
+ License: Apache-2.0
10
+ License-File: LICENSE
11
+ Keywords: async,database,django,orm,tortoise-orm
12
+ Classifier: Development Status :: 4 - Beta
13
+ Classifier: Framework :: Django
14
+ Classifier: Framework :: Django :: 4.2
15
+ Classifier: Framework :: Django :: 5.0
16
+ Classifier: Framework :: Django :: 5.1
17
+ Classifier: Intended Audience :: Developers
18
+ Classifier: License :: OSI Approved :: Apache Software License
19
+ Classifier: Programming Language :: Python :: 3
20
+ Classifier: Programming Language :: Python :: 3.10
21
+ Classifier: Programming Language :: Python :: 3.11
22
+ Classifier: Programming Language :: Python :: 3.12
23
+ Classifier: Programming Language :: Python :: 3.13
24
+ Classifier: Topic :: Database
25
+ Classifier: Topic :: Software Development :: Libraries :: Python Modules
26
+ Classifier: Typing :: Typed
27
+ Requires-Python: >=3.10
28
+ Requires-Dist: django>=4.2
29
+ Requires-Dist: tortoise-orm>=1.1.2
30
+ Provides-Extra: all
31
+ Requires-Dist: aiosqlite>=0.19; extra == 'all'
32
+ Requires-Dist: asyncmy>=0.2; extra == 'all'
33
+ Requires-Dist: psycopg[binary]>=3.1; extra == 'all'
34
+ Provides-Extra: dev
35
+ Requires-Dist: aiosqlite>=0.19; extra == 'dev'
36
+ Requires-Dist: django-stubs>=5.1; extra == 'dev'
37
+ Requires-Dist: mypy>=1.14; extra == 'dev'
38
+ Requires-Dist: pytest-asyncio>=0.21; extra == 'dev'
39
+ Requires-Dist: pytest-django>=4.5; extra == 'dev'
40
+ Requires-Dist: pytest>=7.0; extra == 'dev'
41
+ Requires-Dist: ruff>=0.9; extra == 'dev'
42
+ Provides-Extra: mysql
43
+ Requires-Dist: asyncmy>=0.2; extra == 'mysql'
44
+ Provides-Extra: pg
45
+ Requires-Dist: psycopg[binary]>=3.1; extra == 'pg'
46
+ Provides-Extra: sqlite
47
+ Requires-Dist: aiosqlite>=0.19; extra == 'sqlite'
48
+ Description-Content-Type: text/markdown
49
+
50
+ # django-tortoise-objects
51
+
52
+ Bridge Django ORM models to [Tortoise ORM](https://github.com/tortoise/tortoise-orm) for truly async database access. Define your models once in Django, query them asynchronously via Tortoise — no duplicate schema, no manual sync.
53
+
54
+ ## Why?
55
+
56
+ Django's async ORM wraps synchronous calls in `sync_to_async` threads. Tortoise ORM is natively async but requires its own model definitions. This library eliminates that trade-off:
57
+
58
+ - **Single source of truth** — Django models define the schema, migrations, and admin
59
+ - **Truly async queries** — Tortoise ORM handles the actual database I/O with native async drivers
60
+ - **Zero boilerplate** — Add to `INSTALLED_APPS`, and every model gets a `tortoise_objects` manager
61
+
62
+ ## Installation
63
+
64
+ ```bash
65
+ pip install django-tortoise-objects
66
+ ```
67
+
68
+ With database drivers:
69
+
70
+ ```bash
71
+ # PostgreSQL
72
+ pip install django-tortoise-objects[pg]
73
+
74
+ # SQLite
75
+ pip install django-tortoise-objects[sqlite]
76
+
77
+ # MySQL
78
+ pip install django-tortoise-objects[mysql]
79
+
80
+ # All drivers
81
+ pip install django-tortoise-objects[all]
82
+ ```
83
+
84
+ ## Quick Start
85
+
86
+ ### 1. Add to INSTALLED_APPS
87
+
88
+ ```python
89
+ INSTALLED_APPS = [
90
+ # ...
91
+ "django_tortoise",
92
+ # ... your apps
93
+ ]
94
+ ```
95
+
96
+ ### 2. Query your existing Django models asynchronously
97
+
98
+ ```python
99
+ from myapp.models import Article
100
+
101
+ # All standard query operations — fully async, no sync_to_async wrapper
102
+ articles = await Article.tortoise_objects.filter(published=True)
103
+ article = await Article.tortoise_objects.get(id=42)
104
+ count = await Article.tortoise_objects.count()
105
+ exists = await Article.tortoise_objects.filter(title="foo").exists()
106
+
107
+ # Create, update, delete
108
+ article = await Article.tortoise_objects.create(title="Hello", body="World")
109
+ await Article.tortoise_objects.filter(published=False).update(draft=True)
110
+ await Article.tortoise_objects.filter(id=42).delete()
111
+
112
+ # Chaining
113
+ recent = await Article.tortoise_objects.filter(published=True).order_by("-created_at").limit(10)
114
+
115
+ # Prefetch relations (via underlying Tortoise model)
116
+ employees = await Employee.tortoise_objects.model.all().prefetch_related("team")
117
+ ```
118
+
119
+ ### 3. Use in async Django views
120
+
121
+ ```python
122
+ from django.http import JsonResponse
123
+ from myapp.models import Tag
124
+
125
+ async def tag_list(request):
126
+ tags = await Tag.tortoise_objects.all()
127
+ return JsonResponse({"tags": [{"id": t.id, "name": t.name} for t in tags]})
128
+ ```
129
+
130
+ That's it. No Tortoise model definitions, no configuration files, no manual initialization.
131
+
132
+ ## Configuration
133
+
134
+ Optional settings via `TORTOISE_OBJECTS` in your Django settings:
135
+
136
+ ```python
137
+ TORTOISE_OBJECTS = {
138
+ # Include only specific app models (fnmatch patterns)
139
+ "INCLUDE_MODELS": ["myapp.*", "blog.*"],
140
+
141
+ # Exclude specific models
142
+ "EXCLUDE_MODELS": ["auth.*", "admin.*"],
143
+
144
+ # Override database backend mapping
145
+ "DB_ENGINE_MAP": {
146
+ "django.db.backends.postgresql": "tortoise.backends.psycopg",
147
+ },
148
+
149
+ # Connection pool settings per database alias
150
+ "CONNECTION_POOL": {
151
+ "default": {"minsize": 5, "maxsize": 20},
152
+ },
153
+
154
+ # Logging level
155
+ "LOG_LEVEL": "WARNING",
156
+ }
157
+ ```
158
+
159
+ All settings are optional. With no configuration, all models are included and database backends are auto-detected from Django's `DATABASES` setting.
160
+
161
+ ## Supported Field Types
162
+
163
+ | Django Field | Tortoise Field |
164
+ |---|---|
165
+ | CharField, SlugField, EmailField, URLField | CharField |
166
+ | TextField | TextField |
167
+ | IntegerField, BigIntegerField, SmallIntegerField | IntField, BigIntField, SmallIntField |
168
+ | BooleanField | BooleanField |
169
+ | FloatField | FloatField |
170
+ | DecimalField | DecimalField |
171
+ | DateField, DateTimeField, TimeField | DateField, DatetimeField, TimeField |
172
+ | DurationField | TimeDeltaField |
173
+ | UUIDField | UUIDField |
174
+ | JSONField | JSONField |
175
+ | BinaryField | BinaryField |
176
+ | FileField, ImageField | CharField (stores path) |
177
+ | ForeignKey | ForeignKeyField |
178
+ | OneToOneField | OneToOneField |
179
+ | ManyToManyField | ManyToManyField |
180
+ | AutoField, BigAutoField, SmallAutoField | IntField/BigIntField/SmallIntField (pk=True) |
181
+
182
+ ## ASGI Lifespan
183
+
184
+ For production ASGI deployments, explicitly manage Tortoise connections:
185
+
186
+ ```python
187
+ # asgi.py
188
+ from django_tortoise import init, close
189
+
190
+ async def lifespan(scope, receive, send):
191
+ if scope["type"] == "lifespan":
192
+ while True:
193
+ message = await receive()
194
+ if message["type"] == "lifespan.startup":
195
+ await init()
196
+ await send({"type": "lifespan.startup.complete"})
197
+ elif message["type"] == "lifespan.shutdown":
198
+ await close()
199
+ await send({"type": "lifespan.shutdown.complete"})
200
+ return
201
+ ```
202
+
203
+ If you don't set up lifespan, connections are initialized lazily on first query.
204
+
205
+ ## Generating Static Tortoise Models
206
+
207
+ By default, Tortoise models are generated dynamically at runtime. If you want static files you can inspect, customize, or version-control, use the management command:
208
+
209
+ ```bash
210
+ # Generate for all apps
211
+ python manage.py generate_tortoise_models --output-dir ./tortoise_models
212
+
213
+ # Generate for a specific app
214
+ python manage.py generate_tortoise_models --app-label demo --output-dir .
215
+
216
+ # Custom Tortoise app name
217
+ python manage.py generate_tortoise_models --tortoise-app-name myapp
218
+ ```
219
+
220
+ This produces one file per Django app (e.g., `tortoise_models_demo.py`):
221
+
222
+ ```python
223
+ # Auto-generated by django-tortoise-objects. Do not edit manually.
224
+
225
+ import uuid
226
+
227
+ from tortoise import fields
228
+ from tortoise.fields.relational import OnDelete
229
+ from tortoise.models import Model
230
+
231
+
232
+ class DepartmentTortoise(Model):
233
+ id = fields.BigIntField(primary_key=True, generated=True)
234
+ name = fields.CharField(max_length=200)
235
+ code = fields.CharField(unique=True, max_length=20)
236
+ budget = fields.DecimalField(default=0, max_digits=14, decimal_places=2)
237
+ is_active = fields.BooleanField(default=True)
238
+
239
+ class Meta:
240
+ table = "demo_department"
241
+ app = "django_tortoise"
242
+
243
+
244
+ class TeamTortoise(Model):
245
+ id = fields.BigIntField(primary_key=True, generated=True)
246
+ name = fields.CharField(max_length=200)
247
+ department = fields.ForeignKeyField(
248
+ "django_tortoise.DepartmentTortoise",
249
+ related_name='teams',
250
+ on_delete=OnDelete.CASCADE,
251
+ source_field='department_id',
252
+ )
253
+
254
+ class Meta:
255
+ table = "demo_team"
256
+ app = "django_tortoise"
257
+ ```
258
+
259
+ The command respects `INCLUDE_MODELS` and `EXCLUDE_MODELS` from your `TORTOISE_OBJECTS` settings.
260
+
261
+ ## Limitations & Non-Goals
262
+
263
+ **Query results are Tortoise model instances, not Django models.** Methods like `tortoise_objects.get()` and `tortoise_objects.filter()` return Tortoise ORM objects. They cannot be passed directly to Django forms, serializers, admin, or template tags that expect Django model instances. Use `tortoise_objects` for async read/write paths (APIs, WebSockets, background tasks) and the regular Django ORM for everything else.
264
+
265
+ This library is **not** intended to:
266
+
267
+ - **Replace Django ORM** — it is a complementary tool for async-critical paths, not a full substitute. Django ORM remains the right choice for admin, forms, management commands, and sync views.
268
+ - **Manage schema or migrations** — all schema management is delegated to Django. Tortoise never writes to your database schema.
269
+ - **Support cross-ORM transactions** — you cannot mix Django and Tortoise queries in a single database transaction.
270
+ - **Provide Django admin integration** — Tortoise query results don't work with Django's admin site. Use Django's ORM for admin.
271
+ - **Expose Tortoise-specific features** — Tortoise signals, custom managers, and validators are not bridged.
272
+ - **Generate Django models from Tortoise** — the bridge is one-way only (Django → Tortoise).
273
+
274
+ **Other things to keep in mind:**
275
+
276
+ - Tortoise maintains its own connection pool, separate from Django's. Configure pool sizes via the `CONNECTION_POOL` setting to avoid excess connections.
277
+ - Unsupported or custom Django field types are silently skipped during model generation. Check logs at `DEBUG` level if a field is missing.
278
+ - ManyToManyField relations require that the related model is also included in the Tortoise bridge (not excluded via `EXCLUDE_MODELS`).
279
+
280
+ ## Performance
281
+
282
+ Benchmarks comparing `tortoise_objects` vs Django's native async ORM (`aget`, `acreate`, etc.) on the same models and data. Measured on PostgreSQL (both using psycopg).
283
+
284
+ ### PostgreSQL
285
+
286
+ ![Benchmark results — PostgreSQL](example_project/diagrams/bench_postgres_summary.png)
287
+
288
+ - `tortoise_objects` wins on single-record ops: `get` 1.3-2.0x, `count` 1.3-1.5x, `exists` 1.5x
289
+ - `tortoise_objects` wins on writes: `create+delete` 1.4-2.6x
290
+ - Bulk fetches nearly tied
291
+
292
+ See [`example_project/README.md`](example_project/README.md) for full benchmark details, methodology, and raw data.
293
+
294
+ ## Requirements
295
+
296
+ - Python >= 3.10
297
+ - Django >= 4.2
298
+ - Tortoise ORM >= 1.1.2
299
+
300
+ ## License
301
+
302
+ Apache 2.0 — see [LICENSE](LICENSE) for details.
@@ -0,0 +1,20 @@
1
+ django_tortoise/__init__.py,sha256=xzz7Ik8KOFbGG_unJHBmMkebf0uNcGi0MBizM5gzeps,689
2
+ django_tortoise/_models.py,sha256=aVTW3RjLPzBzVE66y1laGqMdbojal8W9WHiAgwPMXqk,478
3
+ django_tortoise/apps.py,sha256=3Xlyrot4GCW6SGoMdJGEDmzJX1dLDeF0U44vkhFQRds,4116
4
+ django_tortoise/code_generator.py,sha256=Ao5acrJYw01wwLg6bbWZE9iNGRV81oxl7vc4Ifwqtcg,21544
5
+ django_tortoise/conf.py,sha256=AlmPg5ckYOSEmgnXpVyW10zTmbon_UvLOczt0CHwZfw,2503
6
+ django_tortoise/db_config.py,sha256=-SyesMFKrkvrBNhkV3sr7b7DhRjGlItJrHq-L_-IXB0,3127
7
+ django_tortoise/exceptions.py,sha256=7xgmf70kTSmhnAg-23NdjGHZ8urNogBKfd5yCWp-y54,763
8
+ django_tortoise/fields.py,sha256=ldrF5YH44Guzg3ylZr9RLe58PYZQAwHGJrQQHCLXP2w,16893
9
+ django_tortoise/generator.py,sha256=rS6adLXeVTr84aWpB-f_jKH9ifUtOhgRq3kLU3v2_ek,6606
10
+ django_tortoise/initialization.py,sha256=zAWRHPdMHPcAUlRPXESQ6A3klsBuAbKC3A2Rrc5POSc,2841
11
+ django_tortoise/introspection.py,sha256=MicVL6UFxKU4_WEnsjokLXoB8LP_BMopM8tTNK-SgUI,9094
12
+ django_tortoise/manager.py,sha256=olaStapBU9FlQ2lxlVgEkdtQ4n7IeiBrowZvKOhZ6SY,5553
13
+ django_tortoise/registry.py,sha256=6PIlWM8KvDvF3QhwQ8nvRdrXmORMVVFBBT8uYUnn498,4465
14
+ django_tortoise/management/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
15
+ django_tortoise/management/commands/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
16
+ django_tortoise/management/commands/generate_tortoise_models.py,sha256=UaIigB4jgzvLq9DtOq3Gevn6q57HnH-hAdeGy7waaKQ,4063
17
+ django_tortoise_objects-0.1.0.dist-info/METADATA,sha256=x-O67hPVa2vCtMzKT_Ub3w_v03_nYF_6FFzJ300XwN4,10914
18
+ django_tortoise_objects-0.1.0.dist-info/WHEEL,sha256=WLgqFyCfm_KASv4WHyYy0P3pM_m7J5L9k2skdKLirC8,87
19
+ django_tortoise_objects-0.1.0.dist-info/licenses/LICENSE,sha256=nMXg2ykzQ9M9PgmkGBI-CJOY2yQ7alX5EcDugUFV1VY,10765
20
+ django_tortoise_objects-0.1.0.dist-info/RECORD,,
@@ -0,0 +1,4 @@
1
+ Wheel-Version: 1.0
2
+ Generator: hatchling 1.28.0
3
+ Root-Is-Purelib: true
4
+ Tag: py3-none-any