summaryrefslogtreecommitdiff
path: root/tests/filtered_relation
diff options
context:
space:
mode:
authordjango-bot <ops@djangoproject.com>2022-02-03 20:24:19 +0100
committerMariusz Felisiak <felisiak.mariusz@gmail.com>2022-02-07 20:37:05 +0100
commit9c19aff7c7561e3a82978a272ecdaad40dda5c00 (patch)
treef0506b668a013d0063e5fba3dbf4863b466713ba /tests/filtered_relation
parentf68fa8b45dfac545cfc4111d4e52804c86db68d3 (diff)
downloaddjango-9c19aff7c7561e3a82978a272ecdaad40dda5c00.tar.gz
Refs #33476 -- Reformatted code with Black.
Diffstat (limited to 'tests/filtered_relation')
-rw-r--r--tests/filtered_relation/models.py64
-rw-r--r--tests/filtered_relation/tests.py911
2 files changed, 601 insertions, 374 deletions
diff --git a/tests/filtered_relation/models.py b/tests/filtered_relation/models.py
index c7efa4cd04..d34a86305f 100644
--- a/tests/filtered_relation/models.py
+++ b/tests/filtered_relation/models.py
@@ -1,6 +1,4 @@
-from django.contrib.contenttypes.fields import (
- GenericForeignKey, GenericRelation,
-)
+from django.contrib.contenttypes.fields import GenericForeignKey, GenericRelation
from django.contrib.contenttypes.models import ContentType
from django.db import models
@@ -8,9 +6,9 @@ from django.db import models
class Author(models.Model):
name = models.CharField(max_length=50, unique=True)
favorite_books = models.ManyToManyField(
- 'Book',
- related_name='preferred_by_authors',
- related_query_name='preferred_by_authors',
+ "Book",
+ related_name="preferred_by_authors",
+ related_query_name="preferred_by_authors",
)
content_type = models.ForeignKey(ContentType, models.CASCADE, null=True)
object_id = models.PositiveIntegerField(null=True)
@@ -22,20 +20,20 @@ class Editor(models.Model):
class Book(models.Model):
- AVAILABLE = 'available'
- RESERVED = 'reserved'
- RENTED = 'rented'
+ AVAILABLE = "available"
+ RESERVED = "reserved"
+ RENTED = "rented"
STATES = (
- (AVAILABLE, 'Available'),
- (RESERVED, 'reserved'),
- (RENTED, 'Rented'),
+ (AVAILABLE, "Available"),
+ (RESERVED, "reserved"),
+ (RENTED, "Rented"),
)
title = models.CharField(max_length=255)
author = models.ForeignKey(
Author,
models.CASCADE,
- related_name='books',
- related_query_name='book',
+ related_name="books",
+ related_query_name="book",
)
editor = models.ForeignKey(Editor, models.CASCADE)
generic_author = GenericRelation(Author)
@@ -47,45 +45,45 @@ class Borrower(models.Model):
class Reservation(models.Model):
- NEW = 'new'
- STOPPED = 'stopped'
+ NEW = "new"
+ STOPPED = "stopped"
STATES = (
- (NEW, 'New'),
- (STOPPED, 'Stopped'),
+ (NEW, "New"),
+ (STOPPED, "Stopped"),
)
borrower = models.ForeignKey(
Borrower,
models.CASCADE,
- related_name='reservations',
- related_query_name='reservation',
+ related_name="reservations",
+ related_query_name="reservation",
)
book = models.ForeignKey(
Book,
models.CASCADE,
- related_name='reservations',
- related_query_name='reservation',
+ related_name="reservations",
+ related_query_name="reservation",
)
state = models.CharField(max_length=7, choices=STATES, default=NEW)
class RentalSession(models.Model):
- NEW = 'new'
- STOPPED = 'stopped'
+ NEW = "new"
+ STOPPED = "stopped"
STATES = (
- (NEW, 'New'),
- (STOPPED, 'Stopped'),
+ (NEW, "New"),
+ (STOPPED, "Stopped"),
)
borrower = models.ForeignKey(
Borrower,
models.CASCADE,
- related_name='rental_sessions',
- related_query_name='rental_session',
+ related_name="rental_sessions",
+ related_query_name="rental_session",
)
book = models.ForeignKey(
Book,
models.CASCADE,
- related_name='rental_sessions',
- related_query_name='rental_session',
+ related_name="rental_sessions",
+ related_query_name="rental_session",
)
state = models.CharField(max_length=7, choices=STATES, default=NEW)
@@ -103,18 +101,18 @@ class ExchangeRate(models.Model):
from_currency = models.ForeignKey(
Currency,
models.CASCADE,
- related_name='rates_from',
+ related_name="rates_from",
)
to_currency = models.ForeignKey(
Currency,
models.CASCADE,
- related_name='rates_to',
+ related_name="rates_to",
)
rate = models.DecimalField(max_digits=6, decimal_places=4)
class BookDailySales(models.Model):
- book = models.ForeignKey(Book, models.CASCADE, related_name='daily_sales')
+ book = models.ForeignKey(Book, models.CASCADE, related_name="daily_sales")
sale_date = models.DateField()
currency = models.ForeignKey(Currency, models.CASCADE)
seller = models.ForeignKey(Seller, models.CASCADE)
diff --git a/tests/filtered_relation/tests.py b/tests/filtered_relation/tests.py
index 5ab273aeb5..bf099f763a 100644
--- a/tests/filtered_relation/tests.py
+++ b/tests/filtered_relation/tests.py
@@ -4,43 +4,57 @@ from unittest import mock
from django.db import connection, transaction
from django.db.models import (
- Case, Count, DecimalField, F, FilteredRelation, Q, Sum, When,
+ Case,
+ Count,
+ DecimalField,
+ F,
+ FilteredRelation,
+ Q,
+ Sum,
+ When,
)
from django.test import TestCase
from django.test.testcases import skipUnlessDBFeature
from .models import (
- Author, Book, BookDailySales, Borrower, Currency, Editor, ExchangeRate,
- RentalSession, Reservation, Seller,
+ Author,
+ Book,
+ BookDailySales,
+ Borrower,
+ Currency,
+ Editor,
+ ExchangeRate,
+ RentalSession,
+ Reservation,
+ Seller,
)
class FilteredRelationTests(TestCase):
-
@classmethod
def setUpTestData(cls):
- cls.author1 = Author.objects.create(name='Alice')
- cls.author2 = Author.objects.create(name='Jane')
- cls.editor_a = Editor.objects.create(name='a')
- cls.editor_b = Editor.objects.create(name='b')
+ cls.author1 = Author.objects.create(name="Alice")
+ cls.author2 = Author.objects.create(name="Jane")
+ cls.editor_a = Editor.objects.create(name="a")
+ cls.editor_b = Editor.objects.create(name="b")
cls.book1 = Book.objects.create(
- title='Poem by Alice',
+ title="Poem by Alice",
editor=cls.editor_a,
author=cls.author1,
)
cls.book1.generic_author.set([cls.author2])
cls.book2 = Book.objects.create(
- title='The book by Jane A',
+ title="The book by Jane A",
editor=cls.editor_b,
author=cls.author2,
)
cls.book3 = Book.objects.create(
- title='The book by Jane B',
+ title="The book by Jane B",
editor=cls.editor_b,
author=cls.author2,
)
cls.book4 = Book.objects.create(
- title='The book by Alice',
+ title="The book by Alice",
editor=cls.editor_a,
author=cls.author1,
)
@@ -48,81 +62,124 @@ class FilteredRelationTests(TestCase):
cls.author1.favorite_books.add(cls.book3)
def test_select_related(self):
- qs = Author.objects.annotate(
- book_join=FilteredRelation('book'),
- ).select_related('book_join__editor').order_by('pk', 'book_join__pk')
+ qs = (
+ Author.objects.annotate(
+ book_join=FilteredRelation("book"),
+ )
+ .select_related("book_join__editor")
+ .order_by("pk", "book_join__pk")
+ )
with self.assertNumQueries(1):
- self.assertQuerysetEqual(qs, [
- (self.author1, self.book1, self.editor_a, self.author1),
- (self.author1, self.book4, self.editor_a, self.author1),
- (self.author2, self.book2, self.editor_b, self.author2),
- (self.author2, self.book3, self.editor_b, self.author2),
- ], lambda x: (x, x.book_join, x.book_join.editor, x.book_join.author))
+ self.assertQuerysetEqual(
+ qs,
+ [
+ (self.author1, self.book1, self.editor_a, self.author1),
+ (self.author1, self.book4, self.editor_a, self.author1),
+ (self.author2, self.book2, self.editor_b, self.author2),
+ (self.author2, self.book3, self.editor_b, self.author2),
+ ],
+ lambda x: (x, x.book_join, x.book_join.editor, x.book_join.author),
+ )
def test_select_related_multiple(self):
- qs = Book.objects.annotate(
- author_join=FilteredRelation('author'),
- editor_join=FilteredRelation('editor'),
- ).select_related('author_join', 'editor_join').order_by('pk')
- self.assertQuerysetEqual(qs, [
- (self.book1, self.author1, self.editor_a),
- (self.book2, self.author2, self.editor_b),
- (self.book3, self.author2, self.editor_b),
- (self.book4, self.author1, self.editor_a),
- ], lambda x: (x, x.author_join, x.editor_join))
+ qs = (
+ Book.objects.annotate(
+ author_join=FilteredRelation("author"),
+ editor_join=FilteredRelation("editor"),
+ )
+ .select_related("author_join", "editor_join")
+ .order_by("pk")
+ )
+ self.assertQuerysetEqual(
+ qs,
+ [
+ (self.book1, self.author1, self.editor_a),
+ (self.book2, self.author2, self.editor_b),
+ (self.book3, self.author2, self.editor_b),
+ (self.book4, self.author1, self.editor_a),
+ ],
+ lambda x: (x, x.author_join, x.editor_join),
+ )
def test_select_related_with_empty_relation(self):
- qs = Author.objects.annotate(
- book_join=FilteredRelation('book', condition=Q(pk=-1)),
- ).select_related('book_join').order_by('pk')
+ qs = (
+ Author.objects.annotate(
+ book_join=FilteredRelation("book", condition=Q(pk=-1)),
+ )
+ .select_related("book_join")
+ .order_by("pk")
+ )
self.assertSequenceEqual(qs, [self.author1, self.author2])
def test_select_related_foreign_key(self):
- qs = Book.objects.annotate(
- author_join=FilteredRelation('author'),
- ).select_related('author_join').order_by('pk')
+ qs = (
+ Book.objects.annotate(
+ author_join=FilteredRelation("author"),
+ )
+ .select_related("author_join")
+ .order_by("pk")
+ )
with self.assertNumQueries(1):
- self.assertQuerysetEqual(qs, [
- (self.book1, self.author1),
- (self.book2, self.author2),
- (self.book3, self.author2),
- (self.book4, self.author1),
- ], lambda x: (x, x.author_join))
-
- @skipUnlessDBFeature('has_select_for_update', 'has_select_for_update_of')
- def test_select_related_foreign_key_for_update_of(self):
- with transaction.atomic():
- qs = Book.objects.annotate(
- author_join=FilteredRelation('author'),
- ).select_related('author_join').select_for_update(of=('self',)).order_by('pk')
- with self.assertNumQueries(1):
- self.assertQuerysetEqual(qs, [
+ self.assertQuerysetEqual(
+ qs,
+ [
(self.book1, self.author1),
(self.book2, self.author2),
(self.book3, self.author2),
(self.book4, self.author1),
- ], lambda x: (x, x.author_join))
+ ],
+ lambda x: (x, x.author_join),
+ )
+
+ @skipUnlessDBFeature("has_select_for_update", "has_select_for_update_of")
+ def test_select_related_foreign_key_for_update_of(self):
+ with transaction.atomic():
+ qs = (
+ Book.objects.annotate(
+ author_join=FilteredRelation("author"),
+ )
+ .select_related("author_join")
+ .select_for_update(of=("self",))
+ .order_by("pk")
+ )
+ with self.assertNumQueries(1):
+ self.assertQuerysetEqual(
+ qs,
+ [
+ (self.book1, self.author1),
+ (self.book2, self.author2),
+ (self.book3, self.author2),
+ (self.book4, self.author1),
+ ],
+ lambda x: (x, x.author_join),
+ )
def test_without_join(self):
self.assertSequenceEqual(
Author.objects.annotate(
- book_alice=FilteredRelation('book', condition=Q(book__title__iexact='poem by alice')),
+ book_alice=FilteredRelation(
+ "book", condition=Q(book__title__iexact="poem by alice")
+ ),
),
- [self.author1, self.author2]
+ [self.author1, self.author2],
)
def test_with_join(self):
self.assertSequenceEqual(
Author.objects.annotate(
- book_alice=FilteredRelation('book', condition=Q(book__title__iexact='poem by alice')),
+ book_alice=FilteredRelation(
+ "book", condition=Q(book__title__iexact="poem by alice")
+ ),
).filter(book_alice__isnull=False),
- [self.author1]
+ [self.author1],
)
def test_with_exclude(self):
self.assertSequenceEqual(
Author.objects.annotate(
- book_alice=FilteredRelation('book', condition=Q(book__title__iexact='poem by alice')),
+ book_alice=FilteredRelation(
+ "book", condition=Q(book__title__iexact="poem by alice")
+ ),
).exclude(book_alice__isnull=False),
[self.author2],
)
@@ -131,55 +188,72 @@ class FilteredRelationTests(TestCase):
self.assertSequenceEqual(
Author.objects.annotate(
book_alice=FilteredRelation(
- 'book', condition=Q(
- Q(book__title__iexact='poem by alice') |
- Q(book__state=Book.RENTED)
+ "book",
+ condition=Q(
+ Q(book__title__iexact="poem by alice")
+ | Q(book__state=Book.RENTED)
),
),
).filter(book_alice__isnull=False),
- [self.author1]
+ [self.author1],
)
def test_internal_queryset_alias_mapping(self):
queryset = Author.objects.annotate(
- book_alice=FilteredRelation('book', condition=Q(book__title__iexact='poem by alice')),
+ book_alice=FilteredRelation(
+ "book", condition=Q(book__title__iexact="poem by alice")
+ ),
).filter(book_alice__isnull=False)
self.assertIn(
- 'INNER JOIN {} book_alice ON'.format(connection.ops.quote_name('filtered_relation_book')),
- str(queryset.query)
+ "INNER JOIN {} book_alice ON".format(
+ connection.ops.quote_name("filtered_relation_book")
+ ),
+ str(queryset.query),
)
def test_with_multiple_filter(self):
self.assertSequenceEqual(
Author.objects.annotate(
book_editor_a=FilteredRelation(
- 'book',
- condition=Q(book__title__icontains='book', book__editor_id=self.editor_a.pk),
+ "book",
+ condition=Q(
+ book__title__icontains="book", book__editor_id=self.editor_a.pk
+ ),
),
).filter(book_editor_a__isnull=False),
- [self.author1]
+ [self.author1],
)
def test_multiple_times(self):
self.assertSequenceEqual(
Author.objects.annotate(
- book_title_alice=FilteredRelation('book', condition=Q(book__title__icontains='alice')),
- ).filter(book_title_alice__isnull=False).filter(book_title_alice__isnull=False).distinct(),
- [self.author1]
+ book_title_alice=FilteredRelation(
+ "book", condition=Q(book__title__icontains="alice")
+ ),
+ )
+ .filter(book_title_alice__isnull=False)
+ .filter(book_title_alice__isnull=False)
+ .distinct(),
+ [self.author1],
)
def test_exclude_relation_with_join(self):
self.assertSequenceEqual(
Author.objects.annotate(
- book_alice=FilteredRelation('book', condition=~Q(book__title__icontains='alice')),
- ).filter(book_alice__isnull=False).distinct(),
- [self.author2]
+ book_alice=FilteredRelation(
+ "book", condition=~Q(book__title__icontains="alice")
+ ),
+ )
+ .filter(book_alice__isnull=False)
+ .distinct(),
+ [self.author2],
)
def test_with_m2m(self):
qs = Author.objects.annotate(
favorite_books_written_by_jane=FilteredRelation(
- 'favorite_books', condition=Q(favorite_books__in=[self.book2]),
+ "favorite_books",
+ condition=Q(favorite_books__in=[self.book2]),
),
).filter(favorite_books_written_by_jane__isnull=False)
self.assertSequenceEqual(qs, [self.author1])
@@ -187,79 +261,120 @@ class FilteredRelationTests(TestCase):
def test_with_m2m_deep(self):
qs = Author.objects.annotate(
favorite_books_written_by_jane=FilteredRelation(
- 'favorite_books', condition=Q(favorite_books__author=self.author2),
+ "favorite_books",
+ condition=Q(favorite_books__author=self.author2),
),
- ).filter(favorite_books_written_by_jane__title='The book by Jane B')
+ ).filter(favorite_books_written_by_jane__title="The book by Jane B")
self.assertSequenceEqual(qs, [self.author1])
def test_with_m2m_multijoin(self):
- qs = Author.objects.annotate(
- favorite_books_written_by_jane=FilteredRelation(
- 'favorite_books', condition=Q(favorite_books__author=self.author2),
+ qs = (
+ Author.objects.annotate(
+ favorite_books_written_by_jane=FilteredRelation(
+ "favorite_books",
+ condition=Q(favorite_books__author=self.author2),
+ )
)
- ).filter(favorite_books_written_by_jane__editor__name='b').distinct()
+ .filter(favorite_books_written_by_jane__editor__name="b")
+ .distinct()
+ )
self.assertSequenceEqual(qs, [self.author1])
def test_values_list(self):
self.assertSequenceEqual(
Author.objects.annotate(
- book_alice=FilteredRelation('book', condition=Q(book__title__iexact='poem by alice')),
- ).filter(book_alice__isnull=False).values_list('book_alice__title', flat=True),
- ['Poem by Alice']
+ book_alice=FilteredRelation(
+ "book", condition=Q(book__title__iexact="poem by alice")
+ ),
+ )
+ .filter(book_alice__isnull=False)
+ .values_list("book_alice__title", flat=True),
+ ["Poem by Alice"],
)
def test_values(self):
self.assertSequenceEqual(
Author.objects.annotate(
- book_alice=FilteredRelation('book', condition=Q(book__title__iexact='poem by alice')),
- ).filter(book_alice__isnull=False).values(),
- [{'id': self.author1.pk, 'name': 'Alice', 'content_type_id': None, 'object_id': None}]
+ book_alice=FilteredRelation(
+ "book", condition=Q(book__title__iexact="poem by alice")
+ ),
+ )
+ .filter(book_alice__isnull=False)
+ .values(),
+ [
+ {
+ "id": self.author1.pk,
+ "name": "Alice",
+ "content_type_id": None,
+ "object_id": None,
+ }
+ ],
)
def test_extra(self):
self.assertSequenceEqual(
Author.objects.annotate(
- book_alice=FilteredRelation('book', condition=Q(book__title__iexact='poem by alice')),
- ).filter(book_alice__isnull=False).extra(where=['1 = 1']),
- [self.author1]
+ book_alice=FilteredRelation(
+ "book", condition=Q(book__title__iexact="poem by alice")
+ ),
+ )
+ .filter(book_alice__isnull=False)
+ .extra(where=["1 = 1"]),
+ [self.author1],
)
- @skipUnlessDBFeature('supports_select_union')
+ @skipUnlessDBFeature("supports_select_union")
def test_union(self):
qs1 = Author.objects.annotate(
- book_alice=FilteredRelation('book', condition=Q(book__title__iexact='poem by alice')),
+ book_alice=FilteredRelation(
+ "book", condition=Q(book__title__iexact="poem by alice")
+ ),
).filter(book_alice__isnull=False)
qs2 = Author.objects.annotate(
- book_jane=FilteredRelation('book', condition=Q(book__title__iexact='the book by jane a')),
+ book_jane=FilteredRelation(
+ "book", condition=Q(book__title__iexact="the book by jane a")
+ ),
).filter(book_jane__isnull=False)
self.assertSequenceEqual(qs1.union(qs2), [self.author1, self.author2])
- @skipUnlessDBFeature('supports_select_intersection')
+ @skipUnlessDBFeature("supports_select_intersection")
def test_intersection(self):
qs1 = Author.objects.annotate(
- book_alice=FilteredRelation('book', condition=Q(book__title__iexact='poem by alice')),
+ book_alice=FilteredRelation(
+ "book", condition=Q(book__title__iexact="poem by alice")
+ ),
).filter(book_alice__isnull=False)
qs2 = Author.objects.annotate(
- book_jane=FilteredRelation('book', condition=Q(book__title__iexact='the book by jane a')),
+ book_jane=FilteredRelation(
+ "book", condition=Q(book__title__iexact="the book by jane a")
+ ),
).filter(book_jane__isnull=False)
self.assertSequenceEqual(qs1.intersection(qs2), [])
- @skipUnlessDBFeature('supports_select_difference')
+ @skipUnlessDBFeature("supports_select_difference")
def test_difference(self):
qs1 = Author.objects.annotate(
- book_alice=FilteredRelation('book', condition=Q(book__title__iexact='poem by alice')),
+ book_alice=FilteredRelation(
+ "book", condition=Q(book__title__iexact="poem by alice")
+ ),
).filter(book_alice__isnull=False)
qs2 = Author.objects.annotate(
- book_jane=FilteredRelation('book', condition=Q(book__title__iexact='the book by jane a')),
+ book_jane=FilteredRelation(
+ "book", condition=Q(book__title__iexact="the book by jane a")
+ ),
).filter(book_jane__isnull=False)
self.assertSequenceEqual(qs1.difference(qs2), [self.author1])
def test_select_for_update(self):
self.assertSequenceEqual(
Author.objects.annotate(
- book_jane=FilteredRelation('book', condition=Q(book__title__iexact='the book by jane a')),
- ).filter(book_jane__isnull=False).select_for_update(),
- [self.author2]
+ book_jane=FilteredRelation(
+ "book", condition=Q(book__title__iexact="the book by jane a")
+ ),
+ )
+ .filter(book_jane__isnull=False)
+ .select_for_update(),
+ [self.author2],
)
def test_defer(self):
@@ -267,127 +382,198 @@ class FilteredRelationTests(TestCase):
with self.assertNumQueries(2):
self.assertQuerysetEqual(
Author.objects.annotate(
- book_alice=FilteredRelation('book', condition=Q(book__title__iexact='poem by alice')),
- ).filter(book_alice__isnull=False).select_related('book_alice').defer('book_alice__title'),
- ['Poem by Alice'], lambda author: author.book_alice.title
+ book_alice=FilteredRelation(
+ "book", condition=Q(book__title__iexact="poem by alice")
+ ),
+ )
+ .filter(book_alice__isnull=False)
+ .select_related("book_alice")
+ .defer("book_alice__title"),
+ ["Poem by Alice"],
+ lambda author: author.book_alice.title,
)
def test_only_not_supported(self):
- msg = 'only() is not supported with FilteredRelation.'
+ msg = "only() is not supported with FilteredRelation."
with self.assertRaisesMessage(ValueError, msg):
Author.objects.annotate(
- book_alice=FilteredRelation('book', condition=Q(book__title__iexact='poem by alice')),
- ).filter(book_alice__isnull=False).select_related('book_alice').only('book_alice__state')
+ book_alice=FilteredRelation(
+ "book", condition=Q(book__title__iexact="poem by alice")
+ ),
+ ).filter(book_alice__isnull=False).select_related("book_alice").only(
+ "book_alice__state"
+ )
def test_as_subquery(self):
inner_qs = Author.objects.annotate(
- book_alice=FilteredRelation('book', condition=Q(book__title__iexact='poem by alice')),
+ book_alice=FilteredRelation(
+ "book", condition=Q(book__title__iexact="poem by alice")
+ ),
).filter(book_alice__isnull=False)
qs = Author.objects.filter(id__in=inner_qs)
self.assertSequenceEqual(qs, [self.author1])
def test_nested_foreign_key(self):
- qs = Author.objects.annotate(
- book_editor_worked_with=FilteredRelation(
- 'book__editor',
- condition=Q(book__title__icontains='book by'),
- ),
- ).filter(
- book_editor_worked_with__isnull=False,
- ).select_related(
- 'book_editor_worked_with',
- ).order_by('pk', 'book_editor_worked_with__pk')
+ qs = (
+ Author.objects.annotate(
+ book_editor_worked_with=FilteredRelation(
+ "book__editor",
+ condition=Q(book__title__icontains="book by"),
+ ),
+ )
+ .filter(
+ book_editor_worked_with__isnull=False,
+ )
+ .select_related(
+ "book_editor_worked_with",
+ )
+ .order_by("pk", "book_editor_worked_with__pk")
+ )
with self.assertNumQueries(1):
- self.assertQuerysetEqual(qs, [
- (self.author1, self.editor_a),
- (self.author2, self.editor_b),
- (self.author2, self.editor_b),
- ], lambda x: (x, x.book_editor_worked_with))
+ self.assertQuerysetEqual(
+ qs,
+ [
+ (self.author1, self.editor_a),
+ (self.author2, self.editor_b),
+ (self.author2, self.editor_b),
+ ],
+ lambda x: (x, x.book_editor_worked_with),
+ )
def test_nested_foreign_key_nested_field(self):
- qs = Author.objects.annotate(
- book_editor_worked_with=FilteredRelation(
- 'book__editor',
- condition=Q(book__title__icontains='book by')
- ),
- ).filter(
- book_editor_worked_with__isnull=False,
- ).values(
- 'name', 'book_editor_worked_with__name',
- ).order_by('name', 'book_editor_worked_with__name').distinct()
- self.assertSequenceEqual(qs, [
- {'name': self.author1.name, 'book_editor_worked_with__name': self.editor_a.name},
- {'name': self.author2.name, 'book_editor_worked_with__name': self.editor_b.name},
- ])
+ qs = (
+ Author.objects.annotate(
+ book_editor_worked_with=FilteredRelation(
+ "book__editor", condition=Q(book__title__icontains="book by")
+ ),
+ )
+ .filter(
+ book_editor_worked_with__isnull=False,
+ )
+ .values(
+ "name",
+ "book_editor_worked_with__name",
+ )
+ .order_by("name", "book_editor_worked_with__name")
+ .distinct()
+ )
+ self.assertSequenceEqual(
+ qs,
+ [
+ {
+ "name": self.author1.name,
+ "book_editor_worked_with__name": self.editor_a.name,
+ },
+ {
+ "name": self.author2.name,
+ "book_editor_worked_with__name": self.editor_b.name,
+ },
+ ],
+ )
def test_nested_foreign_key_filtered_base_object(self):
- qs = Author.objects.annotate(
- alice_editors=FilteredRelation(
- 'book__editor',
- condition=Q(name='Alice'),
- ),
- ).values(
- 'name', 'alice_editors__pk',
- ).order_by('name', 'alice_editors__name').distinct()
- self.assertSequenceEqual(qs, [
- {'name': self.author1.name, 'alice_editors__pk': self.editor_a.pk},
- {'name': self.author2.name, 'alice_editors__pk': None},
- ])
+ qs = (
+ Author.objects.annotate(
+ alice_editors=FilteredRelation(
+ "book__editor",
+ condition=Q(name="Alice"),
+ ),
+ )
+ .values(
+ "name",
+ "alice_editors__pk",
+ )
+ .order_by("name", "alice_editors__name")
+ .distinct()
+ )
+ self.assertSequenceEqual(
+ qs,
+ [
+ {"name": self.author1.name, "alice_editors__pk": self.editor_a.pk},
+ {"name": self.author2.name, "alice_editors__pk": None},
+ ],
+ )
def test_nested_m2m_filtered(self):
- qs = Book.objects.annotate(
- favorite_book=FilteredRelation(
- 'author__favorite_books',
- condition=Q(author__favorite_books__title__icontains='book by')
- ),
- ).values(
- 'title', 'favorite_book__pk',
- ).order_by('title', 'favorite_book__title')
- self.assertSequenceEqual(qs, [
- {'title': self.book1.title, 'favorite_book__pk': self.book2.pk},
- {'title': self.book1.title, 'favorite_book__pk': self.book3.pk},
- {'title': self.book4.title, 'favorite_book__pk': self.book2.pk},
- {'title': self.book4.title, 'favorite_book__pk': self.book3.pk},
- {'title': self.book2.title, 'favorite_book__pk': None},
- {'title': self.book3.title, 'favorite_book__pk': None},
- ])
+ qs = (
+ Book.objects.annotate(
+ favorite_book=FilteredRelation(
+ "author__favorite_books",
+ condition=Q(author__favorite_books__title__icontains="book by"),
+ ),
+ )
+ .values(
+ "title",
+ "favorite_book__pk",
+ )
+ .order_by("title", "favorite_book__title")
+ )
+ self.assertSequenceEqual(
+ qs,
+ [
+ {"title": self.book1.title, "favorite_book__pk": self.book2.pk},
+ {"title": self.book1.title, "favorite_book__pk": self.book3.pk},
+ {"title": self.book4.title, "favorite_book__pk": self.book2.pk},
+ {"title": self.book4.title, "favorite_book__pk": self.book3.pk},
+ {"title": self.book2.title, "favorite_book__pk": None},
+ {"title": self.book3.title, "favorite_book__pk": None},
+ ],
+ )
def test_nested_chained_relations(self):
- qs = Author.objects.annotate(
- my_books=FilteredRelation(
- 'book', condition=Q(book__title__icontains='book by'),
- ),
- preferred_by_authors=FilteredRelation(
- 'my_books__preferred_by_authors',
- condition=Q(my_books__preferred_by_authors__name='Alice'),
- ),
- ).annotate(
- author=F('name'),
- book_title=F('my_books__title'),
- preferred_by_author_pk=F('preferred_by_authors'),
- ).order_by('author', 'book_title', 'preferred_by_author_pk')
- self.assertQuerysetEqual(qs, [
- ('Alice', 'The book by Alice', None),
- ('Jane', 'The book by Jane A', self.author1.pk),
- ('Jane', 'The book by Jane B', self.author1.pk),
- ], lambda x: (x.author, x.book_title, x.preferred_by_author_pk))
+ qs = (
+ Author.objects.annotate(
+ my_books=FilteredRelation(
+ "book",
+ condition=Q(book__title__icontains="book by"),
+ ),
+ preferred_by_authors=FilteredRelation(
+ "my_books__preferred_by_authors",
+ condition=Q(my_books__preferred_by_authors__name="Alice"),
+ ),
+ )
+ .annotate(
+ author=F("name"),
+ book_title=F("my_books__title"),
+ preferred_by_author_pk=F("preferred_by_authors"),
+ )
+ .order_by("author", "book_title", "preferred_by_author_pk")
+ )
+ self.assertQuerysetEqual(
+ qs,
+ [
+ ("Alice", "The book by Alice", None),
+ ("Jane", "The book by Jane A", self.author1.pk),
+ ("Jane", "The book by Jane B", self.author1.pk),
+ ],
+ lambda x: (x.author, x.book_title, x.preferred_by_author_pk),
+ )
def test_deep_nested_foreign_key(self):
- qs = Book.objects.annotate(
- author_favorite_book_editor=FilteredRelation(
- 'author__favorite_books__editor',
- condition=Q(author__favorite_books__title__icontains='Jane A'),
- ),
- ).filter(
- author_favorite_book_editor__isnull=False,
- ).select_related(
- 'author_favorite_book_editor',
- ).order_by('pk', 'author_favorite_book_editor__pk')
+ qs = (
+ Book.objects.annotate(
+ author_favorite_book_editor=FilteredRelation(
+ "author__favorite_books__editor",
+ condition=Q(author__favorite_books__title__icontains="Jane A"),
+ ),
+ )
+ .filter(
+ author_favorite_book_editor__isnull=False,
+ )
+ .select_related(
+ "author_favorite_book_editor",
+ )
+ .order_by("pk", "author_favorite_book_editor__pk")
+ )
with self.assertNumQueries(1):
- self.assertQuerysetEqual(qs, [
- (self.book1, self.editor_b),
- (self.book4, self.editor_b),
- ], lambda x: (x, x.author_favorite_book_editor))
+ self.assertQuerysetEqual(
+ qs,
+ [
+ (self.book1, self.editor_b),
+ (self.book4, self.editor_b),
+ ],
+ lambda x: (x, x.author_favorite_book_editor),
+ )
def test_relation_name_lookup(self):
msg = (
@@ -397,8 +583,8 @@ class FilteredRelationTests(TestCase):
with self.assertRaisesMessage(ValueError, msg):
Author.objects.annotate(
book_title=FilteredRelation(
- 'book__title__icontains',
- condition=Q(book__title='Poem by Alice'),
+ "book__title__icontains",
+ condition=Q(book__title="Poem by Alice"),
),
)
@@ -410,8 +596,8 @@ class FilteredRelationTests(TestCase):
with self.assertRaisesMessage(ValueError, msg):
Author.objects.annotate(
book_editor=FilteredRelation(
- 'book__editor',
- condition=Q(book__author__name__icontains='book'),
+ "book__editor",
+ condition=Q(book__author__name__icontains="book"),
),
)
@@ -424,63 +610,66 @@ class FilteredRelationTests(TestCase):
with self.assertRaisesMessage(ValueError, msg):
Author.objects.annotate(
book_editor=FilteredRelation(
- 'book',
- condition=Q(book__editor__name__icontains='b'),
+ "book",
+ condition=Q(book__editor__name__icontains="b"),
),
)
def test_with_empty_relation_name_error(self):
- with self.assertRaisesMessage(ValueError, 'relation_name cannot be empty.'):
- FilteredRelation('', condition=Q(blank=''))
+ with self.assertRaisesMessage(ValueError, "relation_name cannot be empty."):
+ FilteredRelation("", condition=Q(blank=""))
def test_with_condition_as_expression_error(self):
- msg = 'condition argument must be a Q() instance.'
+ msg = "condition argument must be a Q() instance."
expression = Case(
- When(book__title__iexact='poem by alice', then=True), default=False,
+ When(book__title__iexact="poem by alice", then=True),
+ default=False,
)
with self.assertRaisesMessage(ValueError, msg):
- FilteredRelation('book', condition=expression)
+ FilteredRelation("book", condition=expression)
def test_with_prefetch_related(self):
- msg = 'prefetch_related() is not supported with FilteredRelation.'
+ msg = "prefetch_related() is not supported with FilteredRelation."
qs = Author.objects.annotate(
- book_title_contains_b=FilteredRelation('book', condition=Q(book__title__icontains='b')),
+ book_title_contains_b=FilteredRelation(
+ "book", condition=Q(book__title__icontains="b")
+ ),
).filter(
book_title_contains_b__isnull=False,
)
with self.assertRaisesMessage(ValueError, msg):
- qs.prefetch_related('book_title_contains_b')
+ qs.prefetch_related("book_title_contains_b")
with self.assertRaisesMessage(ValueError, msg):
- qs.prefetch_related('book_title_contains_b__editor')
+ qs.prefetch_related("book_title_contains_b__editor")
def test_with_generic_foreign_key(self):
self.assertSequenceEqual(
Book.objects.annotate(
generic_authored_book=FilteredRelation(
- 'generic_author',
- condition=Q(generic_author__isnull=False)
+ "generic_author", condition=Q(generic_author__isnull=False)
),
).filter(generic_authored_book__isnull=False),
- [self.book1]
+ [self.book1],
)
def test_eq(self):
- self.assertEqual(FilteredRelation('book', condition=Q(book__title='b')), mock.ANY)
+ self.assertEqual(
+ FilteredRelation("book", condition=Q(book__title="b")), mock.ANY
+ )
class FilteredRelationAggregationTests(TestCase):
-
@classmethod
def setUpTestData(cls):
- cls.author1 = Author.objects.create(name='Alice')
- cls.editor_a = Editor.objects.create(name='a')
+ cls.author1 = Author.objects.create(name="Alice")
+ cls.editor_a = Editor.objects.create(name="a")
cls.book1 = Book.objects.create(
- title='Poem by Alice',
+ title="Poem by Alice",
editor=cls.editor_a,
author=cls.author1,
)
- cls.borrower1 = Borrower.objects.create(name='Jenny')
- cls.borrower2 = Borrower.objects.create(name='Kevin')
+ cls.borrower1 = Borrower.objects.create(name="Jenny")
+ cls.borrower2 = Borrower.objects.create(name="Kevin")
# borrower 1 reserves, rents, and returns book1.
Reservation.objects.create(
borrower=cls.borrower1,
@@ -517,130 +706,158 @@ class FilteredRelationAggregationTests(TestCase):
Goal: Count number of books that are either currently reserved or
rented by borrower1 or available.
"""
- qs = Book.objects.annotate(
- is_reserved_or_rented_by=Case(
- When(reservation__state=Reservation.NEW, then=F('reservation__borrower__pk')),
- When(rental_session__state=RentalSession.NEW, then=F('rental_session__borrower__pk')),
- default=None,
+ qs = (
+ Book.objects.annotate(
+ is_reserved_or_rented_by=Case(
+ When(
+ reservation__state=Reservation.NEW,
+ then=F("reservation__borrower__pk"),
+ ),
+ When(
+ rental_session__state=RentalSession.NEW,
+ then=F("rental_session__borrower__pk"),
+ ),
+ default=None,
+ )
)
- ).filter(
- Q(is_reserved_or_rented_by=self.borrower1.pk) | Q(state=Book.AVAILABLE)
- ).distinct()
+ .filter(
+ Q(is_reserved_or_rented_by=self.borrower1.pk) | Q(state=Book.AVAILABLE)
+ )
+ .distinct()
+ )
self.assertEqual(qs.count(), 1)
# If count is equal to 1, the same aggregation should return in the
# same result but it returns 4.
- self.assertSequenceEqual(qs.annotate(total=Count('pk')).values('total'), [{'total': 4}])
+ self.assertSequenceEqual(
+ qs.annotate(total=Count("pk")).values("total"), [{"total": 4}]
+ )
# With FilteredRelation, the result is as expected (1).
- qs = Book.objects.annotate(
- active_reservations=FilteredRelation(
- 'reservation', condition=Q(
- reservation__state=Reservation.NEW,
- reservation__borrower=self.borrower1,
- )
- ),
- ).annotate(
- active_rental_sessions=FilteredRelation(
- 'rental_session', condition=Q(
- rental_session__state=RentalSession.NEW,
- rental_session__borrower=self.borrower1,
+ qs = (
+ Book.objects.annotate(
+ active_reservations=FilteredRelation(
+ "reservation",
+ condition=Q(
+ reservation__state=Reservation.NEW,
+ reservation__borrower=self.borrower1,
+ ),
+ ),
+ )
+ .annotate(
+ active_rental_sessions=FilteredRelation(
+ "rental_session",
+ condition=Q(
+ rental_session__state=RentalSession.NEW,
+ rental_session__borrower=self.borrower1,
+ ),
+ ),
+ )
+ .filter(
+ (
+ Q(active_reservations__isnull=False)
+ | Q(active_rental_sessions__isnull=False)
)
- ),
- ).filter(
- (Q(active_reservations__isnull=False) | Q(active_rental_sessions__isnull=False)) |
- Q(state=Book.AVAILABLE)
- ).distinct()
+ | Q(state=Book.AVAILABLE)
+ )
+ .distinct()
+ )
self.assertEqual(qs.count(), 1)
- self.assertSequenceEqual(qs.annotate(total=Count('pk')).values('total'), [{'total': 1}])
+ self.assertSequenceEqual(
+ qs.annotate(total=Count("pk")).values("total"), [{"total": 1}]
+ )
class FilteredRelationAnalyticalAggregationTests(TestCase):
@classmethod
def setUpTestData(cls):
- author = Author.objects.create(name='Author')
- editor = Editor.objects.create(name='Editor')
+ author = Author.objects.create(name="Author")
+ editor = Editor.objects.create(name="Editor")
cls.book1 = Book.objects.create(
- title='Poem by Alice',
+ title="Poem by Alice",
editor=editor,
author=author,
)
cls.book2 = Book.objects.create(
- title='The book by Jane A',
+ title="The book by Jane A",
editor=editor,
author=author,
)
cls.book3 = Book.objects.create(
- title='The book by Jane B',
+ title="The book by Jane B",
editor=editor,
author=author,
)
- cls.seller1 = Seller.objects.create(name='Seller 1')
- cls.seller2 = Seller.objects.create(name='Seller 2')
- cls.usd = Currency.objects.create(currency='USD')
- cls.eur = Currency.objects.create(currency='EUR')
+ cls.seller1 = Seller.objects.create(name="Seller 1")
+ cls.seller2 = Seller.objects.create(name="Seller 2")
+ cls.usd = Currency.objects.create(currency="USD")
+ cls.eur = Currency.objects.create(currency="EUR")
cls.sales_date1 = date(2020, 7, 6)
cls.sales_date2 = date(2020, 7, 7)
- ExchangeRate.objects.bulk_create([
- ExchangeRate(
- rate_date=cls.sales_date1,
- from_currency=cls.usd,
- to_currency=cls.eur,
- rate=0.40,
- ),
- ExchangeRate(
- rate_date=cls.sales_date1,
- from_currency=cls.eur,
- to_currency=cls.usd,
- rate=1.60,
- ),
- ExchangeRate(
- rate_date=cls.sales_date2,
- from_currency=cls.usd,
- to_currency=cls.eur,
- rate=0.50,
- ),
- ExchangeRate(
- rate_date=cls.sales_date2,
- from_currency=cls.eur,
- to_currency=cls.usd,
- rate=1.50,
- ),
- ExchangeRate(
- rate_date=cls.sales_date2,
- from_currency=cls.usd,
- to_currency=cls.usd,
- rate=1.00,
- ),
- ])
- BookDailySales.objects.bulk_create([
- BookDailySales(
- book=cls.book1,
- sale_date=cls.sales_date1,
- currency=cls.usd,
- sales=100.00,
- seller=cls.seller1,
- ),
- BookDailySales(
- book=cls.book2,
- sale_date=cls.sales_date1,
- currency=cls.eur,
- sales=200.00,
- seller=cls.seller1,
- ),
- BookDailySales(
- book=cls.book1,
- sale_date=cls.sales_date2,
- currency=cls.usd,
- sales=50.00,
- seller=cls.seller2,
- ),
- BookDailySales(
- book=cls.book2,
- sale_date=cls.sales_date2,
- currency=cls.eur,
- sales=100.00,
- seller=cls.seller2,
- ),
- ])
+ ExchangeRate.objects.bulk_create(
+ [
+ ExchangeRate(
+ rate_date=cls.sales_date1,
+ from_currency=cls.usd,
+ to_currency=cls.eur,
+ rate=0.40,
+ ),
+ ExchangeRate(
+ rate_date=cls.sales_date1,
+ from_currency=cls.eur,
+ to_currency=cls.usd,
+ rate=1.60,
+ ),
+ ExchangeRate(
+ rate_date=cls.sales_date2,
+ from_currency=cls.usd,
+ to_currency=cls.eur,
+ rate=0.50,
+ ),
+ ExchangeRate(
+ rate_date=cls.sales_date2,
+ from_currency=cls.eur,
+ to_currency=cls.usd,
+ rate=1.50,
+ ),
+ ExchangeRate(
+ rate_date=cls.sales_date2,
+ from_currency=cls.usd,
+ to_currency=cls.usd,
+ rate=1.00,
+ ),
+ ]
+ )
+ BookDailySales.objects.bulk_create(
+ [
+ BookDailySales(
+ book=cls.book1,
+ sale_date=cls.sales_date1,
+ currency=cls.usd,
+ sales=100.00,
+ seller=cls.seller1,
+ ),
+ BookDailySales(
+ book=cls.book2,
+ sale_date=cls.sales_date1,
+ currency=cls.eur,
+ sales=200.00,
+ seller=cls.seller1,
+ ),
+ BookDailySales(
+ book=cls.book1,
+ sale_date=cls.sales_date2,
+ currency=cls.usd,
+ sales=50.00,
+ seller=cls.seller2,
+ ),
+ BookDailySales(
+ book=cls.book2,
+ sale_date=cls.sales_date2,
+ currency=cls.eur,
+ sales=100.00,
+ seller=cls.seller2,
+ ),
+ ]
+ )
def test_aggregate(self):
tests = [
@@ -649,25 +866,37 @@ class FilteredRelationAnalyticalAggregationTests(TestCase):
]
for condition in tests:
with self.subTest(condition=condition):
- qs = Book.objects.annotate(
- recent_sales=FilteredRelation('daily_sales', condition=condition),
- recent_sales_rates=FilteredRelation(
- 'recent_sales__currency__rates_from',
- condition=Q(
- recent_sales__currency__rates_from__rate_date=F('recent_sales__sale_date'),
- recent_sales__currency__rates_from__to_currency=self.usd,
+ qs = (
+ Book.objects.annotate(
+ recent_sales=FilteredRelation(
+ "daily_sales", condition=condition
),
- ),
- ).annotate(
- sales_sum=Sum(
- F('recent_sales__sales') * F('recent_sales_rates__rate'),
- output_field=DecimalField(),
- ),
- ).values('title', 'sales_sum').order_by(
- F('sales_sum').desc(nulls_last=True),
+ recent_sales_rates=FilteredRelation(
+ "recent_sales__currency__rates_from",
+ condition=Q(
+ recent_sales__currency__rates_from__rate_date=F(
+ "recent_sales__sale_date"
+ ),
+ recent_sales__currency__rates_from__to_currency=self.usd,
+ ),
+ ),
+ )
+ .annotate(
+ sales_sum=Sum(
+ F("recent_sales__sales") * F("recent_sales_rates__rate"),
+ output_field=DecimalField(),
+ ),
+ )
+ .values("title", "sales_sum")
+ .order_by(
+ F("sales_sum").desc(nulls_last=True),
+ )
+ )
+ self.assertSequenceEqual(
+ qs,
+ [
+ {"title": self.book2.title, "sales_sum": Decimal(150.00)},
+ {"title": self.book1.title, "sales_sum": Decimal(50.00)},
+ {"title": self.book3.title, "sales_sum": None},
+ ],
)
- self.assertSequenceEqual(qs, [
- {'title': self.book2.title, 'sales_sum': Decimal(150.00)},
- {'title': self.book1.title, 'sales_sum': Decimal(50.00)},
- {'title': self.book3.title, 'sales_sum': None},
- ])