Metadata-Version: 2.4
Name: django-rentals
Version: 0.3.0
Summary: A Django Rest API for rental (vehicle/gear) listings, availability, and bookings.
Home-page: https://github.com/DestinationPak/django-rentals
Author: Awais Jibran
Author-email: awaisdar001@gmail.com
License: MIT
Keywords: django,rentals,rest,api
Classifier: Environment :: Web Environment
Classifier: Framework :: Django
Classifier: Framework :: Django :: 4.2
Classifier: Framework :: Django :: 5.2
Classifier: Intended Audience :: Developers
Classifier: License :: OSI Approved :: MIT License
Classifier: Operating System :: OS Independent
Classifier: Programming Language :: Python
Classifier: Programming Language :: Python :: 3 :: Only
Classifier: Programming Language :: Python :: 3.11
Classifier: Programming Language :: Python :: 3.12
Classifier: Programming Language :: Python :: 3.13
Classifier: Topic :: Internet :: WWW/HTTP
Classifier: Topic :: Internet :: WWW/HTTP :: Dynamic Content
Requires-Python: >=3.11
Description-Content-Type: text/markdown
License-File: LICENSE
Requires-Dist: Django<6.0,>=4.2
Requires-Dist: djangorestframework<3.19,>=3.16
Requires-Dist: django-filter<26.2,>=23.2
Requires-Dist: drf-spectacular>=0.28.0
Requires-Dist: swapper>=1.3.0
Provides-Extra: dev
Requires-Dist: pytest>=7.0; extra == "dev"
Requires-Dist: pytest-django>=4.5.0; extra == "dev"
Requires-Dist: pytest-cov>=4.0; extra == "dev"
Requires-Dist: factory-boy>=3.3; extra == "dev"
Requires-Dist: Faker; extra == "dev"
Requires-Dist: ddt; extra == "dev"
Requires-Dist: pylint>=2.0; extra == "dev"
Requires-Dist: pylint-django>=2.5.0; extra == "dev"
Dynamic: license-file

# Django Rentals API

[![PyPI version](https://img.shields.io/pypi/v/django-rentals.svg)](https://pypi.org/project/django-rentals/)
[![Python versions](https://img.shields.io/pypi/pyversions/django-rentals.svg)](https://pypi.org/project/django-rentals/)
[![License](https://img.shields.io/pypi/l/django-rentals.svg)](https://github.com/DestinationPak/django-rentals/blob/master/LICENSE)
[![Unit Tests](https://github.com/DestinationPak/django-rentals/actions/workflows/unit-tests.yml/badge.svg)](https://github.com/DestinationPak/django-rentals/actions/workflows/unit-tests.yml)

A Django REST API for vehicle/gear rental operators, listings, availability, and bookings —
the sibling package to [django-trips](https://pypi.org/project/django-trips/), part of the
[DestinationPak](https://destinationpak.com) platform.

## Installation

```bash
pip install django-rentals
```

## Usage

Add the app (and `django_filters`, used by the catalog/availability filtering below) to
your installed apps:

```python
INSTALLED_APPS = [
    ...
    'django_filters',
    'django_rentals',
]
```

## Migrate

```bash
python manage.py migrate
```

Mount its urls under a namespace of your choosing:

```python
urlpatterns = [
    ...
    path('rentals/', include('django_rentals.urls')),
]
```

This mounts the whole app under your own chosen prefix (`rentals/` above) with the lib's
own `v1/` version underneath it, e.g. `rentals/v1/listings/`,
`rentals/v1/schema/redoc/`. The app versions itself independently of your project's own
API version.

## Domain model

`RentalOperator` (the tenant/owner entity, mirrors `django_trips.Host`) → `RentalListing`
(one bookable vehicle or gear kit, mirrors `Trip`) → `RentalAvailability` (a bookable date,
mirrors `TripSchedule`) → `RentalBooking` (mirrors `TripBooking`, but books a
`start_date`/`end_date` range rather than a single departure date).

There is deliberately no separate tier/package model the way `django_trips` has
`TripPackage` — a distinct `RentalListing` per vehicle/kit already serves that purpose.

Like `django_trips`, this package is tenancy-oblivious: it has no concept of which user is
allowed to manage a given `RentalOperator`. That authorization layer belongs to whichever
project installs this app (see destipak's `docs/multi-tenancy-design.md` for the pattern
this is meant to plug into).

## Public API

Read-only and unauthenticated (`AllowAny`) unless noted:

- `listings/` - the published catalog. Filterable via query params: `?category=`,
  `?location=<id>`, `?operator=<id>`.
- `listings/<slug>/` - one listing's detail, including its images and availabilities.
- `operators/` - active, verified `RentalOperator`s.
- `availabilities/` - date-range availability search across active listings. Filterable via
  `?listing=<slug>`, `?date_from=`, `?date_to=` (any combination; omitting all three returns
  every upcoming bookable date).
- `bookings/create/` - guest booking (no auth required).
- `bookings/lookup/?number=&email=` - guest "find my booking".
- `bookings/<number>/` - authenticated traveller's own booking (retrieve/update/cancel).
- `schema/`, `schema/swagger-ui/`, `schema/redoc/` - this app's own OpenAPI schema, scoped
  to just these endpoints regardless of what else your project mounts.

## Custom Location model

`django_rentals.Location` (a plain `name`/`slug`/`lat`/`lng` model - no region/parent
hierarchy, unlike `django_trips.Location`) is swappable, the same way Django's own
`AUTH_USER_MODEL` is. `RentalListing.location` is the only location field on `RentalListing`
now - the original free-text `RentalListing.city` field has been dropped. If you're upgrading
from a version that still had it, a prior migration best-effort backfilled `location` from each
existing `city` string before `city` itself was removed.

Two settings, both optional and both defaulting to this package's own bundled model:

- **`DJANGO_RENTALS_LOCATION_MODEL`** - an `"app_label.ModelName"` string naming which model
  actually satisfies the FK, e.g. `DJANGO_RENTALS_LOCATION_MODEL = "myapp.City"`. Your model
  doesn't need to share `Location`'s field names.
- **`DJANGO_RENTALS_LOCATION_ADAPTER`** - a dotted path to a `django_rentals.location_adapter
  .LocationAdapter` subclass telling this app how to read your model's fields as if they were
  `Location`'s (`get_name`, `get_slug`, `get_lat`, `get_lng`). `RentalListingSerializer` exposes
  `location` as a nested object through this adapter, and `?location=<id>` filters on it directly.

Building a brand-new Location model rather than reusing one you already have? Inherit
`django_rentals.models.AbstractLocation` instead of writing an adapter - it's a plain abstract
Django model (the same shape `AbstractUser` is - real fields and concrete methods, not an
interface class) already carrying `name`/`slug`/`lat`/`lng` and their read methods, so you get
a working swap with no `DJANGO_RENTALS_LOCATION_ADAPTER` at all:

```python
# myapp/models.py
from django_rentals.models import AbstractLocation

class MyLocation(AbstractLocation):
    city_code = models.CharField(max_length=10)
```

```python
# settings.py
DJANGO_RENTALS_LOCATION_MODEL = "myapp.MyLocation"
```

Reusing an existing model instead - one you can't restructure, or one shared with other
libraries - stick with the adapter approach above; that's what it's for.

**Set both before your project's first `migrate`.** Like `AUTH_USER_MODEL`, this is a
swappable-model setting - Django resolves it once when the app loads, and a swap made after
`Location`'s own table has already been created (and other tables have already foreign-keyed
into it) doesn't retroactively move that data; it needs a real data migration instead of a
config change.

For a worked example of a real swap: the DestinationPakistan platform (this package's own
primary consumer, a private project) points this setting directly at its own `public.Location`
model, with no adapter override at all - `public.Location` already has `name`/`slug`/`lat`, plus
an `lng` property alias (its own field is `lon`, matching django-trips' naming), so the default
`LocationAdapter` reads it correctly with no subclass. See `docs/location-model-swap-design.md`
in that project for the full writeup.

## Development

All development happens inside Docker (`make dev.up`, `make update_db`, `make test`,
`make random_rentals`) — see the Makefile (`make help` lists every target).
