Skip to content

KazakovDenis/django-join-unrelated

Repository files navigation

django-join-unrelated

Python version Tests

Use SQL Join with Django ORM models having no relations.

If you have models in your project that share the same data but have no relations, you might face a situation when you need to join them. But Django does not provide this functionality yet.

With django-join-unrelated you can do the following (models defined here):

from app.core.models import Jedi, Person

Person.objects.create(first_name='Padme', last_name='Amidala', birth_place='Naboo')
Person.objects.create(first_name='Obi-Wan', last_name='Kenobi', birth_place='Stewjon')
Jedi.objects.create(first_name='Obi-Wan', last_name='Kenobi', force=100)
Jedi.objects.create(first_name='Mace', last_name='Windu', force=80)

Jedi.objects.join(first_name=Person.first_name).values_list('first_name')
# <UnrelatedJoinQuerySet [('Obi-Wan',)]>

print(Jedi.objects.join(first_name=Person.first_name).values_list('first_name').query)
# SELECT "core_jedi"."first_name" FROM "core_jedi" INNER JOIN core_person ON ("core_jedi"."first_name" = core_person."first_name")

# Set join type explicitly
from django_join_unrelated import JoinType

Jedi.objects.join(first_name=Person.first_name, join_type=JoinType.FULL)

# Join on multiple fields
Jedi.objects.join(first_name=Person.first_name, last_name=Person.last_name)

# Join multiple models
Jedi.objects.join(first_name=Person.first_name, id=User.id)

django-join-unrelated tries to keep all QuerySet power where it is possible:

Jedi.objects.join(first_name=Person.first_name).filter(last_name='Kenobi')
# <UnrelatedJoinQuerySet [<Jedi: Obi-Wan Kenobi>]>

Jedi.objects.join(first_name=Person.first_name).annotate(name=F('first_name'))[0].name
# 'Obi-Wan'

Jedi.objects.join(first_name=Person.first_name).aggregate(Sum('force'))
# {'force__sum': 100}

Installation

You can install django-join-unrelated using pip:

pip install django-join-unrelated

Usage

Just import UnrelatedJoinManager and set it as an attribute to your models:

from django.db import models
from django_join_unrelated import UnrelatedJoinManager

class Jedi(models.Model):
    first_name = models.CharField('First name', max_length=128)
    last_name = models.CharField('Last name', max_length=128)
    force = models.IntegerField('Force')

    objects = UnrelatedJoinManager()

    class Meta:
        verbose_name = 'Jedi'

Already implemented:

  • filtering via joins

Not implemented yet but planned:

  • unrelated objects selection
  • return of joined values