Metadata-Version: 2.5
Name: django-date-input
Version: 1.0.0
Summary: A native HTML5 date input widget for Django, with optional bounds.
Project-URL: Changelog, https://github.com/niccolomineo/django-date-input/blob/main/CHANGELOG.md
Project-URL: Documentation, https://github.com/niccolomineo/django-date-input#readme
Project-URL: Homepage, https://github.com/niccolomineo/django-date-input
Project-URL: Issues, https://github.com/niccolomineo/django-date-input/issues
Author-email: Niccolò Mineo <niccolo@20tab.com>
License-Expression: MIT
License-File: LICENSE
Keywords: date,django,forms,html5,widget
Classifier: Development Status :: 5 - Production/Stable
Classifier: Environment :: Web Environment
Classifier: Framework :: Django
Classifier: Framework :: Django :: 5.2
Classifier: Framework :: Django :: 6.0
Classifier: Framework :: Django :: 6.1
Classifier: Intended Audience :: Developers
Classifier: Operating System :: OS Independent
Classifier: Programming Language :: Python :: 3 :: Only
Classifier: Programming Language :: Python :: 3.12
Classifier: Programming Language :: Python :: 3.13
Classifier: Programming Language :: Python :: 3.14
Classifier: Topic :: Internet :: WWW/HTTP
Classifier: Topic :: Software Development :: User Interfaces
Requires-Python: >=3.12
Requires-Dist: django>=5.2
Description-Content-Type: text/markdown

# django-date-input

[![PyPI](https://img.shields.io/pypi/v/django-date-input?label=pypi)](https://pypi.org/project/django-date-input/)
[![Python](https://img.shields.io/pypi/pyversions/django-date-input?label=python)](https://pypi.org/project/django-date-input/)
[![Django](https://img.shields.io/pypi/frameworkversions/django/django-date-input?label=django)](https://pypi.org/project/django-date-input/)
[![CI](https://github.com/niccolomineo/django-date-input/actions/workflows/ci.yml/badge.svg)](https://github.com/niccolomineo/django-date-input/actions/workflows/ci.yml)
[![License](https://img.shields.io/pypi/l/django-date-input?label=license)](LICENSE)

A native **HTML5 date input** widget for Django, with optional lower and upper bounds.

Requires Python 3.12 or newer and Django 5.2 LTS or newer. Every supported
combination — Python 3.12/3.13/3.14 against Django 5.2/6.0/6.1 — is exercised
by the test suite on every push.

## Why

Django's `DateInput` renders `type="text"`. Reaching for the browser's own date
picker means `attrs={"type": "date"}`, and that opens a trap: `<input type="date">`
accepts **only** ISO 8601 values, while Django localises the value it renders. Under
an Italian locale the widget emits `value="19/08/2026"`, the browser cannot parse it,
and the field silently appears empty — with the date still in the database.

This widget renders `type="date"`, pins the value to ISO 8601, and adds optional
`min`/`max` bounds.

## Installation

```bash
pip install django-date-input
```

No `INSTALLED_APPS` entry is needed — it is a widget, not an application.

## Usage

```python
from datetime import date

from django import forms

from django_date_input import DateInput, end_of_year


class BookingForm(forms.Form):
    starts_on = forms.DateField(
        widget=DateInput(min_date=date(2020, 1, 1), max_date=end_of_year(5))
    )
```

Renders as:

```html
<input type="date" name="starts_on" min="2020-01-01" max="2031-12-31" required>
```

Bounds are optional — omit both and you get a plain native date input.

### Declaring bounds on a subclass

```python
class BookingDateInput(DateInput):
    min_date = date(2020, 1, 1)
    max_date = end_of_year(5)
```

### Relative bounds

A bound may be a `date` or any zero-argument callable returning one. Two helpers
cover the common cases:

| Helper | Returns |
| --- | --- |
| `start_of_year(offset=0)` | 1 January, `offset` years from the current year |
| `end_of_year(offset=0)` | 31 December, `offset` years from the current year |

Callables are resolved **at render time**, not on instantiation. That matters in a
long-running process: a worker booted in December that computed its upper bound once
would keep serving last year's limit after midnight on 1 January.

```python
DateInput(min_date=start_of_year(-1), max_date=end_of_year(5))
```

### Precedence

An explicit `min` or `max` in `attrs` always wins:

```python
DateInput(attrs={"min": "1999-01-01"}, min_date=date(2020, 1, 1))  # min stays 1999-01-01
```

### Overriding the format

The ISO default is a default, not a lock — though anything else will stop the
browser's date picker from reading the value:

```python
DateInput(format="%d/%m/%Y")
```

## Notes

- Bounds are a client-side convenience. Browsers block out-of-range dates in the
  picker, but a crafted POST will sail past them — validate on the field too, with
  `MinValueValidator` / `MaxValueValidator` or a `clean_*` method.
- Parsing the submitted value relies on `%Y-%m-%d` being present in
  `DATE_INPUT_FORMATS`. Django's global default and its bundled locale formats all
  include it, so this works out of the box; if you override `DATE_INPUT_FORMATS`
  yourself, keep the ISO entry.

## License

MIT — see [LICENSE](LICENSE).
