MultiRangeField and MultiRangeFormField
**Designed to hold a list of pages and page ranges for a book/magazine index.** A custom model field (and accompanying form field) that saves comma-separated pages and page ranges in human-readable string form. Includes some clean-up code, so that you can add a new page or range at the end of an existing entry, and it will put it in numeric order and combine runs into ranges. So this: 4-33, 43, 45, 60-65, 44, 59 becomes the tidy 4-33, 43-45, 59-65 **NOTE:** If you comment out the raising of the `ValidationError` in the form field's validate() method, it will actually clean up any extraneous characters for you (which could be dangerous, but for me is usually what I want), so even this horrible mess: ;4-33, 46a fads i44 ,p45o gets cleaned to 4-33, 44-46 **Updated 2026-06-26 — modernized for Django 3.2+ / 5.x + Python 3, and added a list-of-ints accessor.** - Removed the Python-2 / pre-1.10 leftovers: `__metaclass__ = SubfieldBase`, `super(Class, self)`, and `_get_val_from_obj` (now `value_from_object` in `value_to_string`). - Added `deconstruct()` so the field works with migrations. - **New `to_list(value)` plus a generated `<field>_list` property** on the model — the pages expanded to a list of ints, for matching (`page in obj.pages_list`). - `first_page()` returns `0` on empty input instead of raising `IndexError`. - `depack()` now tolerates reversed ranges (`53-51`) and skips malformed parts (`12-13-15`, letters) instead of raising. - Centralized normalization in one helper; the form field rejects stray characters with a clear error while the model stays lenient. - Dropped the obsolete `from_db_value` `context` arg (removed in Django 2.0); precompiled the validation regex. The `repack` consecutive-run grouping (groupby on value − index) is unchanged.
- multiple
- stringformat
- range
- pages
- index