Security News
Fluent Assertions Faces Backlash After Abandoning Open Source Licensing
Fluent Assertions is facing backlash after dropping the Apache license for a commercial model, leaving users blindsided and questioning contributor rights.
django-partial-index
Advanced tools
Partial (sometimes also called filtered or conditional) index support for Django.
With partial indexes, only some subset of the rows in the table have corresponding index entries. This can be useful for optimizing index size and query speed, and to add unique constraints for only selected rows.
More info on partial indexes:
Since the release of Django 2.2 LTS in April 2019, partial indexes are now supported by standard Django.
These are called index conditions there.
The django-partial-index package will live on in maintenance mode.
It can be useful if you are maintaining a project on and older version of Django, or wish to migrate django-partial-index indexes to Django 2.2 style on your own schedule.
pip install django-partial-index
Requirements:
All Python versions which Django supports are also supported by this package. These are:
Set up a PartialIndex and insert it into your model's class-based Meta.indexes list:
from partial_index import PartialIndex, PQ
class MyModel(models.Model):
class Meta:
indexes = [
PartialIndex(fields=['user', 'room'], unique=True, where=PQ(deleted_at__isnull=True)),
PartialIndex(fields=['created_at'], unique=False, where=PQ(is_complete=False)),
]
The PQ
uses the exact same syntax and supports all the same features as Django's Q
objects (see Django docs for a full tutorial). It is provided for compatibility with Django 1.11.
Of course, these (unique) indexes could be created by a handwritten RunSQL migration. But the constraints are part of the business logic, and best kept close to the model definitions.
With unique=True
, this can be used to create unique constraints for a subset of the rows.
For example, you might have a model that has a deleted_at field to mark rows as archived instead of deleting them forever. You wish to add unique constraints on "alive" rows, but allow multiple copies in the archive. Django's unique_together is not sufficient here, as that cannot distinguish between the archived and alive rows.
from partial_index import PartialIndex, PQ
class RoomBooking(models.Model):
user = models.ForeignKey(User)
room = models.ForeignKey(Room)
deleted_at = models.DateTimeField(null=True, blank=True)
class Meta:
# unique_together = [('user', 'room')] - Does not allow multiple deleted rows. Instead use:
indexes = [
PartialIndex(fields=['user', 'room'], unique=True, where=PQ(deleted_at__isnull=True))
]
With unique=False
, partial indexes can be used to optimise lookups that return only a small subset of the rows.
For example, you might have a job queue table which keeps an archive of millions of completed jobs. Among these are a few pending jobs,
which you want to find with a .filter(is_complete=0)
query.
from partial_index import PartialIndex, PQ
class Job(models.Model):
created_at = models.DateTimeField(auto_now_add=True)
is_complete = models.IntegerField(default=0)
class Meta:
indexes = [
PartialIndex(fields=['created_at'], unique=False, where=PQ(is_complete=0))
]
Compared to an usual full index on the is_complete
field, this can be significantly smaller in disk and memory use, and faster to update.
With F
-expressions, you can create conditions that reference multiple fields:
from partial_index import PartialIndex, PQ, PF
class NotTheSameAgain(models.Model):
a = models.IntegerField()
b = models.IntegerField()
class Meta:
indexes = [
PartialIndex(fields=['a', 'b'], unique=True, where=PQ(a=PF('b'))),
]
This PartialIndex allows multiple copies of (2, 3)
, but only a single copy of (2, 2)
to exist in the database.
The PF
uses the exact same syntax and supports all the same features as Django's F
expressions (see Django docs for a full tutorial). It is provided for compatibility with Django 1.11.
Unique partial indexes are validated by the PostgreSQL and SQLite databases. When they reject an INSERT or UPDATE, Django raises a IntegrityError
exception. This results in a 500 Server Error
status page in the browser if not handled before the database query is run.
ModelForms perform unique validation before saving an object, and present the user with a descriptive error message.
Adding an index does not modify the parent model's unique validation, so partial index validations are not handled by them by default. To add that to your model, include the ValidatePartialUniqueMixin
in your model definition:
from partial_index import PartialIndex, PQ, ValidatePartialUniqueMixin
class MyModel(ValidatePartialUniqueMixin, models.Model):
class Meta:
indexes = [
PartialIndex(fields=['user', 'room'], unique=True, where=PQ(deleted_at__isnull=True)),
]
Note that it should be added on the model itself, not the ModelForm class.
Adding the mixin for non-unique partial indexes is unnecessary, as they cannot cause database IntegrityErrors.
Text-based where-conditions are deprecated and will be removed in the next release (0.6.0) of django-partial-index.
They are still supported in version 0.5.0 to simplify upgrading existing projects to the PQ
-based indexes. New projects should not use them.
from partial_index import PartialIndex
class TextExample(models.Model):
class Meta:
indexes = [
PartialIndex(fields=['user', 'room'], unique=True, where='deleted_at IS NULL'),
PartialIndex(fields=['created_at'], unique=False, where_postgresql='is_complete = false', where_sqlite='is_complete = 0')
]
where_postgresql=''
and where_sqlite=''
predicates, when the expression has different syntax on the two
database backends and you wish to support both.FAQs
PostgreSQL and SQLite partial indexes for Django models
We found that django-partial-index demonstrated a healthy version release cadence and project activity because the last version was released less than a year ago. It has 1 open source maintainer collaborating on the project.
Did you know?
Socket for GitHub automatically highlights issues in each pull request and monitors the health of all your open source dependencies. Discover the contents of your packages and block harmful activity before you install or update your dependencies.
Security News
Fluent Assertions is facing backlash after dropping the Apache license for a commercial model, leaving users blindsided and questioning contributor rights.
Research
Security News
Socket researchers uncover the risks of a malicious Python package targeting Discord developers.
Security News
The UK is proposing a bold ban on ransomware payments by public entities to disrupt cybercrime, protect critical services, and lead global cybersecurity efforts.