optimize journal data models
This commit is contained in:
parent
8aa6324334
commit
f24df6c0fd
5 changed files with 114 additions and 67 deletions
|
@ -10,10 +10,10 @@ def thumb(source, alias):
|
|||
This filter modifies that from `easy_thumbnails` so that
|
||||
it can neglect .svg file.
|
||||
"""
|
||||
if source.url.endswith(".svg"):
|
||||
return source.url
|
||||
else:
|
||||
try:
|
||||
try:
|
||||
if source.url.endswith(".svg"):
|
||||
return source.url
|
||||
else:
|
||||
return thumbnail_url(source, alias)
|
||||
except Exception:
|
||||
return ""
|
||||
except Exception as e:
|
||||
return ""
|
||||
|
|
|
@ -13,9 +13,11 @@ from django.utils.translation import gettext_lazy as _
|
|||
from django.core.validators import RegexValidator
|
||||
from functools import cached_property
|
||||
from django.db.models import Count, Avg
|
||||
from django.contrib.contenttypes.models import ContentType
|
||||
import django.dispatch
|
||||
import math
|
||||
import uuid
|
||||
import re
|
||||
from catalog.common.utils import DEFAULT_ITEM_COVER, item_cover_path
|
||||
from django.utils.baseconv import base62
|
||||
from django.db.models import Q
|
||||
|
@ -69,24 +71,6 @@ def query_item_category(item_category):
|
|||
class Piece(PolymorphicModel, UserOwnedObjectMixin):
|
||||
url_path = "piece" # subclass must specify this
|
||||
uid = models.UUIDField(default=uuid.uuid4, editable=False, db_index=True)
|
||||
owner = models.ForeignKey(User, on_delete=models.PROTECT)
|
||||
visibility = models.PositiveSmallIntegerField(
|
||||
default=0
|
||||
) # 0: Public / 1: Follower only / 2: Self only
|
||||
created_time = models.DateTimeField(
|
||||
default=timezone.now
|
||||
) # auto_now_add=True FIXME revert this after migration
|
||||
edited_time = models.DateTimeField(
|
||||
default=timezone.now
|
||||
) # auto_now=True FIXME revert this after migration
|
||||
metadata = models.JSONField(default=dict)
|
||||
attached_to = models.ForeignKey(
|
||||
User,
|
||||
null=True,
|
||||
default=None,
|
||||
on_delete=models.SET_NULL,
|
||||
related_name="attached_with",
|
||||
)
|
||||
|
||||
@property
|
||||
def uuid(self):
|
||||
|
@ -102,10 +86,21 @@ class Piece(PolymorphicModel, UserOwnedObjectMixin):
|
|||
|
||||
@property
|
||||
def api_url(self):
|
||||
return ("/api/" + self.url) if self.url_path else None
|
||||
return f"/api/{self.url}" if self.url_path else None
|
||||
|
||||
|
||||
class Content(Piece):
|
||||
owner = models.ForeignKey(User, on_delete=models.PROTECT)
|
||||
visibility = models.PositiveSmallIntegerField(
|
||||
default=0
|
||||
) # 0: Public / 1: Follower only / 2: Self only
|
||||
created_time = models.DateTimeField(
|
||||
default=timezone.now
|
||||
) # auto_now_add=True FIXME revert this after migration
|
||||
edited_time = models.DateTimeField(
|
||||
default=timezone.now
|
||||
) # auto_now=True FIXME revert this after migration
|
||||
metadata = models.JSONField(default=dict)
|
||||
item = models.ForeignKey(Item, on_delete=models.PROTECT)
|
||||
|
||||
@cached_property
|
||||
|
@ -122,8 +117,22 @@ class Content(Piece):
|
|||
|
||||
|
||||
class Like(Piece):
|
||||
owner = models.ForeignKey(User, on_delete=models.PROTECT)
|
||||
visibility = models.PositiveSmallIntegerField(
|
||||
default=0
|
||||
) # 0: Public / 1: Follower only / 2: Self only
|
||||
created_time = models.DateTimeField(
|
||||
default=timezone.now
|
||||
) # auto_now_add=True FIXME revert this after migration
|
||||
edited_time = models.DateTimeField(
|
||||
default=timezone.now
|
||||
) # auto_now=True FIXME revert this after migration
|
||||
target = models.ForeignKey(Piece, on_delete=models.CASCADE, related_name="likes")
|
||||
|
||||
@staticmethod
|
||||
def user_liked_piece(user, piece):
|
||||
return Like.objects.filter(owner=user, target=piece).first()
|
||||
|
||||
@staticmethod
|
||||
def user_like_piece(user, piece):
|
||||
if not piece or piece.__class__ not in [Collection]:
|
||||
|
@ -139,6 +148,11 @@ class Like(Piece):
|
|||
return
|
||||
Like.objects.filter(owner=user, target=piece).delete()
|
||||
|
||||
@staticmethod
|
||||
def user_likes_by_class(user, cls):
|
||||
ctype_id = ContentType.objects.get_for_model(cls)
|
||||
return Like.objects.filter(owner=user, target__polymorphic_ctype=ctype_id)
|
||||
|
||||
|
||||
class Memo(Content):
|
||||
pass
|
||||
|
@ -272,17 +286,21 @@ list_remove = django.dispatch.Signal()
|
|||
|
||||
|
||||
class List(Piece):
|
||||
owner = models.ForeignKey(User, on_delete=models.PROTECT)
|
||||
visibility = models.PositiveSmallIntegerField(
|
||||
default=0
|
||||
) # 0: Public / 1: Follower only / 2: Self only
|
||||
created_time = models.DateTimeField(
|
||||
default=timezone.now
|
||||
) # auto_now_add=True FIXME revert this after migration
|
||||
edited_time = models.DateTimeField(
|
||||
default=timezone.now
|
||||
) # auto_now=True FIXME revert this after migration
|
||||
metadata = models.JSONField(default=dict)
|
||||
|
||||
class Meta:
|
||||
abstract = True
|
||||
|
||||
_owner = models.ForeignKey(
|
||||
User, on_delete=models.PROTECT
|
||||
) # duplicated owner field to make unique key possible for subclasses
|
||||
|
||||
def save(self, *args, **kwargs):
|
||||
self._owner = self.owner
|
||||
super().save(*args, **kwargs)
|
||||
|
||||
# MEMBER_CLASS = None # subclass must override this
|
||||
# subclass must add this:
|
||||
# items = models.ManyToManyField(Item, through='ListMember')
|
||||
|
@ -375,6 +393,17 @@ class ListMember(Piece):
|
|||
parent = models.ForeignKey('List', related_name='members', on_delete=models.CASCADE)
|
||||
"""
|
||||
|
||||
owner = models.ForeignKey(User, on_delete=models.PROTECT)
|
||||
visibility = models.PositiveSmallIntegerField(
|
||||
default=0
|
||||
) # 0: Public / 1: Follower only / 2: Self only
|
||||
created_time = models.DateTimeField(
|
||||
default=timezone.now
|
||||
) # auto_now_add=True FIXME revert this after migration
|
||||
edited_time = models.DateTimeField(
|
||||
default=timezone.now
|
||||
) # auto_now=True FIXME revert this after migration
|
||||
metadata = models.JSONField(default=dict)
|
||||
item = models.ForeignKey(Item, on_delete=models.PROTECT)
|
||||
position = models.PositiveIntegerField()
|
||||
|
||||
|
@ -430,7 +459,7 @@ class ShelfMember(ListMember):
|
|||
|
||||
class Shelf(List):
|
||||
class Meta:
|
||||
unique_together = [["_owner", "item_category", "shelf_type"]]
|
||||
unique_together = [["owner", "item_category", "shelf_type"]]
|
||||
|
||||
MEMBER_CLASS = ShelfMember
|
||||
items = models.ManyToManyField(Item, through="ShelfMember", related_name="+")
|
||||
|
@ -508,7 +537,7 @@ class ShelfManager:
|
|||
item=item, parent__in=self.owner.shelf_set.all()
|
||||
).first()
|
||||
|
||||
def _shelf_for_item_and_type(item, shelf_type):
|
||||
def _shelf_for_item_and_type(self, item, shelf_type):
|
||||
if not item or not shelf_type:
|
||||
return None
|
||||
return self.owner.shelf_set.all().filter(
|
||||
|
@ -604,6 +633,9 @@ class CollectionMember(ListMember):
|
|||
note = jsondata.CharField(_("备注"), null=True, blank=True)
|
||||
|
||||
|
||||
_RE_HTML_TAG = re.compile(r"<[^>]*>")
|
||||
|
||||
|
||||
class Collection(List):
|
||||
url_path = "collection"
|
||||
MEMBER_CLASS = CollectionMember
|
||||
|
@ -630,7 +662,7 @@ class Collection(List):
|
|||
@property
|
||||
def plain_description(self):
|
||||
html = markdown(self.brief)
|
||||
return RE_HTML_TAG.sub(" ", html)
|
||||
return _RE_HTML_TAG.sub(" ", html)
|
||||
|
||||
def save(self, *args, **kwargs):
|
||||
if getattr(self, "catalog_item", None) is None:
|
||||
|
@ -668,7 +700,7 @@ class Tag(List):
|
|||
# TODO check on save
|
||||
|
||||
class Meta:
|
||||
unique_together = [["_owner", "title"]]
|
||||
unique_together = [["owner", "title"]]
|
||||
|
||||
@staticmethod
|
||||
def cleanup_title(title):
|
||||
|
|
|
@ -96,12 +96,12 @@
|
|||
<div class="action-panel">
|
||||
<div class="action-panel__button-group action-panel__button-group--center">
|
||||
{% if following %}
|
||||
<form action="{% url 'collection:unfollow' collection.id %}" method="post">
|
||||
<form action="{% url 'journal:unlike' collection.uuid %}?back=1" method="post">
|
||||
{% csrf_token %}
|
||||
<button class="action-panel__button">{% trans '取消关注' %}</button>
|
||||
</form>
|
||||
{% else %}
|
||||
<form action="{% url 'collection:follow' collection.id %}" method="post">
|
||||
<form action="{% url 'journal:like' collection.uuid %}?back=1" method="post">
|
||||
{% csrf_token %}
|
||||
<button class="action-panel__button">{% trans '关注' %}</button>
|
||||
</form>
|
||||
|
|
|
@ -18,6 +18,7 @@ def _get_all_shelf_types():
|
|||
urlpatterns = [
|
||||
path("wish/<str:item_uuid>", wish, name="wish"),
|
||||
path("like/<str:piece_uuid>", like, name="like"),
|
||||
path("unlike/<str:piece_uuid>", unlike, name="unlike"),
|
||||
path("mark/<str:item_uuid>", mark, name="mark"),
|
||||
path(
|
||||
"add_to_collection/<str:item_uuid>", add_to_collection, name="add_to_collection"
|
||||
|
|
|
@ -33,42 +33,41 @@ _checkmark = "✔️".encode("utf-8")
|
|||
|
||||
@login_required
|
||||
def wish(request, item_uuid):
|
||||
if request.method == "POST":
|
||||
item = get_object_or_404(Item, uid=base62.decode(item_uuid))
|
||||
if not item:
|
||||
return HttpResponseNotFound(b"item not found")
|
||||
request.user.shelf_manager.move_item(item, ShelfType.WISHLIST)
|
||||
if request.GET.get("back"):
|
||||
return HttpResponseRedirect(request.META.get("HTTP_REFERER"))
|
||||
return HttpResponse(_checkmark)
|
||||
else:
|
||||
if request.method != "POST":
|
||||
return HttpResponseBadRequest(b"invalid request")
|
||||
item = get_object_or_404(Item, uid=base62.decode(item_uuid))
|
||||
if not item:
|
||||
return HttpResponseNotFound(b"item not found")
|
||||
request.user.shelf_manager.move_item(item, ShelfType.WISHLIST)
|
||||
if request.GET.get("back"):
|
||||
return HttpResponseRedirect(request.META.get("HTTP_REFERER"))
|
||||
return HttpResponse(_checkmark)
|
||||
|
||||
|
||||
@login_required
|
||||
def like(request, piece_uuid):
|
||||
if request.method == "POST":
|
||||
piece = get_object_or_404(Collection, uid=base62.decode(piece_uuid))
|
||||
if not piece:
|
||||
return HttpResponseNotFound(b"piece not found")
|
||||
Like.user_like_piece(request.user, piece)
|
||||
return HttpResponse(_checkmark)
|
||||
else:
|
||||
if request.method != "POST":
|
||||
return HttpResponseBadRequest(b"invalid request")
|
||||
piece = get_object_or_404(Collection, uid=base62.decode(piece_uuid))
|
||||
if not piece:
|
||||
return HttpResponseNotFound(b"piece not found")
|
||||
Like.user_like_piece(request.user, piece)
|
||||
if request.GET.get("back"):
|
||||
return HttpResponseRedirect(request.META.get("HTTP_REFERER"))
|
||||
return HttpResponse(_checkmark)
|
||||
|
||||
|
||||
@login_required
|
||||
def unlike(request, piece_uuid):
|
||||
if request.method == "POST":
|
||||
piece = get_object_or_404(Collection, uid=base62.decode(piece_uuid))
|
||||
if not piece:
|
||||
return HttpResponseNotFound(b"piece not found")
|
||||
Like.user_unlike_piece(request.user, piece)
|
||||
if request.GET.get("back"):
|
||||
return HttpResponseRedirect(request.META.get("HTTP_REFERER"))
|
||||
return HttpResponse(_checkmark)
|
||||
else:
|
||||
if request.method != "POST":
|
||||
return HttpResponseBadRequest(b"invalid request")
|
||||
piece = get_object_or_404(Collection, uid=base62.decode(piece_uuid))
|
||||
if not piece:
|
||||
return HttpResponseNotFound(b"piece not found")
|
||||
Like.user_unlike_piece(request.user, piece)
|
||||
if request.GET.get("back"):
|
||||
return HttpResponseRedirect(request.META.get("HTTP_REFERER"))
|
||||
return HttpResponse(_checkmark)
|
||||
|
||||
|
||||
@login_required
|
||||
|
@ -163,7 +162,17 @@ def collection_retrieve(request, collection_uuid):
|
|||
collection = get_object_or_404(Collection, uid=base62.decode(collection_uuid))
|
||||
if not collection.is_visible_to(request.user):
|
||||
raise PermissionDenied()
|
||||
return render(request, "collection.html", {"collection": collection})
|
||||
follower_count = collection.likes.all().count()
|
||||
following = Like.user_liked_piece(request.user, collection) is not None
|
||||
return render(
|
||||
request,
|
||||
"collection.html",
|
||||
{
|
||||
"collection": collection,
|
||||
"follower_count": follower_count,
|
||||
"following": following,
|
||||
},
|
||||
)
|
||||
|
||||
|
||||
def collection_retrieve_items(request, collection_uuid, edit=False):
|
||||
|
@ -563,7 +572,9 @@ def home(request, user_name):
|
|||
Collection.objects.filter(owner=user).filter(qv).order_by("-edited_time")
|
||||
)
|
||||
liked_collections = (
|
||||
Collection.objects.none().filter(likes__owner=user).order_by("-edited_time")
|
||||
Like.user_likes_by_class(user, Collection)
|
||||
.order_by("-edited_time")
|
||||
.values_list("target_id", flat=True)
|
||||
)
|
||||
if user != request.user:
|
||||
liked_collections = liked_collections.filter(query_visible(request.user))
|
||||
|
@ -577,7 +588,10 @@ def home(request, user_name):
|
|||
"shelf_list": shelf_list,
|
||||
"collections": collections[:5],
|
||||
"collections_count": collections.count(),
|
||||
"liked_collections": liked_collections.order_by("-edited_time")[:5],
|
||||
"liked_collections": [
|
||||
Collection.objects.get(id=i)
|
||||
for i in liked_collections.order_by("-edited_time")[:5]
|
||||
],
|
||||
"liked_collections_count": liked_collections.count(),
|
||||
"layout": layout,
|
||||
"reports": reports,
|
||||
|
|
Loading…
Add table
Reference in a new issue