Skip to main content

GTFS Django

PyPI version Python versions Django versions License

A Django package that provides abstract models for GTFS (General Transit Feed Specification) data, covering both static GTFS Schedule feeds and (coming soon) dynamic GTFS Realtime feed messages.

GTFS Reference revised April 27, 2026. See Revision History for more details.

Because all models are abstract, this package creates no database tables of its own. You subclass the models in your own Django app, giving you full control over your schema, like adding foreign keys, extra fields, constraints, or any other customizations your project requires.

The package gtfs-django also includes custom Django model fields that handle GTFS-specific data types and formats, such as time fields that support hours greater than 24, geographic coordinate fields, and validated fields for color codes, language tags, and more, as defined in the field types of the specification.

The abstract models follow these rules:

  • No foreign keys to other abstract models are defined by default. You must explicitly add them in your subclass.
  • For field presence, only fields marked "Required" are enforced by default. Fields marked "Optional," "Conditionally Required," "Conditionally Forbidden," or "Recommended" are not.

Requirements

  • Python: 3.12+
  • Django: 5.2+

Installation

pip install gtfs-django
uv add gtfs-django

Quick Start

1. Add to your Django settings

INSTALLED_APPS = [
    # ...
    "gtfs",
]

2. Subclass the abstract models in your app

Add auxiliary tables or new fields as needed.

from django.db import models
from gtfs.models import (
    BaseAgency,
    BaseRoute,
    BaseCalendar,
    BaseCalendarDate,
    BaseShape,
    BaseTrip,
    BaseStop,
    BaseStopTime,
)
from gtfs.fields import GTFSColorField


class Feed(models.Model):
    name = models.CharField(max_length=255)
    created_at = models.DateTimeField(auto_now_add=True)


class Agency(BaseAgency):
    feed = models.ForeignKey(Feed, on_delete=models.CASCADE)


class Route(BaseRoute):
    feed = models.ForeignKey(Feed, on_delete=models.CASCADE)
    agency = models.ForeignKey(Agency, on_delete=models.CASCADE)


class Calendar(BaseCalendar):
    feed = models.ForeignKey(Feed, on_delete=models.CASCADE)


class CalendarDate(BaseCalendarDate):
    feed = models.ForeignKey(Feed, on_delete=models.CASCADE)
    service = models.ForeignKey(Calendar, on_delete=models.CASCADE)
    holiday_name = models.CharField(
        max_length=255,
        help_text="Name of the holiday (non-GTFS field)",
    )


class Shape(BaseShape):
    feed = models.ForeignKey(Feed, on_delete=models.CASCADE)
    shape_color = GTFSColorField(
        null=True,
        blank=True,
        help_text="Color of the shape for display in maps (non-GTFS field)",
    )


class Trip(BaseTrip):
    feed = models.ForeignKey(Feed, on_delete=models.CASCADE)
    route = models.ForeignKey(Route, on_delete=models.CASCADE)
    service = models.ForeignKey(Calendar, on_delete=models.CASCADE)


class Stop(BaseStop):
    feed = models.ForeignKey(Feed, on_delete=models.CASCADE)


class StopTime(BaseStopTime):
    feed = models.ForeignKey(Feed, on_delete=models.CASCADE)
    trip = models.ForeignKey(Trip, on_delete=models.CASCADE)
    stop = models.ForeignKey(Stop, on_delete=models.CASCADE)

3. Run migrations for your app

python manage.py makemigrations <your_app_name>
python manage.py migrate

Abstract Models

GTFS Schedule

All 32 tables and 220 canonical GTFS fields are represented in the abstract models.

Model GTFS file Description
BaseAgency agency.txt Transit agencies
BaseStop stops.txt Stops and stations
BaseRoute routes.txt Routes
BaseTrip trips.txt Trips
BaseStopTime stop_times.txt Arrival/departure times per stop
BaseCalendar calendar.txt Weekly service schedules
BaseCalendarDate calendar_dates.txt Service exceptions
BaseShape shapes.txt Route shapes
BaseTimeframe timeframes.txt Fare timeframes
BaseRiderCategory rider_categories.txt Fare rider categories
BaseFareMedia fare_media.txt Fare media
BaseFareProduct fare_products.txt Fare products
BaseFareLegRule fare_leg_rules.txt Fare leg rules
BaseFareLegJoinRule fare_leg_join_rules.txt Fare leg join rules
BaseFareTransferRule fare_transfer_rules.txt Fare transfer rules
BaseArea areas.txt Areas
BaseStopArea stop_areas.txt Stop-to-area assignments
BaseNetwork networks.txt Route networks
BaseRouteNetwork route_networks.txt Route-to-network assignments
BaseFrequency frequencies.txt Frequency-based service
BaseTransfer transfers.txt Transfers between stops
BasePathway pathways.txt Station pathways
BaseLevel levels.txt Station levels
BaseLocationGroup location_groups.txt On-demand location groups
BaseLocationGroupStop location_group_stops.txt Group-to-stop assignments
BaseLocation locations.geojson On-demand service areas
BaseBookingRule booking_rules.txt On-demand booking rules
BaseTranslation translations.txt Field translations
BaseFareAttribute fare_attributes.txt Fare definitions
BaseFareRule fare_rules.txt Fare applicability rules
BaseFeedInfo feed_info.txt Feed metadata
BaseAttribution attributions.txt Attribution information

GTFS Realtime

Not implemented yet.

The first option is to create a model for each message object type in the GTFS Realtime feed.

Model Description
BaseFeedMessage Feed message header
BaseFeedHeader Feed message header
BaseFeedEntity Feed entity
BaseTripUpdate Real-time trip schedule updates
BaseStopTimeUpdate Per-stop arrival/departure updates
BaseStopTimeProperties Stop time properties
BaseTripProperties Defines updated properties of the trip
BaseVehiclePosition Live vehicle locations
BaseCarriageDetails Carriage details of the vehicle
BaseAlert Service alerts and disruptions
BaseTimeRange A time interval
BasePosition A geographic position
BaseTripDescriptor Identifies a single instance of a GTFS trip
BaseModifiedTripSelector Selects a modified trip instance
BaseVehicleDescriptor Identifies the vehicle performing the trip
BaseEntitySelector Selects an entity
BaseTranslatedString Translated string
BaseTranslation Translation entry
BaseTranslatedImage Translated image
BaseLocalizedImage Localized image
BaseShape Shape definition
BaseStop Stop definition
BaseTripModifications Trip modifications
BaseModification Single modification
BaseStopSelector Selects a stop
BaseSelectedTrips Selected trips
BaseReplacementStop Replacement stop

Custom Fields

gtfs.fields provides Django model fields that encode GTFS type semantics:

Field GTFS type Notes
GTFSColorField Color 6-digit hex, without #
GTFSCurrencyCodeField Currency code ISO 4217, e.g. USD
GTFSCurrencyAmountField Currency amount Decimal
GTFSDateField Date Accepts/serialises YYYYMMDD
GTFSTimeField Time timedelta; supports hours ≥ 24
GTFSLocalTimeField Local time String HH:MM:SS; hours 0-23
GTFSIDField ID CharField, optionally ASCII-only
GTFSTextField Text CharField with 255-char default
GTFSTimezoneField Timezone Validated IANA timezone name
GTFSLanguageCodeField Language code BCP 47 tag, e.g. en-US
GTFSLatitudeField Latitude Decimal, -90 to 90
GTFSLongitudeField Longitude Decimal, -180 to 180
GTFSPhoneNumberField Phone number Permissive international format
GTFSEnumField Enum Integer with required choices
GTFSNonNegativeIntegerField Non-negative integer Integer ≥ 0
GTFSNonZeroIntegerField Non-zero integer Integer other than 0
GTFSPositiveIntegerField Positive integer Integer > 0
GTFSNonNegativeFloatField Non-negative float Decimal ≥ 0
GTFSPositiveFloatField Positive float Decimal > 0

Development

git clone https://github.com/simovilab/gtfs-django.git
cd gtfs-django

# Install with development dependencies
uv sync

# Run tests
pytest

Regenerating Schedule models

gtfs/builder/schedule.yaml is the source of truth for the GTFS Schedule models. After changing it or gtfs/builder/mappings.py, generate gtfs/builder/models.py from the repository root:

uv run python -m gtfs.builder.builder

The generated file contains Schedule models only. Review and migrate its contents into gtfs/models.py manually; the generator does not modify that file.

Building and publishing to PyPI

Run these steps from the repository root with uv installed. You need a PyPI account with permission to publish gtfs-django and a PyPI API token, preferably scoped to this project.

  1. Choose a new, unpublished version. For example, increment the patch version in pyproject.toml (use --bump minor or --bump major when appropriate):

    uv version --bump patch
    

    PyPI does not allow replacing previously uploaded distribution files, even after deleting a release.

  2. Install development dependencies and run the tests:

    uv sync
    uv run pytest
    
  3. Build the source distribution and wheel using the configured Hatchling backend. Use a version-specific output directory to avoid uploading older releases accidentally:

    VERSION=$(uv version --short)
    uv build --no-sources --out-dir "dist/$VERSION"
    

    Review the generated .tar.gz and .whl files in dist/<version>/ before publishing.

  4. Upload only this release's artifacts to PyPI:

    uv publish --username __token__ "dist/$VERSION/"*
    

    Paste your PyPI API token at the password prompt. Do not put the token in the command, commit it, or save it in pyproject.toml. For automated publishing, supply it through the UV_PUBLISH_TOKEN environment variable using your CI secret store instead.

  5. Verify the release at pypi.org/project/gtfs-django/ and check that the published version can be installed and imported without using the local checkout:

    uv run --no-project --with "gtfs-django==$VERSION" -- python -c "import gtfs"
    

Contributing

Contributions are welcome. For major changes please open an issue first.

  1. Follow Django coding standards
  2. Add tests for new features
  3. Update documentation as needed
  4. Ensure compatibility with Django 5.2+

License

Apache License 2.0 — see LICENSE for details.

Credits

Developed by SIMOVI Lab.


For more information about GTFS, visit gtfs.org.

Metadata

Release files for gtfs-django 0.3.2

For a detailed explanation of source distributions (sdists) and built distributions (wheels), please see the package formats documentation.

Source distribution (sdist)

Source distribution for gtfs-django 0.3.2
File Size Uploaded Provenance Actions
gtfs_django-0.3.2.tar.gz 143.8 kB Without provenance Details for gtfs_django-0.3.2.tar.gz

Built distribution (wheel)

Table of built distributions (wheels) for gtfs-django 0.3.2
File Interpreter ABI Platform Actions
gtfs_django-0.3.2-py3-none-any.whl Python 3 none any Details for gtfs_django-0.3.2-py3-none-any.whl

Total release size: 306.8 kB

Release files / gtfs_django-0.3.2.tar.gz

Download URL gtfs_django-0.3.2.tar.gz
Size 143.8 kB
Tags Source
SHA-256 checksum
How to use checksums
c909877fe8c92f15c29c422c1b5acf1104a74653f2ff61740afddde4a57e7d9c
BLAKE2b-256 checksum
How to use checksums
1775b28e06794ea4d2a8251f3f162e66c1288c062655ff25270fe1e0dc56597f
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
No
Uploaded via uv/0.13.6 {"installer":{"name":"uv","version":"0.13.6","subcommand":["publish"]},"python":null,"implementation":{"name":null,"version":null},"distro":{"name":"macOS","version":null,"id":null,"libc":null},"system":{"name":null,"release":null},"cpu":null,"openssl_version":null,"setuptools_version":null,"rustc_version":null,"ci":null}

Release files / gtfs_django-0.3.2-py3-none-any.whl

Download URL gtfs_django-0.3.2-py3-none-any.whl
Size 163.0 kB
Tags Python 3
SHA-256 checksum
How to use checksums
81cf492a33d6d2dfca570d1b298ce108bfe2f46771610ec4de6e24887a606a7c
BLAKE2b-256 checksum
How to use checksums
c1eeaf82ab36d9c8a9362b982248ec36b6870a610b6646a94f875403d00392e5
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
No
Uploaded via uv/0.13.6 {"installer":{"name":"uv","version":"0.13.6","subcommand":["publish"]},"python":null,"implementation":{"name":null,"version":null},"distro":{"name":"macOS","version":null,"id":null,"libc":null},"system":{"name":null,"release":null},"cpu":null,"openssl_version":null,"setuptools_version":null,"rustc_version":null,"ci":null}

Security for gtfs-django 0.3.2

Provenance

Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.

No provenance found

None of the files in this release were uploaded with provenance metadata.

A note on package security

PyPI is a repository for distributing packages, not a service for auditing them. While third-party security researchers and our staff do their best to detect and remove malicious packages, PyPI cannot guarantee the quality, security or suitability of any given package.

To learn more about protecting yourself from malicious actors, visit our package security documentation.

Anthropic, PBC Visionary sponsor Bloomberg Visionary sponsor Hudson River Trading Visionary sponsor Meta Visionary sponsor NVIDIA Visionary sponsor Microsoft Sustainability sponsor Depot Continuous Integration AWS Cloud computing and Security Sponsor Datadog Monitoring Fastly CDN Google Download Analytics Sentry Error logging StatusPage Status page