Metadata-Version: 2.4
Name: minchin.django.excel
Version: 1.1.0
Summary: Load Excel files into your Django database.
Author-email: William Minchin <w_minchin@hotmail.com>
Maintainer-email: William Minchin <w_minchin@hotmail.com>
License-Expression: MIT
Project-URL: Repository, https://github.com/minchinweb/minchin.django.excel
Classifier: Development Status :: 5 - Production/Stable
Classifier: Environment :: Web Environment
Classifier: Intended Audience :: Developers
Classifier: Operating System :: OS Independent
Classifier: Programming Language :: Python :: 3 :: Only
Classifier: Topic :: Software Development :: Libraries :: Python Modules
Classifier: Framework :: Django
Classifier: Framework :: Django :: 4.2
Classifier: Framework :: Django :: 5.1
Classifier: Framework :: Django :: 5.2
Classifier: Framework :: Django :: 6.0
Description-Content-Type: text/markdown
License-File: License.txt
Requires-Dist: django>=4.2
Requires-Dist: django-auto-prefetch>=0.0.6
Requires-Dist: ciso8601>=2.2.0
Requires-Dist: dateparser>=1.1.1
Requires-Dist: numpy>=1.22.4
Requires-Dist: pandas>=1.4.2
Requires-Dist: colorama>=0.4.4
Requires-Dist: minchin.text>=6.1.0
Requires-Dist: django-prettyjson>=0.4.1
Requires-Dist: django-related-admin==1.0.1
Requires-Dist: openpyxl>=3.0.10
Provides-Extra: dev
Requires-Dist: invoke; extra == "dev"
Requires-Dist: minchin.releaser; extra == "dev"
Requires-Dist: isort; extra == "dev"
Requires-Dist: black; extra == "dev"
Dynamic: license-file

# Minchin.Django.Excel

This is a Django app designed to allow you to push the contents of Excel
workbooks to your Django database. I packaged it because while it seems like it
should be simple, it involved many more moving pieces (including management
commands, templates, template tags, admin pagination, etc.) than I initially
thought it should.


## Installation

The package is installable via `pip`:

```sh
pip install minchin.django.excel
```

This will also install all the required dependencies (of which there are
many...).

From there, you need to add it to your list of installed apps in your Django
`settings.py`:

```py
# settings.py

INSTALLED_APPS = [
  # first in line
  "related_admin",  # for minchin.django.excel

  # others...
  "minchin.django.excel",
  "prettyjson",  # for minchin.django.excel
]

# other settings...
```

Second, ensure that your `TEMPLATES` setting contains a DjangoTemplates backend
whose `APP_DIRS` options is set to `True`:

```py
# settings.py

TEMPLATES = [
    {
        "BACKEND": "django.template.backends.django.DjangoTemplates",
        "APP_DIRS": True,
        # ...
    }
]

# other settings...
```

and then run the included database migrations:

```sh
python manage.py migrate
```

### Loading Excel Data

Included is a Django management command to load the Excel files:

```sh
python manage.py load_excel <excel_file.xlsx> [<excel_file.xlsx> ...]
```

File names can include wildcards (e.g. `reports/*.xlsx`). Each file is stored
as a `Workbook`, with one `Sheet` per tab, and one `Row` per row. Each row's
data is stored as JSON, keyed by the Excel column name (`{"A": ..., "B":
...}`); empty cells are stored as `null`.

If the file is already in the database (matched by its MD5 hash), or a
different file with the same name is, you'll be asked whether to replace it.
To answer ahead of time:

- `--skip-existing` (`-s`): don't reload files already in the database, and
  keep existing workbooks with the same name alongside the new one.
- `--replace-existing` (`-r`): replace existing workbooks, whether matched by
  hash or by name.

Each file is loaded in a single transaction, so a file that can't be read
doesn't leave anything behind (or delete the workbook it was replacing).

From there, you would likely process the data further within Django.


### Django Admin Site

There are also several helpers so that you can see the loaded data on the
built-in Django admin site. However, you'll need to register them to your admin
site:

```py
# admin.py
from django.contrib import admin

from minchin.django.excel.admin import WorkbookAdmin, SheetAdmin, RowAdmin
from minchin.django.excel.models import Workbook, Sheet, Row

admin.site.register(Workbook, WorkbookAdmin)
admin.site.register(Sheet, SheetAdmin)
admin.site.register(Row, RowAdmin)
```

The Sheet page shows its rows in a paginated inline, `RowInline`. That's built
on `minchin.django.excel.utils.PaginationInline`, a `TabularInline` you can use
for your own models too:

- `per_page` (default 20) rows are shown per page, and `max_show_all`
  (default 200) is the most that the "Show all" link will show at once.
- The page is set by the `<prefix>-p` query parameter (e.g. `?row_set-p=3`),
  so several paginated inlines on one page are paged independently.
- The inline (or its model) needs an `ordering`, so rows don't move between
  pages; otherwise `ImproperlyConfigured` is raised.


## License

The code is licensed under the MIT license. See attached `License.txt`


## Changelog

### Version 1.1.0 -- Oct. 5, 2026

In particular, this release is to keep this working (without errors) in Django
5.2 (and newer).

- remove legacy `jasonize-rows` management command
- fix the Sheet admin page crashing on Django 5.0+, and rework the paginated
  inline: 1-based pages, a page parameter per inline (`<prefix>-p`), elided
  page links, a limit on "Show all" (`max_show_all`), and its CSS is now
  loaded as a static file
- **breaking:** remove the `back_url`, `forward_url`, and `make_range` template
  tags (from `excel_tags`), replaced by `inline_pagination`
- `django.contrib.humanize` is no longer required
- `load_excel`: the same-filename prompt now respects `--skip-existing` and
  `--replace-existing`; files are loaded in a single transaction; rows are
  inserted in bulk; warn when a file pattern matches nothing
- `from_timestamp`: integer timestamps are now read as UTC (they were read in
  local time); unparseable strings raise `ValueError`; aware strings work
- the JSON encoder handles numpy booleans
- require Django 4.2 or newer; drop the unused `psycopg2` and `pytz`
  dependencies
- fix the readme and license filenames in the package metadata
- add tests

### Version 1.0.0 -- Dec. 12, 2025

- initial public release

## Contributions

Contributions are welcome, however the scope of the project is intentionally
limited.

To set up a development environment and run the tests:

```sh
pip install -e .[dev]
invoke load-tests
```

Code is formatted with `isort` and `black`.
