django-fsspec 0.1.0__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 (54) hide show
  1. django_fsspec-0.1.0/.github/workflows/ci.yml +366 -0
  2. django_fsspec-0.1.0/.gitignore +9 -0
  3. django_fsspec-0.1.0/CHANGELOG.md +18 -0
  4. django_fsspec-0.1.0/LICENSE +21 -0
  5. django_fsspec-0.1.0/PKG-INFO +109 -0
  6. django_fsspec-0.1.0/README.md +94 -0
  7. django_fsspec-0.1.0/README_zh.md +94 -0
  8. django_fsspec-0.1.0/benchmarks/e2e_test.py +881 -0
  9. django_fsspec-0.1.0/benchmarks/run.py +467 -0
  10. django_fsspec-0.1.0/benchmarks/settings.py +65 -0
  11. django_fsspec-0.1.0/django_fsspec/__init__.py +1 -0
  12. django_fsspec-0.1.0/django_fsspec/_version.py +24 -0
  13. django_fsspec-0.1.0/django_fsspec/admin.py +11 -0
  14. django_fsspec-0.1.0/django_fsspec/apps.py +7 -0
  15. django_fsspec-0.1.0/django_fsspec/buffer.py +74 -0
  16. django_fsspec-0.1.0/django_fsspec/exceptions.py +14 -0
  17. django_fsspec-0.1.0/django_fsspec/fs.py +102 -0
  18. django_fsspec-0.1.0/django_fsspec/management/__init__.py +0 -0
  19. django_fsspec-0.1.0/django_fsspec/management/commands/__init__.py +0 -0
  20. django_fsspec-0.1.0/django_fsspec/management/commands/fsspec_fsck.py +100 -0
  21. django_fsspec-0.1.0/django_fsspec/management/commands/fsspec_gc.py +49 -0
  22. django_fsspec-0.1.0/django_fsspec/management/commands/fsspec_stats.py +69 -0
  23. django_fsspec-0.1.0/django_fsspec/migrations/0001_initial.py +57 -0
  24. django_fsspec-0.1.0/django_fsspec/migrations/__init__.py +0 -0
  25. django_fsspec-0.1.0/django_fsspec/migrations_ops.py +96 -0
  26. django_fsspec-0.1.0/django_fsspec/models.py +58 -0
  27. django_fsspec-0.1.0/django_fsspec/operations.py +437 -0
  28. django_fsspec-0.1.0/django_fsspec/tests/__init__.py +0 -0
  29. django_fsspec-0.1.0/django_fsspec/tests/test_admin.py +29 -0
  30. django_fsspec-0.1.0/django_fsspec/tests/test_commands.py +194 -0
  31. django_fsspec-0.1.0/django_fsspec/tests/test_fs.py +220 -0
  32. django_fsspec-0.1.0/django_fsspec/tests/test_migrations_ops.py +118 -0
  33. django_fsspec-0.1.0/django_fsspec/tests/test_models.py +103 -0
  34. django_fsspec-0.1.0/django_fsspec/tests/test_operations.py +425 -0
  35. django_fsspec-0.1.0/django_fsspec/tests/test_validators.py +89 -0
  36. django_fsspec-0.1.0/django_fsspec/validators.py +52 -0
  37. django_fsspec-0.1.0/docker-compose.yml +30 -0
  38. django_fsspec-0.1.0/docs/en/architecture.md +99 -0
  39. django_fsspec-0.1.0/docs/en/configuration.md +27 -0
  40. django_fsspec-0.1.0/docs/en/exceptions.md +52 -0
  41. django_fsspec-0.1.0/docs/en/getting-started.md +68 -0
  42. django_fsspec-0.1.0/docs/en/management-commands.md +67 -0
  43. django_fsspec-0.1.0/docs/en/migration-guide.md +57 -0
  44. django_fsspec-0.1.0/docs/en/usage.md +69 -0
  45. django_fsspec-0.1.0/docs/zh/architecture.md +99 -0
  46. django_fsspec-0.1.0/docs/zh/configuration.md +27 -0
  47. django_fsspec-0.1.0/docs/zh/exceptions.md +52 -0
  48. django_fsspec-0.1.0/docs/zh/getting-started.md +69 -0
  49. django_fsspec-0.1.0/docs/zh/management-commands.md +69 -0
  50. django_fsspec-0.1.0/docs/zh/migration-guide.md +56 -0
  51. django_fsspec-0.1.0/docs/zh/usage.md +69 -0
  52. django_fsspec-0.1.0/pyproject.toml +47 -0
  53. django_fsspec-0.1.0/tests/__init__.py +0 -0
  54. django_fsspec-0.1.0/tests/settings.py +37 -0
@@ -0,0 +1,366 @@
1
+ name: CI
2
+
3
+ on:
4
+ push:
5
+ branches: [main]
6
+ tags: ["v*"]
7
+ pull_request:
8
+ branches: [main]
9
+
10
+ jobs:
11
+ # ---------------------------------------------------------------
12
+ # Stage 1: Unit tests (SQLite, fast, matrix)
13
+ # ---------------------------------------------------------------
14
+ unit-test:
15
+ runs-on: ubuntu-latest
16
+ strategy:
17
+ matrix:
18
+ python-version: ["3.11", "3.12", "3.13"]
19
+ django-version: ["3.2", "4.2", "5.2"]
20
+ exclude:
21
+ - python-version: "3.12"
22
+ django-version: "3.2"
23
+ - python-version: "3.13"
24
+ django-version: "3.2"
25
+
26
+ steps:
27
+ - uses: actions/checkout@v4
28
+
29
+ - name: Set up Python ${{ matrix.python-version }}
30
+ uses: actions/setup-python@v5
31
+ with:
32
+ python-version: ${{ matrix.python-version }}
33
+
34
+ - name: Install dependencies
35
+ run: |
36
+ pip install -e ".[dev]"
37
+ pip install "django~=${{ matrix.django-version }}.0"
38
+
39
+ - name: Run unit tests
40
+ run: |
41
+ python -m pytest django_fsspec/tests/ -v --cov=django_fsspec --cov-report=term-missing
42
+
43
+ lint:
44
+ runs-on: ubuntu-latest
45
+ steps:
46
+ - uses: actions/checkout@v4
47
+
48
+ - name: Set up Python
49
+ uses: actions/setup-python@v5
50
+ with:
51
+ python-version: "3.11"
52
+
53
+ - name: Install dependencies
54
+ run: pip install -e ".[dev]"
55
+
56
+ - name: Check migrations
57
+ run: |
58
+ DJANGO_SETTINGS_MODULE=tests.settings python -m django makemigrations --check --dry-run
59
+
60
+ # ---------------------------------------------------------------
61
+ # Stage 2: E2E + Benchmark (real databases, after unit tests pass)
62
+ # ---------------------------------------------------------------
63
+ e2e-sqlite:
64
+ needs: unit-test
65
+ runs-on: ubuntu-latest
66
+ steps:
67
+ - uses: actions/checkout@v4
68
+
69
+ - name: Set up Python
70
+ uses: actions/setup-python@v5
71
+ with:
72
+ python-version: "3.11"
73
+
74
+ - name: Install dependencies
75
+ run: pip install -e ".[dev]"
76
+
77
+ - name: E2E tests
78
+ run: DJANGO_FSSPEC_BENCH_DB=sqlite python benchmarks/e2e_test.py
79
+
80
+ - name: Benchmark
81
+ run: DJANGO_FSSPEC_BENCH_DB=sqlite python benchmarks/run.py --db sqlite --json benchmarks/results-sqlite.json
82
+
83
+ - name: Upload results
84
+ uses: actions/upload-artifact@v4
85
+ with:
86
+ name: benchmark-sqlite
87
+ path: benchmarks/results-sqlite.json
88
+
89
+ e2e-mysql:
90
+ needs: unit-test
91
+ runs-on: ubuntu-latest
92
+ strategy:
93
+ fail-fast: false
94
+ matrix:
95
+ include:
96
+ - mysql-version: "5.7"
97
+ django-version: "3.2"
98
+ - mysql-version: "8.0"
99
+ django-version: "5.2"
100
+ services:
101
+ mysql:
102
+ image: mysql:${{ matrix.mysql-version }}
103
+ env:
104
+ MYSQL_ROOT_PASSWORD: fsspec_test
105
+ MYSQL_DATABASE: fsspec_test
106
+ MYSQL_USER: fsspec
107
+ MYSQL_PASSWORD: fsspec_test
108
+ ports:
109
+ - 3306:3306
110
+ options: >-
111
+ --health-cmd="mysqladmin ping -h localhost -u root -pfsspec_test"
112
+ --health-interval=5s
113
+ --health-timeout=3s
114
+ --health-retries=10
115
+
116
+ steps:
117
+ - uses: actions/checkout@v4
118
+
119
+ - name: Set up Python
120
+ uses: actions/setup-python@v5
121
+ with:
122
+ python-version: "3.11"
123
+
124
+ - name: Configure MySQL charset
125
+ run: |
126
+ for i in 1 2 3 4 5; do
127
+ mysql -h 127.0.0.1 -P 3306 -u root -pfsspec_test -e "
128
+ ALTER DATABASE fsspec_test CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci;
129
+ " && break || sleep 3
130
+ done
131
+
132
+ - name: Install dependencies
133
+ run: |
134
+ pip install -e ".[dev]"
135
+ pip install mysqlclient
136
+ pip install "django~=${{ matrix.django-version }}.0"
137
+
138
+ - name: E2E tests
139
+ env:
140
+ DJANGO_FSSPEC_BENCH_DB: mysql
141
+ MYSQL_PORT: "3306"
142
+ run: python benchmarks/e2e_test.py
143
+
144
+ - name: Benchmark
145
+ env:
146
+ DJANGO_FSSPEC_BENCH_DB: mysql
147
+ MYSQL_PORT: "3306"
148
+ run: python benchmarks/run.py --db mysql --json benchmarks/results-mysql-${{ matrix.mysql-version }}.json
149
+
150
+ - name: Upload results
151
+ uses: actions/upload-artifact@v4
152
+ with:
153
+ name: benchmark-mysql-${{ matrix.mysql-version }}
154
+ path: benchmarks/results-mysql-${{ matrix.mysql-version }}.json
155
+
156
+ e2e-postgres:
157
+ needs: unit-test
158
+ runs-on: ubuntu-latest
159
+ strategy:
160
+ fail-fast: false
161
+ matrix:
162
+ include:
163
+ - pg-version: "9.6"
164
+ django-version: "3.2"
165
+ - pg-version: "16"
166
+ django-version: "5.2"
167
+ services:
168
+ postgres:
169
+ image: postgres:${{ matrix.pg-version }}
170
+ env:
171
+ POSTGRES_DB: fsspec_test
172
+ POSTGRES_USER: fsspec
173
+ POSTGRES_PASSWORD: fsspec_test
174
+ ports:
175
+ - 5432:5432
176
+ options: >-
177
+ --health-cmd="pg_isready -U fsspec -d fsspec_test"
178
+ --health-interval=5s
179
+ --health-timeout=3s
180
+ --health-retries=10
181
+
182
+ steps:
183
+ - uses: actions/checkout@v4
184
+
185
+ - name: Set up Python
186
+ uses: actions/setup-python@v5
187
+ with:
188
+ python-version: "3.11"
189
+
190
+ - name: Install dependencies
191
+ run: |
192
+ pip install -e ".[dev]"
193
+ pip install psycopg2-binary
194
+ pip install "django~=${{ matrix.django-version }}.0"
195
+
196
+ - name: E2E tests
197
+ env:
198
+ DJANGO_FSSPEC_BENCH_DB: postgres
199
+ POSTGRES_PORT: "5432"
200
+ run: python benchmarks/e2e_test.py
201
+
202
+ - name: Benchmark
203
+ env:
204
+ DJANGO_FSSPEC_BENCH_DB: postgres
205
+ POSTGRES_PORT: "5432"
206
+ run: python benchmarks/run.py --db postgres --json benchmarks/results-postgres-${{ matrix.pg-version }}.json
207
+
208
+ - name: Upload results
209
+ uses: actions/upload-artifact@v4
210
+ with:
211
+ name: benchmark-postgres-${{ matrix.pg-version }}
212
+ path: benchmarks/results-postgres-${{ matrix.pg-version }}.json
213
+
214
+ e2e-oracle:
215
+ needs: unit-test
216
+ runs-on: ubuntu-latest
217
+ services:
218
+ oracle:
219
+ image: gvenzl/oracle-free:23-slim
220
+ env:
221
+ ORACLE_PASSWORD: fsspec_test
222
+ APP_USER: fsspec
223
+ APP_USER_PASSWORD: fsspec_test
224
+ ports:
225
+ - 1521:1521
226
+ options: >-
227
+ --health-cmd="healthcheck.sh"
228
+ --health-interval=10s
229
+ --health-timeout=5s
230
+ --health-retries=30
231
+ --health-start-period=60s
232
+
233
+ steps:
234
+ - uses: actions/checkout@v4
235
+
236
+ - name: Set up Python
237
+ uses: actions/setup-python@v5
238
+ with:
239
+ python-version: "3.11"
240
+
241
+ - name: Install dependencies
242
+ run: |
243
+ pip install -e ".[dev]"
244
+ pip install "oracledb<4"
245
+
246
+ - name: E2E tests
247
+ env:
248
+ DJANGO_FSSPEC_BENCH_DB: oracle
249
+ run: python benchmarks/e2e_test.py
250
+
251
+ - name: Benchmark
252
+ env:
253
+ DJANGO_FSSPEC_BENCH_DB: oracle
254
+ run: python benchmarks/run.py --db oracle --json benchmarks/results-oracle.json
255
+
256
+ - name: Upload results
257
+ uses: actions/upload-artifact@v4
258
+ with:
259
+ name: benchmark-oracle
260
+ path: benchmarks/results-oracle.json
261
+
262
+ # ---------------------------------------------------------------
263
+ # Stage 3: Collect baseline
264
+ # ---------------------------------------------------------------
265
+ baseline:
266
+ needs: [e2e-sqlite, e2e-mysql, e2e-postgres, e2e-oracle]
267
+ if: github.ref == 'refs/heads/main'
268
+ runs-on: ubuntu-latest
269
+ steps:
270
+ - uses: actions/checkout@v4
271
+
272
+ - name: Download all benchmark results
273
+ uses: actions/download-artifact@v4
274
+ with:
275
+ path: benchmarks/results/
276
+
277
+ - name: Set up Python
278
+ uses: actions/setup-python@v5
279
+ with:
280
+ python-version: "3.11"
281
+
282
+ - name: Generate baseline report
283
+ run: |
284
+ python -c "
285
+ import json, glob
286
+
287
+ all_results = []
288
+ for f in sorted(glob.glob('benchmarks/results/**/*.json', recursive=True)):
289
+ with open(f) as fh:
290
+ all_results.extend(json.load(fh))
291
+
292
+ ops = {}
293
+ for r in all_results:
294
+ op = r.get('op', '?')
295
+ if op not in ops:
296
+ ops[op] = {}
297
+ ops[op][r.get('db', '?')] = r
298
+
299
+ print('## Performance Baseline')
300
+ print()
301
+ print('| Operation | Database | Avg (ms) | P95 (ms) | Ops/s |')
302
+ print('|-----------|----------|----------|----------|-------|')
303
+ for op, dbs in sorted(ops.items()):
304
+ for db, r in sorted(dbs.items()):
305
+ if 'error' in r:
306
+ print(f'| {op} | {db} | ERROR | | |')
307
+ else:
308
+ print(f'| {op} | {db} | {r[\"avg_ms\"]:.2f} | {r[\"p95_ms\"]:.2f} | {r[\"ops_per_sec\"]:.0f} |')
309
+
310
+ with open('benchmarks/baseline.json', 'w') as f:
311
+ json.dump(all_results, f, indent=2, default=str)
312
+ "
313
+
314
+ - name: Upload baseline
315
+ uses: actions/upload-artifact@v4
316
+ with:
317
+ name: performance-baseline
318
+ path: benchmarks/baseline.json
319
+ retention-days: 90
320
+
321
+ # ---------------------------------------------------------------
322
+ # Stage 4: Publish to PyPI (tag push only)
323
+ # ---------------------------------------------------------------
324
+ publish:
325
+ needs: [unit-test, lint, e2e-sqlite, e2e-mysql, e2e-postgres, e2e-oracle]
326
+ if: startsWith(github.ref, 'refs/tags/v')
327
+ runs-on: ubuntu-latest
328
+ permissions:
329
+ id-token: write
330
+ steps:
331
+ - uses: actions/checkout@v4
332
+ with:
333
+ fetch-depth: 0
334
+
335
+ - name: Set up Python
336
+ uses: actions/setup-python@v5
337
+ with:
338
+ python-version: "3.11"
339
+
340
+ - name: Install build tools
341
+ run: pip install build twine
342
+
343
+ - name: Build package
344
+ run: python -m build
345
+
346
+ - name: Verify tag matches version
347
+ run: |
348
+ TAG=${GITHUB_REF#refs/tags/v}
349
+ VERSION=$(python -c "
350
+ import zipfile, glob
351
+ whl = glob.glob('dist/*.whl')[0]
352
+ # Extract version from wheel filename: name-version-...
353
+ version = whl.split('/')[-1].split('-')[1]
354
+ print(version)
355
+ ")
356
+ echo "Tag: $TAG, Package version: $VERSION"
357
+ if [ "$TAG" != "$VERSION" ]; then
358
+ echo "ERROR: Tag v$TAG does not match package version $VERSION"
359
+ exit 1
360
+ fi
361
+
362
+ - name: Publish to PyPI
363
+ env:
364
+ TWINE_USERNAME: __token__
365
+ TWINE_PASSWORD: ${{ secrets.PYPI_API_TOKEN }}
366
+ run: twine upload dist/*
@@ -0,0 +1,9 @@
1
+ __pycache__/
2
+ *.pyc
3
+ *.pyo
4
+ *.egg-info/
5
+ dist/
6
+ build/
7
+ .coverage
8
+ .pytest_cache/
9
+ *.sqlite3
@@ -0,0 +1,18 @@
1
+ # Changelog
2
+
3
+ ## [0.1.0] - 2026-06-27
4
+
5
+ ### Added
6
+ - Initial release
7
+ - FileNode, StorageBlock, FileBlock models with configurable block size
8
+ - fsspec integration via `DjangoFileSystem` (protocol: `django`)
9
+ - File modes: `rb`, `wb`, `ab`, `xb`
10
+ - Optimistic locking with version field for concurrent write detection
11
+ - Batch block allocation with free block pool reuse
12
+ - Path validation (blacklist + Unicode NFC normalization)
13
+ - Implicit directory support with database-side pushdown queries
14
+ - `RechunkOperation` for block size migration
15
+ - Management commands: `fsspec_gc`, `fsspec_fsck`, `fsspec_stats`
16
+ - Django Admin integration (FileNode read-only)
17
+ - Multi-database support (MySQL, PostgreSQL, Oracle, domestic databases)
18
+ - Namespace-based multi-tenancy
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2026 MrLYC
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
@@ -0,0 +1,109 @@
1
+ Metadata-Version: 2.4
2
+ Name: django-fsspec
3
+ Version: 0.1.0
4
+ Summary: A Django app that provides a file system interface via fsspec, backed by Django ORM
5
+ License-Expression: MIT
6
+ License-File: LICENSE
7
+ Requires-Python: >=3.11
8
+ Requires-Dist: django>=3.2
9
+ Requires-Dist: fsspec
10
+ Provides-Extra: dev
11
+ Requires-Dist: pytest; extra == 'dev'
12
+ Requires-Dist: pytest-cov; extra == 'dev'
13
+ Requires-Dist: pytest-django; extra == 'dev'
14
+ Description-Content-Type: text/markdown
15
+
16
+ # django-fsspec
17
+
18
+ A Django app that provides a file system interface via [fsspec](https://filesystem-spec.readthedocs.io/), backed by Django ORM.
19
+
20
+ ## Features
21
+
22
+ - **fsspec compatible** — use standard `fsspec.filesystem("django")` API
23
+ - **Multi-database** — MySQL, PostgreSQL, Oracle, SQLite, and domestic databases
24
+ - **Configurable block size** — tune storage granularity per deployment
25
+ - **Optimistic locking** — safe concurrent writes with conflict detection
26
+ - **Block pool reuse** — efficient storage with free block recycling
27
+ - **Namespace isolation** — multi-tenant support via integer namespace
28
+ - **Path validation** — blacklist rules + Unicode NFC normalization
29
+ - **Implicit directories** — no directory records, derived from file paths
30
+ - **Management commands** — `fsspec_gc`, `fsspec_fsck`, `fsspec_stats`
31
+
32
+ ## Quick Start
33
+
34
+ ```bash
35
+ pip install django-fsspec
36
+ ```
37
+
38
+ Add to `INSTALLED_APPS`:
39
+
40
+ ```python
41
+ INSTALLED_APPS = [
42
+ ...
43
+ "django_fsspec",
44
+ ]
45
+ ```
46
+
47
+ Run migrations:
48
+
49
+ ```bash
50
+ python manage.py migrate
51
+ ```
52
+
53
+ Use it:
54
+
55
+ ```python
56
+ import fsspec
57
+
58
+ fs = fsspec.filesystem("django", namespace=0)
59
+
60
+ # Write
61
+ with fs.open("/hello.txt", "wb") as f:
62
+ f.write(b"Hello World")
63
+
64
+ # Read
65
+ data = fs.cat("/hello.txt") # b"Hello World"
66
+
67
+ # List
68
+ fs.ls("/") # ["/hello.txt"]
69
+
70
+ # Delete
71
+ fs.rm("/hello.txt")
72
+ ```
73
+
74
+ ## Configuration
75
+
76
+ Add to your Django `settings.py`:
77
+
78
+ ```python
79
+ # Block size in bytes (default: 256KB)
80
+ DJANGO_FSSPEC_BLOCK_SIZE = 64 * 1024
81
+
82
+ # Maximum file size in bytes (default: 2MB)
83
+ DJANGO_FSSPEC_MAX_FILE_SIZE = 2 * 1024 * 1024
84
+ ```
85
+
86
+ ## Supported File Modes
87
+
88
+ | Mode | Description |
89
+ |------|-------------|
90
+ | `rb` | Read (file must exist) |
91
+ | `wb` | Write (create or overwrite) |
92
+ | `ab` | Append (create or append) |
93
+ | `xb` | Exclusive create (file must not exist) |
94
+
95
+ ## Documentation
96
+
97
+ - [Getting Started](docs/en/getting-started.md)
98
+ - [Configuration](docs/en/configuration.md)
99
+ - [Usage Guide](docs/en/usage.md)
100
+ - [Architecture](docs/en/architecture.md)
101
+ - [Management Commands](docs/en/management-commands.md)
102
+ - [Block Size Migration](docs/en/migration-guide.md)
103
+ - [Exceptions](docs/en/exceptions.md)
104
+
105
+ [中文文档](README_zh.md) | [Chinese Documentation](README_zh.md)
106
+
107
+ ## License
108
+
109
+ MIT — see [LICENSE](LICENSE).
@@ -0,0 +1,94 @@
1
+ # django-fsspec
2
+
3
+ A Django app that provides a file system interface via [fsspec](https://filesystem-spec.readthedocs.io/), backed by Django ORM.
4
+
5
+ ## Features
6
+
7
+ - **fsspec compatible** — use standard `fsspec.filesystem("django")` API
8
+ - **Multi-database** — MySQL, PostgreSQL, Oracle, SQLite, and domestic databases
9
+ - **Configurable block size** — tune storage granularity per deployment
10
+ - **Optimistic locking** — safe concurrent writes with conflict detection
11
+ - **Block pool reuse** — efficient storage with free block recycling
12
+ - **Namespace isolation** — multi-tenant support via integer namespace
13
+ - **Path validation** — blacklist rules + Unicode NFC normalization
14
+ - **Implicit directories** — no directory records, derived from file paths
15
+ - **Management commands** — `fsspec_gc`, `fsspec_fsck`, `fsspec_stats`
16
+
17
+ ## Quick Start
18
+
19
+ ```bash
20
+ pip install django-fsspec
21
+ ```
22
+
23
+ Add to `INSTALLED_APPS`:
24
+
25
+ ```python
26
+ INSTALLED_APPS = [
27
+ ...
28
+ "django_fsspec",
29
+ ]
30
+ ```
31
+
32
+ Run migrations:
33
+
34
+ ```bash
35
+ python manage.py migrate
36
+ ```
37
+
38
+ Use it:
39
+
40
+ ```python
41
+ import fsspec
42
+
43
+ fs = fsspec.filesystem("django", namespace=0)
44
+
45
+ # Write
46
+ with fs.open("/hello.txt", "wb") as f:
47
+ f.write(b"Hello World")
48
+
49
+ # Read
50
+ data = fs.cat("/hello.txt") # b"Hello World"
51
+
52
+ # List
53
+ fs.ls("/") # ["/hello.txt"]
54
+
55
+ # Delete
56
+ fs.rm("/hello.txt")
57
+ ```
58
+
59
+ ## Configuration
60
+
61
+ Add to your Django `settings.py`:
62
+
63
+ ```python
64
+ # Block size in bytes (default: 256KB)
65
+ DJANGO_FSSPEC_BLOCK_SIZE = 64 * 1024
66
+
67
+ # Maximum file size in bytes (default: 2MB)
68
+ DJANGO_FSSPEC_MAX_FILE_SIZE = 2 * 1024 * 1024
69
+ ```
70
+
71
+ ## Supported File Modes
72
+
73
+ | Mode | Description |
74
+ |------|-------------|
75
+ | `rb` | Read (file must exist) |
76
+ | `wb` | Write (create or overwrite) |
77
+ | `ab` | Append (create or append) |
78
+ | `xb` | Exclusive create (file must not exist) |
79
+
80
+ ## Documentation
81
+
82
+ - [Getting Started](docs/en/getting-started.md)
83
+ - [Configuration](docs/en/configuration.md)
84
+ - [Usage Guide](docs/en/usage.md)
85
+ - [Architecture](docs/en/architecture.md)
86
+ - [Management Commands](docs/en/management-commands.md)
87
+ - [Block Size Migration](docs/en/migration-guide.md)
88
+ - [Exceptions](docs/en/exceptions.md)
89
+
90
+ [中文文档](README_zh.md) | [Chinese Documentation](README_zh.md)
91
+
92
+ ## License
93
+
94
+ MIT — see [LICENSE](LICENSE).
@@ -0,0 +1,94 @@
1
+ # django-fsspec
2
+
3
+ 基于 Django ORM 的文件系统,通过 [fsspec](https://filesystem-spec.readthedocs.io/) 提供标准接口。
4
+
5
+ ## 特性
6
+
7
+ - **fsspec 兼容** — 使用标准 `fsspec.filesystem("django")` API
8
+ - **多数据库支持** — MySQL、PostgreSQL、Oracle、SQLite、信创数据库
9
+ - **可配置块大小** — 按部署需求调整存储粒度
10
+ - **乐观锁** — 安全的并发写入与冲突检测
11
+ - **块池复用** — 空闲块回收,高效存储
12
+ - **命名空间隔离** — 整数命名空间实现多租户
13
+ - **路径校验** — 黑名单规则 + Unicode NFC 归一化
14
+ - **隐式目录** — 无目录记录,从文件路径推导
15
+ - **管理命令** — `fsspec_gc`、`fsspec_fsck`、`fsspec_stats`
16
+
17
+ ## 快速开始
18
+
19
+ ```bash
20
+ pip install django-fsspec
21
+ ```
22
+
23
+ 添加到 `INSTALLED_APPS`:
24
+
25
+ ```python
26
+ INSTALLED_APPS = [
27
+ ...
28
+ "django_fsspec",
29
+ ]
30
+ ```
31
+
32
+ 运行迁移:
33
+
34
+ ```bash
35
+ python manage.py migrate
36
+ ```
37
+
38
+ 使用:
39
+
40
+ ```python
41
+ import fsspec
42
+
43
+ fs = fsspec.filesystem("django", namespace=0)
44
+
45
+ # 写入
46
+ with fs.open("/hello.txt", "wb") as f:
47
+ f.write(b"Hello World")
48
+
49
+ # 读取
50
+ data = fs.cat("/hello.txt") # b"Hello World"
51
+
52
+ # 列目录
53
+ fs.ls("/") # ["/hello.txt"]
54
+
55
+ # 删除
56
+ fs.rm("/hello.txt")
57
+ ```
58
+
59
+ ## 配置
60
+
61
+ 在 Django `settings.py` 中添加:
62
+
63
+ ```python
64
+ # 块大小(字节),默认 256KB
65
+ DJANGO_FSSPEC_BLOCK_SIZE = 64 * 1024
66
+
67
+ # 文件大小上限(字节),默认 2MB
68
+ DJANGO_FSSPEC_MAX_FILE_SIZE = 2 * 1024 * 1024
69
+ ```
70
+
71
+ ## 支持的文件模式
72
+
73
+ | 模式 | 说明 |
74
+ |------|------|
75
+ | `rb` | 只读(文件必须存在) |
76
+ | `wb` | 写入(创建或覆盖) |
77
+ | `ab` | 追加(创建或追加) |
78
+ | `xb` | 排他创建(文件必须不存在) |
79
+
80
+ ## 文档
81
+
82
+ - [快速入门](docs/zh/getting-started.md)
83
+ - [配置说明](docs/zh/configuration.md)
84
+ - [使用指南](docs/zh/usage.md)
85
+ - [架构设计](docs/zh/architecture.md)
86
+ - [管理命令](docs/zh/management-commands.md)
87
+ - [块大小迁移](docs/zh/migration-guide.md)
88
+ - [异常体系](docs/zh/exceptions.md)
89
+
90
+ [English](README.md) | [英文文档](README.md)
91
+
92
+ ## 许可证
93
+
94
+ MIT — 见 [LICENSE](LICENSE)。