This is the full developer documentation for SaaS Pegasus # SaaS Pegasus Documentation > Everything you need to know about setting up and configuring Pegasus for your project. ### Quicklinks [Section titled “Quicklinks”](#quicklinks) [Getting Started ](/getting-started/) [Configuration ](/configuration/) [Teams ](/teams/) [Subscriptions ](/subscriptions/) [Deployment ](/deployment/overview/) [Get Help From AI ](/ai/development/) # Getting Started > Complete setup guide for Pegasus projects with Docker or native Python, including database configuration and post-installation steps. Here’s everything you need to start your first Pegasus project. ## Watch the video [Section titled “Watch the video”](#watch-the-video) Visual learner? The above video should get you going. Else read on below for the play-by-play. ## Create and download your project codebase [Section titled “Create and download your project codebase”](#create-and-download-your-project-codebase) If you haven’t already, you’ll need to [purchase a Pegasus License on saaspegasus.com](http://www.saaspegasus.com/licenses/). Then, [create a new project on saaspegasus.com](https://www.saaspegasus.com/projects/), following the prompts and filling in whatever configuration options you want to use for your new project. Make sure that the “license” field at the bottom is set. Once you’re done, [connect your project to Github](/github) or download your project’s source code as a zip file. **Note: it’s recommended to use the Github integration which will make future upgrades and changes to your project easier to manage.** ## Set up source control [Section titled “Set up source control”](#set-up-source-control) It is highly recommended to use git for source control. [Install git](https://git-scm.com/downloads) and then follow the instructions below: ### If using the Github integration [Section titled “If using the Github integration”](#if-using-the-github-integration) If you created your project on Github, you can use `git clone` to get the code. Get your git URL from the Github page and then run the following command, swapping in your user account and project id: ```bash git clone https://github.com/user/project-id.git ``` ### If using the Zip file download [Section titled “If using the Zip file download”](#if-using-the-zip-file-download) If you chose to use a zip file instead, unzip it to a folder where you want to do your development and then manually initialize your repository: ```bash git init git add . git commit -am "initial project creation" ``` It is also recommended to create a `pegasus` branch at this time for future upgrades. ```bash git branch pegasus ``` You can read [more about upgrading here](/upgrading). ## Install prerequisites [Section titled “Install prerequisites”](#install-prerequisites) The following prerequisites are needed to run the app in the recommended configuration: * [Docker](/docker#install-prerequisites) * [uv](/python/setup/#using-uv) * [Node and npm](/front-end/overview/#prerequisites-to-building-the-front-end) On Windows, you will also need to install `make`, which you can do by [following these instructions](https://stackoverflow.com/a/57042516/8207). Other configurations * If you use Docker “full mode” you do not need to install uv or Node/npm. * If you do not want to use Docker for Postgres and Redis, you will need to install them separately. * To run Python without `uv` you’ll need to follow the instructions for your own environment on [this page](/python/setup#using-native--system-python-with-virtual-environments-and-pip-tools). ## Quick start [Section titled “Quick start”](#quick-start) Once you’ve installed the prerequisites, you can get up and running with the following commands: ```bash make init make dev # This is not required if running Docker in "full mode" ``` Open a browser and visit and you should see your application! Then skip ahead to the [post-install steps](/getting-started/#post-installation-steps). ## Manual setup [Section titled “Manual setup”](#manual-setup) The `make` quick start commands cover a lot for you. If you’d rather do everything manually, continue to the sections below. ### Enter the project directory [Section titled “Enter the project directory”](#enter-the-project-directory) ```bash cd {{ project_name }} ``` You should see your project files, including a `manage.py` file. ### Set up your Python environment [Section titled “Set up your Python environment”](#set-up-your-python-environment) There are several ways of setting up your Python environment. See [this page](/python/setup) for information on choosing an option and setting up your environment. ### Install package requirements [Section titled “Install package requirements”](#install-package-requirements) With `uv`: ```bash # with uv uv sync # or if using pip tools pip install -r dev-requirements.txt ``` Note: if you have issues installing `psycopg2`, try installing the dependencies outlined in [this thread](https://stackoverflow.com/questions/22938679/error-trying-to-install-postgres-for-python-psycopg2) (specifically `python3-dev` and `libpq-dev`). On Macs you may also need to follow the instructions from [this thread](https://stackoverflow.com/a/58722268/8207). And specifically, run: ```bash brew reinstall openssl export LIBRARY_PATH=$LIBRARY_PATH:/usr/local/opt/openssl/lib/ ``` ### Create your .env file [Section titled “Create your .env file”](#create-your-env-file) If you installed with Github, you’ll have to create your `.env` file for your environment variables and secrets. You can do this from the example, by running: ```bash cp .env.example .env ``` ### Set up database [Section titled “Set up database”](#set-up-database) If you installed with Postgres, edit the `DATABASE_URL` value in `.env` with the appropriate username and password for connecting to your DB. You will also need to create a database for your project if you haven’t already. Assuming that your postgres admin user is named `postgres`: ```bash createdb -U postgres -h localhost -p 5432 {{ project_name }} ``` Followed by the password for the postgres user. Or, using identity authentication: ```bash sudo -u postgres createdb {{ project_name }} ``` ### Create database migrations [Section titled “Create database migrations”](#create-database-migrations) ```bash # with uv uv run manage.py makemigrations # or with normal venv python ./manage.py makemigrations ``` ### Run database migrations [Section titled “Run database migrations”](#run-database-migrations) ```bash # with uv uv run manage.py migrate # or with normal venv python ./manage.py migrate ``` ### Run server [Section titled “Run server”](#run-server) ```bash # with uv uv run manage.py runserver # or with normal venv python ./manage.py runserver ``` ### Build/run front end [Section titled “Build/run front end”](#buildrun-front-end) ```bash npm install npm run dev ``` For more details, see the [front end docs](/front-end/overview). ### Load your app [Section titled “Load your app”](#load-your-app) Open a browser and visit and you should see your application! Continue to the post-installation steps below. ## Post-installation steps [Section titled “Post-installation steps”](#post-installation-steps) Once up and running, you’ll want to review these common next-steps. ### Create a User [Section titled “Create a User”](#create-a-user) To create your first user account, just go through the sign up flow in your web browser. From there you should be able to access all built-in functionality and examples. ### Enable admin access [Section titled “Enable admin access”](#enable-admin-access) Use [the `promote_user_to_superuser` management command](/cookbooks/#use-the-django-admin-ui) to enable access to the Django Admin site. ### Confirm your site URL [Section titled “Confirm your site URL”](#confirm-your-site-url) For Stripe callbacks, email links, and JavaScript API clients to work, you must make sure that you have [configured absolute URLs correctly](/configuration/#absolute-urls). ### Set up your Stripe subscriptions [Section titled “Set up your Stripe subscriptions”](#set-up-your-stripe-subscriptions) If you’ve installed with subscriptions, you’ll want to set things up next. Head to the [subscriptions documentation](/subscriptions) and follow the steps there! ### Set up background tasks [Section titled “Set up background tasks”](#set-up-background-tasks) For the progress bar example to work---and to run background tasks of your own---you’ll need a Celery environment running. Head to [celery](/celery) and follow the steps there! ## Using the Makefile [Section titled “Using the Makefile”](#using-the-makefile) Pegasus ships with a self-documenting `Makefile` that will run common commands for you, including starting your containers, performing database operations, and building your front end. You can run `make` to list helper functions, and you can view the source of the `Makefile` file in case you need to add to it or run any once-off commands. Commands are also documented in your project’s AI rules files. You can add custom commands to the `Makefile` by editing `custom.mk`. ## Customize your application [Section titled “Customize your application”](#customize-your-application) At this point, Pegasus has installed scaffolding for all of the user management, authentication, and (optionally) team views and Stripe subscriptions, and given you a beautiful base UI template and clear code structure to work from. Now that you’re up and running it’s time for the fun part: building your new application! This can obviously be done however you like. Some examples of things you might want to do next include: * Customize your landing page and set up a pricing page * Start modifying the list of navigation tabs and logged-in user experience * Create a new django app and begin building out your data models in `models.py`. It’s recommended to use the [Pegasus CLI](https://github.com/saaspegasus/pegasus-cli/) for this. For some initial pointers on where to to make Pegasus your own, head on over to the [Customizations Page](/customizations). For the nitty-gritty details on setting up things like email, error logging, sign up flow, analytics, and more go to [Settings and Configuration](/configuration). # Working with Python Packages (uv) > Fast Python package management with uv using pyproject.toml and uv.lock files for adding, removing, and upgrading dependencies efficiently. Recent versions of Pegasus use [uv](https://docs.astral.sh/uv/) to manage Python packages. It provides all the functionality of `pip-tools` while being much faster and offering more flexibility and features. ### Requirements Files [Section titled “Requirements Files”](#requirements-files) `uv` uses two files to manage requirements. The first is a `pyproject.toml` file, which contains the base list of packages. The `pyproject.toml` file also supports dependency groups, which are used for development and production requirements. `pyproject.toml` replaces the previous `requirements.in`, `dev-requirements.in`, and `prod-requirements.in` files. The second file is the `uv.lock` file. This file contains the pinned versions of dependencies that are used by the project’s environment. This file is automatically generated from the `pyproject.toml` file and *should not be edited by hand*. `uv.lock` replaces the previous `requirements.txt`, `dev-requirements.txt`, and `prod-requirements.txt` files. #### Adding or removing a package [Section titled “Adding or removing a package”](#adding-or-removing-a-package) To add or remove packages you can run the following commandss: ```bash # native version uv add uv remove # docker version make uv add make uv remove ``` If you’re using natively this is all you have to do! The command will update your `pyproject.toml` file, your `uv.lock` file, and sync your virtual environment. On Docker, you will have to also rebuild the container. You can do that with: ```bash make build make restart ``` The `make requirements` command can also be used to sync your `uv.lock` file and rebuild / restart your containers. #### Upgrading a package [Section titled “Upgrading a package”](#upgrading-a-package) You can upgrade a package with: ```bash # native version - update the lockfile uv lock --upgrade-package # native version - update the lockfile and sync the virtual environment uv sync --upgrade-package # docker version make uv "lock --upgrade-package wagtail" ``` You can upgrade *all* packages with: ```bash # native version - update the lockfile uv lock --upgrade # native version - update the lockfile and sync the virtual environment uv sync --upgrade # docker version make uv "lock --upgrade" ``` Like with adding packages, if you’re using Docker, you’ll have to rebuild and restart Docker containers for the updated environment to work: ```bash make build make restart ``` # Pegasus's Code Structure > Understand Pegasus project organization with apps, static files, templates, and code formatting using pre-commit hooks and ruff. ## Overall structure [Section titled “Overall structure”](#overall-structure) This is the overall structure of a new Pegasus project: * {{project\_name}}/ * {{project\_name}}/ * … * apps * subscriptions/ * … * teams/ * … * users/ * … * utils/ * … * web/ * … * pegasus * apps/ * … * assets * javascript/ * … * styles/ * … * requirements/ * … * static * css/ * … * images/ * … * js/ * … * templates/ * … The first three directories are Python modules while the remaining ones are not. ## Your `{{project_name}}` module [Section titled “Your {{project\_name}} module”](#your-project_name-module) This is your Django project root directory. It’s where your settings, root urlconf and `wsgi.py` file will live. ## Your `apps` module [Section titled “Your apps module”](#your-apps-module) This is where your project’s apps will live. It is pre-populated with Pegasus’s default apps for you to further customize to your needs. The module starts with several apps, depending on your configuration. Here are some of the main ones: * `content` is where the [Wagtail CMS models](/wagtail) are configured. * `subscriptions` is for functionality related to [Stripe subscriptions](/subscriptions). * `users` is where your user models and views are defined. * `teams` is where [team models and views](/teams) are defined. * `utils` is a set of functionality shared across the project. * `web` contains utilities and components related to the generic views, layouts and templates. ## The `pegasus` module [Section titled “The pegasus module”](#the-pegasus-module) This is where the Pegasus examples live. In general, it is not expected that you’ll need to modify much in this module, though feel free to do so! ## The `requirements` folder [Section titled “The requirements folder”](#the-requirements-folder) This is where you define your project’s Python requirements. Requirements are managed using `pip-tools`. For more information on using it see [their documentation](https://github.com/jazzband/pip-tools). ## The `assets` folder [Section titled “The assets folder”](#the-assets-folder) This is where the source files for your site’s JavaScript and CSS live. These files are what you should edit to change your JS and CSS. See [front-end](/front-end/overview) for more information on how to compile these files. ## The `static` folder [Section titled “The static folder”](#the-static-folder) This folder contains your project’s static files, including the compiled output files from the `assets` folder as well as images. ## The `templates` folder [Section titled “The templates folder”](#the-templates-folder) This folder contains your project’s Django templates. There is one sub-folder for each application that has templates. The majority of the project’s base template layouts are in the `templates/web` folder. ## Code formatting [Section titled “Code formatting”](#code-formatting) For projects that have enabled the `Autoformat code` option, the code will have been formatted using [ruff](https://github.com/astral-sh/ruff)—a drop-in replacement for [black](https://black.readthedocs.io/en/stable/) and [isort](https://pycqa.github.io/isort/) that runs much faster than those tools. The project will also include [pre-commit](https://pre-commit.com/) as a dependency in the requirements file as well as the `.pre-commit-config.yaml` file in the root directory. pre-commit is a tool for managing pre-commit hooks - which can be used to ensure your code matches the correct format when it’s committed. After installing the project dependencies you can install the pre-commit hooks: ```bash $ pre-commit install --install-hooks pre-commit installed at .git/hooks/pre-commit ``` The default configuration that ships with Pegasus will run `ruff` and `ruff-format` prior to every Git commit. If there are fixes that are needed you will be notified in the shell output. ### pre-commit Usage [Section titled “pre-commit Usage”](#pre-commit-usage) **Manually running hooks** ```bash # run all hooks against currently staged files pre-commit run # run all the hooks against all the files. This is a useful invocation if you are using pre-commit in CI. pre-commit run --all-files ``` **Temporarily disable hooks** See For more information on using and configuring pre-commit check out the [pre-commit docs](https://pre-commit.com/#quick-start) ### Tool configurations [Section titled “Tool configurations”](#tool-configurations) The configuration for the tools can be found in the [`pyproject.toml`](https://black.readthedocs.io/en/stable/usage_and_configuration/the_basics.html#what-on-earth-is-a-pyproject-toml-file) file, using the same syntax as `black`. For the most part the default black/ruff formats have been preserved, with a few updates, for example, increasing the line length to 120. You can find more information about these values in the [ruff README](https://github.com/astral-sh/ruff?tab=readme-ov-file#configuration). ### Upgrading [Section titled “Upgrading”](#upgrading) See [this cookbook](/cookbooks/#migrating-to-auto-formatted-code) for guidance on how to enable code formatting on an existing Pegasus project. # Settings and Configuration > Configure Django settings, environment variables, email providers, social authentication, Stripe payments, and production deployments. This section describes some of the settings and configuration details you can change inside Pegasus. ## Settings and environment files [Section titled “Settings and environment files”](#settings-and-environment-files) Pegasus uses environment variables and `django-environ` to manage settings. You *can* modify values directly in `settings.py`, but the recommended way to modify any setting that varies across environments is to use a `.env` file. Out-of-the-box, Pegasus will include multiple `.env` files for your settings: **`.env` is for development in either a native or a Docker-based environnment.** It will be picked up by default if you run `./manage.py runserver` or `docker compose start`. If you need to swap between these environments you might need to modify a few variables in this file---in particular the database and redis URLs. The `.env` is typically not checked into source control (since it may include secrets like API keys), so is included in the `.gitignore`. **`.env.example` is an example file.** It is not used for anything, but can be checked into source control so that developers can use it as a starting point for their `.env` file. Projects downloaded as zip files will include a `.env` file, but projects created or pulled from Github will typically only include a `.env.example` file, so you will need to copy this file locally to run your development server. *Note: Pegasus versions prior to 2024.3 also included a `.env.docker` file. This has been merged with the `.env` file.* ### Settings environment precedence [Section titled “Settings environment precedence”](#settings-environment-precedence) Most settings are configured in the form: ```python SOME_VALUE = env('SOME_VALUE', default='') ``` As mentioned above, *it is recommended to set these values in your environment / `.env` file*, which will always work as expected. The environment takes precedence over the default if it’s set---even if it is set to an empty value. This can lead to confusing behavior. For example, if in your `.env` file you have this line: ```dotenv SOME_VALUE='' ``` And in your `settings.py` you provide a default: ```python SOME_VALUE = env('SOME_VALUE', default='my value') ``` The default will be ignored, and `SOME_VALUE` will be an empty string. To fix this, either *remove the value entirely from your `.env` file*, or *explicitly set the value in your `settings.py`* (instead of using the `default` argument). E.g. ```python SOME_VALUE = 'my value' ``` ## Project Metadata [Section titled “Project Metadata”](#project-metadata) When you first setup Pegasus it populated the `PROJECT_METADATA` setting in `settings.py` with various things like page titles and social sharing information. These settings can later be changed as you like by editing the setting directly: ```python PROJECT_METADATA = { 'NAME': 'Your Project Name', 'URL': 'http://www.example.com', 'DESCRIPTION': 'My Amazing SaaS Application', 'IMAGE': 'https://upload.wikimedia.org/wikipedia/commons/2/20/PEO-pegasus_black.svg', 'KEYWORDS': 'SaaS, django', 'CONTACT_EMAIL': 'you@example.com', } ``` See the [project metadata documentation](/page-metadata) for more information about how this is used. ## Absolute URLs [Section titled “Absolute URLs”](#absolute-urls) In most of Django/Pegasus, URLs are *relative*, represented as paths like `/account/login/` and so forth. But in some cases you need a complete URL, including the *protocol* (http vs https) and *server* (e.g. [www.example.com](http://www.example.com)). These are necessary whenever you use a link in an email, with an external site (e.g. Stripe API callbacks and social authentication), and in some places when APIs are accessed from your front end. ### Setting your site’s protocol [Section titled “Setting your site’s protocol”](#setting-your-sites-protocol) The *protocol* is configured by the `USE_HTTPS_IN_ABSOLUTE_URLS` variable in `settings.py`. You should set this to `True` when using https and `False` when not (typically only in development). ### Setting your server URL [Section titled “Setting your server URL”](#setting-your-server-url) When you first install Pegasus it will use the `URL` value from `PROJECT_METADATA` above to create a Django `Site` object in your database. The domain name of this `Site` will be used for your server address. If you need to change the URL after installation, you can go to the site admin at `admin/sites/site/` and modify the values accordingly, leaving off any http/https prefix. In development, you’ll typically want a domain name of `localhost:8000`, and in production this should be the domain where your users access your app. Note that this URL must match *exactly* what is in the browser address bar. So, for example, if you load your development site from `127.0.0.1:8000` instead of `localhost:8000` then that is what you should put in. **Example Development Configuration** ![Development Site Settings](/_astro/site-admin-dev.D-ocuSIA_Z2rjy05.webp) **Example Production Configuration** ![Site Settings](/_astro/site-admin.B37AAtIM_Z25prPY.webp) ## Sending Email [Section titled “Sending Email”](#sending-email) Pegasus is setup to use [django-anymail](https://github.com/anymail/django-anymail) to send email via Amazon SES, Mailgun, Postmark, and a variety of other email providers. To use one of these email backends, change the email backend in `settings.py` to: ```python EMAIL_BACKEND = 'anymail.backends.mailgun.EmailBackend' ``` And populate the `ANYMAIL` setting with the required information. For example, to use [Mailgun](https://www.mailgun.com/) you’d populate the following values: ```python ANYMAIL = { "MAILGUN_API_KEY": "key-****", "MAILGUN_SENDER_DOMAIN": 'mg.{{project_name}}.com', # should match what's in mailgun } ``` If you are in the EU you may also need to add the following entry: ```python 'MAILGUN_API_URL': 'https://api.eu.mailgun.net/v3', ``` The [anymail documentation](https://anymail.readthedocs.io/en/stable/) has much more information on these options. The following django settings should also be set: ```python SERVER_EMAIL = 'noreply@{{project_name}}.com' DEFAULT_FROM_EMAIL = 'you@{{project_name}.com' ADMINS = [('Your Name', 'you@{{project_name}}.com'),] ``` See [Sending email](https://docs.djangoproject.com/en/stable/topics/email/) in the django docs for more information. ## User Sign Up [Section titled “User Sign Up”](#user-sign-up) The sign up workflow is managed by [django-allauth](https://allauth.org/) with a sensible set of defaults and templates. ### Social logins [Section titled “Social logins”](#social-logins) Pegasus optionally ships with “Login with Google/Twitter/Github” options. You’ll separately need to follow the steps listed on the [provider-specific pages here](https://docs.allauth.org/en/latest/socialaccount/providers/index.html) to configure things on the other side. These steps can sometimes be a bit involved and vary by platform. But will generally entail two steps: 1. Creating a new application / client on the service you want to use. 2. Adding the credentials to your environment (`.env`) file. See the Google guide below for an example you can follow. If you want to add a social login that’s not supported out of the box (e.g. Facebook/Meta or Apple), you can follow the existing patterns and configure things based on the allauth docs. If you need help setting this up feel free to get in touch! Additionally, see the resources below. #### Google OAuth Specific instructions [Section titled “Google OAuth Specific instructions”](#google-oauth-specific-instructions) 1. Register the application with google by following just the “App registration” section [here](https://docs.allauth.org/en/latest/socialaccount/providers/google.html). Note that the trailing slash for the “Authorized redirect URLs” is required. For example, assuming you are developing locally, it should be set to exactly `http://localhost:8000/accounts/google/login/callback/`. 2. Set the resulting client id and secret key in the `.env` file in the root of your project. ```dotenv GOOGLE_CLIENT_ID="actual client id from the google console" GOOGLE_SECRET_ID="actual secret id from the google console" ``` #### Other Social Setup Guides [Section titled “Other Social Setup Guides”](#other-social-setup-guides) The Pegasus community has recommended the following guides to set things up with specific providers: * [Github](https://python.plainenglish.io/django-allauth-a-guide-to-enabling-social-logins-with-github-f820239fb73f) ### Requiring email confirmation [Section titled “Requiring email confirmation”](#requiring-email-confirmation) Pegasus does not require users to confirm their email addresses prior to logging in. However, this can be easily changed by changing the following value in `settings.py` ```python ACCOUNT_EMAIL_VERIFICATION = 'optional' # change to "mandatory" to require users to confirm email before signing in. ``` *Note: The email verification step will be skipped if using a social login.* ### Enabling sign in by email code [Section titled “Enabling sign in by email code”](#enabling-sign-in-by-email-code) Sign in by email code is controlled by the `ACCOUNT_LOGIN_BY_CODE_ENABLED` setting. You can enable / disable it in `settings.py`. ```python ACCOUNT_LOGIN_BY_CODE_ENABLED=True ``` ### Two-factor authentication [Section titled “Two-factor authentication”](#two-factor-authentication) Two-Factor authentication (2FA) is configured using the [allauth’s mfa](https://docs.allauth.org/en/latest/mfa/index.html) support. When using Two-Factor Auth with Pegasus, a new section is added to the user profile for enabling & configuring the OTP (one-time password) devices for the user. If a user has a Two-Factor device configured then they will be prompted for a token after logging in. ### Customizing emails [Section titled “Customizing emails”](#customizing-emails) Pegasus ships with simple, responsive email templates for password reset and email address confirmation. These templates can be further customized by editing the files in the `templates/account/email` directory. See [the allauth email documentation](https://docs.allauth.org/en/latest/common/email.html) for more information about customizing account emails. ### Disabling public sign ups [Section titled “Disabling public sign ups”](#disabling-public-sign-ups) If you’d like to prevent everyone from signing up for your app, set the following in your `settings.py`, replacing the existing value: ```python ACCOUNT_ADAPTER = 'apps.users.adapter.NoNewUsersAccountAdapter' ``` This will prevent all users from creating new accounts, though existing users can continue to login and use the app. ### Further configuration [Section titled “Further configuration”](#further-configuration) Allauth is highly configurable. It’s recommended that you look into the various [configuration settings available within allauth](https://docs.allauth.org/en/latest/account/configuration.html) for any advanced customization. ## Stripe [Section titled “Stripe”](#stripe) If you’re using [Stripe](https://www.stripe.com/) to collect payments you’ll need to fill in the following in `settings.py` (or populate them in the appropriate environment variables): ```python STRIPE_LIVE_PUBLIC_KEY = os.environ.get("STRIPE_LIVE_PUBLIC_KEY", "") STRIPE_LIVE_SECRET_KEY = os.environ.get("STRIPE_LIVE_SECRET_KEY", "") STRIPE_TEST_PUBLIC_KEY = os.environ.get("STRIPE_TEST_PUBLIC_KEY", "") STRIPE_TEST_SECRET_KEY = os.environ.get("STRIPE_TEST_SECRET_KEY", "") STRIPE_LIVE_MODE = False # Change to True in production ``` ## Google Analytics [Section titled “Google Analytics”](#google-analytics) To enable Google Analytics, add your analytics tracking ID to your `.env` file or `settings.py` file: ```python GOOGLE_ANALYTICS_ID = 'UA-XXXXXXX-1' ``` Pegasus uses a “global site tag” with gtag.js by default, which is a simpler version of Google Analytics that can be rolled out with zero additional configuration. If you use Google Tag Manager, you can make changes in `templates/web/components/google_analytics.html` to match the snippet provided by Google. See [this article](https://support.google.com/tagmanager/answer/7582054) for more on the differences between gtag.js and Google Tag Manager. ## Sentry [Section titled “Sentry”](#sentry) [Sentry](https://sentry.io/) is the gold standard for tracking errors in Django applications and Pegasus can connect to it with a few lines of configuration. If you build with Sentry enabled, all you need to do is populate the `SENTRY_DSN` setting - either directly in your `settings.py` or via an environment variable. After setting it up on production, you can test your Sentry integration by visiting `https:///simulate_error`. This should trigger an exception which will be logged by Sentry. ## OpenAI and LLMs [Section titled “OpenAI and LLMs”](#openai-and-llms) For help configuring LLMs and AIs, see the [AI docs](/ai/development/). ## Celery [Section titled “Celery”](#celery) See the [celery docs](/celery) for set up and configuration of Celery. ## Turnstile [Section titled “Turnstile”](#turnstile) To enable support for [Cloudflare Turnstile](https://www.cloudflare.com/products/turnstile/), set `TURNSTILE_KEY` and `TURNSTILE_SECRET` in your settings or environment variables. This should automatically enable turnstile on your sign up pages. It is recommended to create two different Turnstile accounts on Cloudflare for development and production. In development you can specify “localhost” as your domain like this: ![Turnstile Dev](/_astro/turnstile.1SEbnPxr_Z1jch3g.webp) In production, you should replace that with your site’s production domain. ## Mailing List [Section titled “Mailing List”](#mailing-list) Pegasus includes support for subscribing users to a marketing email list upon signup. Currently, three platforms are supported: 1. [Mailchimp](https://mailchimp.com/) 2. [Kit (formerly ConvertKit)](https://kit.com/) 3. [Email Octopus](https://emailoctopus.com/?urli=Cd7hX) Make sure you choose the platform you would like to use when building your Pegasus project. Then follow the instructions below for the platform you’ve chosen. After completing these steps, new sign-ups will automatically be added to your configured marketing list. Note that it is your responsibility to notify your users / get their consent as per your local privacy regulations. ### Mailchimp [Section titled “Mailchimp”](#mailchimp) To enable the Mailchimp integration, first create a mailing list, then fill in the following to values in your environment/settings. ```python MAILCHIMP_API_KEY = '' MAILCHIMP_LIST_ID = '' ``` ### Kit (formerly ConvertKit) [Section titled “Kit (formerly ConvertKit)”](#kit-formerly-convertkit) To enable the Kit integration, create your Kit account and go to Settings —> Developer, and create a new V4 API key. Then add the API key value to your `.env` file or your environment/settings. ```python KIT_API_KEY = "" ``` That’s it! New user sign-ups will automatically be added as Kit subscribers. ### Email Octopus [Section titled “Email Octopus”](#email-octopus) To enable the Email Octopus integration, first create a mailing list, then fill in the following values in your environment/settings. ```python EMAIL_OCTOPUS_API_KEY = "" EMAIL_OCTOPUS_LIST_ID = "" ``` Note: If you use [this link](https://emailoctopus.com/?urli=Cd7hX) to sign up for email octopus, you’ll get $15 off your first payment, and help support Pegasus. ## Logging [Section titled “Logging”](#logging) Pegasus ships with a default Django log configuration which outputs logs to the console as follows: * Django log messages at level INFO and above * Pegasus log messages at level INFO and above The Pegasus loggers are all namespaced with the project name e.g. `{{project_name}}.subscriptions`. ### Changing log levels [Section titled “Changing log levels”](#changing-log-levels) There are two environment variables which can be used to control the log levels of either Django messages or Pegasus message: * `DJANGO_LOG_LEVEL` * `{{project_name.upper()}}_LOG_LEVEL` Alternatively the entire log configuration can be overridden using the `LOGGING` setting as described in the [Django docs](https://docs.djangoproject.com/en/stable/topics/logging/). ## Storing media files [Section titled “Storing media files”](#storing-media-files) SaaS Pegasus ships with optional configuration for storing dynamic media files in S3 e.g. user profile pictures. If you do not have this enabled the [default Django configuration](https://docs.djangoproject.com/en/stable/topics/files/) will be used which requires you to have persistent storage available for your site such as a Docker volume. ### Setting up S3 media storage [Section titled “Setting up S3 media storage”](#setting-up-s3-media-storage) *For a video walkthrough of this content (using kamal deployment), see below:* This section assumes you have set up your SaaS Pegasus project with the **S3 media file storage** enabled. In order to use S3 for media storage you will need to create an S3 bucket and provide authentication credentials for writing data to the bucket. Once you have done the S3 setup (see below), you can update your `.env` file as follows: ```dotenv USE_S3_MEDIA=True AWS_ACCESS_KEY_ID= AWS_SECRET_ACCESS_KEY= ``` With this configuration your media files will be accessible at `https://{{ project_name }}-media.s3.amazonaws.com/media/`. [This guide](https://testdriven.io/blog/storing-django-static-and-media-files-on-amazon-s3/) is an excellent resource with step-by-step instructions for the S3 setup. #### Additional settings [Section titled “Additional settings”](#additional-settings) AWS\_STORAGE\_BUCKET\_NAME : Name of the S3 bucket to use. Defaults to `{{project_name}}-media`. ### Alternative storage backends [Section titled “Alternative storage backends”](#alternative-storage-backends) Should you wish to use a different storage backed e.g. [Digital Ocean Spaces](https://www.digitalocean.com/products/spaces) you can follow the setup described in the [django-storages](https://django-storages.readthedocs.io/en/latest/index.html) documentation. There is also a [Pegasus community guide](/community/digital-ocean-spaces) that walks through this in more detail. ## Django Debug Toolbar [Section titled “Django Debug Toolbar”](#django-debug-toolbar) Pegasus ships with [Django Debug Toolbar](https://github.com/jazzband/django-debug-toolbar#readme) as an optional package. This section describes how the feature is configured in Pegasus. The `django-debug-toolbar` package is placed in the `dev-requirements.txt` file which means it will only be available in dev environments. Should you wish to use it in a production environment you will need to add it to your `prod-requirements.in` file and [re-build](/python/setup) your `prod-requirements.txt` file. By default, the toolbar is enabled in development environments via the `ENABLE_DEBUG_TOOLBAR` setting in your `.env` file(s). You can change this setting in any environment to turn it on/off. ```dotenv ENABLE_DEBUG_TOOLBAR=True ``` # APIs > Django REST Framework APIs with auto-generated OpenAPI schemas, TypeScript clients, and authentication support for building modern web applications. Pegasus comes with a rich ecosystem of APIs that can used by your app’s front end as well as exposed to third-party developers. ## APIs in Pegasus [Section titled “APIs in Pegasus”](#apis-in-pegasus) APIs in Pegasus consist of three pieces: 1. **API endpoints**, created with [Django Rest Framework (DRF)](https://www.django-rest-framework.org/). These are the Django views that serve your APIs. 2. **API schemas**, created with [drf-spectacular](https://drf-spectacular.readthedocs.io/en/latest/). These are automatically created by your APIs, and can be used for API documentation and client generation. They follow the [OpenAPI 3](https://spec.openapis.org/oas/v3.1.0) specification. 3. **API clients**, created by [OpenAPI Generator](https://openapi-generator.tech/). These can be used by developers to interact with your APIs. Pegasus ships with a TypeScript (JavaScript) client that is used in your app’s front end by the parts of the app that interact with the backend APIs (e.g. JavaScript charts, and the React/Vue demos). This might sound like a lot of moving parts, but, critically, *all the logic lives in the API endpoints themselves*. The schemas are auto-generated by the endpoints, and the clients are auto-generated by the schemas. So you only have to maintain your APIs in a single place, and everything else is kept in sync with tooling. Using the schemas and clients is optional. You can always interact with a Pegasus API by making the appropriate HTTP requests directly. However, using a client can greatly simplify the code you write and improve the development experience. Front end code in Pegasus that interacts with APIs uses it by default. Additionally, getting API docs “for free” from the schemas can be a big win if you plan to make your project’s API third-party-developer-facing. ## API Documentation [Section titled “API Documentation”](#api-documentation) By default, your Pegasus app ships with two built-in sets of API documentation available at the `/api/schema/swagger-ui/` endpoint ( in development) and `/api/schema/redoc/` endpoint ( in development). The API docs will look something like this: **Swagger API docs:** ![Swagger API Docs](/_astro/swagger-api-docs.CyMzYekt_1jxuTi.webp) **Redoc API docs:** ![Redoc API Docs](/_astro/redoc-api-docs.9KICU7qX_22NPjz.webp) ## API Clients [Section titled “API Clients”](#api-clients) As part of the [front end](/front-end/overview), Pegasus ships with an API client that can be used to interact with your project’s APIs. **This client is automatically generated from your APIs and should not be modified by hand.** You can find the source code of the API client(s) in the `api-client` folder in your project’s root directory. *Note: In releases prior to 2024.3 the API client was in the `assets/javascript/api-client` directory.* ### Using the API client [Section titled “Using the API client”](#using-the-api-client) There are several example usages of the API client in the Pegasus codebase. The steps, as seen in the employee app demo, are as follows: **Initialize the API client** ```javascript import {Cookies} from "./app"; import {Configuration, PegasusApi} from "./api-client"; const apiConfig = new Configuration({ basePath: 'https://yourserver.com/', // or pass this in via {{server_url}} template variable headers: { 'X-CSRFToken': Cookies.get('csrftoken'), } }) const client = new PegasusApi(apiConfig); ``` **Call an API** ```javascript client.employeesList().then((result) => { // do something with the API result here console.log('your employees are ', result.results); }); ``` ### Client method names [Section titled “Client method names”](#client-method-names) The easiest way to find out the methods available in the API client is by looking at the source code in `api-client/apis/Api.ts`. Method names are determined by the `operationId` value for the API in the auto-generated `schema.yaml` file. These identifiers are auto-generated, but can be overridden using DRF Spectacular’s `extend_schema_view` and `extend_schema` helper functions. This can be done for an entire `ViewSet` as follows: ```python from drf_spectacular.utils import extend_schema_view, extend_schema from rest_framework import viewsets @extend_schema_view( create=extend_schema(operation_id='employees_create'), list=extend_schema(operation_id='employees_list'), retrieve=extend_schema(operation_id='employees_retrieve'), update=extend_schema(operation_id='employees_update'), partial_update=extend_schema(operation_id='employees_partial_update'), destroy=extend_schema(operation_id='employees_destroy'), ) class EmployeeViewSet(viewsets.ModelViewSet): # rest of viewset code here ``` The IDs in the Python code will be converted to camelCase in the JavaScript client. ### Generating the OpenAPI3 schema.yml file [Section titled “Generating the OpenAPI3 schema.yml file”](#generating-the-openapi3-schemayml-file) In a new Pegasus installation, the OpenAPI3 `schema.yml` will be available at the `/api/schema/` endpoint ( in dev). If you plan to use the `schema.yml` file in production, it is more efficient to create it once and serve it as a static file. This can be done by running: ```bash ./manage.py spectacular --file static/api-schema.yml ``` Then you can reference the file by using `{% static /api-schema.yml %}` in a Django template. ### Generating the API client [Section titled “Generating the API client”](#generating-the-api-client) Anytime you change your APIs you should create a new API client to keep things in sync. This can be done using the [OpenAPI Generator](https://openapi-generator.tech/) project. The [typescript-fetch](https://openapi-generator.tech/docs/generators/typescript-fetch) client is the one used by Pegasus. #### Running natively (requires Java) [Section titled “Running natively (requires Java)”](#running-natively-requires-java) To generate your API client natively, first install the `openapi-generator-cli` (this library also requires `java`): ```bash npm install @openapitools/openapi-generator-cli -g ``` Then run it as follows: ```bash openapi-generator-cli generate -i http://localhost:8000/api/schema/ -g typescript-fetch -o ./api-client/ ``` The above assumes your Django server is running at , but you can replace that value with any URL or file system reference to your `schema.yml` file. #### Running in docker [Section titled “Running in docker”](#running-in-docker) You can also generate your API client with docker to avoid having to install Java by running: ```bash make build-api-client ``` while your server is running. You should see the files in `api-client` get updated. #### Rebuilding your front end [Section titled “Rebuilding your front end”](#rebuilding-your-front-end) After re-creating the API client, you’ll have to rebuild your front end: ```bash npm run dev ``` Note that introducing breaking changes to your APIs can also break your API client! If you’re unsure if you introduced breaking changes it is worth testing any functionality that depends on the API client. ## Authentication APIs [Section titled “Authentication APIs”](#authentication-apis) *Added in version 2024.3. Changed in 2025.4.1* If you enable the “Use Authentication APIs” checkbox in your project, Pegasus will generate a set of API endpoints for registering and logging in users. These endpoints can be used to integrate your backend with single page applications (SPAs) and mobile apps. Under the hood, Pegasus uses [allauth headless](https://docs.allauth.org/en/dev/headless/openapi-specification/) for these endpoints. This feature uses Django’s session-based authentication by default---which works great for single page apps---though it is possible to add in JWT or another token-based authentication scheme to better support mobile applications. A complete end-to-end example that uses the API authentication feature in a React SPA can be found in the experimental [standalone front end](/experimental/react-front-end). This example includes React/API-based sign up, login, password reset, two-factor authentication, email confirmation and more. ## API Keys [Section titled “API Keys”](#api-keys) Pegasus supports the use of API Keys to access APIs, built on top of the [Django REST Framework API Key](https://florimondmanca.github.io/djangorestframework-api-key/) project. Pegasus includes the ability to create API keys, associate them with your User objects, and access APIs using the key. ### Creating and managing API keys [Section titled “Creating and managing API keys”](#creating-and-managing-api-keys) A simple UI for creating, viewing, and revoking API keys is available to end users from the Profile page. More advanced/customized management of API keys---including the ability to associate names and expiry dates with keys---is available through the Django admin interface. Note that when an API key is created it will be displayed *once* and will not be available after that. For more details on working with API keys see [the library documentation](https://florimondmanca.github.io/djangorestframework-api-key/guide/#creating-and-managing-api-keys). ### API keys and Users [Section titled “API keys and Users”](#api-keys-and-users) Pegasus associates API keys with your Django `User` objects. This is a good, practical way to get started with API key scoping. All access granted by the key will the same as the associated `CustomUser` object, which allow you to easily create APIs that work with logged-in users *or* API keys. The `apps.api.models.UserAPIKey` class is used to associate an API key with a `CustomUser`. You can then enable API keys for any user-specific views, by following the instructions for `APIView`s and `ViewSet`s below. More complex API key permissions---for example, associating a key with a single API or a single team---can be created by following [these instructions](https://florimondmanca.github.io/djangorestframework-api-key/guide/#api-key-models). To enable API-key support for an `APIView`, or `ViewSet`, use the `IsAuthenticatedOrHasUserAPIKey` permission class in place of `IsAuthenticated`. This will allow either authenticated users or UserAPIKey users to access the APIs. In either case, the associated user object will be available as `request.user`. You can see an example `APIView` in the `EmployeeDataAPIView` class that ships with the Pegasus examples, and an example `ViewSet` in the `EmployeeViewSet` code. ### Testing API keys [Section titled “Testing API keys”](#testing-api-keys) The easiest way to test API key functionality is to use a tool like [curl](https://curl.se/). The following command can be used to test a user-based API key with a default Pegasus installation: ```bash curl http://localhost:8000/pegasus/employees/api/employees/ -H "Authorization: Api-Key " ``` You should replace `` with the API key displayed when it is created. ## Troubleshooting [Section titled “Troubleshooting”](#troubleshooting) ### API client requests are failing [Section titled “API client requests are failing”](#api-client-requests-are-failing) When API client requests fail you will get error messages in parts of the application that use the API clients, including the Teams UI (if you are using React), and the React/Vue employee examples. The most common reason that API client requests fail is a mismatch between the absolute URL configured in the server and the servers *actual* URL. This mismatch be fixed by modifying the Django Site object and settings to match the URL you’re loading the site from, as described in the documentation on [absolute URLs](/configuration/#absolute-urls). In *development* the most common issues are: 1. Your Django Site is not set up for development. Ensure the site’s domain name is `localhost:8000` in your Django admin, [as described here](/configuration/#absolute-urls). 2. You are loading from a mismatched domain. Be sure you are loading your browser at and not . Or alternatively, if you want to use the 127.0.0.1 address, update the Django site accordingly to use that. # Async and Websocket Support > Enable asynchronous Django views and real-time websockets using Daphne, Uvicorn, and Django Channels for modern web applications. As of version 2023.10, Pegasus provides support [asynchronous support](https://docs.djangoproject.com/en/stable/topics/async/), as well as support for websockets via the [channels library](https://channels.readthedocs.io/). ## Enabling Async Support [Section titled “Enabling Async Support”](#enabling-async-support) You can enable Async support by checking the “Use Async / Websockets” option in your project settings. Enabling Async will: 1. Change your default development server to [Daphne](https://docs.djangoproject.com/en/stable/howto/deployment/asgi/daphne/). 2. Change your default production server to [Uvicorn](https://www.uvicorn.org/) (via gunciorn). 3. Add and configure `channels` in your project for websocket support. In addition to the above configuration changes, enabling async will also use it for LLM chats if available. Finally, there is an optional group chat application you can separately add (details below). ## The Async / Websocket Demo Application [Section titled “The Async / Websocket Demo Application”](#the-async--websocket-demo-application) Pegasus includes an optional demo application to demonstrate the asynchronous and socket capabilities. The demo application is an extension of the demo application that you build while completing the [channels tutorial](https://channels.readthedocs.io/en/latest/tutorial/index.html). You can see a demo below. The demo application uses the [HTMX websockets extension](https://htmx.org/extensions/ws/) to simplify the implementation. If you prefer not to use HTMX at all, you can change your websocket connection logic to use vanilla JavaScript instead, as shown in the [channels tutorial here](https://channels.readthedocs.io/en/latest/tutorial/part_2.html#add-the-room-view). A React-based websocket demo is on the roadmap. ## Websocket urls [Section titled “Websocket urls”](#websocket-urls) Websocket URLs are defined separately from your app’s main `urls.py` file. In Pegasus, the convention is to put your websocket urls in `channels_urls.py` in your project folder (the same one containing `urls.py`). Because websocket urls are separate from your main app, and because they follow a different protocol, they must be referenced as absolute URLs in your front end (including prepending “ws\://” or “wss\://” depending on whether you’re using HTTPS). Pegasus ships with two helper functions you can use to assist with working with URLs, so long as you follow Pegasus conventions. The `websocket_reverse` function will reverse a relative websocket URL, and the `websocket_absolute_url` function will turn a relative URL into an absolute websocket URL based on your Site address and the `USE_HTTPS_IN_ABSOLUTE_URLS` setting. You can combine these functions like so to pass the URL of a websocket endpoint to a template: ```python room_ws_url = websocket_absolute_url(websocket_reverse("ws_group_chat", args=[room_id])) ``` You can then use the websocket URL in a template/JavaScript like this: ```js const chatSocket = new WebSocket({{ room_ws_url}}); chatSocket.onmessage = function(e) { // handle message }; ``` ## Asynchronous web servers [Section titled “Asynchronous web servers”](#asynchronous-web-servers) There are several ASGI servers supported by Django. By default, Pegasus uses the Daphne web server in development and the Uvicorn web server in production, for reasons described below. That said, you can customize your app to use whichever server you prefer. ### Daphne [Section titled “Daphne”](#daphne) In development, Pegasus uses the [Daphne](https://pypi.org/project/daphne/) web server for its tight integration with Django’s `runserver` command, as [outlined in the Django docs](https://docs.djangoproject.com/en/stable/howto/deployment/asgi/daphne/). Daphne is installed via `dev-requirements` and will be added to your `INSTALLED_APPS` whenever `settings.DEBUG` is `True`. ### Uvicorn [Section titled “Uvicorn”](#uvicorn) In production, Pegasus uses the [Uvicorn](https://www.uvicorn.org/) web server. Uvicorn has a seamless integration with `gunicorn`, making transitioning to it very easy. Uvicorn is installed via `prod-requirements`, and if you build with async features enabled, your `gunicorn` command will be updated to use it. ## Troubleshooting [Section titled “Troubleshooting”](#troubleshooting) **The chat app loads but nothing happens when I send a message.** The most likely reason this would happen is if your site URLs are not set up properly, which would cause the websocket endpoints to not hit the right address. See the documentation on [absolute URLs](/configuration/#absolute-urls) to fix this, and in particular make sure your Django site object has the right domain. In development this should be set to `localhost:8000`. **I’m getting an error: No module named ‘daphne’** If you are getting this error *in production* it is likely because your `DEBUG` environment variable is not set. Due to the order in which settings are imported, you *must* define `DEBUG=False` in your *environment*, `.env` file, or main `settings.py` file. This is in addition to (or instead of) setting `DEBUG=False` in your `settings_production.py` file. If you are getting this error *in development*, be sure that Daphne is installed. You should have the a `channels[daphne]` entry in your `dev-requirements.in` file, and you should [build and install your requirements](/python/setup) as needed. To do this in a non-Docker environment, run: ```plaintext pip-compile requirements/dev-requirements.in pip install -r requirements/dev-requirements.txt ``` **I’m having another issue deploying to production.** Since this is a new feature there may be some speed-bumps getting it into production on all platforms. While every deployment platform is expected to work, it is not possible to test every app/configuration. So, if you have any issues please reach out over email () or on Slack and I will do my best to help! # Celery > Set up Celery distributed task queues with Redis for background tasks, scheduled jobs, and async processing in Pegasus applications. [Celery](https://docs.celeryq.dev/) is a distributed task queue used to run background tasks. It is required by several Pegasus features, including: 1. The “background task” example. 2. Per-unit subscriptions (celery runs the background task to sync unit amounts with Stripe). 3. AI Chat (it is used in all builds to set chat names, and, if async is not enabled, for the chats themselves). If you aren’t using any of the above features, you can disable celery by unchecking the “use celery” option---added in version 2025.1---in your project settings. **If you *are* using any of the above features, this option will not do anything.** ## Quick Start [Section titled “Quick Start”](#quick-start) **If you’re using [Docker in development](/docker) then Celery should automatically be configured and running. The instructions in this section are for running Celery outside of Docker.** The easiest way to get going in development is to [download and install Redis](https://redis.io/download) (if you don’t already have it) and then run: *With uv:* ```bash uv run celery -A {{ project_name }} worker -l info --pool=solo ``` *With standard Python:* ```bash celery -A {{ project_name }} worker -l info --pool=solo ``` Note that the ‘solo’ pool is recommended for development but not for production. When running in production, you should use a more robust pool implementation such as `prefork` (for CPU bound tasks) or `gevent` (for I/O bound tasks). ### Celery and Gevent [Section titled “Celery and Gevent”](#celery-and-gevent) In production Celery is configured to run with the `gevent` pool, which drastically improves performance of Celery when running tasks that are I/O bound (which tends to be most tasks that make API or database calls). However, `gevent` does have some limitations, including that it does not work well `asyncio`. This means that if you are calling lots of async code in your Celery tasks, you should consider a different pool. To change the pool used by Celery you can modify (or remove) the `--pool` command when you call it. Note that `--pool=solo` or `--pool=gevent` is recommended for running Celery on Windows, since Celery 4.x [no longer officially supports Windows](https://docs.celeryq.dev/en/4.0/whatsnew-4.0.html#removed-features). For more information see the [Celery documentation](https://docs.celeryq.dev/en/stable/userguide/concurrency/gevent.html). ## Setup and Configuration [Section titled “Setup and Configuration”](#setup-and-configuration) The above setup uses [Redis](https://redis.io/) as a message broker and result backend. If you want to use a different message broker, for example [RabbitMQ](https://www.rabbitmq.com/), you will need to modify the `CELERY_BROKER_URL` and `CELERY_RESULT_BACKEND` values in `settings.py`. More details can be found in the [Celery documentation](https://docs.celeryq.dev/en/stable/getting-started/backends-and-brokers/index.html). ## Monitoring with Flower [Section titled “Monitoring with Flower”](#monitoring-with-flower) [Flower](https://flower.readthedocs.io/en/latest/) is an open-source web application for monitoring and managing Celery clusters. It provides real-time information about the status of Celery workers and tasks. If you’d like to use Flower in development, add the following to the `services` section of your `docker-compose.yml`: ```yaml flower: image: mher/flower environment: - CELERY_BROKER_URL=redis://redis:6379 command: celery flower ports: - 5555:5555 depends_on: - redis ``` In production, you will likely want to run Flower behind a private VPN, or [set up authentication](https://flower.readthedocs.io/en/latest/auth.html) on your Flower instance, and use a [reverse proxy](https://flower.readthedocs.io/en/latest/reverse-proxy.html) to expose it. ## Scheduled Tasks with Celery Beat [Section titled “Scheduled Tasks with Celery Beat”](#scheduled-tasks-with-celery-beat) [Celery Beat](https://docs.celeryq.dev/en/stable/userguide/periodic-tasks.html) is a scheduler that triggers tasks at regular intervals, which can be used to run periodic tasks like daily reports, or sending scheduled notifications. ### Configuration [Section titled “Configuration”](#configuration) By default, Celery Beat will store the schedule in file on the filesystem. When running in a production environment and especially in a containerized environment, you should use persistent storage to store the schedule. Pegasus is pre-configured to store the schedule in the Pegasus database using [`django-celery-beat`.](https://django-celery-beat.readthedocs.io/en/latest/). You can place the schedule task definitions in the `SCHEDULED_TASKS` setting in your `settings.py` file and then run the `bootstrap_celery_tasks` management command to create the tasks in the database. ```python from celery.schedules import crontab SCHEDULED_TASKS = { 'example-task-every-morning': { 'task': '{{ project_name }}.tasks.example_task', 'schedule': crontab(hour=7, minute=0), # Run every day at 7:00 AM }, 'another-example-every-hour': { 'task': '{{ project_name }}.tasks.another_example', 'schedule': 3600.0, # Run every hour (in seconds) 'args': (16, 16), # Arguments to pass to the task }, } ``` ```bash python manage.py bootstrap_celery_tasks --remove-stale ``` This will create or update the tasks in the database and remove any stale tasks that are no longer defined in `SCHEDULED_TASKS`. If you want to bootstrap the tasks automatically during you application deploy process you can do so by running the bootstrap command alongside the Django migration command. ### Running Celery Beat [Section titled “Running Celery Beat”](#running-celery-beat) To run Celery Beat in development: *With Docker:* If you are using the local dockerized setup with docker compose, then Celery Beat will already be running as part of the `celery` service. *With uv:* ```bash # Alongside the Celery worker, you can run Celery Beat uv run celery -A {{ project_name }} worker -l info --beat # AS a dedicated process uv run celery -A {{ project_name }} beat -l info ``` Note that if you run Celery Beat as a standalone process, you will need to ensure that the Celery worker is running separately. This is because Celery Beat is responsible for scheduling tasks while the worker executes them. #### Production Setup [Section titled “Production Setup”](#production-setup) In production, you can run Celery Beat as a separate process. You must ensure that there is only ever one Celery Beat process running at a time to avoid multiple instances of the same task being scheduled. It’s also important to note that you can not run Celery Beat in the same process as a worker that is using the `gevent` pool. For more information, see the [Celery Beat documentation](https://docs.celeryq.dev/en/stable/userguide/periodic-tasks.html). # Cookbooks > Step-by-step guides for Django admin setup, migrating from pip-tools to uv, enabling code formatting, and common development tasks. Step-by-step guides to some different things you might want to do with Pegasus. ## Use the Django Admin UI [Section titled “Use the Django Admin UI”](#use-the-django-admin-ui) Pegasus ships with a simple script to promote any user to a superuser who can access the Django admin. After going through the sign up flow, to convert your newly-created user into an admin, run the following command, being sure to replace the email address with the one you used to sign up: **Docker:** ```bash docker compose exec web python ./manage.py promote_user_to_superuser yourname@example.com ``` **Native:** ```bash python ./manage.py promote_user_to_superuser yourname@example.com ``` Now you should be able to access the django admin at ## Migrating from pip-tools to uv [Section titled “Migrating from pip-tools to uv”](#migrating-from-pip-tools-to-uv) To migrate your project from pip-tools to uv follow these steps. ### Install uv [Section titled “Install uv”](#install-uv) If you haven’t already, [install uv](https://docs.astral.sh/uv/getting-started/installation/): ```bash curl -LsSf https://astral.sh/uv/install.sh | sh ``` ### Update your project code [Section titled “Update your project code”](#update-your-project-code) It’s recommended to do this in two steps: 1. [Upgrade your project](/upgrading) to the latest Pegasus version, keeping your package manager as “pip-tools”. Merge all conflicts and ensure your project is working properly on this version. 2. Then, change the package manager from pip-tools to uv in your project settings and do another upgrade/pull request. At this point you will likely have conflicts in your requirements files, but hopefully nowhere else. See the next sections for resolving these. ### Prepare to resolve conflicts [Section titled “Prepare to resolve conflicts”](#prepare-to-resolve-conflicts) First, follow the github instructions to merge your project on your local machine, by checking out the pegasus upgrade branch and merging the main branch into it. You will have to update the command below with the exact branch name of the pull request created by Pegasus: ```bash git fetch origin git checkout pegasus-- git merge main ``` At this point you’ll have a partially merged branch with conflicts. ### Migrate your requirements.in files [Section titled “Migrate your requirements.in files”](#migrate-your-requirementsin-files) The uv build of Pegasus no longer uses requirements files, so any changes you’ve made to these will need to be migrated to `pyproject.toml` and `uv.lock`. You can use the [reqs-sync](https://github.com/saaspegasus/reqs-sync/) package to help with this. Follow the steps below for any file with conflicts. To migrate your main *requirements.in* file: ```bash uv tool run reqs-sync reqs-to-toml requirements/requirements.in ``` To migrate your development *dev-requirements.in* file: ```bash uv tool run reqs-sync reqs-to-toml requirements/dev-requirements.in --group=dev ``` To migrate your production *prod-requirements.in* file: ```bash uv tool run reqs-sync reqs-to-toml requirements/prod-requirements.in --group=prod ``` These commands should copy all project requirements from your `requirements.in` file(s) to your `pyproject.toml` file (into the appropriate group, if necessary). ### Update your uv.lock file [Section titled “Update your uv.lock file”](#update-your-uvlock-file) Next you should rebuild your `uv.lock` file from the updated `pyproject.toml` file: ```bash uv lock ``` You should then check the versions that were added to the `uv.lock` file and update any as needed based on the versions your requirements.txt files. ### Test the migration [Section titled “Test the migration”](#test-the-migration) Run your project (`uv run python manage.py runserver`) and verify everything works as expected. ### Remove your requirements files [Section titled “Remove your requirements files”](#remove-your-requirements-files) Finally, run: ```bash git rm requirements/*` ``` To remove all your requirements files. Congratulations, you’ve migrated to uv! Resolve any other conflicts, push and merge your code, and you’re done! ## Migrating to auto-formatted code [Section titled “Migrating to auto-formatted code”](#migrating-to-auto-formatted-code) As of February, 2023 all Pegasus projects have the option to auto-format your Python code. To migrate a project from non-formatted to formatted code, you can go through the following steps: 1. First, do a full Pegasus upgrade to the version you want to update to, as described [here](/upgrading). **Do *not* check the “autoformat” checkbox yet.** 2. Next, run the formatting tools on your project’s `main` branch: 1. Install ruff: `pip install ruff` 2. Run ruff linting `ruff check --extend-exclude migrations --line-length 120 . --fix` 3. Run ruff formatting: `ruff format --line-length 120 .` 3. Commit the result: 1. `git add .` 2. `git commit -m "apply formatting changes"` 4. Finally, check the “autoformat” box on your Pegasus project, and do *another* upgrade according to the same process. ## Migrating an existing project to Pegasus [Section titled “Migrating an existing project to Pegasus”](#migrating-an-existing-project-to-pegasus) There is not a one-size-fits-all answer to how to migrate an existing app to Pegasus, as it can depend on the size, complexity, age, and architecture of the project you’re migrating from. That said, the strategy that has worked best for most people on small-to-medium-sized projects is to basically **start a new project on Pegasus and merge your existing functionality into it**. Here is a rough guideline for how you can do that: 1. Create a new Pegasus project with the exact settings you want your app to have. If your existing app uses certain technologies (e.g. css frameworks, deployment, etc.) it’s probably easiest to pick all the same ones, if possible, unless you know you want to change those at the same time. 2. Bring across the custom logic of your legacy project, while largely preserving the previous project’s structure. So, for example, if your project was split into multiple Django apps, just copy those across. If it was a monolith, just leave it that way, and so on. 3. Reconcile any conflicting data models. E.g. you will only want a single user model. Ideally you would use Pegasus’s built-in `CustomUser` model and update your foreign keys accordingly, although this can make data migrations more complicated. 4. Try and get the urls/views etc. working for your previous app’s functionality. Don’t worry about UI, but just try to get the routes and business logic working and routing to the right tmeplates. 5. Migrate those templates to use the Pegasus base templates, etc. (if possible). You can kind of do this page by page, making each one look good as you go. Alternatively, keep your own base template if it is different enough from Pegasus’s or if you want to keep your existing app scaffolding. The latter option will require updating Pegasus’s built-in functionality to work with your own templates, and will make future upgrades/merges more complicated. 6. Figure out a data migration (assuming you already have production data). This can often be the trickiest part, especially if you’re swapping the user model or othe foregn keys. It can often be easiest to write scripts to copy the data across from one instance to the other, but in some cases you might prefer to keep your previous migration history in place and run migrations on the live database. The larger the project is, the more likely it is you’ll want to keep the database and existing models and just use migrations to do the minimal set of Pegasus changes. The main downsides of the above approach are that you lose your git history on the previous project, data migrations can be tricky, and if you have a complex UI then it might take some effort to port across. The main upside is that once you get through the pain, all future Pegasus updates will likely be much smoother. ## Delete Pegasus Examples [Section titled “Delete Pegasus Examples”](#delete-pegasus-examples) You can remove the Pegasus examples by unchecking the “Include Examples” checkbox on your project page and re-downloading (/or [upgrading](/upgrading)) your codebase. For earlier versions you can use [these instructions](https://github.com/saaspegasus/pegasus-docs/blob/1becc2cb8f86738eeba85c9faddb15f69b8ad7bc/cookbooks.md#delete-pegasus-examples). # Customizations > Customize landing pages, navigation, styles, and JavaScript in your Pegasus application with popular CSS frameworks. This page outlines the basics of customizing Pegasus to meet your application’s needs. ## Personalize your landing page [Section titled “Personalize your landing page”](#personalize-your-landing-page) Pegasus ships with a simple landing page that varies based on your selected CSS framework. Most projects will want to highly customize the landing page from what comes out of the box. Unless you are planning on building a marketing site on a different platform, this is likely one of the first things you’ll do. To modify the default landing page, you can edit the `./templates/web/landing_page.html` file (and any included sub-templates) and make the customizations you want. Another good option is to use a paid or open-source alternative for your marketing content. Some recommended places to get marketing templates include: * **Tailwind**: [Tailwind UI](https://tailwindui.com/), [Flowbite](https://flowbite.com/). * **Bootstrap**: [Official themes](https://themes.getbootstrap.com/), [other free recommendations](https://dev.to/bootstrap/bootstrap-5-templates-91p). * **Bootstrap (Material)**: [Material Kit Pro](https://www.creative-tim.com/product/material-kit-pro) ## Update the logged-in experience [Section titled “Update the logged-in experience”](#update-the-logged-in-experience) After you’ve tweaked your landing page, you’ll likely want to dive into the nuts and bolts that make up your app. To modify the logged-in default page, edit the `./templates/web/app_home.html` file to your liking. ### Changing the navigation [Section titled “Changing the navigation”](#changing-the-navigation) There are two levels of navigation that ship with Pegasus, the top nav and the sidebar nav. You’ll likely want to modify both. To change the top nav edit the `./templates/web/components/top_nav.html` file. To change the sidebar nav edit the `./templates/web/components/app_nav.html` file. ## Styles [Section titled “Styles”](#styles) All of Pegasus’s CSS frameworks are designed to be customized to your needs. You can set specific colors or override the themes entirely. How styles are customized depends on the CSS framework. For more information, see the individual page for your framework in [the CSS docs](/css/overview) ## Javascript [Section titled “Javascript”](#javascript) The project uses a Vite build pipeline to compile the JavaScript files. For more details on how it works see the [front-end documentation](/front-end/overview). # Using Docker in Development > Set up Django development environment with Docker Compose including PostgreSQL, Redis, Celery, and debugging configuration. Pegasus recommends using [Docker](https://www.docker.com/) during development. Although Docker is not strictly required, many parts of the documentation and helper tools do assume you are using it. In production, Docker can also be used to deploy your application to containerized platforms. See [the deployment page](/deployment/overview) for more details on Docker in production. ## Choosing a Docker Setup [Section titled “Choosing a Docker Setup”](#choosing-a-docker-setup) When configuring your Pegasus project to use Docker, you can select from two different options: **services-only**, and **full-Docker development**. In **services-only mode**, Docker is only used to run the external services: PostgreSQL and Redis. The Django server, Celery and any other processes are run directly on the local machine. In this mode, you don’t need to install PostreSQL and Redis on your local machine, which simplifies the setup and maintenance. You also have direct access to the other dev processes which simplifies debugging and inspection. The main downside of services-only mode is that it requires installing `uv` and `npm`. In **full-Docker** mode, Docker is used to run the services, as above, but also runs Django, npm, and Celery. No processes are run directly on your local machine. This mode makes it easier to get up and running---since all you need to install is Docker---but it can make development more complicated, since all the processes are running inside Docker. As a rough guideline: **If you are comfortable installing and running Python and Node.js on your machine, use services-only mode. Otherwise, use full-Docker mode.** ## Install prerequisites [Section titled “Install prerequisites”](#install-prerequisites) You need to install [Docker](https://www.docker.com/get-started) prior to setting up your environment. Mac users have reported better performance on Docker using [OrbStack](https://orbstack.dev/), which is a Docker Desktop alternative optimized for performance. ## Starting the application [Section titled “Starting the application”](#starting-the-application) To start the Docker services, run: ```bash make start ``` This will start the Database services (PostgreSQL and Redis) and in full-mode, start all the processes needed to run your application, including Django, the front end server / bundler, and Celery. make and Windows Windows users may need to install a 3rd-party package to run `make` commands. The easiest way to do that is via [these instructions](https://stackoverflow.com/a/57042516/8207). The first time you run the app you should run: ```bash make init ``` Which will also create and run database migrations and bootstrap your application. ## Stopping the application [Section titled “Stopping the application”](#stopping-the-application) To stop the Docker services, run: ```bash make stop ``` This will stop the Database services (PostgreSQL and Redis) and in full-mode, stop the other container processes (Django, Vite/Webpack, and Celery). ## Architecture and how it works [Section titled “Architecture and how it works”](#architecture-and-how-it-works) This section provides technical details about the Docker setup and how it works. The Docker configuration is primarily in `docker-compose.yml`, where you can inspect the configured containers. ### Services only mode [Section titled “Services only mode”](#services-only-mode) In this mode, the `docker-compose.yml` file will only include container definitions for PostgreSQL and Redis. The containers listed below will run with their default ports exposed. Use `docker ps` to check. | Container Name | Purpose | Port | | -------------- | ------------------------------------ | ---- | | `db` | Runs Postgres (primary Database) | 5432 | | `redis` | Runs Redis (Cache and Celery Broker) | 6379 | ### Full Docker dev mode [Section titled “Full Docker dev mode”](#full-docker-dev-mode) In this mode, the `docker-compose.yml` file will also include containers for Django, node, and Celery. Depending on your project settings, there are several containers that might be running. These are outlined in the table below: | Container Name | Purpose | Included | | -------------- | ---------------------------------------- | ----------------------------------------------------------------------- | | `db` | Runs Postgres (primary Database) | Always | | `redis` | Runs Redis (Cache and Celery Broker) | Always | | `web` | Runs Django | Always | | `vite` | Runs Vite (for CSS/JavaScript assets) | If [building with Vite](/front-end/vite) | | `webpack` | Runs Webpack (for CSS/JavaScript assets) | If [building with Webpack](/front-end/webpack) | | `celery` | Runs Celery (for background tasks) | If [Celery is enabled](/celery) | | `frontend` | Runs the React Front End | If [the standalone front end is enabled](/experimental/react-front-end) | Like above, the DB containers will expose their default ports. You can inspect the `Dockerfile`s being used in `docker-compose.yml`. Python containers use the `Dockerfile.dev` file. ### Settings [Section titled “Settings”](#settings) The docker environment sets environment variables using the included `.env` file. The `.env` file is automatically ignored by git, so you can put any additional secrets there. It generally should not be checked into source control. You can instead add variables to `.env.example` to show what should be included. ## Working with full docker mode [Section titled “Working with full docker mode”](#working-with-full-docker-mode) The following instructions are specific to “full” docker mode, where Docker is also running your application. ### Python environments [Section titled “Python environments”](#python-environments) The Python environment is run in the containers, which means you do not need to have your own local environment if you are always using Docker for development. Python requirements are automatically installed when the container builds. However, keep in mind that if you go this route, you will need to run all commands inside the containers as per the instructions below. ### Running once-off management commands [Section titled “Running once-off management commands”](#running-once-off-management-commands) Running commands on the server can be done using `docker compose`, by following the pattern used in the `Makefile`. For example, to bootstrap Stripe subscriptions, run: ```bash docker compose exec web python manage.py bootstrap_subscriptions ``` Or to promote a user to superuser, run: ```bash docker compose exec web python manage.py promote_user_to_superuser me@example.com ``` You can also use the `make manage` command, passing in `ARGS` like so: ```bash make manage ARGS='promote_user_to_superuser me@example.com' ``` You can add any commonly used commands you want to `custom.mk` for convenience. ### Updating Python packages [Section titled “Updating Python packages”](#updating-python-packages) If you add or modify anything in your `requirements.in` (and `requirements.txt`) files, you will have to rebuild your containers. The easiest way to add new packages is to add them to `requirements.in` and then run: ```bash make requirements ``` Which will rebuild your `requirements.txt` file, rebuild your Docker containers, and then restart your app with the latest dependencies. ### Debugging [Section titled “Debugging”](#debugging) You can use debug tools like `pdb` or `ipdb` by enabling service ports. This can be done by running your web container with the following: ```bash docker compose run --service-ports web ``` If you want to set up debugging with PyCharm, it’s recommended to follow [this guide on the topic](https://testdriven.io/blog/django-debugging-pycharm/). ## Troubleshooting [Section titled “Troubleshooting”](#troubleshooting) ### ”No such file or directory” errors [Section titled “”No such file or directory” errors”](#no-such-file-or-directory-errors) Some environments---especially on Windows---can have trouble finding the files on your local machine. This will often show up as an error like this when starting your app: ```plaintext python: can't open file '/code/manage.py': [Errno 2] No such file or directory ``` These issues are usually related to your *disk setup*. For example, mounting your code on a remote filesystem or external drive to your machine. To fix, try running the code on the same drive where Docker Desktop is installed, or on your machine’s default “C:” drive. You can also get around this issue by running your application natively, instead of with Docker. ## Other Resources [Section titled “Other Resources”](#other-resources) * [Dockerizing Django with Postgres, Gunicorn, and Nginx](https://testdriven.io/blog/dockerizing-django-with-postgres-gunicorn-and-nginx/) provides an overview of the setup, and has additional information about using Docker in production * [Environment variables in Compose](https://docs.docker.com/compose/environment-variables/) is a good resource on the different ways to work with environment variables in Docker # Early Access Program > How to get early access to new SaaS Pegasus features and development releases. If you’d like to try out new Pegasus features before they’re officially released, you can request access to the early-access / beta testing program. ## What’s in Early Access [Section titled “What’s in Early Access”](#whats-in-early-access) You can see the [Development Release Notes](/release-notes-dev/) for a preview of what’s currently in development. With early access you have the opportunity to: * **Test new features** before they’re officially released * **Provide feedback** that shapes the final implementation * **Get a head start** on upcoming changes It is worth noting that early-access features are more likely to have bugs as they have not gone through the same rigor of QA and testing as final release features. ## How to Request Access [Section titled “How to Request Access”](#how-to-request-access) To request early access, reach out through one of these channels: * **Slack**: Message in the SaaS Pegasus Slack community * **Email**: # Feature Flags > Implement feature flags with Django Waffle to control feature rollouts, A/B testing, and user-specific or team-based feature access. [Waffle](https://waffle.readthedocs.io/en/stable/) is the top library for managing feature flags in Django. Pegasus includes configuration for using Waffle with or without teams. If you are using [Teams](/teams) then the Waffle flags can be turned on based on the user or the team. If you are not using Teams then flags only apply to users. ## Usage [Section titled “Usage”](#usage) Waffle can be used to turn on and off features. For example: ```python import waffle def my_view(request): if waffle.flag_is_active(request, 'flag_name'): """Behavior if flag is active.""" else: """Behavior if flag is inactive.""" ``` The flags themselves are managed via the Django Admin site where each flag can be activated for specific users or teams, or based on certain conditions such as *superuser* status. Flags can also be managed via the command line. For full details on configuring flags see the [Flag Attributes](https://waffle.readthedocs.io/en/stable/types/flag.html#flag-attributes) of the Waffle docs. Flags may be used in views, templates, JavaScript and more. For full details see the [Waffle docs](https://waffle.readthedocs.io/en/stable/usage/index.html) ## Usage with *Teams* [Section titled “Usage with Teams”](#usage-with-teams) If you are using [Teams](/teams), Pegasus ships with a [custom flag model](https://waffle.readthedocs.io/en/stable/types/flag.html#custom-flag-models) which allows you to activate flags on a per-team basis in addition to the other default options. ## Example usage [Section titled “Example usage”](#example-usage) To see flags in actions look at the “Flags” example in the Pegasus Example Gallery. The flag in the example is configured in [test mode](https://waffle.readthedocs.io/en/stable/testing/user.html) which allows us to activate the flag with a URL parameter. # Forms > Render Django forms with CSS framework integration, dynamic Alpine.js functionality, and custom template tags for better UX. Pegasus ships with some extensions to Django forms to integrate with different CSS frameworks and add some extensions. ## The `form_tags` module [Section titled “The form\_tags module”](#the-form_tags-module) You can use default Django form rendering for forms, but if you want all the built-in style support, you should instead use the utilities in the `form_tags` module. To use it, first include `form_tags` in any Django template file: ```jinja {% load form_tags %} ``` Then, you can render a form using the `render_form_fields` template tag. Here is a basic example: ```jinja
{% csrf_token %} {{ form.non_field_errors }} {% render_form_fields form %}
``` You can also render individual fields using `render_field`: ```jinja
{% csrf_token %} {{ form.non_field_errors }} {% render_field form.username %} {% render_field form.password %}
``` ## Dynamic forms with Alpine.js [Section titled “Dynamic forms with Alpine.js”](#dynamic-forms-with-alpinejs) *Added in version 2023.6* The form rendering helpers also support adding attributes, which can be useful to add Alpine.js to make a form more dynamic. For example, you can bind a form value to an alpine model by passing it in `attrs` like this: ```python class ExampleFormAlpine(forms.Form): YES_NO_OTHER = ( ("yes", gettext("Yes")), ("no", gettext("No")), ("other", gettext("Other")), ) like_django = forms.ChoiceField( label=gettext("Do you like Django?"), choices=YES_NO_OTHER, widget=forms.Select(attrs={"x-model": "likeDjango"}), # this line will bind the value to an alpine model ) ``` Then in the HTML template you have to add an alpine model to the form: ```jinja
{% render_field form.like_django %} ``` The `render_field` tags support two special syntaxes to make using alpine easier: 1. Any attribute starting with `x` will be automatically converted to `x-`. 2. Double underscores (`__`) will be replaced with colons (`:`). The following example alpine form and template (which also ship with Pegasus, available at ) demonstrate this usage, including hiding/showing a field based on the value of another field, rendering field values in labels, and changing the style of a field based on its value. Django form class: ```python class ExampleFormAlpine(forms.Form): YES_NO_OTHER = ( ("yes", gettext("Yes")), ("no", gettext("No")), ("other", gettext("Other")), ) STYLES = ( ("regular", gettext("Normal")), ("success", gettext("Success")), ("danger", gettext("Danger")), ) like_django = forms.ChoiceField( label=gettext("Do you like Django?"), help_text=gettext("Try choosing 'other' to see unhiding a form field based on a value."), choices=YES_NO_OTHER, widget=forms.Select(attrs={"x-model": "likeDjango"}), ) like_django_other = forms.CharField(label=gettext("Please specify more details about your answer.")) styled_options = forms.ChoiceField( label=gettext("Styled Options"), help_text=gettext("Try picking an option to see how you can style a component based on its value."), choices=STYLES, widget=forms.Select(attrs={"x-model": "styleValue"}), ) ``` Django template: ```jinja {% csrf_token %} {% render_field form.like_django %} {% render_field form.like_django_other xshow="likeDjango === 'other'" xcloak='True' %} {% render_field form.styled_options xbind__class="'pg-bg-' + styleValue" %}

You can also use alpine to display selected values. Like Django: , Style:

``` # GitHub Integration > Integrate projects with GitHub using the GitHub App, OAuth, or personal access tokens for automated updates, pull requests, and version control. You can connect your Pegasus projects directly to GitHub instead of downloading them as a zip file. This makes for a more streamlined workflow---especially when changing or upgrading your project. ## Watch the video [Section titled “Watch the video”](#watch-the-video) The following video shows how to create and update a project using the Github integration. ## Connecting your account [Section titled “Connecting your account”](#connecting-your-account) There are three ways to connect your Github account to Pegasus. The **GitHub App** is the recommended approach for most users. ### Using the GitHub App (Recommended) [Section titled “Using the GitHub App (Recommended)”](#using-the-github-app-recommended) The GitHub App is the easiest and most secure way to connect your account. Unlike the other methods, the GitHub App only grants Pegasus access to repositories you explicitly choose, and it works seamlessly with both personal and organization-owned repositories. 1. **Create a repository.** First, [create a new private repository](https://github.com/new) on GitHub for your project. 2. **Install the app.** From your project download page, click “Install GitHub App”. You’ll be redirected to GitHub to install the app and select which repositories it can access. Make sure to grant access to the repository you just created. 3. **Connect your repo.** After installing the app, you’ll be redirected back to Pegasus. Select your repository from the dropdown and click “Connect Repository”. Once connected, Pegasus can push code and create pull requests in your repository. **Managing permissions:** If you need to grant access to additional repositories later, click “Configure permissions” from the project download page. You can also manage your installation from your [GitHub App settings](https://github.com/settings/installations). ### Using “Connect Github” (OAuth) [Section titled “Using “Connect Github” (OAuth)”](#using-connect-github-oauth) You can also connect your account using the “Connect Github” button on the project download page. This is an easy way to get set up, but grants access to all private repositories in your account. Pegasus does not view or modify data in any repositories unless you connect them, but it theoretically could. ### Using Personal Access Tokens [Section titled “Using Personal Access Tokens”](#using-personal-access-tokens) Pegasus can also connect to your repositories using [Personal Access Tokens](https://docs.github.com/en/authentication/keeping-your-account-and-data-secure/managing-your-personal-access-tokens). These work similarly to the Github app, but are less user-friendly and require more manual setup. There is no real reason to use them now that the Github app exists, but they are still supported for legacy projects. #### With Classic Tokens [Section titled “With Classic Tokens”](#with-classic-tokens) To use Pegasus with a classic token, visit the [Personal access tokens](https://github.com/settings/tokens) page on Github, then select “Generate new token (classic)” from the dropdown, or [visit this page](https://github.com/settings/tokens/new). Choose a note and expiration date for your token and grant the following scopes: * user:email (Access user email addresses (read-only)) * repo (Full control of private repositories) * workflow (Update GitHub Action workflows) Then click “Generate token”. You will be taken to a page where your token is displayed. Copy this value and paste it into the “personal access token” field from your project download page on Pegasus. Note that you won’t be able to view the token again! #### With Fine-Grained Access Tokens [Section titled “With Fine-Grained Access Tokens”](#with-fine-grained-access-tokens) If you want the most control over your permissions, you should use a fine-grained access token, which allow you to control access to specific repositories. Note that if you use fine-grained tokens **you must create the repository for your project before creating the token**. Pegasus cannot create the project for you with these tokens. After creating the repository, [create a new fine-grained-token from this page](https://github.com/settings/personal-access-tokens/new). Set a token name and expiration date, and then use “Only select repositories” to choose the repositories you want to grant access to (the one you just created). Under “Permissions” —> “Account Permissions” you must grant *read* access to: * Email addresses Then under “Permissions” —> “Repository Permissions” you must grant **read and write** access to: * Contents * Pull Requests * Workflows Then click “Generate token”. You will be taken to a page where your token is displayed. Copy this value and paste it into the “personal access token” field from your project download page on Pegasus. Note that you won’t be able to view the token again! ## Connecting an existing project to Github [Section titled “Connecting an existing project to Github”](#connecting-an-existing-project-to-github) Projects that were created before February 2024, or that didn’t use the Github integration can still be connected to Github via a one-time process. After completing this, you will be able to upgrade and change your Pegasus project using automatic pull requests. First, you’ll have to connect your Github account using one of the methods described above. Next, you will need to find the commit id of the last Pegasus update you have made. If you have never updated your codebase, this will be the first commit in the repository, which you can find by running `git log --reverse`. If you have updated your codebase using one of the other methods below, this will be the last commit on the `pegasus` branch of your repository, which you can find by running `git checkout pegasus` followed by `git log`. Once you have the commit id ready, add your existing Github repository to your Pegasus project from the downloads page. After completing this step you will be prompted with a page that looks like this: ![Set Commit](/_astro/set-commit.Bh21vQoQ_Z1pzoMK.webp) Enter the commit ID there, and you should now be able to update your project with pull requests. ## Working with repositories owned by an organization [Section titled “Working with repositories owned by an organization”](#working-with-repositories-owned-by-an-organization) If you’re using the **GitHub App**, organization repositories work automatically---just make sure to install the app on the organization account (not your personal account) and grant access to the relevant repositories. For **OAuth** and **personal access tokens**, Github organizations do not allow API-based repository access by default, so you will also need to grant programmatic access. Github provides detailed guidance on how to do this. For “Connect Github,” follow the [oauth instructions](https://docs.github.com/en/organizations/managing-oauth-access-to-your-organizations-data), and for personal access tokens, follow the [personal access token instructions](https://docs.github.com/en/organizations/managing-programmatic-access-to-your-organization/setting-a-personal-access-token-policy-for-your-organization). ## Pushing Pegasus code to a subdirectory in your repository [Section titled “Pushing Pegasus code to a subdirectory in your repository”](#pushing-pegasus-code-to-a-subdirectory-in-your-repository) By default, your entire git repository is dedicated to Pegasus, with all of Pegasus’s files included at the root of the repository. Some projects---especially those with a separate front end---may want to instead include Pegasus code in a subdirectory of the repository (e.g. “backend”), so that other projects (e.g. “frontend”) can be included in the same repository. It is possible to configure your Github integration this way. Connect your repository first, then expand “Repo settings” on the project download page and set the subdirectory there. **Make sure to set this before pushing your project for the first time.** If you would like to update an existing project to use a subdirectory, you’ll have unlink and re-add your repository, then [reconnect it](#connecting-an-existing-project-to-github). ## Troubleshooting [Section titled “Troubleshooting”](#troubleshooting) **I keep getting “Error pushing to GitHub. Please check your token scopes.” when pushing my project.** While Pegasus does its best to catch errors that come from Github and show them to you, sometimes it will return this generic error. One common reason a valid token is unable to push code is related to email privacy settings. Specifically the “Blocking command line pushes that expose your personal email address” setting---which currently must be *disabled* in order to use the Github integration. To check and disable this setting: 1. Go to your [Github email settings](https://github.com/settings/emails) 2. Scroll down to where it says “Keep my email addresses private”. 3. If that option is checked, ensure that the “Block command line pushes that expose my email” option below it is *not* checked. 4. If that option is *not* checked, then it is a different problem. You are welcome to reach out directly for support **My GitHub App can no longer access my repository.** If you remove a repository from your GitHub App’s permissions (or uninstall and reinstall the app), Pegasus will lose access to the repository. To fix this, click “Update your app permissions” from the error message on your project download page. This will redirect you to GitHub where you can re-grant access to the repository. # Using Github Actions > Automate Django testing and front-end builds with GitHub Actions CI/CD workflows for continuous integration. [GitHub Actions](https://github.com/features/actions) allows you to automate your software workflows. Pegasus apps optionally ship with Github actions support for a few things to build off. If you’ve built with Github actions support, they should successfully run the first time you push your code to Github. Actions are configured in the `.github` directory in your project. The following actions ship with Pegasus: ## Running Django Tests [Section titled “Running Django Tests”](#running-django-tests) The Django tests are configured in `.github/tests.yml`. By default, it will: * Run on every push to the `main` branch and every pull request. * Run on Python version 3.14 (other Python versions can be added by modifying the `python-version` list) * Use the latest version of Postgres * Run `./manage.py test` All of these can be changed by modifying the relevant sections of the `.github/tests.yml` file. ## Building the Front End [Section titled “Building the Front End”](#building-the-front-end) The front end build is configured in `.github/build_frontend.yml`. By default, it will: * Run on every push to the `main` branch and every pull request. * Run on Node version 22 (other Node versions can be added by modifying the `node-version` list). * Run `npm run build`, ensuring your front end builds properly. * Run `npm run type-check`, ensuring all type checks pass. Any compilation errors in your JavaScript should show up as build failures. # Internationalization > Add multi-language support and timezone handling to Django applications with translation files, locale management, and user preferences. Pegasus supports internationalization via built-in support for timezones and language translations. To enable timezone and multi-language support, you must select the “use internationalization” option in your project settings. ## Translation Demo [Section titled “Translation Demo”](#translation-demo) This two-minute demo highlights how translations work in Pegasus apps. ## Localization [Section titled “Localization”](#localization) Pegasus ships with full support for localizing user-facing text. Currently, not all the user-facing text is properly tagged for localization but this will be incrementally addressed in future releases. For full documentation on localization see the [Django docs](https://docs.djangoproject.com/en/stable/topics/i18n/). ## Big picture [Section titled “Big picture”](#big-picture) Big picture there are two steps to translation: 1. **Define the text you want to translate (in Python, HTML, or JavaScript)**. This step happens in your project’s code. 2. **Add a translation for that text to other languages**. This step happens in your project’s translation files, which can be found in the `locale//LC_MESSAGES/` folders (there will be one for each language). ## Managing enabled languages [Section titled “Managing enabled languages”](#managing-enabled-languages) There are two steps to updating the list of languages that will be available on your site. The first step is to define it in `settings.LANGUAGES`. Out of the box this will be English and French: ```python from django.utils.translation import gettext_lazy LANGUAGES = [ ('en', gettext_lazy('English')), ('fr', gettext_lazy('French')), # add other languages here ] ``` The second step is to create the translations folder for the language. This can be done by running: ```bash python ./manage.py makemessages -l [new lang code] --ignore node_modules --ignore venv ``` Or in Docker: ```bash docker compose exec web python manage.py makemessages -l [new lang code] --ignore node_modules --ignore venv ``` ## Marking text in your app for translation [Section titled “Marking text in your app for translation”](#marking-text-in-your-app-for-translation) All text you want to be translatable must be tagged in your application. This can be done as follows: **In Python:** ```python from django.utils.translation import gettext def my_view(request): output = gettext("Welcome to my site.") return HttpResponse(output) ``` See the [Django docs](https://docs.djangoproject.com/en/stable/topics/i18n/translation/#internationalization-in-python-code) for more. **In Django templates:** ```jinja {% load i18n %} {% translate "This is the title." %} ``` See the [Django docs](https://docs.djangoproject.com/en/stable/topics/i18n/translation/#internationalization-in-template-code) for more. **In JavaScript:** ```javascript document.write(gettext('this is to be translated')); ``` See the [Django docs](https://docs.djangoproject.com/en/stable/topics/i18n/translation/#internationalization-in-javascript-code) for more. **In Wagtail:** See the [Wagtail docs](/wagtail/#internationalization). ## Creating / updating translation files [Section titled “Creating / updating translation files”](#creating--updating-translation-files) After you’ve marked text for translation, you’ll need to update your language files. This can be done by running: ```bash python ./manage.py makemessages --all --ignore node_modules --ignore venv python ./manage.py makemessages -d djangojs --all --ignore node_modules --ignore venv ``` Or in Docker: ```bash make translations ``` Note: if you get any errors you may need to [install gettext](https://stackoverflow.com/q/35101850/8207). ## Adding actual translations for other languages [Section titled “Adding actual translations for other languages”](#adding-actual-translations-for-other-languages) To add a translation for another language you need to edit that languages messages (.po) file. For example, to edit a French translation, you would update `locale/fr/LC_MESSAGES/django.po`. Then search for the text you want to translate, and add the French translation: ```plaintext msgid "My Team" msgstr "Mon Équipe" ``` The above lines will replace “My Team” with “Mon Équipe” whenever the French language is configured. After editing any message (.po) file, you will have to compile the messages for the updates to show up in your app. This can be done by: ```bash python ./manage.py compilemessages ``` Or in Docker: ```bash make translations ``` ## Technical notes [Section titled “Technical notes”](#technical-notes) Pegasus is configured to use cookies to track the current locale. This allows localization to work for both authenticated and unauthenticated users. More information on this approach is available the Django docs: [How Django discovers language preference](https://docs.djangoproject.com/en/stable/topics/i18n/translation/#how-django-discovers-language-preference) ## Timezones [Section titled “Timezones”](#timezones) Pegasus includes support for user’s setting their own time zones via their profile (version 2023.7 and later). When a user sets a timezone, it will be automatically activated by the `UserTimezoneMiddleware` so that by default all dates and times will appear in their local time. For more information on working with timezones in Django, see [Django’s timezone documentation](https://docs.djangoproject.com/en/stable/topics/i18n/timezones/). # Project/Page Metadata and SEO > Configure SEO metadata, page titles, social sharing tags, and XML sitemaps for better search engine optimization and discoverability. Pegasus comes with some built in tools and best-practices for setting page-level metadata (e.g. title, image URL, etc.). ## The `PROJECT_METADATA` setting [Section titled “The PROJECT\_METADATA setting”](#the-project_metadata-setting) Your Pegasus project will ship with a `settings.py` variable called `PROJECT_METADATA` with the following values: ```python PROJECT_METADATA = { 'NAME': '', 'URL': '', 'DESCRIPTION': '', 'IMAGE': 'https://upload.wikimedia.org/wikipedia/commons/2/20/PEO-pegasus_black.svg', 'KEYWORDS': 'SaaS, django', 'CONTACT_EMAIL': '', } ``` This information will be available in every view under the variable name `project_meta`. Out of the box, the values are used in a number of places, though can be overridden/modified at the view level. ## Page Titles [Section titled “Page Titles”](#page-titles) The default title for your pages will be your project name and description from `PROJECT_METADATA`. If you want to add a custom page title, you can pass a `page_title` context variable to the template. For example: ```python def my_new_view(request): return render('a/template.html', {'page_title': 'My New Page'}) ``` Pegasus will then set your title to be `My New Page | `. If you’d like to change the way the title is formatted (e.g. remove the project name), you can change that behavior in `web.templatetags.meta_tags.get_title`. In Pegasus versions after 2022.4 you can also override the title directly in a template by overriding the `page_title` block. For example: ```jinja {% block page_title %}This title will be used instead of the Pegasus versions{% endblock %} ``` ## Sitemaps [Section titled “Sitemaps”](#sitemaps) As of version 2022.6, Pegasus will automatically generate a basic [sitemap](https://developers.google.com/search/docs/advanced/sitemaps/overview) for your site at `sitemap.xml`. Out of the box, the sitemap will only contain your application’s homepage, but can be readily extended by adding URLs in `apps/web/sitemaps.py`. If you have [enabled Wagtail](/wagtail), your sitemap will also include any content managed by Wagtail. Make sure you [properly set the hostname in your Wagtail site](https://docs.wagtail.org/en/stable/reference/contrib/sitemaps.html#setting-the-hostname). # E-Commerce / Payments > Build digital storefronts with Stripe integration for one-time and recurring payments, product management, and purchase tracking. Pegasus (version 2023.9.1 and up) includes an out-of-the-box E-Commerce/Payments demo. In a few clicks you can have a fully functional digital storefront in your application, allowing you to collect and track one-time or recurring payments with Stripe. ## Watch a video [Section titled “Watch a video”](#watch-a-video) To see how this feature works, you can watch the following video: ## Getting Started [Section titled “Getting Started”](#getting-started) ### Set up Stripe Products [Section titled “Set up Stripe Products”](#set-up-stripe-products) First add your products in the Stripe dashboard. Be sure to add readable product names, descriptions, and images, as these will be used for the in-app store. Additionally, make sure each product includes at least one Price. ### Set up your development environment [Section titled “Set up your development environment”](#set-up-your-development-environment) Setting up your development is similar to the [process for subscriptions](/subscriptions), but has fewer steps. 1. If you haven’t already, update the `STRIPE_*` variables in `settings.py` or in your os environment variables to match the keys from Stripe. See [this page](https://stripe.com/docs/keys) to find your API keys. 2. Run `python manage.py bootstrap_ecommerce` to sync your Stripe products and prices to your local database. Once you’ve done this, login and click on the e-commerce tab in the navigation, and you should see your store. ## Data models [Section titled “Data models”](#data-models) ### `ProductConfiguration` [Section titled “ProductConfiguration”](#productconfiguration) What shows up in your store is controlled by the `ProductConfiguration` data model. You can manage these objects from the Django admin (available at locally). For example, to remove a product from the store you can uncheck “is active”. The `ProductConfiguration` model is also a good place to add additional information to your products. For example, you can add additional display data there, or add a `FileField` if you want purchases to grant access to a digital download. ### `Purchase` [Section titled “Purchase”](#purchase) The `Purchase` model is used to record user purchases. A `Purchase` is associated with a `User` and a `ProductConfiguration` and also has details of the Stripe checkout session, date of purchase, and product/price used at the time of purchase. ## Feature gating [Section titled “Feature gating”](#feature-gating) The `@product_required` decorator can be used to restrict access to a view based on whether or not the logged-in user has purchased a particular product. This decorator expects a `product_slug` field in the URL / view with the slug of the `ProductConfiguration` object to be checked. If the user owns the product, they will be granted access to the view. Additionally, if the user gets access, two additional field will be populated on the `request` object: * `request.product_config` will have the `ProductConfiguration` object. * `request.product_purchase` will have the `Purchase` object. If the user does *not* have access to the product, the decorator will redirect them back to the store homepage. ## Webhooks [Section titled “Webhooks”](#webhooks) Like subscriptions, it’s recommended to use webhooks to ensure you receive all updates from Stripe. For the e-commerce store, the only required webhook is `checkout.session.completed`. Follow [the subscriptions documentation](/subscriptions/#webhooks) to set up webhooks in development and production. # Version History and Release Notes > Complete changelog and version history for SaaS Pegasus Django boilerplate with detailed feature updates and migration guides. Releases of [SaaS Pegasus: The Django SaaS Boilerplate](https://www.saaspegasus.com/) are documented here. In Development See the [Development Release Notes](/release-notes-dev/) for a preview of what’s coming in the next release. ## Version 2026.6 [Section titled “Version 2026.6”](#version-20266) This release adds the ability to manage your Pegasus project settings with AI agents. It also adds a number of smaller improvements, utiliites and hardening fixes. ### Manage your projects with AI agents [Section titled “Manage your projects with AI agents”](#manage-your-projects-with-ai-agents) All projects now include a `pegasus-projects` skill that tells agents how to update your projects (or create new projects) automatically. You can say things like “let’s add subscriptions” or “switch the front-end framework to React” and the agent will update your config and use its other skills to rebuild your project and merge conflicts. Behind the scenes this uses the updated [`pegasus-cli`](https://github.com/saaspegasus/pegasus-cli) which now has commands to create, update, inspect, and push your projects for you. Watch a quick demo below: ### All Changes [Section titled “All Changes”](#all-changes) **Added** * **Added a `@team_task` decorator for team-scoped Celery tasks**. It works very similarly to the `@login_and_team_required` decorator, automatically loading the team into the context variable based on the task’s first argument. This lets you easily drop in team-level permissions into any celery task. See [the new teams background tasks docs](/teams/#background-tasks) for more info. * **Added a `get_team_object_or_404` shortcut** that scopes `get_object_or_404` to the current team context, avoiding accidental cross-team lookups via the unfiltered default manager. It’s recommended to use this instead of `get_object_or_404(id=id)` which will not explicitly add a team filer. See [the teams docs](/teams/#team-scoped-shortcuts) for more info. * **Markdown rendering now supports tables**, via the markdown `TableExtension`. This works well in the chat features when AI agents generate tables. * Added a number of small built-in options for pre-commit, including `check-yaml`, `check-merge-conflict`, `check-added-large-files`, `end-of-file-fixer`, and `trailing-whitespace`. * Also removed trailing whitespace from a number of files. **Changed** * **Upgraded most Python packages to their latest versions**. * **Logout and email confirmation now require `POST`.** The user experience doesn’t change, some JavaScript code handles the switch from GET to POST, while preventing accidental or malicious triggering of these flows from GET requests. * Updated the built-in `package.json` to be marked private and removed placeholder author/description/homepage fields. * CI now uses `uv sync --frozen` to match the exact dependencies used in production. * Fixed the Avatar upload flow to work better when your session expires while the page is open. * Also updated Avatar upload validation failures to return a `400` instead of `403` to make this smoother. * Made improvements to AI rules files: * Fixed Python version to 3.14. * Added explicit PEP 758 `except` syntax examples since agents kept thinking it was Python 2 syntax. * Added emphasis on how to use multi-line template comments. * Updated the `simulate_error` debugging view to superusers-only. * Added several more default variables to various production config/env files, including `PEGASUS_LOG_LEVEL`, `STRIPE_LIVE_MODE`, and `SENTRY_DSN` and social login variables. * Refactored `get_image_url` meta-tag handling to be more consistent in how relative and absoulte paths are handled. * Added a comment explaining why the schema view is public. * Narrowed the admin/health-check mypy overrides and tightened a few type annotations. **Fixed** * The admin DB MCP tool now bundles `mcp-alchemy` into the virtualenv so it works in production without `uvx`, and uses `psycopg3` instead of `psycopg2`. This fixes an issue with production Docker setups where the installation would sometimes “time out”, breaking the admin chat bot. * Fixed a bug where changing your email address to a previously-verified email would not restore it as the primary email. (Thanks Justin for reporting!) * Also added tests for this. * Fixed a bug where having and empty `REDIS_URL` in your environment would cause a crash. Now it falls back to the default behavior. (Thanks Justin for reporting!) * Fixed a bug where updating a team’s `slug` to a unicode-only value could cause a conflict/crash due to it being blanked out. (Thanks Justin for reporting!) * Also added tests for this. * Added `min-w-0` to the app content base template to prevent content inside from overflowing it. ### Upgrading [Section titled “Upgrading”](#upgrading) * **Logout and email confirmation now require `POST`.** Pegasus should handle this for you unless you have added additional logout links or set up a custom email-confirmation template. Those may need to be updated as follows: * For logout links, you should add a `data-logout-link` to any link to the logout page. * For email confirmation, refer to Pegasus’s `templates/account/email_confirm.html` template for auto-submitting. * Alternatively, you can set `ACCOUNT_LOGOUT_ON_GET=True` and/or `ACCOUNT_CONFIRM_EMAIL_ON_GET=True` in your settings file to restore the old behavior. * The `pre-commit` changes may cause errors in your existing code in CI when upgrading. It’s recommended to run `pre-commit` on your whole project with the updated rules: ```plaintext uv run pre-commit run --all-files ``` *June 2, 2026* ## Version 2026.5.1 [Section titled “Version 2026.5.1”](#version-202651) This release updates the `pydantic-ai` dependency to `pydantic-ai-slim[openai,anthropic,google,mcp]>=1.0` to work around build/install issues after the recent [mistral supply chain attack](https://github.com/mistralai/client-python/issues/523). See this [pydantic-ai issue](https://github.com/pydantic/pydantic-ai/issues/5382) for more details. If you’re having trouble building your project on 2026.5, updating to 2026.5.1 should fix the issue. If you were using a different model provider or more pydantic-ai features than the ones included above, you will need to update your dependencies to add them back. **Security note:** The compromised `mistralai` package was a transitive dependency of `pydantic-ai`, so it could theoretically have affected Pegasus projects. In practice the risk was very low: 1. Pegasus’s lockfile pinned `mistralai==2.4.1` (released April 21), well before the malicious `2.4.6` was published. You’d only have been exposed if you ran an unpinned upgrade (`uv lock --upgrade`, `pip install -U`, etc.) during the roughly six-hour window on May 12, 2026 between when the bad version went live (00:05 UTC) and when PyPI quarantined the entire `mistralai` project. 2. You wouldn’t have triggered the vulnerability unless you were intentionally using mistral or set something like `DEFAULT_AI_MODEL=mistral:mistral-large-latest` in your settings/env. *May 12, 2026* ## Version 2026.5 [Section titled “Version 2026.5”](#version-20265) This release upgrades all projects to Python 3.14, and has a few very small improvements. ### Python 3.14 [Section titled “Python 3.14”](#python-314) Pegasus projects now use Python 3.14 (specifically 3.14.4) by default, and CI runs on 3.14. A couple of small code cleanups came along with the upgrade: * Adopted [PEP 758](https://peps.python.org/pep-0758/)’s unparenthesized `except` syntax in a few places (e.g. `except E1, E2:` instead of `except (E1, E2):`). * Removed unnecessary quoted forward references — under [PEP 649](https://peps.python.org/pep-0649/) annotations are now evaluated lazily, so the quoting workaround is no longer needed. All projects should get the new Python by default in dev and prod. ### Minor Changes [Section titled “Minor Changes”](#minor-changes) * The `upgrade-pegasus` and `resolve-pegasus-conflicts` Claude Code skills now run the post-merge verification steps (`npm` install/build, `uv sync`, migrations, tests, and `ruff`) and push the upgrade branch automatically by default, pausing only if a step fails or the merge required non-trivial judgment. * Removed an old `claude.yml` GitHub Actions workflow that had a minor prompt-injection surface and is no longer needed. If you still want it, you can pull the file from a previous Pegasus version. Thanks Viktor for flagging! ### Removed [Section titled “Removed”](#removed) * Dropped support for `pip-tools` as a Python environment/package manager. All projects now use `uv` for Python environment and dependency management. ### Upgrading [Section titled “Upgrading”](#upgrading-1) There are not any special upgrade steps for Python 3.14 as it should be managed automatically by uv and the deployment scripts / Docker files. It is recommended to test your projects and any custom dependencies thoroughly on Python 3.14 before pushing the upgrade to production. For projects that were still using `pip-tools`, see the [migration guide](/cookbooks/#migrating-from-pip-tools-to-uv) to upgrade to `uv`. It’s recommended to do this *before* upgrading to Python 3.14. *May 5, 2026* ## Version 2026.4 [Section titled “Version 2026.4”](#version-20264) This release adds the option to use native Tailwind/DaisyUI classes instead of the `pg-` helper classes (easier for coding agents to understand). It also lays down the foundation for building more complex agent applications, by adding a new streaming/thinking UI and showing tool calls/results in real time. There are also a lot of minor fixes for various edge-case bugs, and several new tests. ### Native Tailwind / DaisyUI Classes by Default [Section titled “Native Tailwind / DaisyUI Classes by Default”](#native-tailwind--daisyui-classes-by-default) Previously, Pegasus projects used its own set of `pg-` prefixed CSS classes (e.g., `pg-button-primary`, `pg-title`) that mapped to the underlying framework’s styles via Sass `@extend` or Tailwind `@apply`. This was primarily a convenience used to enable multiple CSS frameworks, but means that generated projects were not using native Tailwind and DaisyUI classes, which could confuse people and agents. **Now, Tailwind projects can output native Tailwind and DaisyUI classes directly** for most Pegasus helper classes. So you will see `btn btn-primary` instead of `pg-button-primary` in your templates and JavaScript files. This means agents will better understand the app styles without having to look up the Pegasus-specific class names, and Pegasus apps will feel more like “standard” Tailwind and DaisyUI apps. Notes: * Existing projects will stay on `pg-` classes by default, but migration should be straightforward and mostly automated. See the [migration guide](/css/pegasus-css/#migrating-from-pg--classes-to-native-classes) for details on how to switch your project over to native classes. * Legacy Bootstrap and Bulma projects are unaffected and will continue to use `pg-` classes as before. See [the updated CSS docs](/css/pegasus-css/) for full details, including which classes are covered by the migration and which (like chat and breadcrumb styles) remain as `pg-`. ### Improved AI Agent Chat Experience [Section titled “Improved AI Agent Chat Experience”](#improved-ai-agent-chat-experience) A few big upgrades to the agent chat experience: * **Agents will now stream thinking text, and tool calls/results in real time.** This provides a richer chat experience and makes it easier to follow fthe agent’s thought process. This happens inside a new collapsible “thinking” section in the chat UI. * The chat sidebar is now resizable, giving more room to breathe for chats. * Added tests for the new agent streaming behavior. ### Supply Chain Protections [Section titled “Supply Chain Protections”](#supply-chain-protections) A few changes to help protect against supply-chain attacks on package dependencies, which have become much more common recently: * Added a 7-day cooldown before picking up newly-published npm packages using npm’s `min-release-age` setting, so that compromised packages caught shortly after release don’t end up in your project. * Updated The `upgrade-python-deps` skill to apply a similar cooldown to Python packages. * Note: `uv`’s `exclude-newer` setting was not used because it ignores lockfile constraints. * **Added a committed `package-lock.json`** (and `frontend/package-lock.json` for standalone frontend builds) to generated projects. This ensures `npm install` produces deterministic results out-of-the-box with known good versions, and gives you a lockfile to review when dependencies change. ### Other Changes [Section titled “Other Changes”](#other-changes) * **Overhauled the default top navigation** with a sticky header that uses a glassmorphism effect, plus a new mobile slide-in drawer for smaller screens. * **Overhauled logging to be more consistent.** * Use `__name__` everywhere in loggers instead of custom names. * Always name logger variables `logger`. * **Added a friendly `403_csrf.html` template** so expired-session CSRF failures show a clear “Your session has expired” page instead of Django’s default. Thanks Finbar for the suggestion! * Updated the Pegasus CLI and agent skills to be able to generate custom pull request titles for upgrades. ### Upgrades [Section titled “Upgrades”](#upgrades) * **Upgraded Node and npm to their latest versions**, and upgraded most JavaScript packages. * **Upgraded TypeScript to version 6**, with associated `tsconfig.json` fixes. * **Upgraded most Python packages** to their latest versions. * Pinned the `stripe` library to `>=14,<15` so the upcoming Stripe 15 release isn’t picked up automatically. We’ll bump this once dj-stripe and the rest of the stack are ready for it. ### Fixes [Section titled “Fixes”](#fixes) Conducted multiple AI-assisted audits of the codebase and made a number of small/obscure fixes: * Added server-side gating to the admin AI agent WebSocket to superusers only. The admin tool calls were already gated, but this helps prevent accidentally exposing future non-gated tools. * Added validation to Stripe price IDs — prices are only sent to Stripe when they match the expected format. Also added tests for this. * Fixed a bug in fixed-amount discount coupons where the discount was being applied at 100x the intended value (the code multiplied Stripe’s `amount_off` — already in cents — by 100). * Simplified the `REDIS_URL` settings logic so it no longer runs a no-op SSL-param update on deploy platforms that don’t need it. * Catch `IntegrityError` in the ecommerce purchase flow to avoid a race between the Stripe webhook and the success redirect. * Made `message_type` read-only in the Chat API so clients can’t inject system-role messages. * HTML-escaped streamed AI chat tokens to prevent XSS via model output. * Fixed `subscription_is_trialing` to properly compare a unix timestamp to a datetime using `convert_tstamp` (it was raising `TypeError` on trialing subscriptions). * Included trialing subscriptions in `Team.get_items_needing_sync()` so seat-quantity changes during a trial are synced to Stripe instead of waiting until the trial ends. * Handled invalid user timezones in the locale middleware instead of crashing on every request. * Replaced `assert` statements in the ecommerce and subscription checkout flows with proper `PermissionDenied` raises and structured logging. * Made the team context processor fall back to `default_team` when no team is set in the URL. * Added tests around many of the above areas, including timezone middleware handling, teams middleware 404 behavior, and the chat message API. *April 24, 2026* ### 2026.3.2 [Section titled “2026.3.2”](#202632) This is minor hotfix release for 2026.3.1 * Downgrades the NPM version to 11.11 to workaround [this issue with npm 11.12](https://github.com/npm/cli/issues/9151). * Updates the standalone frontend Docker container to install packages with `--legacy-peer-deps` which was still needed. * Adds `psycopg[binary]` to dev requirements so that you don’t need dependencies on your local system. Thanks Randall for reporting! *March 27, 2026* ### 2026.3.1 [Section titled “2026.3.1”](#202631) This is a minor hotfix release for 2026.3. * Fixes a bug where some dates (like “since”) were not showing on the subscription page when you have an active subscription. Thanks Brennon for reporting / suggesting the fix! * Upgrades the latest JS packages. * Removes the `legacy-peer-deps` installation flags that were added for `tailwindcss/vite` which now supports Vite 8. * Consistently uses npm version 11.12 everywhere. *March 24, 2026* ## Version 2026.3 [Section titled “Version 2026.3”](#version-20263) This release includes some big improvements to the [standalone frontend](/experimental/react-front-end), upgrades everything to [Vite 8](https://vite.dev/blog/announcing-vite8), and includes the usual small improvements. ### Standalone Frontend Improvements [Section titled “Standalone Frontend Improvements”](#standalone-frontend-improvements) The [standalone frontend](/experimental/react-front-end) now fully supports user profiles, social authentication, email setup, two-factor authentication, and team management (for team-based builds). #### Profile Page [Section titled “Profile Page”](#profile-page) The profile page has been rebuilt with improved account management capabilities. Details: * **Added email address management** — add, remove, verify, and set primary email addresses. * **Added connected accounts management** — connect and disconnect social accounts. * **Added two-factor authentication setup** — enable/disable TOTP-based 2FA, scan QR codes, and manage recovery codes. * Improved the profile page layout with card-based sections for a cleaner UI built on shadcn. #### Teams Management [Section titled “Teams Management”](#teams-management) Teams can now be managed from the React frontend: * **Added a Team Settings page** to the dashboard with team name editing, member list, invitation management, and a danger zone for deleting teams. * **Added a Team Switcher component** to the dashboard sidebar for switching between teams and creating new teams. * **Added a `TeamProvider` context** that makes the active team available throughout the React app. #### Other Standalone Frontend Changes [Section titled “Other Standalone Frontend Changes”](#other-standalone-frontend-changes) * Improved auth page forms to use proper `
` elements with `onSubmit` handlers instead of button click handlers, improving keyboard accessibility and UX (Enter key to submit). * Updated various auth pages (social auth callback, provider signup, email verification, MFA) to use consistent shadcn/ui components (`Button`, `Input`, `Label`) instead of DaisyUI class-based styling. * Added CSS theme tokens for `success` and `warning` colors, based off the existing DaisyUI theme colors. * Migrated from individual `@radix-ui/*` packages to the unified `radix-ui` package based on the latest recommendations from shadcn. ### Vite 8 support [Section titled “Vite 8 support”](#vite-8-support) Both front ends in Pegasus now use Vite 8. This should dramatically speed up production build times. See the upgrading section for details. ### Other Changes [Section titled “Other Changes”](#other-changes-1) * **Upgraded most JavaScript packages to their latest versions**. * **Switch to using the `tailwindcss/vite` plugin instead of postcss for Tailwind CSS in the Django front end.** * Also dropped the no-longer used `postcss.config.js` file. * **Made improvements to the existing React teams code**: * Use relative URLs instead of absolute URLs for internal links. * Make it possible to pass a urlBase prop to the components. * Make it possible to pass image paths to the components. * Explicitly declare when `gettext` is assumed to be a global function. * Added the ability to resend team invitations via a new API endpoint. This supports the new standalone front end feature. * Also added basic tests for this API. * Switched from `psycopg2` to `psycopg` (psycopg 3) in development and `psycopg[c]` in production. Thanks Brennon for the suggestion! The production Dockerfile now installs `libpq-dev` and `gcc` for compiling the C extension. * See the upgrading section for details. * Fixed `bootstrap_subscriptions` management command for dj-stripe 2.10+ compatibility. The command now syncs the Stripe Account before syncing products, preventing “Account matching query does not exist” errors. Thanks Julian for reporting! * Also switched the command to use `self.stdout.write` instead of `print`. * Fixed a bug on React builds on the page that users see when they don’t have any teams. It said “you have pending invitations” even if the user did not have any. Thanks Jonathan for reporting! * Upgrade `sentry-python` to the latest version and re-enable the Pydantic AI sentry integration, now that [the integration bug](https://github.com/getsentry/sentry-python/pull/5522) is fixed. * Added `legacy-peer-deps=true` to `.npmrc` files and production Docker installs, because `@tailwindcss/vite` (4.2.1) only declares peer support for Vite 5/6/7, not Vite 8. This will be removed once Tailwind releases a version that supports Vite 8 in its peer dependencies. * Temporarily suppress requests/urllib3 warnings. Thanks Geoff and Jonathan for suggesting! ### Upgrading [Section titled “Upgrading”](#upgrading-2) **Vite 8** See the [Vite 8 migration guide](https://vite.dev/guide/migration) for details on upgrading your project to Vite 8. If you haven’t made heavy customizations to your front end, you should not need to change anything on top of what Pegasus does automatically. One gotcha is that if you have overridden a built-in DaisyUI theme you will need to remove it from the initialization section of `site-tailwind.css` to avoid CSS specificity conflicts in production builds. You can remove all built-in themes by initializing DaisyUI like this: ```css @plugin "daisyui" { themes: false; } ``` Then adding your custom themes: ```css @plugin "daisyui/theme" { name: "light"; default: true; --color-primary: blue; --color-secondary: teal; } @plugin "daisyui/theme" { name: "dark"; prefersdark: true; --color-primary: red; --color-secondary: teal; } ``` **psycopg** The psycopg change should work seamlessly with supported Pegasus deployments, however if you have a non-standard/manual set up, you may need to install the prerequisites for building the psycopg C extension. For example, on Ubuntu: ```bash sudo apt install clang build-essential libpq-dev python3-dev ``` Alternatively, you can change `psycopg[c]` to `psycopg[binary]` in your `pyproject.toml`/`uv.lock` file which should not require any additional changes. *March 18, 2026* ## Version 2026.2.2 [Section titled “Version 2026.2.2”](#version-202622) This maintenance release upgrades Python dependencies, including `stripe` and `dj-stripe`, to their latest major versions, and has a few small updates to agent skills. * **Upgraded Python packages to their latest versions.** * **Upgraded `stripe` from version 11 to the latest version (14).** * Moved exception imports from `stripe.error` (e.g. `stripe.error.InvalidRequestError`) to the top-level `stripe` module (e.g. `stripe.InvalidRequestError`). * Removed the `mypy` override for `stripe.error`, which is no longer needed with the new Stripe import paths. * **Upgraded `dj-stripe` from 2.9 to 2.10.** The dj-stripe 2.10 upgrade is a significant change that affects how many Stripe model fields are stored. In dj-stripe 2.10, many fields that were previously database columns are now read from the `stripe_data` JSONField via property accessors. * Updated all ORM queries that filtered on removed fields (e.g. `price__recurring`, `subscription__status`) to use `stripe_data`-based lookups (e.g. `price__stripe_data__recurring`, `subscription__stripe_data__status`). * Updated subscription serializers to explicitly declare fields that are now property accessors (`unit_amount`, `quantity`, `status`, `start_date`, `current_period_start`, `current_period_end`, `cancel_at_period_end`). * Updated the generated API client to reflect the above changes. * Updated the `UsageRecordForm` to use the Stripe API directly, since the `UsageRecord` model was removed in dj-stripe 2.10. * Updated the subscription webhook handler to sync subscriptions from the Stripe API instead of directly writing to removed DB columns. * Updated ecommerce views to remove `select_related` calls on `default_price`, which is no longer a foreign key. * Updated the product image helper to read from `stripe_data` instead of the removed `images` field. * Updated billing utilities to handle `unit_amount_decimal` being returned as a string from `stripe_data`. * Updated all tests to create Stripe objects with the new `stripe_data` format. * Removed the `RunSQL` migration workaround for `djstripe_paymentintent.capture_method` column length, which is no longer needed. * Updated migration dependencies from `("djstripe", "0012_2_8")` to `("djstripe", "0001_initial")` to match dj-stripe 2.10’s squashed migrations. * **See the upgrading section below for more details.** * Removed custom `django-celery-beat` installation, now that the library officially supports Django 6. * Removed git installation from production Dockerfiles (since no libraries are installed from git anymore). * Updated health check setup to use the new `django-health-check` API. The health check view now uses `HealthCheckView` (replacing the deprecated `MainView`) and configures checks inline instead of via `INSTALLED_APPS`. * See upgrade notes if you were using custom health checks * Switched default database and Redis URLs from `localhost` to `127.0.0.1` to fix a performance issue on Windows where DNS resolution of `localhost` could be slow. * Cleaned up unused undraw illustration images that were always included in builds regardless of project configuration. Only images that are actually used based on your build configuration are now included. * **Added an “upgrade-pegasus” Claude Code skill** to help automate the Pegasus upgrade process via the CLI. * Updated the “resolve-pegasus-conflicts” skill with guidance for handling the dj-stripe 2.10 migration squash, field changes, and the need to re-sync Stripe data after migrating. * Included `pegasus-cli` in all projects, not just HTMX builds. * Added `.playwright-mcp/` to the `.gitignore` for projects with any AI rules enabled. * Temporarily disabled the pydantic ai sentry integration due to [this bug in the current sentry release](https://github.com/getsentry/sentry-python/issues/5518). ### Upgrading [Section titled “Upgrading”](#upgrading-3) *Note: It’s recommended to use the `/resolve-pegasus-conflicts` skill to handle some of these instructions automatically.* **Stripe / dj-stripe upgrades** If you are using Stripe/dj-stripe pay careful attention to the [upgrade notes](https://github.com/dj-stripe/dj-stripe/releases). Code changes: * If you have custom code that imports from `stripe.error`, update those imports to use the top-level `stripe` module (e.g. `from stripe.error import InvalidRequestError` → `from stripe import InvalidRequestError`). * If you have custom migration files that depend on `("djstripe", "0012_2_8")`, update them to `("djstripe", "0001_initial")`. * If you had the `RunSQL` migration workaround for `djstripe_paymentintent.capture_method`, you can safely remove it. * If you have custom code that queries on Stripe model fields that were previously DB columns (e.g. `filter(status=...)`, `filter(recurring__interval=...)`), update them to use `stripe_data`-based lookups (e.g. `filter(stripe_data__status=...)`, `filter(stripe_data__recurring__interval=...)`). Running the migration: * It’s recommeneded to back up your production database before migrating so it can be rolled back in case there are any issues. * After migrating, run `./manage.py djstripe_sync_models Product Price Subscription Customer` to repopulate `stripe_data` for existing records. Without this, properties like `product.default_price` may return `None`. Note that if you have used custom fields on other djstripe models you should include those in the set that you sync. *You should also run this on your production site after upgrading.* **Health check upgrade** If you had configured any custom health checks, you should update `HealthCheckView` in `apps/web/urls.py` to include those checks. *March 2, 2026* ## Version 2026.2.1 [Section titled “Version 2026.2.1”](#version-202621) This release adds tooling for updating projects via the `pegasus projects` cli and removes `litellm` and unifies all AI calls through Pydantic AI. It also includes a number of smaller features and fixes. ### Update your project from the command line [Section titled “Update your project from the command line”](#update-your-project-from-the-command-line) The latest version of the [`pegasus` cli](https://github.com/saaspegasus/pegasus-cli) now includes a `projects` subcommand that allows you to update your projects directly from the command line. It will automatically build your project (optionally updating it to the latest version of Pegasus) and submit a pull request to your repository with the changes. This currently only works if you have connected your repository to GitHub. To try it out, after updating the `pegasus-cli` run: ```plaintext pegasus projects update ``` You’ll need an API key, which you can create from your [profile page](https://www.saaspegasus.com/users/profile/). For more details, see [the updated CLI documentation](https://github.com/saaspegasus/pegasus-cli?tab=readme-ov-file#pushing-to-github). ### AI app migration and cleanup [Section titled “AI app migration and cleanup”](#ai-app-migration-and-cleanup) * **All AI calls now go through [Pydantic AI](https://ai.pydantic.dev/) instead of using a mix of LiteLLM and Pydantic AI**: * Removed the `litellm` dependency. All chat and agent interactions now use Pydantic AI exclusively. * Replaced `DEFAULT_LLM_MODEL` and `DEFAULT_AGENT_MODEL` settings with a single `DEFAULT_AI_MODEL` setting. * The `LLM_MODELS` dictionary has also been removed in favor of Pydantic AI’s built-in model configuration. * API keys are now read directly from standard environment variables (`OPENAI_API_KEY`, `ANTHROPIC_API_KEY`, etc.) instead of being configured per-model. * Removed the `chat/utils.py` module (`get_llm_kwargs` and `UnknownModelError`) which is no longer needed. * **Simplified the chat system, by removing the distinction between “chat” and “agent” session types**: * Removed the `Chat.chat_type` field. Chats are now identified solely by their `Chat.agent_type` field (e.g. `chat`, `weather`, `admin`, `employees`). Added migration to populate the `agent_type` field for existing chats. * Merged `ChatSession` and `AgentSession` into a single `ChatSession` class. * Consolidated WebSocket routing. Instead of separate WebSocket endpoints per agent type (e.g. `/ws/weatheragent/`, `/ws/adminagent/`), all chats now use the existing `/ws/aichat/` endpoint with an `agent_type` query parameter. * Removed per-agent consumer classes in favor of a unified `ChatConsumer`. * Fixed a few bug in certain cases where chats did not always resume with the correct agent or history when loaded from the chat history page. See the upgrading section below for migration details. ### Other Changes [Section titled “Other Changes”](#other-changes-2) * **Upgraded all JavaScript packages** including Flowbite to 4.x. * Added minimal theme variables needed for Flowbite 4.x dashboard compatibility. * **Upgraded all Python packages**, with `dj-stripe` still pinned to 2.9.1 to avoid compatibility issues with newer versions. * **Added Claude Code skills for upgrading JavaScript and Python dependencies** to generated projects. These have also been added to the [django-skills](https://github.com/saaspegasus/django-skills) repository. * Migrated the test 400, 403, 404, and 500 error pages to Django’s built in views for more realistic error simulations. Thanks Brennon for suggesting! ## Other Fixes [Section titled “Other Fixes”](#other-fixes) * Added `types-requests` as an explicit mypy dev dependency. * Fixed the MCP agent tool to use `psycopg2-binary` instead of `psycopg[binary]` for the database connection. ### Upgrading [Section titled “Upgrading”](#upgrading-4) To migrate to the latest AI code changes, you will need to update your environment variables: * Replace `DEFAULT_LLM_MODEL` and `DEFAULT_AGENT_MODEL` with `DEFAULT_AI_MODEL`. The new format is `provider:model_name` (e.g. `openai:gpt-4o`). See the [Pydantic AI model docs](https://ai.pydantic.dev/models/overview/) for available models. * Also make sure your API keys are defined in your enviornment variables, such as `OPENAI_API_KEY`, `ANTHROPIC_API_KEY`, etc. *February 10, 2026* ## Version 2026.2 [Section titled “Version 2026.2”](#version-20262) This release focuses on improving AI-assisted development with a number direct and indirect improvements. ### Standalone Frontend ported to shadcn/ui [Section titled “Standalone Frontend ported to shadcn/ui”](#standalone-frontend-ported-to-shadcnui) **The standalone React frontend has been completely redesigned using shadcn/ui components.** Shadcn has become the de-facto recommended way to build a standalone React frontend and LLMs are very good at working with it. This makes it a natural choice for the standalone frontend for Pegasus moving forwards. Details: * New sidebar component using shadcn’s responsive sidebar pattern * All form inputs, buttons, labels, and cards now use shadcn components * Authentication pages (login, signup, password reset) redesigned with consistent styling * Profile page and navigation demo updated to use shadcn components * Added shadcn configuration file (`components.json`) for easy component management * Added path alias `@/*` for cleaner imports See the [updated Standalone React Front End documentation](/experimental/react-front-end/) for details. ### Skills for Agents [Section titled “Skills for Agents”](#skills-for-agents) This release of Pegasus starts introducing [agent skills](https://platform.claude.com/docs/en/agents-and-tools/agent-skills/overview) to assist with common and repetitive tasks when working with Pegasus projects. In a new Pegasus project, all skills will be included if you enable Claude support. There are currently skills to help you upgrade your Pegasus project (`/resolve-pegasus-conflicts`), fix type errors in your code (`/fix-types`), and work with worktrees (`/worktree`). Pegasus skills are open source and will be maintained in the following two repositories: * **[Pegasus Skills](https://github.com/saaspegasus/pegasus-skills)**: Skills that are specific to working on Pegasus projects. * **[Django Skills](https://github.com/saaspegasus/django-skills)**: Skills that are more generally useful for any Django project. See the [AI Integrations documentation](/ai/development/#pegasus-skills-for-claude-code) for installation instructions and details. ### Type Checking with Mypy [Section titled “Type Checking with Mypy”](#type-checking-with-mypy) One of the most important principles of successfully building complex applications with agents is that you should give them as many tools as possible to help them verify their work. Type checking is one of these tools that can help agents catch errors and bugs and verify their work. To assist with this, **Pegasus now includes optional support for static type checking using mypy and django-stubs.** When enabled, your project will include pre-configured mypy settings, and type checking runs automatically in GitHub Actions CI. In addition, a large number of small changes were made to the codebase to add type hints and fix typing issues so that mypy works out of the box on all tested build configurations. Note: this features is labeled as experimental because not every combination of Pegasus features has been fully tested by mypy, but it will become an official supported feature in the next release and is safe to enable and use. See the new [Type Checking documentation](/python/type-checking/) for details. ### Git Worktree Support (Experimental) [Section titled “Git Worktree Support (Experimental)”](#git-worktree-support-experimental) Another growing trend in AI programming is the use of git worktrees to enable parallel development on multiple branches simultaneously. Worktrees allow you (or agents) to work on multiple branches at once without overwriting each branch’s changes. To assist with using worktrees, Pegasus adds some utilities that help you create and manage worktrees. Specifically, you can now create isolated git worktrees with unique ports for all services, enabling parallel development on multiple branches simultaneously, giving each worktree its own database and web ports (so multiple worktrees can run in browsers concurrently). Specifically, each worktree gets its own: * Django port (starting at 8000, incrementing for each worktree) * Vite dev server port (starting at 5173) * PostgreSQL instance port (starting at 5432) * Redis instance port (starting at 6379) To create a worktree, use the new `/worktree` Claude Code skill or run: ```bash ./scripts/create-worktree.sh feature-name ``` This feature is experimental. See the [Git Worktrees documentation](/experimental/git-worktrees/) for details. ### Other Changes [Section titled “Other Changes”](#other-changes-3) **Added** * **Added support for superuser API permissions.** New `IsSuperUser`, `IsAuthenticatedSuperUser`, and `IsAuthenticatedOrHasUserAPIKeySuperUser` permission classes for restricting API endpoints to superusers only. * Also added tests. * Added an error notification when HTMX requests fail. * The `dev.sh` script (and `make dev`) now starts the standalone frontend dev server alongside Django and Vite (if enabled). **Changed** * Django and Vite ports are now configurable via environment variables. Set `DJANGO_PORT`, `DJANGO_VITE_PORT`, `POSTGRES_PORT`, and `REDIS_PORT` in your `.env` file. Docker Compose should use these ports automatically. This helps enable the worktree workflow outlined above. * **Changed the default development docker setup to only expose postgres and redis ports over localhost.** This improves security if you are doing development on a machine that’s exposed to the public internet. * Moved `django-browser-reload` and `django-watchfiles` to development requirements and only set them up if `DEBUG=True`. Thanks Saif for the suggestion! * Switched from `psycopg2-binary` to `psycopg[binary]`, as recommended by the latest Django docs. Thanks Saif for the suggestion! * API key display and revocation now uses key prefix instead of ID. Thanks Brennon for the suggestion! * Added configurable Docker user/group in production Dockerfile. You can now customize the user and group IDs via build args (`SITE_UID`, `SITE_GID`, `SITE_USER`, `SITE_GROUP`). Thanks Geoff for the suggestion! * Added a longer default deploy timeout (180 seconds) for Kamal deployments. Thanks Aurelien for the suggestion! * Changed assertion statements to proper exceptions in subscription views and employee views for better error handling. * The `.git` and `config` directories are now excluded in production images via the `.dockerignore` file. Thanks Geoff for the suggestion! * Font awesome is now installed via NPM instead of the CDN. Thanks Geoff for suggesting! **Fixed** * Fixed shadcn/daisyUI CSS variable mappings. Accent colors are no longer overridden by shadcn by default, which was causing inconsistent styling on DaisyUI accent components. * Vite now ignores large directories (`.venv`, `node_modules`, `.git`) when watching files. This helps prevent exhausting system inotify watchers. Thanks Jacob for the suggestion! * Switched some model references to use `gettext_lazy` instead of `gettext` for translatable strings, which is the correct pattern for model-level translations. Thanks Saif for reporting! * Fixed tests failing when using manifest storage by adding a test base class that overrides storage settings. Thanks Brennon for reporting! * Don’t add `django-celery-beat` uv source to `pyproject.toml` if not using celery. * **Made many minor code-quality improvements caught by the type checker. Examples:** * Subscription signals now handle missing subscriptions more gracefully/explicitly. * Added proper return types and null checks in subscription helpers * Fixed type hints in AI agents and tools * Added type annotations to settings and model files * Fixed import paths for `Message` class in teams tests * Added type ignores with explanatory comments where necessary *February 2, 2026* ## Version 2025.12.2 [Section titled “Version 2025.12.2”](#version-2025122) This is a hotfix release that bumps the Pegasus CLI version to fix [a permissions issue with the created views](https://github.com/saaspegasus/pegasus-cli/issues/29). Thanks Mark for reporting! It’s recommended that all projects upgrade the CLI version to 0.11 and/or add the appropriate permissions checks to the created views (by adding `team=request.team` or `user=request.user` to the object access queries in the generated `views.py` files). *January 8, 2026* ## Version 2025.12.1 [Section titled “Version 2025.12.1”](#version-2025121) This is a hotfix release that fixes an issue where production Dockerfiles weren’t building on 2025.12 when celery was enabled because `git` was not installed. The fix is installing `git` before installing Python dependencies. Thanks Denis and Chris for reporting! *Dec 14, 2025* ## Version 2025.12 [Section titled “Version 2025.12”](#version-202512) This release upgrades Pegasus to Django 6, adds Google Gemini (Nano Banana Pro) as an AI image generation option, and includes several minor fixes and updates. ### Django 6 [Section titled “Django 6”](#django-6) Pegasus has been upgraded to Django 6! This includes: * **Upgraded Django to version 6.0.** * **Bumped the minimum Python version to 3.12** to match Django 6’s requirements. * **Removed `django-template-partials` dependency**, as template partials are now built into Django 6. Also simplified the template loader configuration in `settings.py` by removing the partials-related setup. * Temporarily added a workaround to `pyproject.toml` / `requirements.in` to allow installing `django-celery-beat` from source, until [this issue is resolved](https://github.com/celery/django-celery-beat/issues/977). See the upgrading section below for more details. ### Gemini / Nano Banana Image Generation [Section titled “Gemini / Nano Banana Image Generation”](#gemini--nano-banana-image-generation) You can now generate images in the image demo with Google Gemini (Nano Banana Pro). This model is *very* good at image generation and opens up a lot of new application use cases. See the [image generation documentation](/ai/images) for setup details. ### Other Changes [Section titled “Other Changes”](#other-changes-4) * **Upgraded most JavaScript packages to their latest versions.** * **Upgraded most Python packages to their latest versions.** * **Added `django.contrib.postgres` to installed apps when using Postgres.** This is required to use the Wagtail integration on Django 6. * **Pinned the Postgres Docker image to version 17 everywhere.** Thanks Eugen for reporting a configuration mismatch for version 18! * **Improved admin defaults** * The ecommerce `Purchase` admin now includes search fields (user email, checkout session ID) and autocomplete for the user field. * The `CustomUser` admin now includes search fields for email, first name, and last name. * **Fixed various type annotations throughout the codebase to pass stricter type checking.** This is part of an ongoing effort to improve the type safety of the codebase. * Upgraded the default uv version to 0.9.17 * Updated `settings.ADMINS` to use a simpler format with just email addresses instead of name/email tuple, which will be removed in Django 7. * Added translations in a few places. * Added a project description field to `pyproject.toml` that uses your project’s description. #### Fixed [Section titled “Fixed”](#fixed) * Fixed a typo in the subscription template filename (`no_subsription_access.html` → `no_subscription_access.html`). * Fixed a typo in a comment in the team model’s manager documentation. * Fixed timezone warnings in subscription tests by using `timezone.now()` instead of `datetime.utcnow()`. * Fixed missing return type annotations and added better error handling in ecommerce helpers. * Added a return statement to the `get_discounted_price` billing utility to match the return type of the function. * Removed a redundant import in the team invitation views. * Fixed a few compatibility issues on the experimental Django Ninja build. ### CLI changes [Section titled “CLI changes”](#cli-changes) * Released [v0.10 of the CLI](https://github.com/saaspegasus/pegasus-cli/releases/tag/v0.10) which adds Django 6.0 support and fixes a few bugs (details in release notes there). ### Upgrading [Section titled “Upgrading”](#upgrading-5) A few notes on upgrading to Django 6: * **Django 6 requires Python 3.12 or later.** This was already the default version in Pegasus, but if you are on an older version you will need to upgrade. Using any supported Pegasus deployment option should handle this for you. * If your project was using `django-template-partials` (used by the Pegasus CLI), you should review [Django’s migration guide](https://github.com/carltongibson/django-template-partials/blob/main/Migration.md). Most of this is handled by Pegasus, but **you must remove the `{% load partials %}` from all your existing templates that have it**. *December 12, 2025* ## Version 2025.11.2 [Section titled “Version 2025.11.2”](#version-2025112) This is a bugfix release addressing a few issues: * **Switched the default production celery pool from `gevent` to `threads`.** This fixes compatibility issues between `gevent` and `asyncio` that arose when using agents inside Celery. Thanks Matt for reporting and suggesting the fix! * This change also removes `gevent` from production requirements. If you were using it elsewhere you should keep it. * Fixed a bug where the “New Agent Chat” button didn’t properly set the agent name on React / non-async builds. * Fixed a whitespace issue in the Digital Ocean `app-spec.yaml` file that was causing an invalid config. * Bumped the Postgres version on Digital Ocean to the latest (version 18). * Added the `/.vite/` path back to the `.gitignore` file. *November 27, 2025* ## Version 2025.11.1 [Section titled “Version 2025.11.1”](#version-2025111) This is a minor hotfix release fixing two issues: * Fixed a crash in the teams example app, due to an incorrect reference to `model.for_team()` (instead of the correct `model.for_team`). Thanks Eugen for reporting! * Fixed an issue where Heroku Redis URLs accidentally removed `?ssl_cert_reqs=none` if you used the Docker-based build. Thanks Steven for reporting! *November 17, 2025* ## Version 2025.11 [Section titled “Version 2025.11”](#version-202511) Here’s what’s in the November release. ### New Team scoping features [Section titled “New Team scoping features”](#new-team-scoping-features) These updates provide more consistent ways to filter your models based on the current team and help avoid writing bugs related to forgetting to apply a team filter to your DB queries. If you’re happy with the current teams setup you can largely ignore these changes—they mainly add new, optional functionality on top of the existing system. If you would like to introduce more strict Team filtering and checking in your app, review the changes below and updated sections of the documentation. Details: * Added a new [context variable](/teams/#team-context-variable) to keep track of the current team. * Updated the `TeamsMiddleware` to automatically set/unset the variable for the user’s current team. * Added a new `TeamScopedManager` class to automatically filter a queryset based on the current team (from the context variable). * Updated `BaseTeamModel` to add `for_team = TeamScopedManager()`, which can be used to automatically filter a team moodel based on the current team. * `TeamsMiddleware` will no longer set `request.team` to the user’s default team if it is not in the URL. Previously it would return the most recently visited team or the first team that the user is a member of. If you need that behavior, you can now use `request.default_team`. * Added several tests for the above functionality. See [the updated teams documentation](/teams) for more information about working with these tools, including how to use them to [always enforce that a team is set](/teams/#strict-team-access). ### Other changes [Section titled “Other changes”](#other-changes-5) **Added** * **Added [AGENTS.md](https://agents.md/) as an additional output format for AI rules files.** * **You can now clone/copy projects in SaaS Pegasus**—starting a new project with an existing project’s configuration instead of the defaults each time. (Thanks Patrick for the suggestion!) **Changed** * **Upgraded all Python packages.** * **Upgraded all JavaScript packages.** * Updated `.vite` declaration in the `.gitignore` to make it more obvious how to check in vite’s built static files if you want to do that. Thanks Lile for suggesting! * Updated AI API key environment variables to be the defaults used by Pydantic AI so they can be set in a single place. You should now set `OPENAI_API_KEY` instead of `AI_CHAT_OPENAI_API_KEY` and `ANTHROPIC_API_KEY` instead of `AI_CHAT_ANTHROPIC_API_KEY`. * Updated links to the Django docs to always point to the latest stable release. * Updated Kit (formerly ConvertKit) mailing list integration to V4 of the API. Thanks Ben H for suggesting! * Changed `CONVERTKIT_API_KEY` setting / environment variable name to `KIT_API_KEY`. * Also updated [the docs](/configuration/#kit-formerly-convertkit). * Updated `django_browser_reload` to only setup the app/middleware if `DEBUG=True`. This removes a warning in production. (Thanks Zac for the suggestion!) * Made minor updates to AI rules files. **Fixed** * The employee agent demo now uses a proper `Enum` for departments, preventing invalid options from being used. * Fixed an issue with using `TransactionTestCase` in certain build configurations due to an issue with `django-waffle`. This was done by updating a migration to remove the unexpected tables, as outlined in [this comment](https://github.com/django-waffle/django-waffle/issues/317#issuecomment-488398832). Thanks Ben N for reporting! * The migration was also renamed - see upgrade section for details. * Fixed some places where types were set incorrectly or didn’t pass type-checking. * Fixed a bug where `django_browser_reload` was always enabled, even if you had turned it off. ### Upgrading [Section titled “Upgrading”](#upgrading-6) * If you had any code dependent on `request.team` being set even if there was no team in the URL, you should update that code to use `request.default_team`. * If you were using the (Convert)Kit integration, you should update based on the [latest documentation](/configuration/#kit-formerly-convertkit). * The migration `/apps/web/migrations/0002_patch_djstripe_column.py` was renamed to `/apps/web/migrations/0002_patch_third_party_tables.py`. * In most cases, this should apply correctly, but if you have any issues with it, you can re-create the migration by running `./manage.py makemigrations web --empty` and then copying the contents of the file across (except for the generated `("web", "000x_xxxx"),` dependency line). Alternatively, if you don’t use `TransactionTestCase`, you can just reject the migration file changes. *Nov 10, 2025* ## Version 2025.10 [Section titled “Version 2025.10”](#version-202510) This release improves the developer experience of working with Pegasus. It has a number of changes that make things simpler, smoother, and more consistent. These changes also make it easier for AI agents to work in Pegasus codebases. Key changes: * All projects will default to Postgres, Vite, and Tailwind moving forwards. Other options will be slowly phased out. * All projects will ship with Docker containers for Postgres and Redis (you can ignore these if you want to run them separately) * The `Makefile` now ships with every project and includes commands for running everything you need in development. These commands are also available in AI rules files for AI agents. * `make init` will work on all projects, and is all you need to get running (after installing prerequisites) * Added some dev quality-of-life tools. Details below. ### Services-only Docker setup [Section titled “Services-only Docker setup”](#services-only-docker-setup) This release adds default “services-only” Docker setup for projects that weren’t using Docker already. In this mode, Postgres and Redis run via Docker, but Python and Node/npm run natively. *This is now the recommended way to develop Pegasus applications.* You can read more in the updated [Docker docs](/docker). ### Expanded `make` support [Section titled “Expanded make support”](#expanded-make-support) The `Makefile` will now be included on all Pegasus builds. This can be used to run the app, tests, migrations, front end, and more without having to remember the exact commands, and can be easily customized. You can run `make` without any arguments to see the available options. ### Added a `dev.sh` script to run Django and Vite/Webpack in a single command [Section titled “Added a dev.sh script to run Django and Vite/Webpack in a single command”](#added-a-devsh-script-to-run-django-and-vitewebpack-in-a-single-command) For projects not already using Docker, a `dev.sh` script was added that runs both Django and Webpack/Vite in a single command, handling process management for you. Running `make dev` will use script, though you can also run it manually via `./scripts/dev.sh`. This allows you to easily stop and start these two coupled processes together without having to fiddle with multiple terminals or tools like `tmux`. ### Expanded README and Agents files [Section titled “Expanded README and Agents files”](#expanded-readme-and-agents-files) The project’s README and the various Agent files (cursor rules, CLAUDE.md, etc.) have been overhauled and expanded to use the `make` commands. This provides a simpler on-ramp both for developers and agents to run the app and the various supporting commands. ### Streamlined development tooling [Section titled “Streamlined development tooling”](#streamlined-development-tooling) Two new packages were added to improve the developer experience: * [django-watchfiles](https://github.com/adamchainz/django-watchfiles) improves the Django dev server’s autoreloading behavior in several ways, making it both faster and use less system resources. [Learn more here](https://adamj.eu/tech/2025/09/22/introducing-django-watchfiles/). * [django-browser-reload](https://github.com/adamchainz/django-browser-reload) adds automatic page reloading to your browser whenever code changes. Both of these options are development features, and should have no impact on applications in production. Browser reload is enabled on all projects by default, while watchfiles is off by defualt because of [this “file watch limit reached” issue on some filesystems](https://github.com/adamchainz/django-watchfiles/issues/135). Both options can be enabled/disabled under advanced development settings. Thanks to [Adam Johnson](https://adamj.eu/) for the great tools! ### Deprecating configuration options [Section titled “Deprecating configuration options”](#deprecating-configuration-options) The following options have been deprecated and will be dropped in a future release. * **Bulma and Bootstrap CSS Frameworks have been deprecated.** Tailwind will be the primary CSS framework moving forwards. * **Webpack has been deprecated.** Existing projects should [migrate to Vite](https://docs.saaspegasus.com/front-end/migrating/). * **SQLite has been deprecated.** While it will likely still work, it will no longer be QA’d, and various pieces of infrastructure and documentation will assume development is done on Postgres. Postgres can be easily run locally using the Docker services option referenced above. Removing these options will provide a more consistent set of best-practices, that work well for nearly every project and will improve the stability and velocity of Pegasus updates. If you have any questions or concerns about migrating, don’t hesitate to get in touch. ### Other changes [Section titled “Other changes”](#other-changes-6) * Overhauled the “[Gettting started](/getting-started/)” and “[Docker](/docker/)” sections of the docs to reflect the latest changes. * In addition to the major changes mentioned above, made some minor changes and corrections to the README and AI Rules files. * Postgres and Redis docker containers now expose their service ports on the host container. * The development Postgres Docker container is now pinned to version 17. * Added a url route to test the `429` (too many requests) error page. Thanks Brennon for suggesting! * Updated the default Anthropic LLM model to the newly-released Claude Sonnet 4.5. *Oct 10, 2025* ## Version 2025.9.2 [Section titled “Version 2025.9.2”](#version-202592) This release fixes some small bugs when deploying `uv` projects to Heroku with the Python buildpack: * Don’t include `runtime.txt` for Heroku deployments that use uv * Include production dependencies in main `dependencies` section of `pyproject.toml` for Heroku Python builds. Thanks Norman for reporting these! *Sep 25, 2025* ## Version 2025.9.1 [Section titled “Version 2025.9.1”](#version-202591) This is a minor maintenance/bugfix release that upgrades packages, improves the employee agent demo, and addresses some minor bugfixes. Details: * **Upgraded nearly every Python package to the latest version, except a few that had compatibility issues.** * Pinned dj-stripe to < 2.10, which is not yet supported. * Pinned litellm to < 1.77.2 to prevent [this installation issue](https://github.com/BerriAI/litellm/issues/14762) * **Add better UI status indicator if chat websocket connection fails or is interrupted.** * Add `created_at` / `updated_at` fields to the Employee agent tools. * The `delete_employee` agent tool now takes an ID instead of an `EmployeeData`. * Fixed issues in the agent tools that delete and update employees to better handle retries and error conditions (for example, when trying to operate on an employee that doesn’t exist). * Don’t include `websocket_url` templatetag if building without async support. * Fixed broken documentation links in the app that were using the old docs url formats. (Thanks Sam for reporting!) * Fixed a bug in `fly.toml` that had extra newlines in the worker and beat commands. (Thanks Sam for reporting!) * The `fly.toml` file now includes a `vm` section for celerybeat if enabled. (Thanks Sam for reporting!) * Fixed a bug in `fly.toml` that included a worker process even if celery was disabled. *Sep 23, 2025* ## Version 2025.9 [Section titled “Version 2025.9”](#version-20259) ### Agents support [Section titled “Agents support”](#agents-support) The big update in this release is a set of example workflows you can use as a foundation for building agentic chatbots, built with Pydantic AI. These include: * Injecting the logged-in user’s information into the chatbot context. * A tool use example that demonstrates a chatbot accessing live weather information for anywhere in the world. * An agentic editing example, that allows you to use natural language to create, update, list, and delete data in the employee application. * An MCP-based agent that allows superusers to ask questions about the applicaton database. You can learn more about these features and how they work in the following video, as well as the [LLM documentation](/ai/llms/): ### Changes related to agent support [Section titled “Changes related to agent support”](#changes-related-to-agent-support) * **Added Pydantic AI agent applications:** * Weather and location lookup agent, with tools to do geo-lookups and access current weather information. You can [demo this here](https://www.saaspegasus.com/chat/chat/agent/new/) * Chatbot to interact with employee application data models, with tools to work with employee data. This has been added as a new example, if AI chat is enabled. You can [demo this here](https://www.saaspegasus.com/pegasus/employees/objects/agent/) * Chatbot to interact with system database, with MCP tool to access postgres data. This has been added to the project dashboard page and is only available for superusers. * Tool to send emails. * Pydantic AI is now a dependency if you enable AI chat. * **Added a `chat_type` field to `Chat` models to differentiate between normal and agent chats.** * **Added an `agent_type` field to `Chat` models to differentiate between different agents.** * Resuming chats will use the appropriate chat/agent type. * Refactored how chat sessions are managed to a set of `ChatSession` helper classes that help decouple chat session behavior from the websocket / consumer class. * Refactored `ChatConsumer` websocket handler to a base class, and extended the base class to support different chat types (normal and agent) * Added new consumer classes for various agent types for the built-in examples. ### Other changes [Section titled “Other changes”](#other-changes-7) * **Removed the “OpenAI” chat configuration option. All AI chat functionality now uses the `litellm` module, which supports OpenAI, as well as Anthropic, Google, and other models.** * Changed `AI_CHAT_DEFAULT_LLM_MODEL` environment variable to `DEFAULT_LLM_MODEL` * Updated the default LLM model to be `gpt-5-nano`. * Added translations markup to a few places in the chat app. * Added a default log config for the “pegasus” namespace. * Updated the ai chat app logger to use “pegasus.ai” namespace. * Added a `ChatMessageInline` admin class so that chat messages show up in their associated `Chat` admin pages. * Updated the Postgres MCP server (dev tool) to use [mcp-alchemy](https://github.com/runekaagaard/mcp-alchemy), since the original Postgres MCP server is now deprecated. * Removed the `UserSignupStatsSerializer` and the unused `UserSignupStatsView` API view. * Added a `websocket_url` templatetag that can be used in Django templates to reverse websocket URLs similar to how the built-in `{% url %}` tag works. * Extracted employee table component to a template so it can be used in multiple places. * Simplified Stripe product serialization when used in subscription metadata. * Upgraded Django to the latest version (5.2.6). *September 10, 2025* ## Version 2025.8.1 [Section titled “Version 2025.8.1”](#version-202581) This release is focused on improving the AI chat experience, with an eye towards laying the groundwork for some future use cases that are in the works. The main new feature related to this work is an integrated AI chat widget that can be easily embedded on any page of your app. **Screenshots:** ![Chat Widget](/_astro/chat-windows.C3qXCZEi_Zo065W.webp) Complete details are below. ### Added [Section titled “Added”](#added) * **Added a way to add an AI chat to any page in your app.** See [the new documentation for using this feature](/ai/llms/#the-chat-widget) or [try it on saaspegasus.com](https://www.saaspegasus.com/chat/) (requires login). This change also included some refactors and changes that allow re-using parts of the AI code: * JavaScript websocket events are now initialized in an external JavaScript file (`assets/javascript/chat/ws_initialize.ts`) * Message thread component was moved to a separate template to be re-usable by the main chat page and component. ### Changed [Section titled “Changed”](#changed) * **The htmx websocket extension is now installed locally instead of loaded from unpkg.com.** * The default system prompt is now overridden in AI chats, enabling you to easily change it in a single place. * Chat names are now set synchronously if the initial message is short. * Updated websocket URL names from `"ws_openai_..."` to `"ws_ai_..."` since there is no requirement to use OpenAI. * Improved the default chat UI styles on Tailwind builds to be more comatible with DaisyUI themes. * Updated the default claude model used to `claude-sonnet-4-20250514` * Added default `AI_CHAT_ANTHROPIC_API_KEY` to example `.env` files. * Made minor formatting changes to `user_dashboard.html`. * The user-facing error message when creating an account with an existing email no longer reveals that the account is already signed up (this improves privacy/security). Thanks Brennon for the contribution! * Update: Updated `.pre-commit-config.yaml` to run the latest version of `ruff` and explicitly use the `ruff-check` hook. * Also pinned `ruff` dependency to the same minimum version. ### Fixed [Section titled “Fixed”](#fixed-1) * Updated the Digital Ocean deployment to use a managed database instead of a development database. Development databases are no longer well-supported in app platform. * Also updated the [Digital Ocean deployment docs](/deployment/digital-ocean) to reflect the latest changes. * Thanks Jan for the suggestion! * For the production `REDIS_URL`, only add `ssl_cert_reqs=none` for Heroku builds, and set it to required on Digital Ocean, which has valid certificates. Thanks Jan for the suggestion! * Fixed an issue with the honeypot field that caused a large horizontal scroll on the signup page on some CSS Frameworks. * Also improved spacing on the signup forms. * Thanks Finbar for the contribution! * Use Wagtail’s built-in page titles and meta descriptions is the SEO fields for blog posts, if they have been set. Thanks Richard for the suggestion! * Moved `@tailwindcss/typography` from `devDependencies` to `dependencies` in `package.json` ### Deprecated [Section titled “Deprecated”](#deprecated) * Using `pip-tools` as a package manager is deprecated and new projects will automatically default to using `uv`. Existing projects using `pip-tools` are encouraged to [migrate to `uv`](/cookbooks/#migrating-from-pip-tools-to-uv), and support for `pip-tools` will be dropped in an upcoming release. If this is a problem for you, get in touch! *Aug 26, 2025* ## Version 2025.8 [Section titled “Version 2025.8”](#version-20258) This is a maintenance release which improves Docker-based development, upgrades dependencies and addresses a number of minor issues reported by the community. Thanks to everyone who contributed ideas and code to this release! ### Changed [Section titled “Changed”](#changed-1) * **Changed how CSS files are built and imported in vite builds. This fixes the flash of unstyled content when running Vite in development.** * Removed the redundant `site-.js` files and instead added the imported CSS files directly as entry points to `vite.config.ts`. * Updated `base.html` to use `vite_asset_url` instead of `vite_asset` for CSS files. * **Updated development Docker setup to always use a separate container for Node / NPM.** This removes all node/npm logic from `Dockerfile.dev` and uses either `Dockerfile.vite` or `Dockerfile.webpack` for the front end. * Also updated the `Makefile` to reference this new container where necessary. * **Upgraded all Python packages to their latest versions.** * **Upgraded all JavaScript packages to their latest versions.** * Changed `sentry-sdk` to `sentry-sdk[django]` and pinned the version. Thanks Ralph for suggesting! * Changed how email confirmation works when updating an email address to be more aligned with allauth best practices, and stop using a method that was removed in the latest allauth. * Changed the typescript module resolution strategy to “bundler”, which aligns better with how Vite resolves modules in the project. * Added `.claude/settings.local.json` to `.gitignore`. * Updated the behavior of the subscription page for team non-admins so that it shows a useful message telling them they aren’t allowed to manage subscriptions for their team, instead of returning a generic 404. Thanks Haydn for the suggestion! * `./manage.py bootstrap_subscriptions` will now use Stripe’s “marketing features” property of Products to generate the relevant configuration in Pegasus. Thanks Zac for suggesting! * `./manage.py bootstrap_subscriptions` will now only use products that have recurring pricing set when generating the Pegasus configuration. * The `build-api-client` make target will now delete unused files and set correct file permissions on the generated code. Thanks Finbar for the contribution! ### Fixed [Section titled “Fixed”](#fixed-2) * **Improved the Python environment setup in `Dockerfile.dev` to be much more performant. This should make Docker container rebuilds after adding/changing Python dependencies much faster.** * Python environments and packages are now created and installed as the django user to avoid expensive chown calls. Thanks Jacob and Mark for the suggestion! * Uv now uses Docker’s cache system consistently so that dependencies are cached by Docker across builds. * Added a `require_POST` decorator to `create_api_key` view so it doesn’t work with GET requests. Thanks Brennon for reporting! * Fixed a bug where subscriptions tests failed due to a missing `dateutil` dependency under certain build configurations. Thanks Jacob for reporting! * Fixed styling of allauth’s “email change” template, which is used if you set `ACCOUNT_CHANGE_EMAIL = True`. Thanks Finbar for the report and fix! * Fixed a bug where `./manage.py bootstrap_subscriptions` and `./manage.py bootstrap_ecommerce` sometimes had to be run twice to sync all products and prices to a new installation. Thanks Zac for reporting! * Updated stripe API imports to remove warnings about deprecated `stripe.api_resources` packages. Thanks Cristian for reporting! *August 1, 2025* ## Version 2025.6.2 [Section titled “Version 2025.6.2”](#version-202562) This hotfix release addresses two minor issues in the 2025.6 release: * Remove breaking reference to `.babelrc` in `Dockerfile.web` on Vite builds. This was causing deployments to fail on some Docker-based platforms. * Always add `gevent` dependency to production requirements if using celery. This fixes an issue running celery in production on certain deployment platforms. Thanks Justin and Eugene for the bug reports! *June 25, 2025* ## Version 2025.6.1 [Section titled “Version 2025.6.1”](#version-202561) This is a hotfix release which addresses two minor issues: * Fix `make npm-install` and `make npm-uninstall` commands when using vite as a bundler. Thanks Matt for reporting! * Fix broken dark mode behavior on Tailwind when attempting to disable it. Thanks Wik for the report and fix! *June 23, 2025* ## Version 2025.6 [Section titled “Version 2025.6”](#version-20256) This release hardens the production Celery set up, expands AI-development tooling, improves production support for the standalone React front end, and extends the ecommerce application. Read on for details! ### Celery improvements [Section titled “Celery improvements”](#celery-improvements) * Celery periodic tasks can now be configured via `settings.SCHEDULED_TASKS` and synchronized with a new management command (`./manage.py bootstrap_celery_tasks`). The previous migration files that created celery periodic tasks have been removed. * The Celery gunicorn worker pool changed from the default of ‘prefork’ to ‘gevent’ in production, and the concurrency was increased. This should be a more scalable setup for most projects, though may need to be changed for projects that are heavily CPU-bound. * Because of the above change, a separate worker for Celery Beat has been added to all production deploy environments (because beat can’t be run with the gevent pool). * Updated the [Celery documentation](/celery) to reflect these changes. ### AI-Coding improvements [Section titled “AI-Coding improvements”](#ai-coding-improvements) * **Added an optional Claude Code Github workflow**. When enabled, you can mention @claude on a Github pull request or issue to trigger a Claude Code update. Learn more [in the docs here](/ai/development/#the-github-workflow-file). * **Added optional support for JetBrains / PyCharm Junie AI rules files.** [Docs](/ai/development/#working-with-junie) * Edited and expanded the AI rules files based on various user feedback (thanks to many who have contributed to this). ### Standalone front end improvements [Section titled “Standalone front end improvements”](#standalone-front-end-improvements) These updates affect the [standalone React front end](/experimental/react-front-end). * Updated the front end CSS to build the files directly in the front end (and import relevant files from the Django app in `index.css`), rather than including the built Django CSS files directly. * Some required Tailwind CSS files in the `assets` directory will be included if you use the standalone front end even if you build for a different framework. * Added tailwindcss, the typography plugin, and daisyui as explicit dependencies (and plugins) to the front end to enable the above change. * Upgraded all JavaScript dependencies in the front end. * Removed unnecessary default styles from `index.css`. * Updated front end to use aliases for the “assets” directory. Also updated `tsconfig.json` to handle this. * Updated `vite.config.ts` to fix various build issues if the parent `node_modules` isn’t available. * Fixed the default values of `FRONTEND_ADDRESS` and related values in `settings.py` and `.env` files to point to “” (instead of port 5173). * Added `CSRF_COOKIE_DOMAIN`, `CORS_ALLOWED_ORIGINS`, and `SESSION_COOKIE_DOMAIN` to `settings.py` using environment variables. These must be customized when deploying the standalone front end. * Updated Kamal’s `deploy.yml` to include default values for the above settings. * **Added initial documentation on [deploying the standalone front end to production](/experimental/react-front-end/#deployment).** ### Other updates [Section titled “Other updates”](#other-updates) * **Added a digital download example to the ecommerce application.** You can now associate a file with ecommerce products and only people who have purchased the product will be able to access it. * Also added tests for this workflow. * Added a private storage backend, for storing private files on S3-compatible storage backends (used by the above). * Upgraded most Python dependencies to their latest versions. * Fix `target-version` in `pyproject.toml` to match the currently recommended Python 3.12. Thanks Finbar for reporting! * Fixed a bug where group chat avatars were incorrectly styled on Tailwind builds. Added a new `pg-avatar` CSS class to handle this. * Made some updates Digital Ocean deployments: * Switched Redis to Valkey, and upgraded it to version 8. * Upgraded Postgres to version 17. * Updated the [Digital Ocean deployment docs](/deployment/digital-ocean) to reflect the latest changes. * Fixed email verification emails when `ACCOUNT_EMAIL_VERIFICATION_BY_CODE_ENABLED = True`. Thanks Justin for reporting and helping with the fix! * Removed default font-weight styling from `email_template_base.html`. * Api keys associated with inactive users will no longer pass API permission checks. Thanks Brennan for the suggestion! * Removed unused `.babelrc` file if not building with Webpack. * Automatically confirm user emails when they create accounts through the invitation acceptance workflow, since they can only get the invitation URL from the email link. ### Upgrading [Section titled “Upgrading”](#upgrading-7) If your project has existing migration files that create celery tasks (e.g. `/apps/subscriptions/migrations/0001_celery_tasks.py`), you should leave them in your repository to prevent issues running future migrations. The tasks themselves are unaffected, since they live in the database. *June 10, 2025* ## Version 2025.5.1 [Section titled “Version 2025.5.1”](#version-202551) This is a minor bugfix release on top of 2025.5. * Removed bad reference to Modals in `site.js`. Thanks Jacob for reporting! * Fixed Python Celery setup in `build_celery.sh` when using `uv` (Render deployments only). Thanks Jacob for reporting! * Fixed issue with the shadcn dashboard caused by a missing `{% vite_react_refresh %}` tag. Thanks Shoaib for reporting! *May 16, 2025* ## Version 2025.5 [Section titled “Version 2025.5”](#version-20255) This release has a few big updates: ### Use Vite instead of Webpack for building the front end [Section titled “Use Vite instead of Webpack for building the front end”](#use-vite-instead-of-webpack-for-building-the-front-end) This release adds the option to use [Vite](https://vite.dev/) as a bundler instead of Webpack. Vite is a modern build tool that adds a few key benefits over the Webpack build system: 1. It is much faster than Webpack. 2. Hot Module Replacement (HMR)—a development feature that lets code changes in your front end files automatically update without a full-page reload. 3. Code splitting—a production feature that breaks your front end files into individual bundles that encapsulate code dependencies. This leads to less redundant JavaScript and faster page loads. You can watch the video below for a walkthrough of these benefits and how they work in the new setup. You can also see the overhauled [front end documentation](/front-end/overview) and [Vite-specific guidance](/front-end/vite) for more details. ### Gitlab CI support [Section titled “Gitlab CI support”](#gitlab-ci-support) You can now run CI on Gitlab in addition to Github. Gitlab’s CI will run your tests, linting, and build / type-check your front end files. Thanks to Paolo and Simon for contributing to this feature! ### Retiring the Bootstrap Material Theme [Section titled “Retiring the Bootstrap Material Theme”](#retiring-the-bootstrap-material-theme) **The material theme for Bootstrap has been deprecated.** This means that the theme will be in maintenance-only mode, and support will eventually be dropped (probably in 6-12 months). Existing projects can continue using the theme, but new projects should not, and new Pegasus features will eventually not be developed and tested on the theme. Dropping support for this theme was a difficult decision. The main reason it was made is that several Pegasus customers have complained about the lack of documentation and support for this theme from its maintainer, Creative Tim. Additionally, their process around updating the theme has involved releasing large, poorly-documented updates which have been difficult to incorporate back into Pegasus. If you would like help migrating off this theme, you can reach out via standard support channels. ### Complete changelog [Section titled “Complete changelog”](#complete-changelog) **Changes related to Vite support** * **Added Vite as an option for your front end build system. See [the front end](/front-end/overview) and [vite-specific docs](/front-end/vite) for details.** * **`window.SiteJS` is now populated explicitly in JavaScript files (in addition to webpack’s library support, which does not work with Vite builds).** * Affected files include: `app.js` (`window.SiteJS.app`), `pegasus.js` (`window.SiteJS.pegasus`) * Imports in those files were also renamed to avoid namespace confilcts. * Updated all JavaScript files using JSX to have a `.jsx` extension. * Removed legacy Vue2 code and imports from the Vue example. * Removed unused imports shadcn components. * Removed leading tilde (”\~” character) from CSS imports in various places. * Changed CSS imports in JavaScript files from `require` to `import`. * Fixed a few small React warnings/issues in the AI chat app. * Removed no longer needed `vue-template-compiler` dependency. * **Updated the standalone front end to run on port 5174 to not conflict with the default vite port.** **Other Changes** * **Added “Gitlab” as an option for CI.** (Thanks Paolo and Simon!) * **Deprecated the Material Bootstrap theme.** * **Upgraded all Python packages to the latest versions, including Django 5.2.** * **Upgraded all npm packages to the latest versions.** * **Updated all `blocktranslate` tags to use the `trimmed` option for easier translation.** * Added explicit width and height to some svgs to slightly improve styling when CSS is not present. * Made minor updates to AI rules files. * Use the new `ACCOUNT_SIGNUP_FIELDS` setting to configure sign up fields and removed usages of deprecated allauth fields. * **Removed `project_settings` from the `project_meta` context processor.** This was previously only used to pass the now-deprecated `ACCOUNT_SIGNUP_PASSWORD_ENTER_TWICE` setting to sign up templates. The sign up templates now render the second password field based on the form value. ### Upgrading [Section titled “Upgrading”](#upgrading-8) For help switching from Webpack to Vite, see [the Webpack to Vite migration guide](/front-end/migrating). *May 15, 2025* ## Version 2025.4.4 [Section titled “Version 2025.4.4”](#version-202544) This is another minor release: * Stop dynamically setting user/group ID in the `Makefile` and just default to `1000`. The dynamic ID assignment was continuing to cause issues on certain MacOS environments. * Add `make build-api-client` target even when not using Docker. * Added additional guidance on Pegasus’s Django model conventions to the Python AI rules. *May 5, 2025* ## Version 2025.4.3 [Section titled “Version 2025.4.3”](#version-202543) This is another bugfix release: * Make the user/group creation more resilient in development Docker containers, which fixes a permissions issue on MacOS in certain environments. Thanks Chris for reporting! * Add `architecture.md` to cursor rules directory. *May 1, 2025* ## Version 2025.4.2 [Section titled “Version 2025.4.2”](#version-202542) This is a bugfix release that addresses a few problems in the most recent build: * Moved the new `CustomHeadlessAdapter` to `users/adapters.py` to fix an issue with it not being available if you built without teams enabled. Thanks Alex for reporting! * Remove source maps for JavaScript bundles in production. This results in substantially smaller production bundle sizes. Thanks Jan for reporting! * Automatically do a best effort to set the user/group ID used by the development docker container in the `Makefile`. Thanks Jacob for suggesting! For the source map fix, you can change the “devtool” setting in `webpack.config.js` to this: ```javascript devtool: process.env.NODE_ENV === 'production' ? false : "eval-cheap-source-map", ``` *Apr 29, 2025* ## Version 2025.4.1 [Section titled “Version 2025.4.1”](#version-202541) This is a big release with a few major updates. ### Team invitation workflow changes [Section titled “Team invitation workflow changes”](#team-invitation-workflow-changes) The workflow around new users joining teams and accepting invitations has been streamlined based on user feedback. For a summary of the changes you can watch [this walkthrough](https://youtu.be/qxr_WdQEL2g) or read below. Key user-facing changes: * **When a user signs up with a pending invitation they will be redirected to view it before creating their first team.** * **Accepting an invitation requires having a verified email address for the email it was sent to.** * Users can view pending invitations for any of their email addresses from the team selector dropdown. * Inviting an email address of someone who’s already in a team will show an error message that they are already part of the team. In addition, the following fixes and code updates were made: * Added an API and serializer for accessing the logged-in user’s invitations, used by the React view. * React: renamed `getInviteUrl` helper JS function to `getResendInviteUrl`. *Thanks to EJ, Geoff, Valics, Simon, Arno, and possibly others who contributed ideas and feedback on the design of these changes.* ### API authentication and Standalone front end updates [Section titled “API authentication and Standalone front end updates”](#api-authentication-and-standalone-front-end-updates) The [Standalone React Front end](/experimental/react-front-end) underwent a major overhaul. Importantly, it now uses [allauth headless](https://docs.allauth.org/en/dev/headless/index.html) instead of a custom `dj-rest-auth` and custom authentication APIs. On top of this, support for many new authentication workflows was added to the standalone front end, including email confirmation, password reset, and social authentication. The standalone front end—which is still in experimental mode—is now close-to-parity with the Django authentication system. Details: * **Enabled and configured [allauth headless](https://docs.allauth.org/en/dev/headless/index.html)** (if authentication APIs are enabled or using the standalone front end). * **Removed `dj-rest-auth` and `djangorestframework-simplejwt` and associated setup code. Auth now uses allauth headless and sessions by default.** * **Removed the `apps/authentication` app and associated api client code.** * **Updated the standalone front end to use an authentication system against allauth headless and added support for email confirmation, social authentication and password reset.** These changes borrow heavily from the [allauth example](https://github.com/pennersr/django-allauth/tree/main/examples/react-spa) project, and involve a large number of code-level changes which are not fully outlined here, though some of the larger ones are listed below: * Added a `CustomHeadlessAdapter` class to add the user’s profile picture to the API. * Removed translation markup from JavaScript code that is shared with the standalone front end. Translations are not supported, currently. * Upgraded eslint-related libraries. * Updated `.eslintrc.cjs` to `eslint.config.mjs` and tweaked the configuration settings. * Show more/better validation errors on login, signup, etc. * Changed `ProtectedRoute` to `AuthenticatedRoute`. * Added templates and components for various new authentication workflows (email confirmation, password reset, etc.). * Added an `ACCOUNT_USER_DISPLAY` setting. * Updated [the standalone front end docs](/experimental/react-front-end) to reflect the latest setup. ### Djstripe upgrade and webhook updates [Section titled “Djstripe upgrade and webhook updates”](#djstripe-upgrade-and-webhook-updates) This release upgrades `dj-stripe` to version 2.9 and migrates to dj-stripe’s database-backed webhooks. This lets you set up multiple webhook endpoints/secrets, if desired. See the upgrade section below for details on updating. Details: * **Upgraded dj-stripe to version 2.9** * **Webhook endpoints now need to be configured in the database instead of having a single global endpoint.** See [the updated subscription webhooks documentation](/subscriptions/#webhooks) for more details. * Updated webhook handling for subscriptions and ecommerce purchases to be compatible with the above model. * Added a `bootstrap_dev_webhooks` management command to help set up `djstripe` webhooks for development. * Added `apps.utils` to `settings.INSTALLED_APPS` so that management commands inside it are picked up. * Removed the no-longer used `DJSTRIPE_WEBHOOK_SECRET` setting and environment variable. * Upgraded `stripe` to version `11.6` (there is [a bug with djstripe and the latest `12.0` release](https://github.com/dj-stripe/dj-stripe/issues/2153)) * Updated the [subscription docs](/subscriptions/#webhooks) to reflect the latest changes for setting up webhooks in dev and production. ### Ruff linting updates [Section titled “Ruff linting updates”](#ruff-linting-updates) The ruff linting rules were expanded and code has been modified to pass the revised ruleset. This leads to cleaner, more consistent code across the project and should make future Pegasus merges/upgrades smoother. Details: * **Updated the default ruff rules to enable all of the [E (error) Rules](https://docs.astral.sh/ruff/rules/#error-e), as well as the [UP (pyupgrade) Rules](https://docs.astral.sh/ruff/rules/#pyupgrade-up), [B (flake8-bugbear) Rules](https://docs.astral.sh/ruff/rules/#flake8-bugbear-b), and [SIM (flake8-simplify) rules](https://docs.astral.sh/ruff/rules/#flake8-simplify-sim), in addition to the already-enabled [F (Pyflakes) Rules](https://docs.astral.sh/ruff/rules/#pyflakes-f), and [I (isort) Rules](https://docs.astral.sh/ruff/rules/#isort-i).** * These lead to some minor code changes, including: * Use `contextlib.suppress` in a few places instead of the previous exception handling * Use `raise ... from` in several places for more explicit exception handling. * Combined some nested if statements into single lines. * Use `super()` instead of `super(C, self)` * Use f-strings instead of percent style format strings when possible. * Use `Type | OtherType` instead of `Union[Type, OtherType]` in type hints * Use core types for `list`, `dict` etc. instead of the type classes. * Define classes without the object base class. * Increased strictness around line lengths. * Changed rule definition from `extend-select` to `select` based on [ruff’s recommendations](https://docs.astral.sh/ruff/linter/#rule-selection). ### Other updates [Section titled “Other updates”](#other-updates-1) * **Change: Upgraded npm to the latest version (11.3) in Docker containers and docs.** * **Change: Added a honeypot field to the sign up form to help reduce bot/spam sign ups.** (Thanks Chris and Stian for suggesting!) * Change: Added an ”@” alias for the `assets/javascript` folder and started using it in imports. * Change: Updated development Docker setup to run as the logged-in user (under a `django` user account) instead of root. This should help with file ownership permissions being assigned to root after running the project with Docker. Thanks Finbar and Jacob for the suggestion and help with this! * Change: Removed the “app-card” styling from the loading widget to make it more versatile. * Change: Tweaked whitespace in a few templates to be more consistent across the project. * Change: Use `blocktranslate trimmed` instead of `blocktranslate` in some Django templates. * Change: Updated the output of `bootstrap_subscriptions` to communicate that only subscription products should be added to `ACTIVE_PRODUCTS`. * **Fix: Changed reference of `stripe.Invoice.upcoming` to `stripe.Invoice.create_preview` since Stripe [deprecated the upcoming invoice API](https://docs.stripe.com/changelog/basil/2025-03-31/invoice-preview-api-deprecations).** * This fixes an issue with loading the “manage subscription” page when using the latest Stripe API version. * Fix: Added `DEBUG=false` to `heroku.yml` setup section, which helps enforce that debug is disabled when running `collectstatic`. This helps avoid “No module named ‘daphne’” errors in async deployments. Thanks Abhishek for reporting! * Fix: The `dark_mode_selector.html` component is no longer included if you have disabled dark mode. * Fix: Improved chat height styling on mobile screens to avoid extra scrolling. * Fix: Updated the migration that creates the default Site object to also update the table sequence ID. Thanks Julian and Geoff for the suggestion and help with this! * Fix: Fixed a test case in `test_member_management` that wasn’t getting properly exercised. * Fix: Deleted the unused `_create_api_keys_if_necessary` function in `bootstrap_subscriptions.py` * Fix: Fixed the hover text color of the `.pg-button-danger` CSS class styles on tailwind builds. ### Upgrading [Section titled “Upgrading”](#upgrading-9) There are several changes in this release that may require additional steps during the upgrade process. To help with this, I recorded a video walkthrough of myself upgrading one of my own projects, which you can watch below: #### Authentication APIs [Section titled “Authentication APIs”](#authentication-apis) If you were using Pegasus’s [standalone React front end](/experimental/react-front-end) then your setup should work out of the box after upgrading. If you were using the `dj-rest-auth` app and previous authentication APIs in a different way, then you will need to either: 1. Update the client code to work with allauth headless. This can be done by referring to the example front end and [allauth documentation](https://docs.allauth.org/en/dev/headless/index.html). 2. Restore the previous implementation of the authentication APIs. This can be achieved by *rejecting* the proposed changes to remove the `apps.authentication` app and library dependencies/setup during the upgrade process. #### Djstripe and Webhooks [Section titled “Djstripe and Webhooks”](#djstripe-and-webhooks) There are a few issues you might run into with the dj-stripe upgrade. **Database Migrations** If you get an `InconsistentMigrationHistory` running `manage.py migrate` on your database, look for any diffs in your existing migration files that change the `djstripe` dependency from `0012_2_8` to `0014_2_9a`, and then revert these changes back to `0012_2_8`. **Webhooks** The most recent dj-stripe has disabled the global webhook support in favor of database-backed webhooks. These are more versatile, secure, and easier to set up, but require a migration from the previous set up. To migrate your webhooks, follow the instructions to set up a new webhook endpoint from the [subscriptions docs](/subscriptions/#webhooks-in-production) and then delete your previous webhook endpoint. There is a complete walkthrough of this process in the video above. **If you fail to do this your webhooks will stop working in production.** #### Formatting and linting [Section titled “Formatting and linting”](#formatting-and-linting) All Pegasus code should be updated to pass the new ruff linting configuration, but the configuration changes might cause build failures on code that has been added/modified. Many fixes can be automated by running: ```bash (uv run) ruff check --fix --unsafe-fixes ``` On the upgraded codebase and reviewing the changes made. However, some errors will likely require manual fixing, which can be done by reading the output and making the suggested change (or even giving the task to an LLM). You can see how I did this process with Claude code in the above video. Alternatively, you can modify the `[tool.ruff.lint]` section of `pyproject.toml` to remove any categories of fixes you don’t want to turn on for your project. *April 24, 2025* ## Version 2025.4 [Section titled “Version 2025.4”](#version-20254) The main feature of this release is improved support for AI tools and coding assistants. This release adds a suite of rules files that can be used with Cursor, Claude Code, or other AI-enabled IDEs. It also adds an MCP configuration for interfacing with your development database and controlling a web browser. These options are configurable via new project settings. Watch a demo below, or check out the new [AI tool docs](/ai/development). ### Added [Section titled “Added”](#added-1) * **Optional rules files and MCP configuration for Cursor or Claude.** * These files will continue to be modified and iterated on as more developers use them and provide feedback.settings ### Changed [Section titled “Changed”](#changed-2) * Improved default file input styles. * Add front end install / build to `make init`. (Thanks Jacob for reporting!) * Bumped `vite` used by the standalone front end to the latest version. * Upgraded several Python packages to their latest versions. * Removed unused `postcss.config.js` file from the front end. (Thanks Jacob for reporting!) ### Fixed [Section titled “Fixed”](#fixed-3) * **Fixed a potential XSS vulnerability issue with `markdown_tags` not properly escaping vulnerable tags.** This issue existed if you were using the AI chat UI, or built other functionality on top of that library. All markdown is now sanitized with `nh3`. (Thanks Mitja for reporting!) * Also added tests for this functionality. ### Translation Creator updates [Section titled “Translation Creator updates”](#translation-creator-updates) A number of new features were added to [Translation Creator](https://www.saaspegasus.com/store/product/translation-creator/) this month. Big thanks to community member Valics who contributed the first draft of most of these updates. * **Upgraded to the latest Pegasus, including Tailwind 4 and DaisyUI 5.** * **Translations will now retain comments.** * **Added pagination, sort, and filtering to the translations view.** * Added the ability to delete projects and clear translations. * Updated the DB constraint to use a hash of the input text instead of the text itself, which improves performance and fixes a bug with long translations. * Added / updated test cases. *April 4, 2025* ## Version 2025.3 [Section titled “Version 2025.3”](#version-20253) This release upgrades TailwindCSS to version 4 (and DaisyUI to Version 5). It also has several minor updates and fixes. ### Tailwind 4 Update [Section titled “Tailwind 4 Update”](#tailwind-4-update) Pegasus now runs on Tailwind V4! This comes with [a huge number of improvements](https://tailwindcss.com/blog/tailwindcss-v4), including much faster build times, simplified tooling, automatic content detection, and more. Tailwind and DaisyUI were upgraded using the associated guides ([Tailwind](https://tailwindcss.com/docs/upgrade-guide), [DaisyUI](https://daisyui.com/docs/upgrade/)). There is also an [upgrade guide for Pegasus apps](/css/tailwind/#upgrading-from-tailwind-3-to-4). Here’s a detailed breakdown of the changes: * **Upgraded to Tailwind 4 and DaisyUI 5.** * **Changed how tailwind is imported and customized in `site-tailwind.css` to match the V4 guidance.** * **Removed `content` section of `tailwind.config.js`. Tailwind 4 automatically finds all content for the project.** * **Updated `postcss.config.js` to match the Tailwind 4 recommendation (using `@tailwindcss/postcss`).** * **Converted tailwind-specific CSS to V4 syntax, using `npx @tailwindcss/upgrade`. *These changes were automated.*** * Removed `@layer` declarations * Converted some helper classes to use `@utility` * Changed some double quotes to single quotes and cleaned up whitespace in css files. * Updated various classes in templates/JavaScript files according to the migration guide, e.g. `outline-none` —> `outline-hidden`, `flex-grow` —> `grow`, `max-w-screen-xl` —> `max-w-(--breakpoint-xl)` etc. * DaisyUI updates: * **DaisyUI is now initialized as a plugin in `site-tailwind.css` instead of `tailwind.config.js`.** * **Themes are also now handled in this section. The docs have been updated to reflect this.** * Updated Pegasus CSS color variables to use the DaisyUI 5 versions. * Cleaned up Tailwind form rendering tags, removed unnecessary markup, and upgraded markup to be compatible DaisyUI 5, e.g. removing `-bordered` classes. * Checkboxes will now appear on the left instead of the right of labels. * Updated active tabs to use the latest DaisyUI markup (`menu-active` instead of `active`). * Shadcn updates: * **Moved shadcn components from `assets/javascript/components/ui` to `assets/javascript/shadcn/components/ui`.** * **New shadcn components can now be added via the CLI and will end up in the right place with no additional steps.** * Updated `tsconfig.json` and `webpack.config.js` to be consistent with new shadcn setup. * Regenerated shadcn components from the latest version of the library. * Changed shadcn themeing to use `@theme` declaration. * Removed all shadcn customizations from `tailwind.config.js` as they are superceded by the theme system. * Upgraded various shadcn dependencies to their latest versions. * Flowbite updates: * **Upgraded Flowbite to version 3.1.** * **Flowbite is now initialized as a plugin in `site-tailwind.css`.** * Explicitly import flowbite styles when building with flowbite enabled. This fixes out-of-the-box styling of some plugins. (Thanks Eeshan for reporting and fixing!) * Extracted dark mode selector to its own component and upgraded it to work with DaisyUI 5. * Other fixes / changes * Cleaned up various bits of CSS to use nested selectors. * Improved the contrast of the `pg-text-muted` class on dark mode. * Cleaned up commented out code in CSS files. * Removed unused “app” CSS class styles. * Standalone front end updates: * **Removed tailwind entirely from the standalone front end CSS.** The standalone front end currently gets its css from the same built file as the Django app. * Updated the [Tailwind Documentation](/css/tailwind) to reflect the V4 changes. ### Other Updates [Section titled “Other Updates”](#other-updates-2) * Fixed an issue running `./manage.py` commands in production docker containers when using `uv`. Thanks Richard, Bryan, and Ken for reporting! * Fixed active tab highlighting on Flowbite demo. * Removed `--no-emit-package setuptools` from the `make pip-compile` command. Some configurations require setuptools and this was causing issues on some pip-tools builds. Thanks Jim for reporting and fixing! * Changed ruff `exclude` to `extend-exclude` in `pyproject.toml` to keep ruff’s defaults. Thanks Justin for the suggestion! * Added help text to a few `make` targets that were missing it. Thanks Steve for the suggestion! * Removed unused `pg-is-loading` CSS class. * Fix syntax of commented out `EMAIL_BACKEND` variable in `deploy.yml`. * Removed language codes from the language selector dropdown. ### Upgrading [Section titled “Upgrading”](#upgrading-10) See the [Tailwind upgrade guide](/css/tailwind/#upgrading-from-tailwind-3-to-4) for details on upgrading existing Tailwind projects. *Mar 26, 2025* ## Version 2025.2.2 [Section titled “Version 2025.2.2”](#version-202522) This is a hotfix release that fixes a bug in the styling of the avatar in the navbar on Bootstrap using certain browsers. Thanks Luc for reporting! * Restored `navbar.css` on bootstrap builds and moved it out of the bulma-specific folder. * Updated imports in `base.css` accordingly. *Mar 13 2025* ## Version 2025.2.1 [Section titled “Version 2025.2.1”](#version-202521) This is a hotfix release that fixes a missing newline between `REDIS_URL` and `GOOGLE_ANALYTICS_ID` in `.env` / `secrets` files. Thanks Peter for the bug report! *Mar 7 2025* ## Version 2025.2 [Section titled “Version 2025.2”](#version-20252) This is a maintenance release with a number of upgrades and fixes. ### Added [Section titled “Added”](#added-2) * **You can now configure the Github integration to push your Pegasus code to a subdirectory of the repository.** [More details in the updated Github docs here](/github/#pushing-pegasus-code-to-a-subdirectory-in-your-repository). Thanks to Simon for helping with this, and Aaron, Bernard, Danil, and Arno for suggesting it! * Added a `429.html` error template. ### Changed [Section titled “Changed”](#changed-3) * **Migrated the majority of shared style files from sass to css, and removed sass from Tailwind builds.** This makes the setup more consistent with a typical Tailwind project. * Removed “sass” and “sass-loader” packages from Tailwind builds. * Updated `webpack.config.js` on bootstrap and bulma builds to also now handle `.css` files. * Related, ported the `navbar.sass` file to css, moved it to the `bulma` folder, and removed it from non-Bulma builds. * **Set [Django’s cache framework](https://docs.djangoproject.com/en/latest/topics/cache/) to use Redis in production by default.** * The Redis cache will be enabled when `settings.DEBUG` is `False`. * Also explicitly list `redis` as a first-class requirement, which fixes a bug where tests could fail if you disabled celery. * Added `.venv` and `venv` to the `.gitignore` file. (Thanks Peter for suggesting!) * Use the project id in the default `AWS_STORAGE_BUCKET_NAME` in deploy.yml. (Kamal deployments, thanks Peter for suggesting!) * Updated the version of `ruff` used by pre-commit to the one that’s installed in the project, and upgraded ruff to the latest (0.9.7). (Thanks Peter for reporting!) * Added a timeout and error handling to turnstile requests, to prevent hanging if Cloudflare was for some reason down. (Thanks Peter for suggesting!) * Removed `ENABLE_DEBUG_TOOLBAR=True` from production environment/secrets files. * Consistently use double quotes instead of single quotes in environment and deployment files. (Thanks Peter for suggesting!) * Removed duplicate and unused variable declarations across Kamal’s `deploy.yml` and `secrets` files. Public variables are now listed in `deploy.yml` and private ones are listed in `secrets`. (Thanks Peter for suggesting!) ### Fixed [Section titled “Fixed”](#fixed-4) * **Improved edge-case handling the Stripe checkout integration.** * Users should now see helpful error messages instead of getting 500 errors or ending up in an invalid state if they hit certain invalid URLs. * This also fixes a vulnerability where an attacker could potentially simulate e-commerce purchases through manual inspection and manipulation of requests. * Fixed a bug where `setuptools` was accidentally not present in production requirements files when using pip-tools. This caused production deployments to fail in certain cases. (Thanks Eeshan and Jim for reporting!) * Fixed an issue deploying to Heroku with Docker when using uv by removing Docker caching, which Heroku does not support. (thanks Toul for reporting!) * Fixed the active tab highlighting styles in the examples navigation on Bulma builds. * Removed unnecessary `
` elements from `top_nav.html` on Bootstrap builds. * Don’t include Docker translation instructions in README if not using Docker. (Thanks Peter for reporting!) * Updated the Pegasus CLI to [version 0.8](https://github.com/saaspegasus/pegasus-cli/releases/tag/v0.8), which fixes a bad html closing tag in the generated templates. (Thanks Julian for the bugfix!) * Removed celery sections from `deploy.yml` in kamal builds if celery isn’t enabled. ### Removed [Section titled “Removed”](#removed-1) * Removed `django_otp` dependency and configuration, which was only there to facilitate the transition to `allauth.mfa`. See the release notes for [Version 2024.5](/release-notes-history/#version-20245) for more information on this change. * Also removed the associated `migrate_allauth_2fa` management command. * Removed the default user-facing messages on login/logout. You can add them back or customize them by editing the files in `/templates/account/messages/`. ### Documentation [Section titled “Documentation”](#documentation) * Added [a community guide on using Digital Ocean Spaces](/community/digital-ocean-spaces) (alongside Amazon SES). Thanks Neil and Finbar for the contribution! ### Upgrading [Section titled “Upgrading”](#upgrading-11) Tailwind projects that have added their own `.sass` files will need to either restore sass support or port these files to `.css` (llms are good at this!). You can “restore” sass support by rejecting the proposed changes in `package.json` and `webpack.config.js` during upgrade. If you have removed Redis from your project you will need to update the default cache config in `settings.py`. *Feb 28, 2025* ## Version 2025.1.1 [Section titled “Version 2025.1.1”](#version-202511-1) This is a hotfix release that fixes an issue with installing Node 22 in the development Docker container. Thanks Oscar and Emiliano for reporting! If you’d rather manually apply the patch, you can just apply the following patch to your `Dockerfile.dev` file: ```diff RUN --mount=target=/var/lib/apt/lists,type=cache,sharing=locked \ --mount=target=/var/cache/apt,type=cache,sharing=locked \ rm -f /etc/apt/apt.conf.d/docker-clean && \ echo "deb https://deb.nodesource.com/node_22.x bookworm main" > /etc/apt/sources.list.d/nodesource.list && \ wget -qO- https://deb.nodesource.com/gpgkey/nodesource.gpg.key | apt-key add - && \ curl -fsSL https://deb.nodesource.com/setup_22.x | bash - && \ apt-get update && \ apt-get install -yqq nodejs \ ``` *Jan 28, 2025* ## Version 2025.1 [Section titled “Version 2025.1”](#version-20251) This release includes mostly backend infrastructure changes to some Pegasus features to pave the way for a (future) plugin ecosystem. This should make it easier to maintain Pegasus apps as well as possible (in the future) for other people to develop apps that can seamlessly integrate into Pegasus. ### New “async” build flag [Section titled “New “async” build flag”](#new-async-build-flag) Previously when you enabled async / websockets, you also got the group chat example application. Now you can enable async features without this additional example app, and turn it on separately with a new configuration option. This lets you use async but not have to manually delete the example application. ### Added a flag to remove Celery (if possible) [Section titled “Added a flag to remove Celery (if possible)”](#added-a-flag-to-remove-celery-if-possible) Added a configuration option that will remove celery and all dependencies + configuration ***if no other parts of your application need it***. Celery will also be removed from production deployment configurations. Celery is still required (and will be automatically enabled) if you are using any of: 1. The Pegasus examples 2. Subscriptions with per-unit billing enabled 3. Any AI chat features If you’re not using these features and want to disable Celery you can do that from your project settings page. ### Organizational changes to apps for more consistency [Section titled “Organizational changes to apps for more consistency”](#organizational-changes-to-apps-for-more-consistency) The following changes don’t have any new features or functionality, but change small things about how the code is organized for affected apps (AI chat, AI images, and async group chat). It is hoped that these changes will make maintenance, upgrades, and future extensions to Pegasus easier. Changes affecting the AI Chat, AI Images, and Group Chat apps: * Moved app declarations for these apps to the end of `PROJECT_APPS` in `settings.py` * Moved url declarations for these apps to the end of `urls.py`. * Moved settings and environment variables for these apps to be located together. * Settings for these apps are now prefixed with `AI_CHAT_` or `AI_IMAGES_`, respectively. * **This also means that shared settings like `OPENAI_API_KEY` are now declared multiple times and need to be updated in multiple places.** See the “upgrading” section below on how to get around this duplication. * Moved chat JavaScript setup to the end of `module.exports` in `webpack.config.js`. * Depending on your configuration, the order of navigation tabs in the UI may change. * Made minor tweaks to how channels urls are set up. * Image logos used by the AI chat and images apps were moved to `/static../../assets/images/ai_../../assets/images/` and `/static../../assets/images/ai_../../assets/images/`, respectively. * The declaration for these apps has moved to a new “plugins” section of `pegasus-config.yml`. ### Other Changes [Section titled “Other Changes”](#other-changes-8) Other changes included in this release are below. **Changed** * **Upgraded default Python to Python 3.12.** * Bumped the Python version to 3.12 in CI, and dev/production Docker containers. * Also added [a `.python-version` file](https://docs.astral.sh/uv/concepts/python-versions/#python-version-files) for uv builds (set to 3.12) * **Upgraded default Node to 22.** * Bumped the Node version to 22 in CI, and dev/production Docker containers. * **Upgraded nearly all Python packages to their latest versions.** * Added a pin to `dj-stripe<2.9` because 2.9 is not yet supported. * **Upgraded nearly all JavaScript packages to their latest versions.** * Tailwind v4 was not upgraded as it was just released and is not yet supported. * **Ruff and pre-commit will now sort imports by default.** (See upgrade notes below) * **This also updates import sorting in a number of files.** * **Pre-commit now runs ruff with `--fix` enabled, which will automatically apply (but not stage) fixable errors.** * Dependencies are now sorted in `pyproject.toml` (uv builds) and `requirements.in` (pip-tools builds) * Added email address to admin search for team memberships and invitations. Thanks EJ for the suggestion! * Made the “timezone” field editable in the user admin. Thanks Peter for the suggestion! * Changed active tab variable for ai image app from “ai\_images” to “ai-images” to match convention of other apps. * Added a link from the user profile to manage email addresses if the user has more than one email registered. (Thanks Simon for the suggestion!) * Make it so that `./manage.py` commands default to `uv run` if you build with uv enabled. * The `chat_tags` template tag library was moved to the `web` app and renamed to `markdown_tags`, making it easier to use outside the chat application. **Fixed** * **Fixed an issue that caused Render deployments to fail when using uv.** (Thanks Jacob for reporting and helping fix!) * Add `psycopg2-binary` to production requirements if using sqlite, since it still required for production deployments. (Thanks Randall for reporting!) * Updated invitations to always store email addresses in lowercase to be consistent with account emails. Also fixed comparisons between invitations and sign up emails to be case-insensitive. (Thanks EJ for reporting and the fix!) * Renamed `tailwind.config.js` to `tailwind.config.cjs` which prevents build failures on Node 22. **Removed** * Removed no-longer-used `payments.js` and `stripe.sass` files. * Stopped including `pip-tools` in `dev-requirements` when using `uv`, as it is no longer needed. ### Upgrading [Section titled “Upgrading”](#upgrading-12) **Python / Node updates** You may need to manually modify your dev/production environment to upgrade to Python 3.12 and Node 22. If you’re using Docker, this should happen automatically by following the [upgrade process](/upgrading). Pegasus apps should still run on Python 3.11 / Node 20, but will no longer be extensively tested on those versions moving forwards. **Settings Changes** Some settings around AI API keys have been renamed and will need to be updated in your `settings.py` and `.env` files. If you are using AI chat and AI images with OpenAI, the easiest way to use a shared API key is to add the following to your `.env` / environment variables: ```plaintext OPENAI_API_KEY="sk-***" ``` And then modify your settings variables to read from that value: ```python # add an OPENAI_API_KEY setting, in case it was referenced elsewhere in your code OPENAI_API_KEY = env("OPENAI_API_KEY", default="") # modify the image/chat settings to use the same openai key instead of reading from new environment variables AI_IMAGES_OPENAI_API_KEY = OPENAI_API_KEY AI_CHAT_OPENAI_API_KEY = OPENAI_API_KEY ``` **Import Sorting Changes** If you have auto-formatting enabled you will likely get CI errors after upgrading due to the stricter import sorting. You can fix these by running a manual ruff check locally and then committing the result: ```plaintext ruff check --fix # or with uv uv run ruff check --fix ``` *Jan 27, 2025* Old Version? Release notes for updates before 2025 are available on the [Release Notes History](/release-notes-history/) page. # In Development Release Notes > Preview of upcoming features and changes in the next SaaS Pegasus release. In Development These release notes are for a version that is still in development and has not been released yet. Features and changes may be modified before the final release. To try these features early, see the [Early Access Program](/early-access/). *The development release notes are generated by AI and may contain errors.* *** There are not any new changes since the 2026.6 release. # Release Notes History > Historical release notes and changelog for older versions of SaaS Pegasus Django boilerplate. These are old releases. For the latest updates, see the [Release Notes](/release-notes/) page. ## Version 2024.12.1 [Section titled “Version 2024.12.1”](#version-2024121) This is a minor hotfix release for 2024.12 * **Fixed a bug where the delete workflow was broken for apps created by the Pegasus CLI on non-Tailwind builds.** This happened becasue the “css\_framework” cli option was accidentally missing from `pegasus-config.yml`. Thanks Robert for reporting! * Updated the README instructions for setting up pre-commit hooks when using uv. *Jan 13, 2025* ## Version 2024.12 [Section titled “Version 2024.12”](#version-202412) content/ This release adds first-class support for using uv as a complete replacement for development and production workflows (see below), and has a handful of fixes/changes. ### UV support! [Section titled “UV support!”](#uv-support) This release adds full support for [uv](https://docs.astral.sh/uv/) as a replacement package manager for your project. You can use uv by selecting the new “uv” as your “Python package manager” on your project settings page. When you select uv the following changes will be made: * All requirements.in / requirements.txt files are removed. * Your project requirements will now be listed in your `pyproject.toml` file. * Development and production dependencies will be listed under separate dependency-groups. * Your pinned project requirements will be listed in a new `uv.lock` file. * Docker containers (in development and production) will use `uv` to set up and manage the Python environment. * A `make uv` target will be added to Docker builds to run `uv` commands in your container. The main benefits of using uv are: * Speed. It is just way, way faster to anything related to package management. * Easier to setup and install Python. * Lock files (pinned versions) are consistent across any platform. * More tooling. * Speed. (It’s so fast we put it twice.) There will be a longer write up about uv released very soon, but in the meantime you can review the updated [python documentation](/python/setup) and new [uv documentation](/python/uv). The rest of the docs have been updated to accommodate uv, though it’s possible there are some places that were missed. If you spot any issues in the docs, get in touch! ### Other fixes [Section titled “Other fixes”](#other-fixes) * **Upgraded the pegasus cli to fix an issue where the generated list views were not properly scoped to the appropriate team / user.** If you used the CLI to generate any apps it’s highly recommended that you check that you are not exposing objects that should not be viewable. ### Other updates [Section titled “Other updates”](#other-updates) * **Changed the default set up of social logins to use settings-based configuration instead of `SocialApps` in the database.** See the upgrade notes if you are using social logins to prevent issues. Thanks Alex for the suggestion and for helping with the updated documentation! * Updated the default flowbite setup to disable the forms plugin. This was causing styling conflicts with the default DaisyUI styles on certain form elements, including checkboxes. * Re-formatted the default form input template for readability. ### Upgrading [Section titled “Upgrading”](#upgrading) To migrate an existing project to `uv` see [this guide](/cookbooks/#migrating-from-pip-tools-to-uv). If your application was already using social logins defined in the database, the new settings-based declaration will conflict and cause errors on social login. To fix this you can either delete the `APPS` section of the relevant service in `settings.SOCIALACCOUNT_PROVIDERS`, or you can move the credentials into your project environment (e.g. `.env`) and delete relevant the `SocialApp` from the Django admin. *November 29, 2024* ## Version 2024.11.3 [Section titled “Version 2024.11.3”](#version-2024113) This is a minor maintenance release with a few changes in preparation for adding `uv` support (coming soon!). ### Changed [Section titled “Changed”](#changed) * Pinned the version of `uv` used in CI and Dockerfiles. * Added `venv` and `.venv` directories to the `.dockerignore` file and `make translations` target. * The `make requirements` command now restarts containers in the background, making it easier to combine with other make targets. * Added a catch-all to the `Makefile` to prevent error messages when running `make npm-install ` and similar commands. * Updated README commands to consistently use `python manage.py` instead of just `./manage.py`. * Made some minor formatting changes to `pyproject.toml`. * Fixed the link to the multi-stage dockerfile docs in `Dockerfile.web` * Upgraded a number of Python packages. * Updated the `default_stages` of the `.pre-commit-config.yaml` file to the latest expected format (`pre-commit`). *Nov 21, 2024* ## Version 2024.11.2 [Section titled “Version 2024.11.2”](#version-2024112) This release adds the ability to disable dark mode on Tailwind, upgrades front end libraries, bumps the API client version, and has a handful of other small changes and fixes. ## Added [Section titled “Added”](#added) * **Added a new build option to disable dark mode for Tailwind builds.** (Thanks Arno for suggesting!) * Added basic user-facing error messages to the standalone front end sign up and login workflows. ## Changed [Section titled “Changed”](#changed-1) * **Upgraded all JavaScript dependencies.** * **Updated the API client to use the latest version 7.9.0, and updated the standalone front end to work with the latest changes.** * Updated template-partials installation to be manually loaded, to allow for easier integration with other templating systems like django-cotton. * Moved active tab highlighting to the base view in the example object demo. * Made a few very minor edits to comments and whitespace in a few places. ## Fixed [Section titled “Fixed”](#fixed) * Fixed a bug where your migrations and tests would fail if your project name was > 50 characters (thanks Bernard for reporting!). * Fixed a bug in the group chat demo where submitting an empty room name would take you to a 404 page. * The `docker_startup.sh` file is no longer included if you are not using a docker-based deploy platform. * Updated the `config/README` file which had outdated information that predated the migration to Kamal 2. (Thanks Arno for reporting!) * Improved comments in the kamal `secrets` file and `.env` files. (Thanks Arno for suggesting!) ## Removed [Section titled “Removed”](#removed) * The `.env` file is no longer included in zip downloads. This file was already removed from Github builds so this just makes the two consistent. Projects should create `.env` file from the `.env.example` file. * Removed the `migrate_customers_to_teams` management command. This was added for an upgrade two years ago, and is assumed to be no longer needed. *Nov 14 2024* ## Version 2024.11.1 [Section titled “Version 2024.11.1”](#version-2024111) This is a minor hotfix release. ### Fixed [Section titled “Fixed”](#fixed-1) * Fixed an issue where the team selector was accidentally transparent in Tailwind builds. * Removed shadcn template that was accidentally included even if shadcn was disabled. ### Updated [Section titled “Updated”](#updated) * Removed extra whitespace from `form_tags.py`. (Thanks Brennon for reporting!) * Updated `make help` to allow for commands defined in `custom.mk` with digits to also show up. (Thanks Arno for suggesting!) *Nov 4 2024* ## Version 2024.11 [Section titled “Version 2024.11”](#version-202411) This is a feature release with an emphasis on improving the Tailwind CSS experience with Pegasus. Watch the video below for a demo, or read on for the highlights. ### Dark mode improvements [Section titled “Dark mode improvements”](#dark-mode-improvements) A dark mode selector was added to the navigation, allowing users to easily toggle between light, dark, and “system default” mode. The user’s selection is preserved server-side in the session object, which also helps to prevent flickering across page loads. ### Better Theme Support [Section titled “Better Theme Support”](#better-theme-support) It’s now easier than ever to change your project’s theme. Each project now supports a default light and dark theme which will be used throughout the site. The default themes need only be changed in `tailwind.config.js`, and `settings.py` and everything else is taken care of. See the updated [tailwind theme documentation](/css/tailwind/#changing-your-themes) for more details. ### New shadcn integration and demo dashboard [Section titled “New shadcn integration and demo dashboard”](#new-shadcn-integration-and-demo-dashboard) A new build setting allows you to build your project with [shadcn/ui](https://ui.shadcn.com/) installed. Shadcn is a great and versatile component library for React and Tailwind, but it is difficult to integrate it into a Django project without building a separate front end. Now Pegasus takes care of that integration for you, and provides a reference dashboard implementation of how to work with the library. The reference dashboard is a hybrid single-page React app served by Django. It uses the same colors as the DaisyUI theme, and will update when you change your theme, and has many interactive components. However, it is not connected to any backend data—it is just a UI example. Read more in the [shadcn docs here](/css/tailwind/#shadcn). ### New flowbite integration and demo component page [Section titled “New flowbite integration and demo component page”](#new-flowbite-integration-and-demo-component-page) Another new build setting allows you to build your project with [flowbite](https://flowbite.com/) installed. Flowbite is another great component library for Tailwind and does *not* use React—making it a great fit for htmx projects. If you enable this setting, flowbite will automatically be installed and you can drop flowbite components into any Django template. The reference page has an example of a few of these components. Read more in the [flowbite docs here](/css/tailwind/#flowbite). ### Other updates [Section titled “Other updates”](#other-updates-1) * **Upgraded all Python packages to their latest versions.** * **[uv](https://docs.astral.sh/uv/) is now used to install Python packages in Docker files and Github actions.** * Also updated `make pip-compile` target to use `uv`. * This resulted in minor changes to all `requirements.txt` files. * **Team invitation pages now prompt a user to log in instead of sign up if the email is associated with a known account.** (Thanks Daniel for suggesting!) * Your configured Github username, if available, will be used in a few places instead of a default value. (Thanks Richard for suggesting!) * Added `bg-base-100` to the `` tag of the base template and removed it from other components where it was now redundant. This improves theming support when themes heavily modify the base color. (Tailwind builds only) * Added equals signs to `ENV` declarations in production Docker files, for consistency. (Thanks Denis for suggesting!) * Slightly improved the styling of the e-commerce app. * Overhauled the [Tailwind CSS documentation](/css/tailwind). **Updates to the CLI ([release notes](https://github.com/saaspegasus/pegasus-cli/releases))** * Fixed a bug on certain environments where the `pegasus` command conflicted with a local `pegasus` folder, causing import errors running the CLI. * Apps created with `startapp` now use a `POST` for deletion instead of a `GET`. * Deletion now includes a modal confirmation (Tailwind and Bulma builds only). ### Upgrading [Section titled “Upgrading”](#upgrading-1) If you’re using Docker the `make upgrade` command won’t work out-of-the-box due to the change in how requirements files are managed. You will first have to rebuild your containers with: ```bash make build ``` or ```bash docker compose build ``` After that, you should be able to run `make upgrade` as normal. *Nov 1, 2024* ## Version 2024.10 [Section titled “Version 2024.10”](#version-202410) This release upgrades Kamal deployment to Kamal 2 and dramatically simplifies the Kamal deployment process. ### Kamal 2 deployment and related changes [Section titled “Kamal 2 deployment and related changes”](#kamal-2-deployment-and-related-changes) In the upgrade to Kamal 2, the following changes were made: * Updated Kamal to run from the root project directory instead of the `deploy` subdirectory. * Also moved the config file was also moved from `deploy/config/deploy.yml` to `config/deploy.yml` * Moved environment secrets from `deploy/.env` to `.kamal/secrets` to match Kamal 2’s recommendation. * Kamal can now be installed and run with Docker without any additional workarounds [as described here](https://kamal-deploy.org/docs/installation/) The custom docker set up instructions have been removed. * Kamal is now run as root by default, which dramatically simplifies the server setup process. There is now no need to run any manual steps to set up your server. * Kamal now creates and manages its own docker network. * Traefik has been dropped in favor of `kamal-proxy` for the proxy server, as per the new Kamal defaults. * The `.gitignore` and `.dockerignore` files were updated to reflect the new structure. * Added `apps.web.middleware.healthchecks.HealthCheckMiddleware` to workaround Kamal health checks and Django security features, [as outlined here](https://github.com/basecamp/kamal/issues/992#issuecomment-2381122195). * Removed unnecessary media directory set up from `Dockerfile.web`. It is recommended to use an external storage service for media files and not the Docker container. In addition, there were a few changes that affect projects that aren’t using Kamal: * `apps.web.locale_middleware` was moved to `apps.web.middleware.locale` * `docker_startup.sh` was moved from the `deploy` folder to the project root. The [Kamal documentation](/deployment/kamal) has been updated to reflect these changes. ### Other fixes [Section titled “Other fixes”](#other-fixes-1) * **Subscriptions in a “past due” state are now treated as “active” for the purposes of feature gating and accessing the billing portal.** This is more consistent with [how Stripe treats subscriptions in this state](https://docs.stripe.com/api/subscriptions/object#subscription_object-status). (Thanks Luc for suggesting!) * Fixed a bug where several `make` targets mistakenly included a `--no-deps` flag which would fail if your database container was not running. (Thanks Gary for reporting!) * Fixed an issue where Stripe subscription webhooks weren’t properly handled if you were using the embedded Stripe pricing table. (Thanks Andrew for reporting!) * Fixed an issue introduced in 2024.9 where Stripe ecommerce webhooks weren’t always processed correctly. * Added a migration file to automatically work around [this dj-stripe issue](https://github.com/dj-stripe/dj-stripe/issues/2038) so that it wasn’t a manual process. *Oct 15, 2024* ## Version 2024.9.3 [Section titled “Version 2024.9.3”](#version-202493) This release is mainly [an update to the CLI](https://github.com/saaspegasus/pegasus-cli/releases/tag/v0.3): ### CLI updates [Section titled “CLI updates”](#cli-updates) * **You can now generate apps that work seamlessly with Pegasus teams** (will use `BaseTeamModel` and add the team slug and permissions checks to all urls and views). * The CLI now generates a default `admin.py` config for each data model. * User foreign keys now use `settings.AUTH_USER_MODEL` instead of being hardcoded to `apps.users.models.CustomUser`. ### Other changes [Section titled “Other changes”](#other-changes) * Fixed an issue where HTMX links without href tags weren’t showing a pointer cursor on some CSS frameworks. * Add default region to Redis and Postgres configurations in `render.yaml` to make it easier to find/replace them when changing your project’s region. (Thanks Jacob for suggesting!) *Sep 26, 2024* ## Version 2024.9.2 [Section titled “Version 2024.9.2”](#version-202492) This release fixes a bug that prevented the CLI from running on Windows machines. Thanks Jonathan for reporting! If you don’t want to upgrade you can just `pip install pegasus-cli==0.2.1` to apply the fix. *Sep 20, 2024* ## Version 2024.9.1 [Section titled “Version 2024.9.1”](#version-202491) This release fixes a few things in the 2024.9 release. * Updated the `bootstrap_ecommerce` management command to create `ProductConfiguration` objects for all active Products in Stripe. * Fixed an issue on the ecommerce homepage where a closing `
` tag was misplaced if a product didn’t have a default price set. *Sep 18, 2024* ## Version 2024.9 [Section titled “Version 2024.9”](#version-20249) There are two big updates in this release: 1. The Pegasus CLI, which allows you to instantly spin up new apps. 2. E-Commerce/Payments improvements. ### The Pegasus CLI [Section titled “The Pegasus CLI”](#the-pegasus-cli) The [Pegasus CLI](https://github.com/saaspegasus/pegasus-cli/) is a standalone command-line tool that allows you to instantly spin up new Django apps. You can specify as many data models as you want and it will generate a starting CRUD interface for each of them. Here’s a quick demo: **At the moment the CLI only supports HTMX build of Pegasus.** A React-based implementation is planned for a future date. Huge thanks to Peter for his excellent [Pegasus example apps](https://github.com/pcherna/pegasus-example-apps-v2) project which served as a reference for implementing the CRUD application and pagination. ### E-Commerce / Payments demo improvements [Section titled “E-Commerce / Payments demo improvements”](#e-commerce--payments-demo-improvements) This is a series of updates designed to make it easier to build a functional end-to-end application on top of the e-commerce demo. * Added a `ProductConfiguration` model to attach additional metadata to products. * E-Commerce product URLs and views now use the `ProductConfiguration` `slug` field instead of the Stripe Product IDs. * Added a `@product_required` decorator that can be used to restrict access to views based on whether the user has purchased a product. * Added a demo “access product” page that shows how to use the `@product_required` decorator. * Added `user_owns_product` and `get_valid_user_purchase` helper functions. * Improved the navigation and use of breadcrumbs in the demo UI. * **See upgrade notes for information about migrating previous data to the new set up.** See also: the updated [Payments docs](/payments). ### Other Changes [Section titled “Other Changes”](#other-changes-1) #### Added [Section titled “Added”](#added-1) * **Added `django-htmx` and `django-template-partials` as first-class dependencies to HTMX builds.** These libraries are used by the CLI and will be used for more HTMX-based functionality moving forwards. * Added `make manage` command to run arbitrary `manage.py` commands in a docker environment. E.g. `make manage ARGS='createsuperuser'`. * Added the ability to pass arguments to `make test` in docker. E.g. `make tests ARGS='apps.teams --keepdb'`. (Thanks David for the suggestion!) #### Changed [Section titled “Changed”](#changed-2) * Changed links on the tailwind signup page to use `pg-link` class instead of explict tailwind classes. (Thanks Peter for the suggestion!) * Silenced extraneous djstripe warnings when running tests. (Thanks Chris for the suggestion!) * Added `.vscode` and vs workspace files to the project `.gitignore`. * Switched from `assert` statements to `raise ValueError` in the e-commerce Stripe checkout confirmation view. * Moved some of the currency helper functions out of the `subscriptions` app into `utils.billing` so they can be used in ecommerce workflows even if subscriptions are disabled. * Set `PYTHONUNBUFFERED` and `PYTHONDONTWRITEBYTECODE` in docker compose file for python containers. (Thanks Richard for the suggestion!) * Upgraded Django to 5.1.1. #### Fixed [Section titled “Fixed”](#fixed-2) * Fixed a typo in the help text for the `bootstrap_ecommerce` command. * Fixed a bug where `user_teams` context processor could cause a crash if auth middeware didn’t run (for example, on a 500 error page in production). ### Upgrade Notes [Section titled “Upgrade Notes”](#upgrade-notes) If you have existing `Purchase` data in your application you will need to migrate it to the new `ProductConfiguration` structure. This is a three-step process: First you will need to apply the database updates, but allow `Purchase.product_configuration` to be null. Instead of running `./manage.py migrate` you will have to run the following command: ```bash ./manage.py migrate ecommerce 0002 ``` After running this, you can run the following command to migrate the existing data: ```bash ./manage.py migrate_ecommerce ``` The `migrate_ecommerce` management command will: 1. Create `ProductConfiguration` objects for all products in `settings.ACTIVE_ECOMMERCE_PRODUCT_IDS` 2. Create `ProductConfiguration` objects for all products referenced in existing `Purchase` models. 3. Set `purchase.product_configuration` to the new `ProductConfiguration` object for each `Purchase`. Finally, you can make the `Purchase.product_configuration` field non-null, by running: ```bash ./manage.py migrate ecommerce 0003 ``` **New projects, or projects without any existing purchase data can skip these steps and run `./manage.py migrate` directly.** However, you may still want to run `./manage.py migrate_ecommerce` to populate `ProductConfiguration` objects for your active products. *Sep 17, 2024* ## Version 2024.8.2 [Section titled “Version 2024.8.2”](#version-202482) This is a maintenance release that includes a number of mostly small fixes and updates, and updates Django to version 5.1. ### Fixed [Section titled “Fixed”](#fixed-3) * **Fixed a few styling issues on Bulma builds**: * Disabled dark mode. The styling for Dark mode was not fully supported by Bulma and led to strange-looking layouts. * Fixed an issue where the active tab wasn’t properly highlighted in certain cases on Bulma builds. * Fixed an issue with sqlite builds where the default `DATABASE_URL` would cause the DB to switch to Postgres. (Thanks Harry and Richard for reporting!) * Switched allauth from [Twitter](https://docs.allauth.org/en/latest/socialaccount/providers/twitter.html) (which seems no longer supported) to [Twitter Oauth2](https://docs.allauth.org/en/latest/socialaccount/providers/twitter_oauth2.html), which still works. (Thanks Bandi for reporting!) * Fixed an issue introduced in version 2024.8 which caused Heroku Docker deploys to fail. Heroku [does not support caching](https://stackoverflow.com/a/78901250/8207), so it has been removed from Heroku Docker builds. (Thanks Richard for reporting!) * Fixed a bug where the `team_nav_items.html` and `team_selector.html` templates could be accidentally included even if you built without teams. * Changed the (unused) `text-muted` css class to `pg-text-muted` in a handful of places on Tailwind builds. (Thanks Peter for reporting!) * Removed unused `AWS_S3_CUSTOM_DOMAIN` variable from `.env` files. ### Changed [Section titled “Changed”](#changed-3) * **Upgraded Django to version 5.1.** * Upgraded all Python packages to their latest versions. * Updated Pegasus color CSS variables to use the DaisyUI variables, so that they change when you change DaisyUI themes. (Thanks Peter for the suggestion!) * Removed `custom.mk` if your project was not generated with a `Makefile`. (Thanks Finbar for reporting!) * Removed “Containers started” message from `make start` command that never executed. (Thanks Richard for reporting!) * Better style inputs of type `time` and `datetime-local` in forms on all CSS frameworks. (Thanks Peter for reporting and fixing!) * Simplified Bulma navbar to use bulma native classes instead of custom CSS. (See upgrade note below.) * Updated default Github repo in `app-spec.yml` to use raw project slug instead of the hyphenated version. (Digital Ocean deployments, only, thanks Richard for suggesting) * Moved `SERVER_EMAIL` and `DEFAULT_FROM_EMAIL` from `settings_production.py` to main `settings.py` file, and made it possible to set them via the environment/`.env` file. * Added many more common settings and secrets to the Kamal `deploy.yml` file. ### Documentation [Section titled “Documentation”](#documentation) * Improved the documentation on [customizing the Material Bootstrap theme](/css/material). * Added documentation for [deploying multiple apps to the same VPS with Kamal](/deployment/kamal/#cookbooks). ### Upgrading [Section titled “Upgrading”](#upgrading-2) * Bulma builds may need to add the `is-tab` class to `navbar-items` in the top nav to mimic the updated navbar styling. *August 23, 2024* ## Version 2024.8.1 [Section titled “Version 2024.8.1”](#version-202481) This is a maintenance release which upgrades HTMX to version 2.0 and fixes a handful of minor bugs. ### Changed [Section titled “Changed”](#changed-4) * **Upgraded HTMX to [version 2.0](https://htmx.org/posts/2024-06-17-htmx-2-0-0-is-released/).** See upgrade note below. ### Fixed [Section titled “Fixed”](#fixed-4) * Fixed a bug on some environments where `make build-api-client` would wrong relative to the wrong directory. (Thanks Ben for finding and fixing!) * Downgraded Postgres from 16 to 14 on Digital Ocean deployments, due to [an issue with permissions on version 16](https://www.digitalocean.com/community/questions/how-can-i-create-a-postgres-16-user-that-has-permission-to-create-tables-on-an-app-platform-dev-database) that was causing new Digital Ocean deployments to fail. (Thanks Panagiotis for reporting!) * Switched the default celery pool to [solo](https://docs.celeryq.dev/en/stable/internals/reference/celery.concurrency.solo.html) in development, to fix issues running on Windows. See [updated docs](/celery). * Updated in-app help hint to recommend running `./manage.py bootstrap_ecommerce` instead of `./manage.py djstripe_sync_models price`. ### Upgrading [Section titled “Upgrading”](#upgrading-3) Htmx 2.0 requires loading new extensions. If you were loading HTMX extensions in your own templates, you will have to upgrade the location of those to the 2.0 versions. Before: ```html ``` After: ```html ``` *August 13, 2024* ## Version 2024.8 [Section titled “Version 2024.8”](#version-20248) This is a maintenance release with many small updates and fixes. ### Added [Section titled “Added”](#added-2) * **Added test cases for subscription decorators, feature gating, and views.** These can be extended/adapted to test custom subscription logic. Also added utility functions to create test products, subscriptions and mock requests. * Added a test that will fail if your project is missing any database migrations. [More on this concept here](https://adamj.eu/tech/2024/06/23/django-test-pending-migrations/). * **Added an example landing page to Tailwind builds, based largely on [Scriv’s landing page](https://scriv.ai/).** * Added `TURNSTILE_KEY` and `TURNSTILE_SECRET` to Kamal’s default secrets. * Added a section on configuring static files to the [production checklist](/deployment/production-checklist/#check-your-static-file-setup). ### Changed [Section titled “Changed”](#changed-5) * **Code is now automatically formatted for all projects.** The “Autoformat code” check box has been renamed to “Enable linting and formatting” and now only controls whether `ruff` and the pre-commit hooks are included in the project download. Projects that had already enabled auto-formatting are unaffected by this change. (See upgrade notes below.) * **The example landing pages are now used as the project’s landing page instead of being listed in the examples**. (Bulma and Tailwind builds only.) * **Team invitation emails are now better styled, matching the same format as account emails.** (Thanks EJ for the suggestion!) * The `EMAIL_BACKEND` setting is now configurable via an environment variable. Also, added a commented-out example of how to set email settings for a production email provider (Mailgun). * Apt and pip packages are now cached across Docker builds, which should result in faster build times after the first build. (Thanks Tobias for the suggestion!) * Improved the display format of “role” in the team invitation list. (thanks Andy for the suggestion!) * Change `user/` to `YOUR_GITHUB_USERNAME/` in the Digital Ocean `app-spec.yml` file to make it more obvious that it should be edited. (Thanks Stephen for suggesting!) * Changed the UI of social logins on the “sign in” page to match that of the “sign up” page on the Material Bootstrap theme. This makes the implementation more extensible and more consistent with other CSS frameworks. * **Upgraded all Python packages to the latest versions.** ### Fixed [Section titled “Fixed”](#fixed-5) * Fixed a bug where the formatting `make` targets were still calling `black` and `isort` instead of `ruff`. `make black` is now `make ruff-format` and `make isort` is now `make ruff-lint`. * Fixed a bug where the sign up view tests would fail in your environment if `settings.TURNSTILE_SECRET` was set. (Thanks Finbar for reporting!) * Fixed translations on the user profile form field names. * Removed `svg` as an option for profile picture uploads, to prevent the possibility of using it as an XSS attack vector. ([More info on this threat here](https://medium.com/@rdillon73/hacktrick-stored-xss-via-a-svg-image-3def20968d9)). * Disable debug toolbar in tests, which fixes test failures under certain conditions. * Bumped the Postgres version used by Digital Ocean deployments from 12 to 16. Digital Ocean has deprecated support for version 12. (Thanks Stephen for reporting!) * Simplified how the list of social login buttons is rendered, and make social login buttons work when configuring social applications in settings (previously buttons only showed up if you configured apps in the database). See upgrade note below. ### Removed [Section titled “Removed”](#removed-1) * Deleted the “sticky header” html and CSS code that was only used on the example landing pages. ### Upgrade Notes [Section titled “Upgrade Notes”](#upgrade-notes-1) * If you had **not** been using auto-formatting until now, you should first follow the instructions for [migrating to auto-formatted code](/cookbooks/#migrating-to-auto-formatted-code) prior to upgrading to this release. Otherwise you will likely get a lot of formatting-related merge conflicts when trying to upgrade. * If you already enabled auto-formatting (most projects), you don’t need to do anything. * If you had previously configured allauth social applications in the database *and* in your settings file, you may see a duplicate “Login with XXX” button on the sign up and login pages. To fix this, remove the social application from either your settings or the database. *August, 7, 2024* ## Version 2024.6.1 [Section titled “Version 2024.6.1”](#version-202461) This is hotfix release that addresses a few issues from yesterday’s update: * Fix app styles accidentally being purged during the Docker build process. This caused styling on Docker-based deployments for tailwind builds. (Thanks Steve for reporting!) * Moved channels url import to after Django initialization. This fixes an `AppRegistryNotReady` error when deploying asynchronous apps with the AI chat app enabled. (Thanks Roman for reporting!) * Don’t create the periodic task to sync subscriptions unless per-unit billing is enabled. *June 6, 2024* ## Version 2024.6 [Section titled “Version 2024.6”](#version-20246) This is a feature release with a few big updates and a lot of smaller ones. ### AI model changes [Section titled “AI model changes”](#ai-model-changes) The library used for non-OpenAI LLMs has been changed from [`llm`](https://github.com/simonw/llm) to [`litellm`](https://docs.litellm.ai/docs/). Reasons for this change include: * It has far fewer additional dependencies. * It supports async APIs out of the box (for most models). * The `llm` library is more targeted for the command line use-case, whereas `litellm` offers similar functionality as a native Python library with a cleaner API. Litellm can still be used with all common AI models, including OpenAI, Anthropic/Claude, and local models (via ollama). For details on getting started with `litellm` see the updated [AI documentation](/ai/llms). ### Formatting and linting now use Ruff [Section titled “Formatting and linting now use Ruff”](#formatting-and-linting-now-use-ruff) Black and isort have been replaced with [ruff](https://github.com/astral-sh/ruff)—a Python linter/formatter that offers the same functionality as those tools but is much faster. Additionally, Pegasus will now remove unused imports from your files automatically, both when building your project and if you have set up `pre-commit`. This change should be a relatively seamless drop-in replacement, though you may see some new lint errors in your projects which you can choose to address. ### Spam prevention updates [Section titled “Spam prevention updates”](#spam-prevention-updates) There has been a dramatic increase in spam-bots over the last month. Many of these bots target seemingly-innocuous functionality like sign up and password reset forms. This version includes a few updates to help combat these bots. First, you can now easily add [Cloudflare turnstile](https://www.cloudflare.com/products/turnstile/) to your sign up forms, which will present the user with a captcha and should help reduce bot sign-ups. See [the turnstile documentation](/configuration/#turnstile) for information on setting this up. Additionally, the `ACCOUNT_EMAIL_UNKNOWN_ACCOUNTS` setting has been set to `False` by default. This prevents “forgot password” and “magic link” emails from being sent out to unknown accounts. It should also help reduce unnecessary email sending. Finally, the [admin dashboard](#admin-dashboard) no longer shows users with unconfirmed email addresses if you have set `ACCOUNT_EMAIL_VERIFICATION = 'mandatory'`. This helps filter out likely bots from the report to provide clearer visibilty of people actually signing up for your app. ### Complete changelog [Section titled “Complete changelog”](#complete-changelog) Below is the complete set of changes in this release. #### Added [Section titled “Added”](#added-3) * **Added configurable captcha support on sign up pages, using [Cloudflare turnstile](https://www.cloudflare.com/products/turnstile/).** See [the turnstile documentation](/configuration/#turnstile) for more information on setting this up. (Thanks Troy, Jacob, Robert and others for suggesting.) * Added API views for two-factor authentication, and to change the logged-in user’s password. (Thanks Finbar for suggesting!) * Add UI to tell users they need a verified email address prior to setting up two-factor auth. * Also added a `has_verified_email` helper class to the `CustomUser` model. * Added tests for the delete team view for both team admins and members. (HTMX builds only) * Added test for team member removal permissions. * Add display and sort on the number of active members in the teams admin. #### Fixed [Section titled “Fixed”](#fixed-6) * Fixed a bug where team names longer than 50 characters could cause a crash during sign up. * Fixed a bug where multi-factor authentication QR codes had a dark background when dark mode was enabled (Tailwind builds only). (Thanks Artem for reporting!) * Fixed a bug where it was possible to bypass two-factor-authentication when using the API authentication views. (Thanks Finbar for reporting and helping with the fix!) * Fixed a bug where deleting the user’s only team while impersonating them resulted in a temporary crash. (Thanks EJ for reporting!) * Fixed a bug where creating an API key crashed if your user’s first + last name combined to more than 40 characters. (Thanks Luc for reporting!) * Improved the UI feedback when LLMs fail (e.g. if your API key is wrong or ollama is not running). * Removed the `static/css` and `static/js` directories from the `.dockerignore` file so that other project files can be included in these directories. Also updated the production Docker build process so that any existing files are overwritten by the built versions. (Thanks Raul for reporting!) * Made some performance improvements to the production Dockerfile build (don’t rebuild the front end if there are no changes in the dependent files). * Better support trialing subscriptions with no payment methods. The subscription UI will now show the date the trial ends and won’t log errors about missing invoices. (Thanks Jarrett for reporting!) #### Changed [Section titled “Changed”](#changed-6) * **Upgraded all Python packages to the latest versions.** * **Upgraded all JavaScript packages to the latest versions.** * **Non-OpenAI builds now use `litellm` instead of `llm`.** See above. (Thanks Sarthak for the suggestion!) * **Changed the formatter/linter from `black` and `isort` to [ruff](https://github.com/astral-sh/ruff).** See above. * Also addressed a handful of minor linting errors that came up as a result of this change. * Codebase linting is now substantially faster. * Unused imports are now automatically removed when building your projects. * **Celerybeat now uses the `django-celery-beat` library to store tasks in the database instead of on the filesystem.** This improves support for celerybeat on Docker-based platforms. (Thanks Peter and Artem for the suggestion!) * Also added a migration to save the default scheduled tasks in the database. * The login API response has changed, to allow for two-factor auth prompts, and more machine-readable status fields. * Removed the no-longer-used `use_json_field=True` argument from wagtail `StreamField`s. * The admin dashboard no longer shows users with unconfirmed email addresses if you have set `ACCOUNT_EMAIL_VERIFICATION = 'mandatory'`. * The admin dashboard now includes sign ups from the current date, by default. * Changed behavior when team role checks fail from raising a `TeamPermissionError` to returning a 403 response, and updated affected tests. One side effect of this is that the stack traces are removed from successful test runs. * Secret keys should no longer change every time you build your Pegasus project. They are also now clearly prefixed with `django-insecure-` to indicate that they should be changed in production. * Updated the default OpenAI chat model to gpt-4o. * Upgraded the openapi client generator to version 7.5.0 and also pinned the version used by `make build-api-client` to the same one. * Team IDs are now optional on the create team page (HTMX builds only). * Add clearer error message when charts are broken due to api config issue. (Thanks Yngve for reporting!) * Added `assume_scheme="https"` to form `URLField`s to be compatible with Django 6 behavior. * Added `FORMS_URLFIELD_ASSUME_HTTPS = True` to be compatible with Django 6 behavior. * Set `ACCOUNT_EMAIL_UNKNOWN_ACCOUNTS = False` by default, so that “forgot password” emails do not get sent to unknown accounts. This can help prevent spam bots. #### Removed [Section titled “Removed”](#removed-2) * Removed `black` and `isort` from dev-requirements, since they have been replaced by `ruff`. * Removed `llm` library and associated code, since it has been replaced by `litellm`. * Removed no longer used `TeamPermissionError` class. #### Standalone front end [Section titled “Standalone front end”](#standalone-front-end) The following changes affect the experimental [standalone front end](/experimental/react-front-end): * **The standalone React front end now supports two-factor-authentication.** * Improve the UI when you have login issues in the standalone React front end. *June 5, 2024* ## Version 2024.5.3 [Section titled “Version 2024.5.3”](#version-202453) This is a hotfix release that fixes a bug where the landing and dashboard page image was accidentally removed if you built without the examples enabled. *May 21, 2024* ## Version 2024.5.2 [Section titled “Version 2024.5.2”](#version-202452) This is a hotfix release that fixes a bug that prevented the team management page from loading in certain browsers if you built with a React front end and with translations enabled. Thanks Finbar for reporting! * Added `defer` keyword to various bundle scripts so they are loaded after the JavaScript translation catalog. * Updated references to `SiteJS` to run on the `DOMContentLoaded` event to allow for usage of the `defer` tag. *May 16, 2024* ## Version 2024.5.1 [Section titled “Version 2024.5.1”](#version-202451) This is a hotfix release that fixes issues running the [experimental React frontend](/experimental/react-front-end) in Docker. Thanks Mohamed for reporting this! * Fix `api-client` path in the frontend docker container and add to `optimizeDeps` in vite config. * Mount `node_modules` as an anonymous volume in the frontend docker container, so it is not overwritten. * Automatically create `./frontend/.env` when running `make init` if it doesn’t exist. *May 14, 2024* ## Version 2024.5 [Section titled “Version 2024.5”](#version-20245) This is a major release with several big updates. Here are a few highlights: ### New AI models [Section titled “New AI models”](#new-ai-models) In addition to using OpenAI chat models, you can now build the Pegasus AI chat applicaiton with the [`llm` library](https://github.com/simonw/llm). This lets you run the chat application against any supported model—including the Anthropic family (Claude 3), and local models like Llama 3. Additionally, the image generation demo now supports Dall-E-3 and Stable Diffusion 3. For complete details, see the new [AI documentation](/ai/images). ### Health Checks [Section titled “Health Checks”](#health-checks) A new setting allows you to turn on health checks for your application, powered by [django-health-check](https://django-health-check.readthedocs.io/en/latest/). This will create an endpoint (at `/health` by default) that pings your database, Redis instance, and Celery workers and returns a non-200 response code if there are any identified issues. These endpoints can be connected to a monitoring tool like [StatusCake](https://www.statuscake.com/) or [Uptime Robot](https://uptimerobot.com/) so that you can be alerted whenever your site is having issues. See the section on [monitoring](/deployment/production-checklist/#set-up-monitoring) in the production checklist for more information. ### Allauth updates [Section titled “Allauth updates”](#allauth-updates) The [django-allauth](https://docs.allauth.org/en/latest/) library was updated to the latest version, which enabled several useful changes. The first is a “sign in by email code” option which can be used in addition to the standard username/password and social option. Users can request a code be sent to their registered email and can then use that to login. See [the magic code documentation](/configuration/#enabling-sign-in-by-email-code) to enable/disable this. The second is using the recent [multi-factor authentication](https://docs.allauth.org/en/latest/mfa/index.html) support added to allauth in favor of the third-party `django-allauth-2fa` library. This reduces dependencies and puts all of authentication functionality on a standard path moving forwards. The complete release notes are below: ### Added [Section titled “Added”](#added-4) * **Added an optional health check endpoint at /health/.** (see above for details) * **Added an option to connect the chatbot to other LLMs**. (see above for details) * **The AI image generation now supports Dall-E 3 and Stability AI.** * **All generated projects now include a `LICENSE.md` file.** The goal of the license file is not to change how Pegasus can be used in any way, but rather to document those terms in the codebase itself (previously they were only documented on the [terms page](https://www.saaspegasus.com/terms/)). For more information you can see the new [license page](https://www.saaspegasus.com/license/). * **Added support for “magic-code login”, where a user can login to the site by requesting a code to their email address.** [Documentation.](/configuration/#enabling-sign-in-by-email-code) * **Google cloud run builds now support Redis.** For details, see the [updated documentation](/deployment/google-cloud). (Thanks Forrest for suggesting!) * Added a `custom.mk` file where you can add additional `make` targets without worrying about future Pegasus upgrades. (Thanks John for proposing this!) ### Changed [Section titled “Changed”](#changed-7) * Upgraded allauth to the latest version (0.62.1). * **Migrated two-factor authentication from the third-party `django-allauth-2fa` to the `django-allauth` built-in implementation.** See upgrade notes below for migrating existing projects. * Refactored how many allauth views work to be compatible with their new template override system. * **Bootstrap and Bulma builds: Move sidebar navigation into the mobile menu instead of having it take up the top of the screen on mobile screens**, similar to how things already worked on Tailwind and Material. (Thanks Luc for the nudge!) * This includes splitting out the menu items into their own sub-template files so they can be included in both menus. * Inline buttons are now spaced using the `gap` CSS property instead of the `pg-ml` class on individual buttons. * `Alpine.start()` is now called on `DOMContentLoaded` loaded event instead of using `window.load`. This makes Alpine-powered UIs more responsive, especially when used on pages with lots of images. * **Updated external JavaScript imports to use [the `defer` keyword](https://www.w3schools.com/tags/att_script_defer.asp) for slightly better page load performance.** (See upgrade note.) * Also updated inline JavaScript code in a handful of places to be compatible with deferred scripts. * Added a Github logo to connected Github accounts on profile page. * **The AI image demo and code has been moved to a first-class Pegasus application / tab.** * Update the docker container registry used by Google Cloud to reflect the latest version in Google. Also push more Google Cloud configuration variables out of the Makefile and into the environment variables. (Thanks Erwin for reporting!) * Added additional `.env` files to `.dockerignore` for Google Cloud builds. * Bumped django to the latest `5.0.6` release. ### Fixed [Section titled “Fixed”](#fixed-7) * **SQLite build now properly parse `DATABASE_URL` if it is set. This fixes issues deploying to certain platforms when building with SQLite.** (Thanks Manasvini for reporting!) * Updated allauth documentation links in the README to point to the new [allauth docs site](https://docs.allauth.org/). (Thanks Shantu for reporting!) ### Removed [Section titled “Removed”](#removed-3) * Removed several no-longer-needed allauth template files. * Removed deprecated “version” field from the dev `docker-compose.yml` file. (Thanks Moyi for reporting!) * Removed no-longer-used `pg-ml` css spacing class. * Removed redundant type=“text/javascript” declarations from a few ` {% endblock page_js %} ``` If you want to add the chat widget to all pages in your app, you can add it to the `base.html` file. For all logged-in pages, you can add it to the `app_base.html` file. ## Using Agents [Section titled “Using Agents”](#using-agents) As of version 2025.9.1, Pegasus includes a set of example agents that you can use as a foundation for building your own agents. These agents are built with [Pydantic AI](https://docs.pydantic-ai.com/), and include: * A weather and location lookup agent, with tools to do geo-lookups and access current weather information. * A chatbot to interact with employee application data models, with tools to work with employee data. * A chatbot to interact with system database, with MCP tool to access postgres data. * A tool to send emails. For more information on these agents and how they work, you can watch this video: ### Setting the agent model and keys [Section titled “Setting the agent model and keys”](#setting-the-agent-model-and-keys) Agents use the same `DEFAULT_AI_MODEL` setting as the chat UI. See [Configuring your AI model](#configuring-your-ai-model) above for details. ## Previous versions (OpenAI / LiteLLM) [Section titled “Previous versions (OpenAI / LiteLLM)”](#previous-versions-openai--litellm) Previous versions of Pegasus used LiteLLM and had different configuration options including `LLM_MODELS`, `DEFAULT_LLM_MODEL`, and `DEFAULT_AGENT_MODEL`. As of version 2026.2.1, all AI calls go through Pydantic AI exclusively. For documentation on the previous setup, see [the older version of this page](https://github.com/saaspegasus/pegasus-docs/blob/f70c522ec14f469bfce42554f862d82824834289/src/content/docs/ai/llms.mdx). # Using Digital Ocean Spaces for Django Media (in addition to AWS services) > Configure Digital Ocean Spaces for Django media storage while using AWS SES for email, with detailed deployment settings for django-storages. Community Guide The following guide was contributed by Neil Bartlett and Finbar, members of the Pegasus community. Any questions or issues using it should be directed to the #deployment channel of the community Slack. This guide documents how to use a different media storage (in this case, Digital Ocean Spaces), while still using some Amazon services (in this case, SES for email), deployed to Digital Ocean App Platform. The main issue/insight is that `django-storages` allows for [different settings/environment variables](https://django-storages.readthedocs.io/en/latest/backends/amazon-S3.html#authentication-settings), e.g. `AWS_S3_ACCESS_KEY_ID` vs `AWS_ACCESS_KEY_ID` or `AWS_S3_SECRET_ACCESS_KEY` vs `AWS_SECRET_ACCESS_KEY`. This means you can use a different AWS key for S3, SES, or any other service. Many of the steps would be the same when deploying to other platforms, but some of the details around where to put variables or access a shell/console would be different. Here’s a detailed walkthrough: [This post](https://testdriven.io/blog/django-digitalocean-spaces/) is useful but contains a lot of errors. So read it to get an idea of the process, but don’t follow it exactly. First [use `s3cmd`](https://docs.digitalocean.com/products/spaces/reference/s3cmd-usage/) to make sure that Spaces is correctly setup. Once you can see your buckets from `s3cmd`, then you have correctly set up the space and the access keys. BUT note to do the above you need an access key with All Permissions set. This is probably overkill for the access key for running the app — but was needed to configure `s3cmd`. Next, setup all the following in the `app-spec.yaml`. This feels like over spec’ing but I found all settings are necessary. Replace REPLACEME-XXXXXXX, the -aws-region, the-digital-ocean-region, my-bucket-name with your settings. Note AWS\_S3\_ENDPOINT\_URL could be written using `app-spec.yaml` reference syntax but I wanted to be sure so it is explicit. ```yaml - key: AWS_DEFAULT_REGION scope: RUN_TIME value: the-aws-region - key: AWS_ACCESS_KEY_ID scope: RUN_TIME value: REPLACEME-XXXXXXXX - key: AWS_SECRET_ACCESS_KEY scope: RUN_TIME value: REPLACEME-XXXXXXXX - key: SERVER_EMAIL scope: RUN_TIME value: noreply@example.com - key: DEFAULT_FROM_EMAIL scope: RUN_TIME value: info@example.com - key: EMAIL_BACKEND scope: RUN_TIME value: anymail.backends.amazon_ses.EmailBackend - key: AWS_S3_REGION_NAME scope: RUN_TIME value: the-digital-ocean-region - key: AWS_S3_ACCESS_KEY_ID scope: RUN_TIME value: REPLACEME-XXXXXXXX - key: AWS_S3_SECRET_ACCESS_KEY scope: RUN_TIME value: REPLACEME-XXXXXXXX - key: AWS_STORAGE_BUCKET_NAME scope: RUN_TIME value: my-bucket-name - key: AWS_S3_ENDPOINT_URL scope: RUN_TIME value: https://the-digital-ocean-region.digitaloceanspaces.com - key: USE_S3_MEDIA scope: RUN_TIME value: "true" - key: PUBLIC_MEDIA_LOCATION scope: RUN_TIME value: media - key: MEDIA_URL scope: RUN_TIME value: https://my-bucket-name.the-digital-ocean-region.digitaloceanspaces.com/media ``` This will redeploy the app. Then, in the digital ocean app platform console run: ```bash env | grep AWS ``` This should give the same settings as in the app-spec. The pure settings alone did not work: I had to remove the `USE_S3_MEDIA` code from `settings.py` and in `setting_production.py` add the equivalent but using the S3 variants of the environment variables. I could have just edited the stuff in `settings.py`. Part of the issue is that the precedence in the `django-storages` has internal variables take precedence over env variables, so if there are internal variables being used they will override the `app-spec.yaml` settings. Also note that `AWS_S3_ADDRESSING_STYLE` is probably important to override. I could not get it to work without being explicit about this. I prob should have added this to `app-spec.yaml`. ```python USE_S3_MEDIA = env.bool("USE_S3_MEDIA", default=False) if USE_S3_MEDIA: # We are assuming the app-spec.yaml or the .env file has set the production values # But seems we need to pull in some here # Media file storage in S3 # Using this will require configuration of the S3 bucket AWS_STORAGE_BUCKET_NAME = env("AWS_STORAGE_BUCKET_NAME") AWS_S3_ENDPOINT_URL = env("AWS_S3_ENDPOINT_URL") AWS_S3_REGION_NAME = env("AWS_S3_REGION_NAME") AWS_S3_ADDRESSING_STYLE = env("AWS_S3_ADDRESSING_STYLE", default="path") AWS_S3_USE_SSL=True PUBLIC_MEDIA_LOCATION = "media" STORAGES["default"] = { "BACKEND": "apps.web.storage_backends.PublicMediaStorage", } ``` Run the shell from the Digital Ocean console. Run `python3 manage.py shell` and import the settings and make sure there not any settings that are taking prceedence over the `app-spec.yaml` that you are not expecting: ```python from .settings import * print(AWS_ACCESS_KEY_ID) print(AWS_S3_ACCESS_KEY_ID) print(AWS_SECRET_ACCESS_KEY) print(AWS_S3_SECRET_ACCESS_KEY) print(AWS_DEFAULT_REGION) print(AWS_S3_REGION) ``` This is just the “starter” list. If things are not working add more from the `app-spec.yaml` list. Run a test directly from the django shell: ```python from storages.backends.s3boto3 import S3Boto3Storage from io import BytesIO import logging logging.basicConfig(level=logging.DEBUG) logging.getLogger('botocore').setLevel(logging.DEBUG) storage = S3Boto3Storage() print("Bucket:", storage.bucket) print("Endpoint:", storage.connection) test_file = BytesIO(b"Hello, DigitalOcean!") test_file_name = "test_upload.txt" storage.save(test_file_name, test_file) ``` If this works then things are set up. **Targetted debugging** In the django shell. You can use with to target override the default settings. This is very handy to pin things down. ```python from django.test.utils import override_settings from io import BytesIO with override_settings( AWS_STORAGE_BUCKET_NAME='penalty-mentor-spaces', AWS_S3_ENDPOINT_URL='https://tor1.digitaloceanspaces.com', AWS_S3_REGION_NAME='tor1', AWS_S3_ADDRESSING_STYLE='path', AWS_DEFAULT_ACL='public-read', AWS_S3_USE_SSL=True, ): from storages.backends.s3boto3 import S3Boto3Storage storage = S3Boto3Storage() print("Bucket Name:", storage.bucket) print("Endpoint URL:", storage.connection) test_file = BytesIO(b"Hello, DigitalOcean!") test_file_name = "test_upload.txt" storage.save(test_file_name, test_file) Finally use the actual class that Pegasus is using from apps.web.storage_backends import PublicMediaStorage storage = PublicMediaStorage() print("Bucket Name:", storage.bucket) print("Endpoint URL:", storage.connection) test_file = BytesIO(b"Hello, DigitalOcean!") test_file_name = "test_upload.txt" storage.save(test_file_name, test_file) Note also for debugging useful to set import logging logging.basicConfig(level=logging.DEBUG) logging.getLogger('botocore').setLevel(logging.DEBUG) ``` either in `settings_production.py` or the django shell. # Automating Deployment to Cloud Run using GitHub Actions > Automate Pegasus deployment to Google Cloud Run using GitHub Actions with service accounts, Artifact Registry, and CI/CD pipelines. Community Guide The following guide was contributed by Daan Vielen, a member of the Pegasus community. Any questions or issues using it should be directed to the #deployment channel of the community Slack. This guide walks you through setting up a GitHub Action to automatically deploy your Django application to Google Cloud Run when the tests pass on the `main` branch. ## What You’ll Do: [Section titled “What You’ll Do:”](#what-youll-do) 1. Create two Google Cloud service accounts with appropriate permissions: one for deployments and one for running the application. 2. Store GCP credentials and configuration in GitHub Secrets. 3. Write the deployment workflow that triggers upon successful completion of the test workflow. 4. Use Artifact Registry for storing your Docker images. 5. Clearly distinguish between the deployment service account (DEPLOY\_SERVICE\_ACCOUNT) and the service account used to run the Cloud Run application (SERVICE\_ACCOUNT). This distinction ensures security by limiting each service account’s access to only the necessary resources. *** ## **Step 1: Create the Google Cloud Service Accounts** [Section titled “Step 1: Create the Google Cloud Service Accounts”](#step-1-create-the-google-cloud-service-accounts) ### **1.1 Set Up Your Google Cloud Project** [Section titled “1.1 Set Up Your Google Cloud Project”](#11-set-up-your-google-cloud-project) First, set an environment variable for your Google Cloud project ID and deployment service account email. This ensures that all the commands referencing these values are consistent and reusable. ```bash # Set your project ID (replace "replace-with-your-project-id" with your actual project ID) export PROJECT_ID="replace-with-your-project-id" # Set the deployment service account email and the service account email that will run the Cloud Run application export DEPLOY_SERVICE_NAME="cloud-run-deployer" export DEPLOY_SERVICE_ACCOUNT="${DEPLOY_SERVICE_NAME}@${PROJECT_ID}.iam.gserviceaccount.com" # Set the service account that will run the Cloud Run application export SERVICE_ACCOUNT="cloud-run-app@$PROJECT_ID.iam.gserviceaccount.com""cloud-run-deployer@$PROJECT_ID.iam.gserviceaccount.com" ``` ### **1.2 Create the Deployment Service Account** [Section titled “1.2 Create the Deployment Service Account”](#12-create-the-deployment-service-account) We will create a **separate deployment service account** (DEPLOY\_SERVICE\_ACCOUNT) for the following reasons: 1. **Security**: By using a dedicated service account for deployments, you can limit its permissions to only what’s necessary for deploying the application and avoid granting unnecessary access to other resources. 2. **Impersonation**: The deployment service account (used by GitHub Actions) needs to impersonate another service account that is used to **run** the Cloud Run application. This allows it to delegate specific tasks (like database access, secret access, etc.) to that service account. Run the following command to create the **deployment service account** (DEPLOY\_SERVICE\_ACCOUNT): ```bash # Create the deployment service account gcloud iam service-accounts create $DEPLOY_SERVICE_NAME \ --description="Service account for deploying to Cloud Run" \ --display-name="Cloud Run Deployer" \ --project="$PROJECT_ID" ``` ### **1.3 Grant Permissions to the Deployment Service Account** [Section titled “1.3 Grant Permissions to the Deployment Service Account”](#13-grant-permissions-to-the-deployment-service-account) The deployment service account (used by GitHub Actions to deploy the Cloud Run service) needs permissions to deploy applications and impersonate the service account that runs your Cloud Run application. Grant the following roles to the **deployment service account** (DEPLOY\_SERVICE\_ACCOUNT): ```bash # Grant Cloud Run Admin role (allows deploying to Cloud Run) gcloud projects add-iam-policy-binding "$PROJECT_ID" \ --member="serviceAccount:$DEPLOY_SERVICE_ACCOUNT" \ --role="roles/run.admin" # Grant Storage Admin role (to push Docker images to Artifact Registry) gcloud projects add-iam-policy-binding "$PROJECT_ID" \ --member="serviceAccount:$DEPLOY_SERVICE_ACCOUNT" \ --role="roles/artifactregistry.repoAdmin" # Grant Service Account User role (to allow the deployment service account to impersonate the service account that runs the Cloud Run application) # Only for deploying cloud run gcloud iam service-accounts add-iam-policy-binding $SERVICE_ACCOUNT \ --member="serviceAccount:$DEPLOY_SERVICE_ACCOUNT" \ --role="roles/iam.serviceAccountUser" \ --project="$PROJECT_ID" ``` In the above command, replace `$SERVICE_ACCOUNT` with the service account that will **run** the Cloud Run application (more details on this later). ### **1.4 Create and Download the Deployment Service Account Key** [Section titled “1.4 Create and Download the Deployment Service Account Key”](#14-create-and-download-the-deployment-service-account-key) Now generate the deployment service account key, which you will upload to GitHub as a secret. ```bash # Create and download the service account key gcloud iam service-accounts keys create ~/cloud-run-deployer-key.json \ --iam-account=$DEPLOY_SERVICE_ACCOUNT \ --project="$PROJECT_ID" ``` This will generate a JSON key file (cloud-run-deployer-key.json). Save this file as you will need it to authenticate GitHub Actions with Google Cloud. *** ## **Step 2: Store GCP Credentials in GitHub Secrets** [Section titled “Step 2: Store GCP Credentials in GitHub Secrets”](#step-2-store-gcp-credentials-in-github-secrets) 1. Go to your GitHub repository. 2. Navigate to **Settings** > **Secrets and variables** > **Actions**. 3. Click **New repository secret** and add the following secrets: * **GCP\_DEPLOY\_SA\_KEY**: The content of your cloud-run-deployer-key.json file. Copy the entire contents of the JSON file. * **DEPLOY\_SERVICE\_ACCOUNT**: The service account that will **deploy the Cloud Run application**. * **GCP\_PROJECT**: Your Google Cloud project ID (you already set this as `$PROJECT_ID` in the terminal). * **CLOUDRUN\_NAME**: The name of your Cloud Run service. * **IMAGE\_URL**: The URL for your container image. It typically follows the format: REGION-docker.pkg.dev/\[PROJECT\_ID]/\[REPOSITORY]/\[IMAGE\_NAME]:latest. * **REGION**: The Google Cloud region where your Cloud Run service is deployed (e.g., us-central1). * **DATABASE\_ADDRESS**: The Cloud SQL instance connection name (format: project:region:instance). * **APPLICATION\_SETTINGS**: Key reference for secrets manager (format: application\_settings:latest). * **SERVICE\_ACCOUNT**: The service account that will **run the Cloud Run application**. *** ## **Step 3: Create the deploy.yml File** [Section titled “Step 3: Create the deploy.yml File”](#step-3-create-the-deployyml-file) Now that the test workflow is in place and runs successfully, we’ll create a deployment workflow that triggers when the tests pass. In this deployment workflow, we will use the `DEPLOY_SERVICE_ACCOUNT` environment variable in both shell commands and GitHub Actions. 1. In your GitHub repository, create the following directory structure: ```bash .github/ workflows/ ``` 2. Inside the workflows folder, create a file named deploy.yml and add the following content: ```yaml name: Deploy to Cloud Run on: workflow_run: workflows: ["Run Django Tests"] types: [completed] branches: [main] jobs: on-success: if: ${{ github.event.workflow_run.conclusion == 'success' }} runs-on: ubuntu-latest steps: - name: Checkout code uses: actions/checkout@v3 - name: Authenticate with Google Cloud uses: google-github-actions/auth@v1 with: credentials_json: ${{ secrets.GCP_DEPLOY_SA_KEY }} - name: Configure Google Cloud run: | gcloud config set project ${{ secrets.GCP_PROJECT }} gcloud config set run/region ${{ secrets.REGION }} gcloud config set account ${{ secrets.DEPLOY_SERVICE_ACCOUNT }} - name: Authenticate Docker to push images to Artifact Registry run: | gcloud auth configure-docker ${{ secrets.REGION }}-docker.pkg.dev --quiet - name: Build Docker image run: | docker build -t ${{ secrets.IMAGE_URL }} . -f Dockerfile.web --platform linux/amd64 - name: Push Docker image to Artifact Registry run: | docker push ${{ secrets.IMAGE_URL }} - name: Deploy to Cloud Run run: | gcloud run deploy ${{ secrets.CLOUDRUN_NAME }} \ --region ${{ secrets.REGION }} \ --update-env-vars DJANGO_SETTINGS_MODULE=agenda.settings_production \ --image ${{ secrets.IMAGE_URL }} \ --set-cloudsql-instances ${{ secrets.DATABASE_ADDRESS }} \ --set-secrets APPLICATION_SETTINGS=${{ secrets.APPLICATION_SETTINGS }} \ --service-account ${{ secrets.SERVICE_ACCOUNT }} \ --allow-unauthenticated ``` *** ## **Step 4: Test the Deployment Workflow** [Section titled “Step 4: Test the Deployment Workflow”](#step-4-test-the-deployment-workflow) Once everything is set up, test the workflow by making a change in the `main` branch or opening a pull request. 1. Push the changes or create a pull request targeting the `main` branch. 2. The `test` workflow will run automatically. 3. If the tests pass successfully, the `deploy.yml` workflow will trigger, deploying the new changes to Cloud Run. # Bootstrap (Deprecated) > Customize Bootstrap 5 themes in Pegasus with Sass variables, JavaScript integration, and Material Kit alternatives for responsive web design. Deprecated Bootstrap is deprecated in Pegasus. It will continue to work for existing projects, but will not receive new features and support will be removed in a future release. New projects should use [TailwindCSS](/css/tailwind/). There are two Bootstrap themes, both of which use Bootstrap version 5. ## Choosing your theme [Section titled “Choosing your theme”](#choosing-your-theme) The default Bootstrap theme is based off the default settings that ship with Bootstrap. It provides a simple, practical starting point that is easy to customize and extend. This theme is recommended for all new projects using Bootstrap. There is also a deprecated theme is based on Creative Tim’s [Material Kit](https://www.creative-tim.com/product/material-kit) and [Material Dashboard](https://www.creative-tim.com/product/material-dashboard) products. White this theme is flashier than the default theme, it has been retired due to developer experience issues. It is not recommended except for legacy projects, as support will be dropped in the future. ## Customizing the theme [Section titled “Customizing the theme”](#customizing-the-theme) Pegasus’s file structure is based on [the Bootstrap documentation](https://getbootstrap.com/docs/5.0/customize/sass/#importing). Any of the variables used in Bootstrap can be changed by modifying the `assets/styles/site-bootstrap.scss` file. A complete list of available variables can be found in `./node_modules/bootstrap/scss/variables`. Try adding the following lines to your file (after importing `functions`) to see how it changes things: ```scss // Configuration @import "~bootstrap/scss/functions"; $primary: #2e7636; // change primary color to green $body-color: #00008B; // change main text to blue // rest of file here... ``` **You’ll have to run `npm run dev` to see the changes take.** For more details on building the CSS files, see the [front end documentation](/front-end/overview). The [Bootstrap documentation](https://getbootstrap.com/docs/5.0/customize/sass/) has much more detail on customizing your theme! ## Working with JavaScript in Django templates [Section titled “Working with JavaScript in Django templates”](#working-with-javascript-in-django-templates) If you want to call bootstrap JavaScript from a Django template file, you can make the bootstrap library (or subsets of it) available on the browser window. To make all of bootstrap available, you can modify `site-bootstrap.js`b to just be these lines: ```javascript require('./styles/site-bootstrap.scss'); window.bootstrap = require('bootstrap'); ``` After [rebuilding the front end](/front-end/overview) you can then call bootstrap in a Django template like this: ```jinja {% block page_js %} {% endblock %} ``` This example will open the modal with ID `onLoadModal` on page load. Alternatively, you can add individual bootstrap javascript modules via `site-bootstrap.js` like this: ```javascript require('./styles/site-bootstrap.scss'); // window.Modal = require('bootstrap/js/dist/modal'); // modals (used by teams) ``` And then call it in a Django template like this (with no `bootrap.` prefix): ```javascript const onLoadModal = new Modal(document.getElementById('landing-page-modal')); ``` # Bulma (Deprecated) > Customize Bulma CSS framework using Sass variables for colors, typography, and styling in your Pegasus application. Deprecated Bulma is deprecated in Pegasus. It will continue to work for existing projects, but will not receive new features and support will be removed in a future release. New projects should use [TailwindCSS](/css/tailwind/). Bulma is readily customizable via [Sass variables](https://bulma.io/documentation/customize/variables/). Any of the variables used by Bulma can be changed by modifying the `assets/styles/site-bulma.scss` file. Try adding the following lines to the top of your file to see how it changes things: ```scss $primary: #2e7636; // change primary color to green $body-color: #00008B; // change main text to blue ``` **You’ll have to run `npm run dev` to see the changes take.** For more details on building the CSS files, see the [front end documentation](/front-end/overview). # CSS File Structure > Understand Pegasus CSS file organization with framework-independent styles and framework-specific overrides compiled from assets to static directories. CSS source files live in the `assets/styles` folder, and are compiled into the `static/css` folder. Some Pegasus styles are written using [Sass](https://sass-lang.com/), which provides many benefits and features on top of traditional CSS. **Modifying CSS requires having a functional [front-end build setup](/front-end/overview).** All versions of Pegasus contain two main sets of styles: * Styles that are *framework-independent* are contained and imported in `assets/styles/app/base.sass` and compiled into `static/css/site-base.css`. * Styles that *extend or override the CSS framework* are contained in `assets/styles/app//` and compiled into `static/css/site-.css`. This split is not required, and you can optionally combine everything into a single file by importing the styles from `base.sass` into your framework file and deleting `site-base.css`. # The Material Theme (deprecated) > Legacy Material Design theme based on Creative Tim's Material Kit and Dashboard, now deprecated with maintenance-only support until 2025. This feature is deprecated This theme was removed in version 2025.10. It is recommended to switch to [Tailwind CSS](/css/tailwind). This means that the theme is in maintenance-only mode, and support will be dropped by the end of 2025. Existing projects can continue using the theme, but new projects should not, and new Pegasus features will eventually not be developed and tested on the theme. The reason for this is that several Pegasus customers have complained about the lack of documentation and support for this theme from its maintainer, Creative Tim. Additionally, their process around updating the theme has entailed releasing large, poorly-documented updates which have been difficult for me to incorporate back into Pegasus. The following documentation is for people already using the material theme. ## Customizing the Material theme [Section titled “Customizing the Material theme”](#customizing-the-material-theme) The customization process outlined above largely works for the Material theme as well. For example, you can change the primary color from the default magenta to a dark green by adding the following lines towards the top of `assets/styles/site-bootstrap.scss`: ```scss // Configuration @import "~bootstrap/scss/functions"; // add these lines $primary: #2e7636; // change primary color + gradients to green $primary-gradient: #2e7676; $primary-gradient-state: #2e7676; ``` You will also have to [build your front end](/front-end/overview) to see the changes. Material has more customization options than the default theme, which can be found in the [Material Dashboard documentation](https://www.creative-tim.com/learning-lab/bootstrap/overview/material-dashboard). The theme files live in the `assets/material-dashboard` folder. You can see the modifications that have been made for Pegasus support [on Github here](https://github.com/creativetimofficial/material-dashboard/compare/master...czue:pegasus-tweaks). In particular, a few bugs have been fixed, and the unused pro files have been removed. Creative Tim offers pro versions of [Material Dashboard](https://www.creative-tim.com/product/material-dashboard-pro) and [Material Kit](https://www.creative-tim.com/product/material-kit-pro) which are helpful if you want to have access to more pages / components. These should integrate seamlessly with the Pegasus theme. ### Enabling Material’s JavaScript [Section titled “Enabling Material’s JavaScript”](#enabling-materials-javascript) Pegasus doesn’t ship with the Material theme JavaScript built in. If you would like to use their JavaScript functionality (required for many of their components) you can take the following steps: 1. Download [the `material-kit.min.js` file from Creative Tim’s Github repository](https://github.com/creativetimofficial/material-kit/blob/master/assets/js/material-kit.min.js). 2. Copy it into your Django static directory. For example, to `/static/js` 3. Add it to the `` section of your `base.html` template (or wherever you want to use it): ```jinja ``` After completing these steps, the Material Kit JavaScript functionality should work. # Choosing a CSS Theme > Compare TailwindCSS, Bootstrap, Bulma, and Material Design themes with screenshots, features, and recommendations for Django projects. ### Tailwind CSS [Section titled “Tailwind CSS”](#tailwind-css) **Tailwind CSS is the recommended and actively supported CSS framework.** It is the most popular choice, easiest to customize, and supports themes and dark mode out-of-the-box. Here’s what it looks like. **Light mode**: ![Tailwind Home](/_astro/tailwind-home-light.DygBNhqu_Z1qUcIV.webp) **Dark mode**: ![Tailwind Home (Dark Mode)](/_astro/tailwind-home-dark.BJVKg2e7_Dk9G8.webp) ### Deprecated Themes [Section titled “Deprecated Themes”](#deprecated-themes) Previous versions of Pegasus included support for a [Bootstrap 5](https://getbootstrap.com/) theme, a [Bulma](https://bulma.io/) theme, and a theme based on Creative Tim’s [Material Kit](https://www.creative-tim.com/product/material-kit) and [Material Dashboard](https://www.creative-tim.com/product/material-dashboard) products. ***These alternate themes have all been deprecated and are no longer recommended for new projects.*** **Bootstrap Default Theme (Deprecated):** ![Bootstrap Home](/_astro/bootstrap-home.DaBA-2Xi_2dpcEf.webp) **Bulma (Deprecated):** ![Bulma Home](/_astro/bulma-home.DfnQnVsO_1fwOQf.webp) **Bootstrap Material Theme (Deprecated):** ![Material Home](/_astro/material-home.HxW3zxce_Z2b3DcX.webp) # Pegasus CSS > Cross-framework CSS classes with pg- prefixes for consistent styling across Bootstrap, TailwindCSS, and Bulma using Sass @extend and @apply. Pegasus historically shipped a set of CSS classes prefixed with `pg-` to provide compatibility across its supported CSS frameworks (Tailwind, Bootstrap, and Bulma). These classes are proxies for similar classes provided by the underlying frameworks themselves, and are created using the Sass [`@extend` helper](https://sass-lang.com/documentation/at-rules/extend) or Tailwind’s [`@apply` helper](https://tailwindcss.com/docs/reusing-styles#extracting-classes-with-apply). ## Default Behavior (Tailwind) [Section titled “Default Behavior (Tailwind)”](#default-behavior-tailwind) **By default, Pegasus outputs native Tailwind and DaisyUI classes in your templates.** For example, a button that previously used `pg-button-primary` will output `btn btn-primary` (a DaisyUI class). A title that used `pg-title` will output `text-3xl font-bold mb-2` (Tailwind utilities). This means you can style your app using the standard Tailwind and DaisyUI documentation directly, without needing to learn or look up the Pegasus-specific class names. ## Legacy pg- Classes [Section titled “Legacy pg- Classes”](#legacy-pg--classes) If you prefer to continue using `pg-` prefixed classes, you can enable them by checking the “Use Pegasus CSS classes” checkbox in your project configuration. This will output the `pg-` classes in your templates instead of native Tailwind classes. This is useful if: * You are using **Bootstrap** or **Bulma** (where `pg-` classes are always used). * You have an existing project with significant custom code using `pg-` classes and aren’t ready to migrate yet. ### Migrating from pg- Classes to Native Classes [Section titled “Migrating from pg- Classes to Native Classes”](#migrating-from-pg--classes-to-native-classes) If you have an existing project using `pg-` classes and want to switch to native Tailwind/DaisyUI classes, there are two steps: **1. Uncheck “Use Pegasus CSS classes” in your project settings.** In your project on [saaspegasus.com](https://www.saaspegasus.com/), go to **Developer Setup → Advanced** and uncheck **Use Pegasus CSS classes**. This tells Pegasus to output native Tailwind/DaisyUI classes in future builds. **2. Migrate your existing templates and JavaScript files.** Use the `pegasus migrate-css` command from the [Pegasus CLI](https://github.com/saaspegasus/pegasus-cli) to convert any `pg-` classes in your own code to their native equivalents: ```bash # Preview what would change pegasus migrate-css --dry-run # Do the migration pegasus migrate-css ``` The command reads class mappings from your project’s `assets/styles/pegasus/tailwind.css`, so it should match the version of Pegasus your project was built with. See the [pegasus-cli README](https://github.com/saaspegasus/pegasus-cli#migrating-pg--css-classes) for additional options (e.g. `--css-file` and `--search-dir` for non-standard project layouts). ## Class Reference [Section titled “Class Reference”](#class-reference) Pegasus CSS classes are defined in `assets/styles/pegasus/.sass/css`. The following table shows the most common classes and their values across frameworks. | Pegasus Class | Description | Value in Bootstrap | Value in Tailwind | Value in Bulma | | --------------- | ------------------- | ------------------ | --------------------------------------------------------------- | --------------- | | `pg-columns` | Wrapper for columns | `row gy-4` | `flex flex-col space-y-4 lg:flex-row lg:space-x-4 lg:space-y-0` | `columns` | | `pg-column` | Individual column | `col-md` | `flex-1` | `column` | | `pg-title` | A title | `h3` (element) | `text-3xl font-bold mb-2` | `title` | | `pg-subtitle` | A subtitle | `lead` | `text-xl mb-1` | `subtitle` | | `pg-button-***` | A styled button | `btn btn-***` | `btn btn-***` (from daisyUI) | `button is-***` | | `pg-text-***` | Colored text | `text-***` | `text-***` (from daisyUI) | `has-text-***` | For a full list of classes and their mappings, see `assets/styles/pegasus/` in your project. ## Classes Not Covered by Migration [Section titled “Classes Not Covered by Migration”](#classes-not-covered-by-migration) Some `pg-` classes use more complex CSS (not simple utility mappings) and are not affected by the migration. These classes remain as `pg-` in all builds and should continue to be referenced by their `pg-` names: * **`pg-breadcrumbs`**, **`pg-breadcrumb-active`** — breadcrumb navigation with nested styling rules. * **`pg-select`** — wraps a nested `