From d2bfbb727fe7ca83091866d6af31f8790b46525f Mon Sep 17 00:00:00 2001 From: Your Name Date: Mon, 23 Jan 2023 21:24:31 -0500 Subject: [PATCH] recast --- catalog/common/models.py | 19 +++++++++++++++---- catalog/search/typesense.py | 4 +++- catalog/templates/catalog_edit.html | 22 +++++++++++++++++++--- catalog/templates/game.html | 3 --- catalog/templates/tvseason.html | 3 --- catalog/tv/tests.py | 15 +++++++++++++++ catalog/urls.py | 7 +++++++ catalog/views.py | 26 +++++++++++++++++--------- common/templates/partial/_navbar.html | 2 +- 9 files changed, 77 insertions(+), 24 deletions(-) diff --git a/catalog/common/models.py b/catalog/common/models.py index 46e4f87c..c9a40804 100644 --- a/catalog/common/models.py +++ b/catalog/common/models.py @@ -14,6 +14,7 @@ from .utils import DEFAULT_ITEM_COVER, item_cover_path, resource_cover_path from .mixins import SoftDeleteMixin from django.conf import settings from users.models import User +from django.db import connection _logger = logging.getLogger(__name__) @@ -264,10 +265,20 @@ class Item(SoftDeleteMixin, PolymorphicModel): res.item = to_item res.save() - def switch_class_to(self, cls): - _logger.warn(f"switch item across class from {self} to {cls}") - # TODO - pass + def recast_to(self, model): + _logger.warn(f"recast item {self} to {model}") + if self.__class__ == model: + return self + if model not in Item.__subclasses__(): + raise ValueError("invalid model to recast to") + ct = ContentType.objects.get_for_model(model) + tbl = self.__class__._meta.db_table + obj = model(item_ptr_id=self.pk, polymorphic_ctype=ct) + obj.save_base(raw=True) + obj.save(update_fields=["polymorphic_ctype"]) + with connection.cursor() as cursor: + cursor.execute(f"DELETE FROM {tbl} WHERE item_ptr_id = %s", [self.pk]) + return model.objects.get(pk=obj.pk) @property def uuid(self): diff --git a/catalog/search/typesense.py b/catalog/search/typesense.py index 2caacb9f..204521d1 100644 --- a/catalog/search/typesense.py +++ b/catalog/search/typesense.py @@ -170,7 +170,9 @@ class Indexer: for field in obj.__class__.indexable_fields: item[field] = getattr(obj, field) for field in obj.__class__.indexable_fields_time: - item[field] = getattr(obj, field).timestamp() + item[field] = ( + getattr(obj, field).timestamp() if getattr(obj, field) else None + ) for field in obj.__class__.indexable_fields_float: item[field] = float(getattr(obj, field)) if getattr(obj, field) else None for field in obj.__class__.indexable_fields_dict: diff --git a/catalog/templates/catalog_edit.html b/catalog/templates/catalog_edit.html index c43fc57d..bfc112eb 100644 --- a/catalog/templates/catalog_edit.html +++ b/catalog/templates/catalog_edit.html @@ -52,17 +52,33 @@ {% if request.user.is_staff %}
+ + {% if item.class_name == "movie" or item.class_name == "tvshow" %} +
{% trans '切换分类' %}
+
+
+ {% csrf_token %} + {% if item.class_name == "movie" %} + + + {% endif %} + {% if item.class_name == "tvshow" %} + + + {% endif %} +
+
+ {% endif %} +
{% trans '合并到另一条目' %}
{% csrf_token %} -
+
-
-
{% trans '删除' %}
diff --git a/catalog/templates/game.html b/catalog/templates/game.html index ca510fd8..ad9993e2 100644 --- a/catalog/templates/game.html +++ b/catalog/templates/game.html @@ -99,9 +99,6 @@ {% if user.is_authenticated %} {% trans '编辑' %}{{ item.demonstrative }} {% endif %} - {% if user.is_staff %} - / {% trans '删除' %} - {% endif %}
{% endblock %} diff --git a/catalog/templates/tvseason.html b/catalog/templates/tvseason.html index 85fbdbaf..8a4dde7d 100644 --- a/catalog/templates/tvseason.html +++ b/catalog/templates/tvseason.html @@ -177,9 +177,6 @@ {% if user.is_authenticated %} {% trans '编辑' %}{{ item.demonstrative }} {% endif %} - {% if user.is_staff %} - / {% trans '删除' %} - {% endif %}
{% endblock %} diff --git a/catalog/tv/tests.py b/catalog/tv/tests.py index d6bf5c1f..56266f0f 100644 --- a/catalog/tv/tests.py +++ b/catalog/tv/tests.py @@ -134,3 +134,18 @@ class MultiTVSitesTestCase(TestCase): self.assertEqual(p2.item.imdb, p3.item.imdb) self.assertEqual(p1.item.id, p2.item.id) self.assertEqual(p2.item.id, p3.item.id) + + +class MovieTVModelRecastTestCase(TestCase): + @use_local_response + def test_recast(self): + from catalog.models import Movie, TVShow + + url2 = "https://www.imdb.com/title/tt0436992/" + p2 = SiteManager.get_site_by_url(url2).get_resource_ready() + tv = p2.item + self.assertEqual(tv.class_name, "tvshow") + self.assertEqual(tv.title, "神秘博士") + movie = tv.recast_to(Movie) + self.assertEqual(movie.class_name, "movie") + self.assertEqual(movie.title, "神秘博士") diff --git a/catalog/urls.py b/catalog/urls.py index f71a4d57..cd18d870 100644 --- a/catalog/urls.py +++ b/catalog/urls.py @@ -51,6 +51,13 @@ urlpatterns = [ merge, name="merge", ), + re_path( + r"^(?P" + + _get_all_url_paths() + + ")/(?P[A-Za-z0-9]{21,22})/recast$", + recast, + name="recast", + ), re_path( r"^(?P" + _get_all_url_paths() diff --git a/catalog/views.py b/catalog/views.py index 63afe1a1..207c0565 100644 --- a/catalog/views.py +++ b/catalog/views.py @@ -17,7 +17,7 @@ from django.db.models import Count from django.utils import timezone from django.core.paginator import Paginator from polymorphic.base import django -from catalog.common.models import ExternalResource +from catalog.common.models import ExternalResource, ItemCategory from catalog.common.sites import AbstractSite, SiteManager from mastodon import mastodon_request_included from mastodon.models import MastodonApplication @@ -149,14 +149,7 @@ def edit(request, item_path, item_uuid): if item.external_resources.all().count() > 0: form.fields["primary_lookup_id_type"].disabled = True form.fields["primary_lookup_id_value"].disabled = True - return render( - request, - "catalog_edit.html", - { - "form": form, - "item": item, - }, - ) + return render(request, "catalog_edit.html", {"form": form, "item": item}) elif request.method == "POST": item = get_object_or_404(Item, uid=base62.decode(item_uuid)) form_cls = CatalogForms[item.__class__.__name__] @@ -192,6 +185,21 @@ def delete(request, item_path, item_uuid): ) +@login_required +def recast(request, item_path, item_uuid): + if request.method != "POST": + return HttpResponseBadRequest() + if not request.user.is_staff: + raise PermissionDenied() + item = get_object_or_404(Item, uid=base62.decode(item_uuid)) + cls = request.POST.get("class") + model = TVShow if cls == "tvshow" else (Movie if cls == "movie" else None) + if not model: + return HttpResponseBadRequest("invalid class") + new_item = item.recast_to(model) + return redirect(new_item.url) + + @login_required def unlink(request): if request.method != "POST": diff --git a/common/templates/partial/_navbar.html b/common/templates/partial/_navbar.html index d2456c7e..ee08df0b 100644 --- a/common/templates/partial/_navbar.html +++ b/common/templates/partial/_navbar.html @@ -32,7 +32,7 @@ {% trans '数据' %} {% trans '设置' %} {% trans '登出' %} - {% if request.user.is_staff %} + {% if request.user.is_superuser %} {% trans '后台' %} {% endif %}