# Django Anchor
[![Test](https://github.com/knifecake/django-anchor/actions/workflows/test.yml/badge.svg)](https://github.com/knifecake/django-anchor/actions/workflows/test.yml)
[![Documentation Status](https://readthedocs.org/projects/django-anchor/badge/?version=latest)](https://django-anchor.readthedocs.io/en/latest/?badge=latest)
A reusable Django app to handle files attached to models, inspired by Ruby on
Rails' excellent [Active
Storage](https://edgeguides.rubyonrails.org/active_storage_overview.html).
## Features
- **Attach images and other files to your models**. Supports one or more
individual files per model as well as multiple ordered collections of files.
- **Optimized storage.** Deduplicates files for optimized storage
- **Display files in templates.** Render resized thumbnails and optimized
versions of your images in templates via a template tag.
- **Reduce external dependencies.** Django anchor doesn't need any external
services and works Django's local file storage.
### Limitations
- Files are prefixed with a random string which makes the URLs for them hard to
guess, but they are currently not protected against unauthorized attacks.
- It only works with Django storage classes in which files are accessible via
the file system, i.e. where the
[path](https://docs.djangoproject.com/en/5.0/ref/files/storage/#django.core.files.storage.Storage.path)
property of a file is implemented.
- It currently depends on [Huey](https://huey.readthedocs.io/en/latest/) for
background processing.
### Future work
- [ ] Remove dependency on `base58`
- [ ] Implement private file links (maybe via signed URLs?)
- [ ] Support for async/delayed variant generation
- [ ] Reduce number of dependencies:
- [ ] Make PIL dependency optional
## Installation
Django-anchor is compatible with Django >= 4.2 and Python >= 3.10.
1. Add the `django-anchor` package to your dependencies. You can do this by
running:
pip install django-anchor
or by adding `django-anchor` to your `requirements.txt` or `pyproject.toml`
files if you have one.
2. Add `anchor` to `settings.INSTALLED_APPS`
In addition, if you wish to create image variants, a Pillow >= 8.4 should be
available in your system.
## Usage
💡 Check out the [demo](./demo/) Django project for inspiration and the [Getting Started guide](https://django-anchor.readthedocs.io/en/latest/getting_started.html) in the documentation.
### Adding files to models
The easiest way to add a file to a model is to add a `BlobField` to it:
```python
from django.db import models
from anchor.models.fields import BlobField
class Movie(models.Model):
title = models.CharField(max_length=100)
# A compulsory field that must be set on every instance
cover = BlobField()
# An optional file that can be left blank
poster = BlobField(blank=True, null=True)
```
Notice how the `BlobField` above can be customized by setting the `blank` and
`null` options like any other field. It will also accept any other core field
parameters.
BlobFields are ForeignKey fields under the hood, so after you've added or made
changes you need to generate a migration with `python manage.py makemigrations`
and then apply it via `python manage.py migrate`.
Once your migrations are applied you can assign an
`anchor.models.blob.Blob` object to a `BlobField` much like you'd assign a
`DjangoFile` object to a `FileField`:
```python
from anchor.models.blob import Blob
# A new Blob objects is created and saved to the database with the file metadata
cover = Blob.objects.from_url('...')
# Make our movie point to that Blob object
movie.cover = cover
movie.save()
```
### Using files in templates
Django anchor comes with a handy template tag to render URLs of files you've stored:
```
{% load anchor %}
<img src="{% blob_thumbnail movie.poster max_width=300 max_height=600 format='jpeg' %}">
```
The above call to `blob_thumbnail` will generate an optimized version of the
movie's cover in JPEG format which fits inside a 300x600 rectangle. Optimized
versions are generated asynchronously and if they're not ready for immediate use
the original file's URL is returned instead to avoid blocking the request.
## Contributing
PRs and issues are very welcome!
Check out [CONTRIBUTING.md](./CONTRIBUTING.md) to learn how to set up the
project locally.
## License
This project is released under the MIT License. Check out
[LICENSE](./LICENSE.md) to get the full text of the license.
Raw data
{
"_id": null,
"home_page": "https://github.com/knifecake/django-anchor",
"name": "django-anchor",
"maintainer": null,
"docs_url": null,
"requires_python": "<4,>=3.10",
"maintainer_email": null,
"keywords": "django, files, attachments",
"author": "Elias Hernandis",
"author_email": "elias@hernandis.me",
"download_url": "https://files.pythonhosted.org/packages/63/45/e6a2cf3e7d30870b37298583b417b8e3aacc8b2095b6346519bca1656b7d/django_anchor-0.4.0.tar.gz",
"platform": null,
"description": "# Django Anchor\n\n[![Test](https://github.com/knifecake/django-anchor/actions/workflows/test.yml/badge.svg)](https://github.com/knifecake/django-anchor/actions/workflows/test.yml)\n[![Documentation Status](https://readthedocs.org/projects/django-anchor/badge/?version=latest)](https://django-anchor.readthedocs.io/en/latest/?badge=latest)\n\n\nA reusable Django app to handle files attached to models, inspired by Ruby on\nRails' excellent [Active\nStorage](https://edgeguides.rubyonrails.org/active_storage_overview.html).\n\n## Features\n\n- **Attach images and other files to your models**. Supports one or more\n individual files per model as well as multiple ordered collections of files.\n- **Optimized storage.** Deduplicates files for optimized storage\n- **Display files in templates.** Render resized thumbnails and optimized\n versions of your images in templates via a template tag.\n- **Reduce external dependencies.** Django anchor doesn't need any external\n services and works Django's local file storage.\n\n### Limitations\n\n- Files are prefixed with a random string which makes the URLs for them hard to\n guess, but they are currently not protected against unauthorized attacks.\n- It only works with Django storage classes in which files are accessible via\n the file system, i.e. where the\n [path](https://docs.djangoproject.com/en/5.0/ref/files/storage/#django.core.files.storage.Storage.path)\n property of a file is implemented.\n- It currently depends on [Huey](https://huey.readthedocs.io/en/latest/) for\n background processing.\n\n### Future work\n\n- [ ] Remove dependency on `base58`\n- [ ] Implement private file links (maybe via signed URLs?)\n- [ ] Support for async/delayed variant generation\n- [ ] Reduce number of dependencies:\n - [ ] Make PIL dependency optional\n\n## Installation\n\nDjango-anchor is compatible with Django >= 4.2 and Python >= 3.10.\n\n1. Add the `django-anchor` package to your dependencies. You can do this by\n running:\n\n pip install django-anchor\n\n or by adding `django-anchor` to your `requirements.txt` or `pyproject.toml`\n files if you have one.\n\n2. Add `anchor` to `settings.INSTALLED_APPS`\n\nIn addition, if you wish to create image variants, a Pillow >= 8.4 should be\navailable in your system.\n\n\n## Usage\n\n\ud83d\udca1 Check out the [demo](./demo/) Django project for inspiration and the [Getting Started guide](https://django-anchor.readthedocs.io/en/latest/getting_started.html) in the documentation.\n\n### Adding files to models\n\nThe easiest way to add a file to a model is to add a `BlobField` to it:\n\n```python\nfrom django.db import models\nfrom anchor.models.fields import BlobField\n\n\nclass Movie(models.Model):\n title = models.CharField(max_length=100)\n\n # A compulsory field that must be set on every instance\n cover = BlobField()\n\n # An optional file that can be left blank\n poster = BlobField(blank=True, null=True)\n```\n\nNotice how the `BlobField` above can be customized by setting the `blank` and\n`null` options like any other field. It will also accept any other core field\nparameters.\n\nBlobFields are ForeignKey fields under the hood, so after you've added or made\nchanges you need to generate a migration with `python manage.py makemigrations`\nand then apply it via `python manage.py migrate`.\n\nOnce your migrations are applied you can assign an\n`anchor.models.blob.Blob` object to a `BlobField` much like you'd assign a\n`DjangoFile` object to a `FileField`:\n\n```python\nfrom anchor.models.blob import Blob\n\n# A new Blob objects is created and saved to the database with the file metadata\ncover = Blob.objects.from_url('...')\n\n# Make our movie point to that Blob object\nmovie.cover = cover\nmovie.save()\n```\n\n### Using files in templates\n\nDjango anchor comes with a handy template tag to render URLs of files you've stored:\n\n```\n{% load anchor %}\n<img src=\"{% blob_thumbnail movie.poster max_width=300 max_height=600 format='jpeg' %}\">\n```\n\nThe above call to `blob_thumbnail` will generate an optimized version of the\nmovie's cover in JPEG format which fits inside a 300x600 rectangle. Optimized\nversions are generated asynchronously and if they're not ready for immediate use\nthe original file's URL is returned instead to avoid blocking the request.\n\n## Contributing\n\nPRs and issues are very welcome!\n\nCheck out [CONTRIBUTING.md](./CONTRIBUTING.md) to learn how to set up the\nproject locally.\n\n## License\n\nThis project is released under the MIT License. Check out\n[LICENSE](./LICENSE.md) to get the full text of the license.\n",
"bugtrack_url": null,
"license": "MIT",
"summary": "Attach files to Django models",
"version": "0.4.0",
"project_urls": {
"Bug Tracker": "https://github.com/knifecake/django-anchor/issues",
"Documentation": "https://django-anchor.readthedocs.io/en/latest/",
"Homepage": "https://github.com/knifecake/django-anchor",
"Repository": "https://github.com/knifecake/django-anchor"
},
"split_keywords": [
"django",
" files",
" attachments"
],
"urls": [
{
"comment_text": "",
"digests": {
"blake2b_256": "0de9e2a0795dd0e530c74e84c6688c7e9644fdf472bb9f42703c8871d804dca2",
"md5": "0099b5a1e9dce3258d5f342b0287950c",
"sha256": "31da4dba160e1cdd3d5a186dd4e09c154062991da67dd3de17d897f47bf18d0c"
},
"downloads": -1,
"filename": "django_anchor-0.4.0-py3-none-any.whl",
"has_sig": false,
"md5_digest": "0099b5a1e9dce3258d5f342b0287950c",
"packagetype": "bdist_wheel",
"python_version": "py3",
"requires_python": "<4,>=3.10",
"size": 18153,
"upload_time": "2024-07-19T21:15:23",
"upload_time_iso_8601": "2024-07-19T21:15:23.940976Z",
"url": "https://files.pythonhosted.org/packages/0d/e9/e2a0795dd0e530c74e84c6688c7e9644fdf472bb9f42703c8871d804dca2/django_anchor-0.4.0-py3-none-any.whl",
"yanked": false,
"yanked_reason": null
},
{
"comment_text": "",
"digests": {
"blake2b_256": "6345e6a2cf3e7d30870b37298583b417b8e3aacc8b2095b6346519bca1656b7d",
"md5": "b9ec7f372d9c01d1be54635024d8cc4d",
"sha256": "54107cf12c3125435d885e5616cb51cd274dc7a7ceb61cc9ab7f629a4f815527"
},
"downloads": -1,
"filename": "django_anchor-0.4.0.tar.gz",
"has_sig": false,
"md5_digest": "b9ec7f372d9c01d1be54635024d8cc4d",
"packagetype": "sdist",
"python_version": "source",
"requires_python": "<4,>=3.10",
"size": 15400,
"upload_time": "2024-07-19T21:15:25",
"upload_time_iso_8601": "2024-07-19T21:15:25.313049Z",
"url": "https://files.pythonhosted.org/packages/63/45/e6a2cf3e7d30870b37298583b417b8e3aacc8b2095b6346519bca1656b7d/django_anchor-0.4.0.tar.gz",
"yanked": false,
"yanked_reason": null
}
],
"upload_time": "2024-07-19 21:15:25",
"github": true,
"gitlab": false,
"bitbucket": false,
"codeberg": false,
"github_user": "knifecake",
"github_project": "django-anchor",
"travis_ci": false,
"coveralls": false,
"github_actions": true,
"lcname": "django-anchor"
}