Create composite index from a Django model - python

I have the following model:
from django.db import models
class PopulationData(models.Model):
slot = models.IntegerField(db_index=True)
sample = models.IntegerField()
value = models.FloatField()
class Meta:
unique_together = (('slot', 'sample'),)
And I would like to create also a compound index on the column pair that have the UNIQUE constraint, like so:
CREATE INDEX my_compound_index ON myapp_populationdata (slot, sample);
Right now I have a separate code connected to the post_syncdb signal that issues the previous SQL statement. Is there any way to indicate it from the model specification? (Note: I'm using the 1.3 branch).

Starting from django-1.5 you can make compound index using index_together meta option:
https://docs.djangoproject.com/en/dev/ref/models/options/#index-together
Note index_together is deprecated since django-4.2. Instead, use much more powerful indexes meta option. With it you can create not only compound indexes, but also other types of indexes, e.g. function-based indexes, partial (conditional) indexes, covering indexes:
https://docs.djangoproject.com/en/dev/ref/models/options/#django.db.models.Options.indexes

Starting from Django-1.11 use Meta.indexes option https://docs.djangoproject.com/en/1.11/ref/models/indexes/:
from django.db import models
class PopulationData(models.Model):
slot = models.IntegerField(db_index=True)
sample = models.IntegerField()
value = models.FloatField()
class Meta:
unique_together = (('slot', 'sample'),)
indexes = [
models.Index(fields=['slot', 'sample']),
]

Since a unique constraint also creates an index, it would be counterproductive to create both.
for example, from the postgres docs:
There's no need to manually create indexes on unique columns; doing so would just duplicate the automatically-created index.
Credit to Mark Byers for the doc link
If for some reason you still want to create a multi-column index, you can do so via index_together:
class PopulationData(models.Model):
...
class Meta:
index_together = [['slot', 'sample']]

I think that's not currently implemented in the django ORM.
If you use a migration tool (like south) that might be a good place to add that sql statement or if you preffer to avoid raw sql you could use sqlalchemy (core) but this case sounds simple enough to just go with sql.

Note: index_together may be deprecated in the future:
https://docs.djangoproject.com/en/dev/ref/models/options/#index-together
Use indexes in the Meta class instead:
from django.db import models
class Customer(models.Model):
first_name = models.CharField(max_length=100)
last_name = models.CharField(max_length=100)
class Meta:
indexes = [
models.Index(fields=['last_name', 'first_name']),
models.Index(fields=['first_name'], name='first_name_idx'),
]

Related

Django unique_together() for field1 and field2 and vice versa (field2 and field1)

It's possible to create duplicate thread with same first_person and second_person using unique_together() in django.
class Meta:
unique_together = ['first_person', 'second_person']
But just like that,
Is it possible to make it work for same second_person and first_person too ?
For example if first_person is abc#gmail.com and second_person is xyz#gmail.com then I can't create duplicate value but if I try to make second_person is abc#gmail.com and first_person is xyz#gmail.com then it again creates thread with same person that I dont want. Please let me know how to fix this.
Since django-4.1, you can use functional uniqness filters (and indexes) with a UniqueConstraint [Django-doc], and you thus can determine the lower and greater item:
from django.db import models
from django.db.models.functions import Greatest, Least
# …
class MyModel(models.Model):
# …
class Meta:
constraints = [
UniqueConstraint(
Least('first_person', 'second_person'),
Greatest('first_person', 'second_person'),
name='asymetrical_friends',
)
]
Note: As the documentation on unique_together [Django-doc] says, the unique_together constraint will likely become deprecated. The documentation advises to use the UniqueConstraint [Django-doc] from Django's constraint
framework.

Create index on nested element JSONField for Postgres in Django

I have a Django model in my python project with a meta class detailing it's indexes. I'm curious if there's a way to create the index using the nested path of the json object. In this case we know the structure of our json and I wanted to stick with a BTree or Hash index on the specific element.
If I were simply running this as raw sql, I'd expect to just do something like:
CREATE INDEX ON foster_data(root->'level_1'->'level_2'->>'name');
I was hoping I could do something like this in my model:
from django.db import models
from django.contrib.postgres import indexes
class ParentGuardians(Facilitators): # which extends models.Model
parent_identifier = models.IntegerField(db_column='p_id', default=None, blank=True,
null=True)
class Meta:
constraints = [
models.UniqueConstraint(fields=['table_id', name='UniqueConstraint for Parents')
]
indexes = [
models.Index(fields=['p_id', ]),
indexes.BTreeIndex(fields=[models.JSONField('{"root": {"level_1": {"level_2": "name"}}}'), ]
, name="jsonb_p_id_idx"),
]
or even:
...
indexes.BTreeIndex(fields=["root->'level_1'->'level_2'->>'name'", ]
...
But the named field fields only wants strings and only wants them to be the top level field defined in the model.
I'm aware of this questions: Indexing JSONField in Django PostgreSQL but it seems more of a hack and wanted the result generated from the codebase and makemigrations, not to manually edit it. Is this possible more recently?
Django 3.2 introduced native support for these indexes.
The question as asked presently doesn't seem to have the definition of the JSONField, but assuming it is something like
from django.db import models
class Facilitators(models.Model):
foster_data = models.JSONField()
To index a particular key, you combine an F expression with a JSONField path lookup on the model's Meta indexes option:
from django.contrib.postgres.fields import JSONField
class Facilitators(models.Model):
foster_data = models.JSONField()
class Meta:
indexes = [
models.Index(models.F("foster_data__root__level_1__level2__name"), name="foster_data__root__level_1__level2__name_idx"),
]
This will create a B-Tree index. If you are adding these to an existing model, be sure to makemigrations and migrate.
See this answer as well https://stackoverflow.com/a/74619523/

Inheritance model update to its parent model

I need extend a model from another model.
Case:
core/models.py
class Master(models.Model):
code = models.CharField(max_length=30, unique=True)
name = models.CharField(max_length=100, blank=False, null=False)
class Meta:
abstract = True
class City(Master):
zipcode = models.IntegerField()
custom/models.py
from core.models import City
class City(City)
newfield = models.CharField(max_length=20)
custom is an app.
I have tried with proxy model but it is not what I need, since proxy model adds a new table. https://docs.djangoproject.com/en/2.2/topics/db/models/#proxy-models
I need is that when I migrate add the new field to City.
More info.
In core the table is created and in custom you can add new fields that the client needs. The idea is that core is only maintained as standard.
Proxy models don't add new tables. From the docs link you mentioned:
The MyPerson class operates on the same database table as its parent Person class.
If you want one table called core_city, and another called custom_city, the second one having an extra field, you simply subclass it. Perhaps it would be easier to use an alias:
from core.models import City as CoreCity
class City(CoreCity):
newfield = models.CharField(max_length=20)
custom_city will have all fields from core_city, plus a newfield. The description of how this works (and an example) is covered in the docs section Multi-table inheritance.
If what you want is to have one single database table, then you should use a proxy Model, however they really don't allow you to create new fields. The field should be created in the parent model, or otherwise exist in the database and not be handled by Django migrations at all.
You are looking for Abstract base classes models:
Abstract base classes are useful when you want to put some common information into a number of other models. You write your base class and put abstract=True in the Meta class.
This is the base class:
#core/models.py
class City(Master):
zipcode = models.IntegerField()
class Meta:
abstract = True # <--- here the trick
Here your model:
#custom/models.py
from core.models import City as CoreCity
class City(CoreCity):
newfield = models.CharField(max_length=20)
For many uses, this type of model inheritance will be exactly what you want. It provides a way to factor out common information at the Python level, while still only creating one database table per child model at the database level.
You can update or create your class constants after its defined like this
from core.models import City
City.newfield = models.CharField(max_length=20)
You may need to use swappable models, using them you can define a City class and change it with whichever model you need later,
but that way you can't import and use the base City model directly, you may need to provide a method like get_city_model for that, as your public API.
class City(Master):
zipcode = models.IntegerField()
class Meta:
swappable = 'CORE_CITY_MODEL'
and maybe replace it later with some other model, then just set CORE_CITY_MODEL to that model in the form of 'app_name.model_name'.
The django.contrib.auth is a good example of this, you may consider checking User model and get_user_model method. Although I think you may face problems if you change your city model after you did run migrate, it may not move your data to the new table, but I'm not sure about this.

How to retrieve a queryset using any/exists/all logic across foreign key relationships in Django?

Assuming I have two models:
from django.db import models
class Parent(models.Model):
pass
class Child(models.Model):
parent = models.ForeignKey(Parent,
on_delete=models.CASCADE,
related_name='children')
active = models.BooleanField()
How might I go about getting a query set of parents that have at least 1 active child? In other words, how would I get a query set of parents that excludes those without any active children? If using model properties in filters was feasible, this would be a trivial task, but that is not possible. This is also a simple operation using list comprehensions, but it's important here for the queryset to be the end result.
One solution is to use aggregation: https://docs.djangoproject.com/en/2.1/topics/db/aggregation/
We can annotate a queryset of parents with the number of active children they have, and then filter against that queryset to only find parents with a number greater than or equal to 0:
from django.db.models import Count, Q
num_active_children = Count('children', filter=Q(children__active=True))
parents_with_any_active_children = (Parent.objects
.annotate(num_active_children=num_active_children)
.filter(num_active_children__gte=1)
)
Parent.objects.filter(child__active=True)
Hope this is enough for you.

How to combine django "prefetch_related" and "values" methods?

How can prefetch_related and values method be applied in combination?
Previously, I had the following code. Limiting fields in this query is required for performance optimization.
Organizations.objects.values('id','name').order_by('name')
Now, I need to prefetch its association and append it in the serializer using "prefetch_related" method.
Organizations.objects.prefetch_related('locations').order_by('name')
Here, I cannot seem to find a way to limit the fields after using "prefetch_related".
I have tried the following, but on doing so serializer does not see the associated "locations".
Organizations.objects.prefetch_related('locations').values("id", "name").order_by('name')
Model Skeleton:
class Organizations(models.Model):
name = models.CharField(max_length=40)
class Location(models.Model):
name = models.CharField(max_length=50)
organization = models.ForeignKey(Organizations, to_field="name", db_column="organization_name", related_name='locations')
class Meta:
db_table = u'locations'
Use only() to limit number of fields retrieved if you're concerned about your app performances. See reference.
In the example above, this would be:
Organizations.objects.prefetch_related('locations').only('id', 'name').order_by('name')
which would result in two queries:
SELECT id, name FROM organizations;
SELECT * from locations WHERE organization_name = <whatever is seen before>;

Categories

Resources