# Django Scheduler is a calendar app with permission hooks

> Django Scheduler is a BSD licensed calendar application for Django that handles one-time and recurring events, calendar exceptions, and exposes occurrences through an Event API and a Period API. The parts worth knowing about are the settings hooks, which let a project decide who may edit an event and which events a request is allowed to see.

**llazzaro/django-scheduler** — A calendaring app for Django.

- Repository: https://github.com/llazzaro/django-scheduler
- Stars: 1,333 · Forks: 400
- Language: Python
- License: BSD-3-Clause
- Published: 2026-10-19 · Updated: 2026-10-19 · Language: en
- Canonical page: https://hysenlabs.com/projects/llazzaro-django-scheduler

## Install it as an app

The whole installation is one command:

```bash
pip install django-scheduler
```

and then two edits to your settings. The application is added to INSTALLED_APPS under the name schedule:

```python
'schedule',
```

and a context processor is added so the templates can see the request:

```python
"django.template.context_processors.request"
```

That is the entire setup, and it is a reminder of how much Django's app ecosystem still leans on the assumption that a package knows how to install itself. The package is on PyPI as django-scheduler, the documentation is hosted on Read the Docs, and there is a separate sample project repository linked from the README, which is usually the fastest way to see the app working before you wire it into your own project. The project metadata declares Python 3.10 and newer, Django 4.2 and newer, and a BSD-3-Clause licence, with classifiers for Django 4.2, 5.1 and 5.2 and for Python 3.10 through 3.13.

## The frontend is Bootstrap from a CDN

The asset story is short and worth reading before you deploy. The bundled templates use Bootstrap 5 and Bootstrap Icons loaded via CDN, and the FullCalendar view also loads FullCalendar 6 via CDN. No additional installation is needed, because the default templates include all the required CSS and JavaScript from CDN links. If you prefer to manage frontend assets yourself, through npm or self-hosted files, the documented route is to override the templates in your project, which is the standard Django template overriding mechanism rather than anything specific to this package. One instruction is easy to miss, and it is the one that bites in production. Remember to execute python manage.py collectstatic. A calendar that looks fine in development can end up serving a broken page in production precisely because the static files were never collected, and the override route makes that a step you own.

## Events, occurrences and exceptions

The feature list is the model. There are one-time and recurring events, and calendar exceptions, which are occurrences changed or cancelled, so a recurring meeting can skip one Tuesday without losing the series. Occurrences are accessible through an Event API and a Period API, which is the part that makes the package usable from your own code rather than only through the views. The two APIs exist because they answer different questions. The Event API works with a single event and its rule, while the Period API expands a rule over a window of time, and the views and the templates are built on top of the period expansion rather than on a separate calendar model. The other feature worth naming is that events can be related to generic objects, which is how you attach a booking to whatever your application already has, a room, a customer, an appointment, without inventing a parallel hierarchy of models.

## Permission hooks are the extension point

The optional settings are where this package differs from a calendar widget, and the one to look at first is CHECK_EVENT_PERM_FUNC. It controls the callable used to determine if a user has permission to edit an event or occurrence, and the callable must take the object, the event, and the user, and return a boolean. The default is:

```python
    check_edit_permission(ob, user):
        return user.is_authenticated
```

So out of the box, any signed-in user can edit any event. If the object is None the function is being asked about adding a new event. There is a parallel hook for calendars, CHECK_CALENDAR_PERM_FUNC, which governs adding, updating or deleting events in a specific calendar, and it takes the calendar and the user. The third hook is GET_EVENTS_FUNC, which controls the callable that gets all events for calendar display, taking the request and the calendar and returning a QuerySet of events. Modifying it is what lets you pull events from multiple calendars or filter them by permission, and the default is simply:

```python
    get_events(request, calendar):
        return calendar.event_set.all()
```

The three hooks together are the difference between a demo calendar and a calendar inside an application with real users.

## The remaining settings, and the views

The rest of the optional settings are smaller but still worth a look. FIRST_DAY_OF_WEEK decides which day the calendar begins on if your locale does not already set it, and it defaults to 0, which is Sunday. OCCURRENCE_CANCEL_REDIRECT controls the behavior of Views.get_next_url, and if it is set, all calendar modifications redirect there unless a next is present in the request. SHOW_CANCELLED_OCCURRENCES controls the behavior of Period.classify_occurrence, and when it is true, cancelled occurrences are displayed with a css class of canceled, and when it is false they do not appear at all. It defaults to False. SCHEDULER_PREVNEXT_LIMIT_SECONDS sets the upper and lower limit in calendar navigation, the value is in seconds, and the default of 62208000 is two years. On the presentation side, the package ships a ready to use interface with views for day, week, month, three months and year, and the documentation shows a FullCalendar example alongside the static monthly and daily views.

## Packaging, tests and how a release is made

The build configuration is modern, with setuptools 68 or newer as the build backend and the package metadata in pyproject.toml. The runtime dependencies are short: Django, python-dateutil, pytz and icalendar, which is where the recurrence and iCalendar support comes from. Development dependencies are black, coverage, ruff and isort, and the configuration is consistent, with an 88 character line length in both the isort and ruff settings, ruff targeting py310, and the lint rules selecting the pycodestyle, pyflakes and pycodestyle warning families while ignoring the line length check. The coverage configuration runs over the schedule package and omits the tests, the migrations and the models package initialiser. The repository also has a tox.ini, and the documented way to run the tests across all supported versions of Django and Python is to run tox. The release process is written down too, starting with a version bump in pyproject.toml and a changelog entry, then a commit, tag and push, and then a build and publish step driven by uv.

## Where the project stands

The maintenance picture is modest but fine for a stable utility. The licence is BSD-3-Clause, the package metadata marks the development status as Production/Stable, and the author is Leonardo Lazzaro. The version in the project file is 0.11.0, the default branch is develop rather than main, and the repository publishes no GitHub releases, so the PyPI package is the artefact to depend on rather than a tag. The last push was on 2026-10-09, which is recent enough that the project is being worked on. The topics the repository carries are minimal, calendaring and event calendar, which is a fair description of a package that does one thing and exposes hooks rather than a framework of its own. The one thing to check before committing to it in a multi-tenant application is the permission story, since the defaults are permissive and the documentation expects you to replace them.

## Conclusion

Django Scheduler fits a Django project that needs a working calendar with recurring events and a month or week view without writing a scheduling model, and the permission and event query hooks are what make it usable in an application with multiple users rather than a demo. It does not fit a project that wants a fully bespoke calendar interface, since the bundled templates are Bootstrap based and the FullCalendar view is the only rich one, and it does not fit a team that cannot accept CDN loaded assets without overriding the templates. Before you adopt it, read the optional settings, because the defaults are permissive, any authenticated user can edit any event until you change CHECK_EVENT_PERM_FUNC, and GET_EVENTS_FUNC is where you filter by permission, and check the version you install, since the package metadata reads 0.11.0, the default branch is develop and no GitHub releases are published.

## FAQ

### How do I install Django Scheduler?

Run pip install django-scheduler, add 'schedule' to INSTALLED_APPS and add the django.template.context_processors.request context processor, then run python manage.py collectstatic. The templates load Bootstrap 5, Bootstrap Icons and FullCalendar 6 from a CDN by default.

### How do I control who can edit events?

Set CHECK_EVENT_PERM_FUNC to a callable that takes the event object and the user and returns a boolean, and use CHECK_CALENDAR_PERM_FUNC for per-calendar add, update and delete rights. The default only requires the user to be authenticated.

### How can I filter which events a user sees?

Set GET_EVENTS_FUNC to a callable taking the request and the calendar and returning a QuerySet of events. The default returns every event in the calendar, and replacing it lets you combine calendars or filter by permission.

### Does Django Scheduler support recurring events?

Yes. There are one-time and recurring events, plus calendar exceptions for occurrences changed or cancelled, and occurrences are reachable through the Event API and the Period API. Events can also be related to generic objects.

### Is Django Scheduler still updated?

The repository is not archived and the last push was on 2026-10-09, the package version is 0.11.0 on PyPI, and no GitHub releases are published. It supports Django 4.2, 5.1 and 5.2 with Python 3.10 to 3.13 under a BSD-3-Clause licence.

## Sources

- [Issues](https://github.com/llazzaro/django-scheduler/issues)
- [License: BSD-3-Clause](https://github.com/llazzaro/django-scheduler/blob/develop/LICENSE)
- [llazzaro/django-scheduler on GitHub](https://github.com/llazzaro/django-scheduler)
- [README](https://github.com/llazzaro/django-scheduler/blob/develop/README.md)

---

Hysen Labs editorial analysis, written from the project's own repository and release notes. Cite the canonical page: https://hysenlabs.com/projects/llazzaro-django-scheduler
