reduce some N+1 query in mark
This commit is contained in:
parent
70cf534d12
commit
dee347eabf
3 changed files with 67 additions and 12 deletions
|
@ -163,7 +163,7 @@ class Mark:
|
||||||
log entries
|
log entries
|
||||||
log entry will be created when item is added to shelf
|
log entry will be created when item is added to shelf
|
||||||
log entry will be created when item is moved to another shelf
|
log entry will be created when item is moved to another shelf
|
||||||
log entry will be created when item is removed from shelf (TODO change this to DEFERRED shelf)
|
log entry will be created when item is removed from shelf
|
||||||
timestamp of log entry will be updated whenever created_time of shelfmember is updated
|
timestamp of log entry will be updated whenever created_time of shelfmember is updated
|
||||||
any log entry can be deleted by user arbitrarily
|
any log entry can be deleted by user arbitrarily
|
||||||
|
|
||||||
|
|
|
@ -7,6 +7,7 @@ from django.db import connection, models
|
||||||
from django.utils import timezone
|
from django.utils import timezone
|
||||||
from django.utils.translation import gettext_lazy as _
|
from django.utils.translation import gettext_lazy as _
|
||||||
from loguru import logger
|
from loguru import logger
|
||||||
|
from polymorphic.models import PolymorphicManager
|
||||||
|
|
||||||
from catalog.models import Item, ItemCategory
|
from catalog.models import Item, ItemCategory
|
||||||
from takahe.utils import Takahe
|
from takahe.utils import Takahe
|
||||||
|
@ -310,6 +311,28 @@ _SHELF_LABELS = [
|
||||||
# grammatically problematic, for translation only
|
# grammatically problematic, for translation only
|
||||||
|
|
||||||
|
|
||||||
|
class ShelfMemberManager(PolymorphicManager):
|
||||||
|
def get_queryset(self):
|
||||||
|
from .comment import Comment
|
||||||
|
from .rating import Rating
|
||||||
|
|
||||||
|
rating_subquery = Rating.objects.filter(
|
||||||
|
owner_id=models.OuterRef("owner_id"), item_id=models.OuterRef("item_id")
|
||||||
|
).values("grade")[:1]
|
||||||
|
comment_subquery = Comment.objects.filter(
|
||||||
|
owner_id=models.OuterRef("owner_id"), item_id=models.OuterRef("item_id")
|
||||||
|
).values("text")[:1]
|
||||||
|
return (
|
||||||
|
super()
|
||||||
|
.get_queryset()
|
||||||
|
.annotate(
|
||||||
|
_rating_grade=models.Subquery(rating_subquery),
|
||||||
|
_comment_text=models.Subquery(comment_subquery),
|
||||||
|
_shelf_type=models.F("parent__shelf_type"),
|
||||||
|
)
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
class ShelfMember(ListMember):
|
class ShelfMember(ListMember):
|
||||||
if TYPE_CHECKING:
|
if TYPE_CHECKING:
|
||||||
parent: models.ForeignKey["ShelfMember", "Shelf"]
|
parent: models.ForeignKey["ShelfMember", "Shelf"]
|
||||||
|
@ -318,6 +341,8 @@ class ShelfMember(ListMember):
|
||||||
"Shelf", related_name="members", on_delete=models.CASCADE
|
"Shelf", related_name="members", on_delete=models.CASCADE
|
||||||
)
|
)
|
||||||
|
|
||||||
|
objects = ShelfMemberManager()
|
||||||
|
|
||||||
class Meta:
|
class Meta:
|
||||||
unique_together = [["owner", "item"]]
|
unique_together = [["owner", "item"]]
|
||||||
indexes = [
|
indexes = [
|
||||||
|
@ -448,6 +473,15 @@ class ShelfMember(ListMember):
|
||||||
"content": content,
|
"content": content,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
def save(self, *args, **kwargs):
|
||||||
|
try:
|
||||||
|
del self._shelf_type # type:ignore
|
||||||
|
del self._rating_grade # type:ignore
|
||||||
|
del self._comment_text # type:ignore
|
||||||
|
except AttributeError:
|
||||||
|
pass
|
||||||
|
return super().save(*args, **kwargs)
|
||||||
|
|
||||||
@cached_property
|
@cached_property
|
||||||
def sibling_comment(self) -> "Comment | None":
|
def sibling_comment(self) -> "Comment | None":
|
||||||
from .comment import Comment
|
from .comment import Comment
|
||||||
|
@ -470,18 +504,27 @@ class ShelfMember(ListMember):
|
||||||
|
|
||||||
@property
|
@property
|
||||||
def shelf_label(self) -> str | None:
|
def shelf_label(self) -> str | None:
|
||||||
return ShelfManager.get_label(self.parent.shelf_type, self.item.category)
|
return ShelfManager.get_label(self.shelf_type, self.item.category)
|
||||||
|
|
||||||
@property
|
@property
|
||||||
def shelf_type(self):
|
def shelf_type(self):
|
||||||
|
try:
|
||||||
|
return getattr(self, "_shelf_type")
|
||||||
|
except AttributeError:
|
||||||
return self.parent.shelf_type
|
return self.parent.shelf_type
|
||||||
|
|
||||||
@property
|
@property
|
||||||
def rating_grade(self):
|
def rating_grade(self):
|
||||||
|
try:
|
||||||
|
return getattr(self, "_rating_grade")
|
||||||
|
except AttributeError:
|
||||||
return self.mark.rating_grade
|
return self.mark.rating_grade
|
||||||
|
|
||||||
@property
|
@property
|
||||||
def comment_text(self):
|
def comment_text(self):
|
||||||
|
try:
|
||||||
|
return getattr(self, "_comment_text")
|
||||||
|
except AttributeError:
|
||||||
return self.mark.comment_text
|
return self.mark.comment_text
|
||||||
|
|
||||||
@property
|
@property
|
||||||
|
|
|
@ -65,23 +65,34 @@ class ShelfTest(TestCase):
|
||||||
self.assertEqual(q1.members.all().count(), 0)
|
self.assertEqual(q1.members.all().count(), 0)
|
||||||
self.assertEqual(q2.members.all().count(), 0)
|
self.assertEqual(q2.members.all().count(), 0)
|
||||||
Mark(user.identity, book1).update(ShelfType.WISHLIST)
|
Mark(user.identity, book1).update(ShelfType.WISHLIST)
|
||||||
time.sleep(0.001) # add a little delay to make sure the timestamp is different
|
|
||||||
Mark(user.identity, book2).update(ShelfType.WISHLIST)
|
Mark(user.identity, book2).update(ShelfType.WISHLIST)
|
||||||
|
log = [ll.shelf_type for ll in shelf_manager.get_log_for_item(book1)]
|
||||||
|
self.assertEqual(log, ["wishlist"])
|
||||||
|
log = [ll.shelf_type for ll in shelf_manager.get_log_for_item(book2)]
|
||||||
|
self.assertEqual(log, ["wishlist"])
|
||||||
|
time.sleep(0.001) # add a little delay to make sure the timestamp is different
|
||||||
|
|
||||||
|
Mark(user.identity, book1).update(ShelfType.WISHLIST)
|
||||||
|
log = [ll.shelf_type for ll in shelf_manager.get_log_for_item(book1)]
|
||||||
|
self.assertEqual(log, ["wishlist"])
|
||||||
time.sleep(0.001)
|
time.sleep(0.001)
|
||||||
|
|
||||||
self.assertEqual(q1.members.all().count(), 2)
|
self.assertEqual(q1.members.all().count(), 2)
|
||||||
Mark(user.identity, book1).update(ShelfType.PROGRESS)
|
Mark(user.identity, book1).update(ShelfType.PROGRESS)
|
||||||
time.sleep(0.001)
|
|
||||||
self.assertEqual(q1.members.all().count(), 1)
|
self.assertEqual(q1.members.all().count(), 1)
|
||||||
self.assertEqual(q2.members.all().count(), 1)
|
self.assertEqual(q2.members.all().count(), 1)
|
||||||
|
time.sleep(0.001)
|
||||||
|
|
||||||
self.assertEqual(len(Mark(user.identity, book1).all_post_ids), 2)
|
self.assertEqual(len(Mark(user.identity, book1).all_post_ids), 2)
|
||||||
log = shelf_manager.get_log_for_item(book1)
|
log = [ll.shelf_type for ll in shelf_manager.get_log_for_item(book1)]
|
||||||
self.assertEqual(log.count(), 2)
|
|
||||||
|
self.assertEqual(log, ["wishlist", "progress"])
|
||||||
Mark(user.identity, book1).update(ShelfType.PROGRESS, metadata={"progress": 1})
|
Mark(user.identity, book1).update(ShelfType.PROGRESS, metadata={"progress": 1})
|
||||||
time.sleep(0.001)
|
time.sleep(0.001)
|
||||||
self.assertEqual(q1.members.all().count(), 1)
|
self.assertEqual(q1.members.all().count(), 1)
|
||||||
self.assertEqual(q2.members.all().count(), 1)
|
self.assertEqual(q2.members.all().count(), 1)
|
||||||
log = shelf_manager.get_log_for_item(book1)
|
log = [ll.shelf_type for ll in shelf_manager.get_log_for_item(book1)]
|
||||||
self.assertEqual(log.count(), 2)
|
self.assertEqual(log, ["wishlist", "progress"])
|
||||||
self.assertEqual(len(Mark(user.identity, book1).all_post_ids), 2)
|
self.assertEqual(len(Mark(user.identity, book1).all_post_ids), 2)
|
||||||
|
|
||||||
# theses tests are not relevant anymore, bc we don't use log to track metadata changes
|
# theses tests are not relevant anymore, bc we don't use log to track metadata changes
|
||||||
|
@ -127,7 +138,8 @@ class ShelfTest(TestCase):
|
||||||
|
|
||||||
# test delete mark -> one more log
|
# test delete mark -> one more log
|
||||||
Mark(user.identity, book1).delete()
|
Mark(user.identity, book1).delete()
|
||||||
self.assertEqual(log.count(), 4)
|
log = [ll.shelf_type for ll in shelf_manager.get_log_for_item(book1)]
|
||||||
|
self.assertEqual(log, ["wishlist", "progress", "complete", None])
|
||||||
deleted_mark = Mark(user.identity, book1)
|
deleted_mark = Mark(user.identity, book1)
|
||||||
self.assertEqual(deleted_mark.shelf_type, None)
|
self.assertEqual(deleted_mark.shelf_type, None)
|
||||||
self.assertEqual(deleted_mark.tags, [])
|
self.assertEqual(deleted_mark.tags, [])
|
||||||
|
|
Loading…
Add table
Reference in a new issue