| Name | django-range-merge JSON |
| Version |
2024.10.1
JSON |
| download |
| home_page | None |
| Summary | Enables the range_merge Aggregate for Django on Postgres. range_merge 'Computes the smallest range that includes ... the given ranges'. |
| upload_time | 2024-10-26 00:02:55 |
| maintainer | None |
| docs_url | None |
| author | None |
| requires_python | <4.0,>=3.10 |
| license | MIT |
| keywords |
|
| VCS |
 |
| bugtrack_url |
|
| requirements |
No requirements were recorded.
|
| Travis-CI |
No Travis.
|
| coveralls test coverage |
|
# django-range-merge
A Django package that enables the PostgreSQL `range_merge` aggregate function for use with Django’s ORM.
`django-range-merge` provides access to PostgreSQL's `range_merge` aggregate function, which computes the smallest range that includes all input ranges. This is particularly useful when working with Django's range fields like `DateTimeRangeField`, `DateRangeField`, or `IntegerRangeField`.

This package should only be used with Django projects using the Postgres database. See [Postgres docs on Range Functions](https://www.postgresql.org/docs/14/functions-range.html#RANGE-FUNCTIONS-TABLE).
Note: This app is still a work-in-progress, but currently works. Tests have not yet been implemented.
## Installation
```bash
pip install django-range-merge
```
Add to `INSTALLED_APPS`:
```python
INSTALLED_APPS = [
...
"django_range_merge",
...
]
```
Migrate to apply the aggregation to your database:
```bash
> python manage.py migrate
```
## Getting Started
Here is a quick example. We have an `Event` model with two different range fields: `period`, which contains the datetime range period during which the Event occurs; and `potential_visitors`, which is an approximation of the minimum and maximum number of people attending the Event.
We want two different views to help Event organizers understand some aggregate details about Events.
- **range_of_visitors_this_month**: Show the overall lowest and greatest number of people we expect for all events this month
- **overall_dates_of_funded_events**: Shows the overall range of dates for Events which are funded (the `is_funded` BooleanField is set to True)
models.py
```python
class Event(models.Model):
name = models.TextField()
period = models.DateTimeRangeField()
potential_visitors = models.IntegerRangeField()
is_funded = BooleanField(default=False)
class Meta:
verbose_name = "Event"
verbose_name_plural = "Events"
def __str__(self):
return self.name
```
date_utils.py (get a range covering the entire current month)
```python
from django.utils import timezone
from dateutil.relativedelta import relativedelta
from psycopg2.extras import DateTimeTZRange
def get_month_range():
"""Return a DateTimeRange range covering this entire month"""
today = timezone.now().date()
if today.day > 25:
today += timezone.timedelta(7)
this_month_start = today.replace(day=1)
next_month_start = this_month_start + relativedelta(months=1)
return DateTimeTZRange(this_month_start, next_month_start)
```
views.py
```python
from django.db.models import F, Aggregate
from django.template.response import TemplateResponse
from .date_utils import get_month_range
def range_of_visitors_this_month(request):
"""
e.g., given the following instances:
{"id" : 1, "name" : "Birthday", "potential_visitors" : "[2, 3)", ...}
{"id" : 2, "name" : "Bake Sale", "potential_visitors" : "[30, 50)", ...}
{"id" : 3, "name" : "Band Camp", "potential_visitors" : "[22, 28)", ...}
{"id" : 4, "name" : "Cooking Show", "potential_visitors" : "[7, 20)", ...}
{"id" : 5, "name" : "Pajama Day", "potential_visitors" : "[15, 30)", ...}
The result would be:
{'output': NumericRange(2, 50, '[)')}
"""
template = "base.html"
context = Event.objects.filter(period__overlap=get_month_range()).aggregate(
output=Aggregate(F("potential_visitors"), function="range_merge")
)
return TemplateResponse(request, template, context)
def overall_dates_of_funded_events(request):
template = "base.html"
context = Event.objects.filter(is_funded=True).aggregate(
output=Aggregate(F("period"), function="range_merge")
)
# Example result: {'output': DateTimeRange("2022-10-01 02:00:00", "2022-12-07 12:00:00", '[)')}
return TemplateResponse(request, template, context)
```
base.html
```html
<html>
<head></head>
<body>
{{ output }}
</body>
</html>
```
## Performance Considerations
- The `range_merge` aggregate operates efficiently on server side
- Indexes on range fields can improve query performance
- Consider using `values()` to limit data transfer when only ranges are needed
## Development and Testing Setup
This project uses Docker Compose for development and testing. Follow these steps to get started:
### Prerequisites
1. Make sure you have Docker and Docker Compose installed
2. Install `uv` tool: `pip install uv`
### Setting Up the Development Environment
1. Clone the repository:
```bash
git clone https://github.com/OmenApps/django-range-merge.git
cd django-range-merge
```
2. Create a virtual environment and install dependencies:
```bash
uv venv
source .venv/bin/activate # On Windows: .venv\Scripts\activate
uv sync --prerelease=allow --extra=dev
```
3. Build and start the Docker containers:
```bash
docker-compose up -d --build postgres
```
4. Run migrations:
```bash
python manage.py migrate
```
### Running Tests
Using `nox`:
```bash
nox -s tests
```
This will run tests across multiple Django versions.
### Development Database Setup
The project uses PostgreSQL for testing. The Docker Compose setup includes a PostgreSQL instance with the following configuration:
- Host: localhost
- Port: 5436 # To avoid conflicts with local PostgreSQL installations
- Database: postgres
- Username: postgres
- Password: postgres
The database is automatically configured when running tests through Docker Compose.
## License
The code in this repository is licensed under The MIT License. See LICENSE.md in the repository for more details.
## Contributing
Contributions are very welcome.
This project is currently accepting all types of contributions, bug fixes,
security fixes, maintenance work, or new features. However, please make sure
to have a discussion about your new feature idea with the maintainers prior to
beginning development to maximize the chances of your change being accepted.
You can start a conversation by creating a new issue on this repo summarizing
your idea.
Raw data
{
"_id": null,
"home_page": null,
"name": "django-range-merge",
"maintainer": null,
"docs_url": null,
"requires_python": "<4.0,>=3.10",
"maintainer_email": null,
"keywords": null,
"author": null,
"author_email": "Jack Linke <jacklinke@gmail.com>",
"download_url": "https://files.pythonhosted.org/packages/74/28/553e05e69ad51e3011f07eb9f492cfab7f52fd4f1181a55b6485505394a3/django_range_merge-2024.10.1.tar.gz",
"platform": null,
"description": "# django-range-merge\n\nA Django package that enables the PostgreSQL `range_merge` aggregate function for use with Django\u2019s ORM.\n\n`django-range-merge` provides access to PostgreSQL's `range_merge` aggregate function, which computes the smallest range that includes all input ranges. This is particularly useful when working with Django's range fields like `DateTimeRangeField`, `DateRangeField`, or `IntegerRangeField`.\n\n\n\nThis package should only be used with Django projects using the Postgres database. See [Postgres docs on Range Functions](https://www.postgresql.org/docs/14/functions-range.html#RANGE-FUNCTIONS-TABLE).\n\nNote: This app is still a work-in-progress, but currently works. Tests have not yet been implemented.\n\n\n## Installation\n\n```bash\npip install django-range-merge\n```\n\nAdd to `INSTALLED_APPS`:\n\n```python\nINSTALLED_APPS = [\n ...\n \"django_range_merge\",\n ...\n]\n```\n\nMigrate to apply the aggregation to your database:\n\n```bash\n> python manage.py migrate\n```\n\n## Getting Started\n\nHere is a quick example. We have an `Event` model with two different range fields: `period`, which contains the datetime range period during which the Event occurs; and `potential_visitors`, which is an approximation of the minimum and maximum number of people attending the Event.\n\nWe want two different views to help Event organizers understand some aggregate details about Events.\n\n- **range_of_visitors_this_month**: Show the overall lowest and greatest number of people we expect for all events this month\n- **overall_dates_of_funded_events**: Shows the overall range of dates for Events which are funded (the `is_funded` BooleanField is set to True)\n\nmodels.py\n\n```python\nclass Event(models.Model):\n name = models.TextField()\n period = models.DateTimeRangeField()\n potential_visitors = models.IntegerRangeField()\n is_funded = BooleanField(default=False)\n\n class Meta:\n verbose_name = \"Event\"\n verbose_name_plural = \"Events\"\n\n def __str__(self):\n return self.name\n\n```\n\ndate_utils.py (get a range covering the entire current month)\n\n```python\nfrom django.utils import timezone\nfrom dateutil.relativedelta import relativedelta\nfrom psycopg2.extras import DateTimeTZRange\n\ndef get_month_range():\n \"\"\"Return a DateTimeRange range covering this entire month\"\"\"\n today = timezone.now().date()\n if today.day > 25:\n today += timezone.timedelta(7)\n this_month_start = today.replace(day=1)\n next_month_start = this_month_start + relativedelta(months=1)\n return DateTimeTZRange(this_month_start, next_month_start)\n```\n\nviews.py\n\n```python\nfrom django.db.models import F, Aggregate\nfrom django.template.response import TemplateResponse\n\nfrom .date_utils import get_month_range\n\ndef range_of_visitors_this_month(request):\n \"\"\"\n e.g., given the following instances:\n {\"id\" : 1, \"name\" : \"Birthday\", \"potential_visitors\" : \"[2, 3)\", ...}\n {\"id\" : 2, \"name\" : \"Bake Sale\", \"potential_visitors\" : \"[30, 50)\", ...}\n {\"id\" : 3, \"name\" : \"Band Camp\", \"potential_visitors\" : \"[22, 28)\", ...}\n {\"id\" : 4, \"name\" : \"Cooking Show\", \"potential_visitors\" : \"[7, 20)\", ...}\n {\"id\" : 5, \"name\" : \"Pajama Day\", \"potential_visitors\" : \"[15, 30)\", ...}\n\n The result would be:\n {'output': NumericRange(2, 50, '[)')}\n \"\"\"\n template = \"base.html\"\n\n context = Event.objects.filter(period__overlap=get_month_range()).aggregate(\n output=Aggregate(F(\"potential_visitors\"), function=\"range_merge\")\n )\n\n return TemplateResponse(request, template, context)\n\ndef overall_dates_of_funded_events(request):\n template = \"base.html\"\n\n context = Event.objects.filter(is_funded=True).aggregate(\n output=Aggregate(F(\"period\"), function=\"range_merge\")\n )\n # Example result: {'output': DateTimeRange(\"2022-10-01 02:00:00\", \"2022-12-07 12:00:00\", '[)')}\n\n return TemplateResponse(request, template, context)\n\n```\n\nbase.html\n\n```html\n<html>\n <head></head>\n <body>\n {{ output }}\n </body>\n</html>\n```\n\n## Performance Considerations\n\n- The `range_merge` aggregate operates efficiently on server side\n- Indexes on range fields can improve query performance\n- Consider using `values()` to limit data transfer when only ranges are needed\n\n\n## Development and Testing Setup\n\nThis project uses Docker Compose for development and testing. Follow these steps to get started:\n\n\n### Prerequisites\n\n1. Make sure you have Docker and Docker Compose installed\n2. Install `uv` tool: `pip install uv`\n\n\n### Setting Up the Development Environment\n\n1. Clone the repository:\n ```bash\n git clone https://github.com/OmenApps/django-range-merge.git\n cd django-range-merge\n ```\n\n2. Create a virtual environment and install dependencies:\n ```bash\n uv venv\n source .venv/bin/activate # On Windows: .venv\\Scripts\\activate\n uv sync --prerelease=allow --extra=dev\n ```\n\n3. Build and start the Docker containers:\n ```bash\n docker-compose up -d --build postgres\n ```\n\n4. Run migrations:\n ```bash\n python manage.py migrate\n ```\n\n### Running Tests\n\nUsing `nox`:\n\n ```bash\n nox -s tests\n ```\n This will run tests across multiple Django versions.\n\n\n### Development Database Setup\n\nThe project uses PostgreSQL for testing. The Docker Compose setup includes a PostgreSQL instance with the following configuration:\n\n- Host: localhost\n- Port: 5436 # To avoid conflicts with local PostgreSQL installations\n- Database: postgres\n- Username: postgres\n- Password: postgres\n\nThe database is automatically configured when running tests through Docker Compose.\n\n\n## License\n\nThe code in this repository is licensed under The MIT License. See LICENSE.md in the repository for more details.\n\n\n## Contributing\n\nContributions are very welcome.\n\nThis project is currently accepting all types of contributions, bug fixes,\nsecurity fixes, maintenance work, or new features. However, please make sure\nto have a discussion about your new feature idea with the maintainers prior to\nbeginning development to maximize the chances of your change being accepted.\nYou can start a conversation by creating a new issue on this repo summarizing\nyour idea.\n",
"bugtrack_url": null,
"license": "MIT",
"summary": "Enables the range_merge Aggregate for Django on Postgres. range_merge 'Computes the smallest range that includes ... the given ranges'.",
"version": "2024.10.1",
"project_urls": {
"Changelog": "https://github.com/OmenApps/django-range-merge/releases",
"Homepage": "https://github.com/OmenApps/django-range-merge",
"Repository": "https://github.com/OmenApps/django-range-merge"
},
"split_keywords": [],
"urls": [
{
"comment_text": null,
"digests": {
"blake2b_256": "29e6877f21add0ee51a58c5aa9b0f0b673b4402576f6c4bbf70d5b523e34c2a7",
"md5": "f69e150a7c193f74287cbf3072a73247",
"sha256": "038047dfb3fa28f0ccc713dacdaadcefb23b6624cb5f5711c8bae433f751594a"
},
"downloads": -1,
"filename": "django_range_merge-2024.10.1-py3-none-any.whl",
"has_sig": false,
"md5_digest": "f69e150a7c193f74287cbf3072a73247",
"packagetype": "bdist_wheel",
"python_version": "py3",
"requires_python": "<4.0,>=3.10",
"size": 6710,
"upload_time": "2024-10-26T00:02:54",
"upload_time_iso_8601": "2024-10-26T00:02:54.214185Z",
"url": "https://files.pythonhosted.org/packages/29/e6/877f21add0ee51a58c5aa9b0f0b673b4402576f6c4bbf70d5b523e34c2a7/django_range_merge-2024.10.1-py3-none-any.whl",
"yanked": false,
"yanked_reason": null
},
{
"comment_text": null,
"digests": {
"blake2b_256": "7428553e05e69ad51e3011f07eb9f492cfab7f52fd4f1181a55b6485505394a3",
"md5": "4532c82a125164f077ea6a5296fd463b",
"sha256": "94c88d4f6b462a557db76514c387bd94e35586eb69db3c79ce6e2f9ed426262b"
},
"downloads": -1,
"filename": "django_range_merge-2024.10.1.tar.gz",
"has_sig": false,
"md5_digest": "4532c82a125164f077ea6a5296fd463b",
"packagetype": "sdist",
"python_version": "source",
"requires_python": "<4.0,>=3.10",
"size": 9057,
"upload_time": "2024-10-26T00:02:55",
"upload_time_iso_8601": "2024-10-26T00:02:55.778293Z",
"url": "https://files.pythonhosted.org/packages/74/28/553e05e69ad51e3011f07eb9f492cfab7f52fd4f1181a55b6485505394a3/django_range_merge-2024.10.1.tar.gz",
"yanked": false,
"yanked_reason": null
}
],
"upload_time": "2024-10-26 00:02:55",
"github": true,
"gitlab": false,
"bitbucket": false,
"codeberg": false,
"github_user": "OmenApps",
"github_project": "django-range-merge",
"travis_ci": false,
"coveralls": true,
"github_actions": true,
"requirements": [],
"lcname": "django-range-merge"
}