Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 0 additions & 2 deletions .bandit

This file was deleted.

2 changes: 0 additions & 2 deletions .github/FUNDING.yml

This file was deleted.

16 changes: 8 additions & 8 deletions .github/dependabot.yml
Original file line number Diff line number Diff line change
@@ -1,10 +1,10 @@
version: 2
updates:
- package-ecosystem: pip
directory: "/"
schedule:
interval: daily
- package-ecosystem: github-actions
directory: "/"
schedule:
interval: daily
- package-ecosystem: pip
directory: "/"
schedule:
interval: daily
- package-ecosystem: github-actions
directory: "/"
schedule:
interval: daily
105 changes: 24 additions & 81 deletions .github/workflows/ci.yml
Original file line number Diff line number Diff line change
@@ -1,107 +1,50 @@
name: CI

on:
push:
branches:
- main
pull_request:
schedule:
- cron: '0 0 * * *'

jobs:

lint:
strategy:
fail-fast: false
matrix:
lint-command:
- "bandit ."
- "black --check --diff ."
- "flake8 ."
- "isort --check-only --diff ."
- "pydocstyle ."
dist:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v6
- uses: actions/setup-python@v6
with:
python-version: "3.x"
cache: 'pip'
cache-dependency-path: 'pyproject.toml'
- run: python -m pip install -e .[lint]
- run: ${{ matrix.lint-command }}

readme:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v6
- uses: actions/setup-python@v6
with:
python-version: "3.x"
- name: Install Python dependencies
run: python -m pip install --upgrade pip build wheel twine readme-renderer
- run: python -m build --sdist --wheel
- run: python -m twine check dist/*
- uses: astral-sh/setup-uv@v7
- run: uvx --from build pyproject-build --sdist --wheel
- run: uvx twine check dist/*
- uses: actions/upload-artifact@v6
with:
path: dist/*

pytest:
runs-on: ubuntu-latest
needs:
- readme
- lint
- dist
runs-on: ubuntu-latest
strategy:
matrix:
python-version:
- "3.10"
- "3.11"
- "3.12"
- "3.13"
- "3.14"
django-version:
- "4.2"
- "5.0"
- "5.1"
- "5.2"
- "6.0"
exclude:
- python-version: "3.14"
django-version: "4.2"
- python-version: "3.10"
django-version: "6.0"
- python-version: "3.11"
django-version: "6.0"
steps:
- uses: actions/checkout@v6
- name: Set up Python ${{ matrix.python-version }}
uses: actions/setup-python@v6
with:
python-version: ${{ matrix.python-version }}
- run: python -m pip install .[test]
- name: Install Django ${{ matrix.django-version }}
run: python -m pip install "django~=${{ matrix.django-version }}.0"
- name: Run tests
run: python -m pytest
- uses: codecov/codecov-action@v5

analyze:
name: CodeQL Analyze
needs: [ pytest ]
runs-on: ubuntu-latest
permissions:
actions: read
contents: read
security-events: write

strategy:
fail-fast: false
matrix:
language: [ python ]

steps:
- name: Checkout
uses: actions/checkout@v6

- name: Initialize CodeQL
uses: github/codeql-action/init@v4
- uses: actions/checkout@v6
- uses: astral-sh/setup-uv@v7
with:
languages: ${{ matrix.language }}
queries: +security-and-quality

- name: Autobuild
uses: github/codeql-action/autobuild@v4

- name: Perform CodeQL Analysis
uses: github/codeql-action/analyze@v4
python-version: ${{ matrix.python-version }}
- run: uv run --with django~=${{ matrix.django-version }}.0 pytest -m "not selenium"
- uses: codecov/codecov-action@v5
with:
category: "/language:${{ matrix.language }}"
token: ${{ secrets.CODECOV_TOKEN }}
flags: python-${{ matrix.python-version }}-django-${{ matrix.django-version }}
33 changes: 19 additions & 14 deletions .github/workflows/release.yml
Original file line number Diff line number Diff line change
@@ -1,23 +1,28 @@
name: Release

on:
release:
types: [published]

workflow_dispatch:
jobs:
PyPI:
pypi-build:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v6
- uses: actions/setup-python@v6
- uses: astral-sh/setup-uv@v7
- run: uvx --from build pyproject-build --sdist --wheel
- uses: actions/upload-artifact@v6
with:
python-version: "3.10"
- name: Install Python dependencies
run: python -m pip install --upgrade pip build wheel twine
- name: Build dist packages
run: python -m build --sdist --wheel
- name: Upload packages
run: python -m twine upload dist/*
env:
TWINE_USERNAME: __token__
TWINE_PASSWORD: ${{ secrets.TWINE_PASSWORD }}
name: release-dists
path: dist/
pypi-publish:
runs-on: ubuntu-latest
needs:
- pypi-build
permissions:
id-token: write
steps:
- uses: actions/download-artifact@v7
with:
name: release-dists
path: dist/
- uses: pypa/gh-action-pypi-publish@release/v1
5 changes: 4 additions & 1 deletion .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -104,4 +104,7 @@ venv.bak/
.mypy_cache/

# setuptools_scm
_version.py
_version.py

# uv
uv.lock
45 changes: 45 additions & 0 deletions .pre-commit-config.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,45 @@
repos:
- repo: https://github.com/pre-commit/pre-commit-hooks
rev: v6.0.0
hooks:
- id: trailing-whitespace
- id: check-merge-conflict
- id: check-ast
- id: check-toml
- id: check-yaml
- id: check-symlinks
- id: debug-statements
- id: end-of-file-fixer
- id: no-commit-to-branch
args: [--branch, main]
- repo: https://github.com/asottile/pyupgrade
rev: v3.21.2
hooks:
- id: pyupgrade
- repo: https://github.com/adamchainz/django-upgrade
rev: 1.29.1
hooks:
- id: django-upgrade
- repo: https://github.com/hukkin/mdformat
rev: 1.0.0
hooks:
- id: mdformat
additional_dependencies:
- mdformat-ruff
- mdformat-footnote
- mdformat-gfm
- mdformat-gfm-alerts
- repo: https://github.com/astral-sh/ruff-pre-commit
rev: v0.14.9
hooks:
- id: ruff-check
args: [--fix, --exit-non-zero-on-fix]
- id: ruff-format
- repo: https://github.com/google/yamlfmt
rev: v0.20.0
hooks:
- id: yamlfmt
ci:
autoupdate_schedule: weekly
skip:
- no-commit-to-branch
78 changes: 41 additions & 37 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -20,9 +20,10 @@ from django.db import models
from dynamic_filenames import FilePattern

upload_to_pattern = FilePattern(
filename_pattern='{app_label:.25}/{model_name:.30}/{instance.created:%Y-%m-%d}/{uuid:base32}{ext}'
filename_pattern="{app_label:.25}/{model_name:.30}/{instance.created:%Y-%m-%d}/{uuid:base32}{ext}"
)


class FileModel(models.Model):
my_file = models.FileField(upload_to=upload_to_pattern)
created = models.DateTimeField(auto_now_add=True)
Expand All @@ -36,73 +37,75 @@ Auto slug example:

`ext`

: File extension including the dot.
: File extension including the dot.

`name`

: Filename excluding the folders.
: Filename excluding the folders.

`model_name`

: Name of the Django model.
: Name of the Django model.

`app_label`

: App label of the Django model.
: App label of the Django model.

`instance`

: Instance of the model before it has been saved. You may not have a
primary key at this point.
: Instance of the model before it has been saved. You may not have a
primary key at this point.

`uuid`

: UUID version 4 that supports multiple type specifiers. The UUID will
be the same should you use it twice in the same string, but
different on each invocation of the `upload_to` callable.
: UUID version 4 that supports multiple type specifiers. The UUID will
be the same should you use it twice in the same string, but
different on each invocation of the `upload_to` callable.

The type specifiers allow you to format the UUID in different ways,
e.g. `{uuid:x}` will give you a with a hexadecimal UUID.
```
The type specifiers allow you to format the UUID in different ways,
e.g. `{uuid:x}` will give you a with a hexadecimal UUID.

The supported type specifiers are:
The supported type specifiers are:

`s`
`s`

: String representation of a UUID including dashes.
: String representation of a UUID including dashes.

`i`
`i`

: Integer representation of a UUID. Like to `UUID.int`.
: Integer representation of a UUID. Like to `UUID.int`.

`x`
`x`

: Hexadecimal (Base16) representation of a UUID. Like to
`UUID.hex`.
: Hexadecimal (Base16) representation of a UUID. Like to
`UUID.hex`.

`X`
`X`

: Upper case hexadecimal representation of a UUID. Like to
`UUID.hex`.
: Upper case hexadecimal representation of a UUID. Like to
`UUID.hex`.

`base32`
`base32`

: Base32 representation of a UUID without padding.
: Base32 representation of a UUID without padding.

`base64`
`base64`

: Base64 representation of a UUID without padding.
: Base64 representation of a UUID without padding.

:::: warning
::: title
Warning
:::
:::: warning
::: title
Warning
:::

Not all file systems support Base64 file names.
::::
Not all file systems support Base64 file names.
::::

All type specifiers also support precisions to cut the string, e.g.
`{{uuid:.2base32}}` would only return the first 2 characters of a
Base32 encoded UUID.
All type specifiers also support precisions to cut the string, e.g.
`{{uuid:.2base32}}` would only return the first 2 characters of a
Base32 encoded UUID.
```

### Type specifiers

Expand All @@ -115,9 +118,10 @@ from django.db import models
from dynamic_filenames import FilePattern

upload_to_pattern = FilePattern(
filename_pattern='{app_label:.25}/{model_name:.30}/{instance.title:.40slug}{ext}'
filename_pattern="{app_label:.25}/{model_name:.30}/{instance.title:.40slug}{ext}"
)


class FileModel(models.Model):
title = models.CharField(max_length=100)
my_file = models.FileField(upload_to=upload_to_pattern)
Expand Down
Loading