diff --git a/cookbook/admin.py b/cookbook/admin.py index e3d46fee..2146629c 100644 --- a/cookbook/admin.py +++ b/cookbook/admin.py @@ -93,17 +93,6 @@ class KeywordAdmin(TreeAdmin): form = movenodeform_factory(Keyword) ordering = ('space', 'path',) - # removing ability to delete keywords from admin - # to avoid creating orphaned keywords - # def get_actions(self, request): - # actions = super().get_actions(request) - # if 'delete_selected' in actions: - # del actions['delete_selected'] - # return actions - - # def has_delete_permission(self, request, obj=None): - # return False - admin.site.register(Keyword, KeywordAdmin) @@ -144,7 +133,14 @@ class RecipeAdmin(admin.ModelAdmin): admin.site.register(Recipe, RecipeAdmin) admin.site.register(Unit) -admin.site.register(Food) + + +class FoodAdmin(TreeAdmin): + form = movenodeform_factory(Keyword) + ordering = ('space', 'path',) + + +admin.site.register(Food, FoodAdmin) class IngredientAdmin(admin.ModelAdmin): diff --git a/cookbook/migrations/0153_auto_20210915_2327.py b/cookbook/migrations/0153_auto_20210915_2327.py new file mode 100644 index 00000000..3997e3c3 --- /dev/null +++ b/cookbook/migrations/0153_auto_20210915_2327.py @@ -0,0 +1,106 @@ +# Generated by Django 3.2.7 on 2021-09-15 21:27 + +import django.contrib.postgres.indexes +from django.db import migrations, models + + +class Migration(migrations.Migration): + + dependencies = [ + ('cookbook', '0152_automation'), + ] + + operations = [ + migrations.RemoveIndex( + model_name='cooklog', + name='cookbook_co_id_93d841_idx', + ), + migrations.RemoveIndex( + model_name='food', + name='cookbook_fo_id_22b733_idx', + ), + migrations.RemoveIndex( + model_name='ingredient', + name='cookbook_in_id_3368be_idx', + ), + migrations.RemoveIndex( + model_name='recipe', + name='cookbook_re_name_se_bdf3ca_gin', + ), + migrations.RemoveIndex( + model_name='recipe', + name='cookbook_re_id_e4c2d4_idx', + ), + migrations.RemoveIndex( + model_name='recipebook', + name='cookbook_re_name_bbe446_idx', + ), + migrations.AddIndex( + model_name='cooklog', + index=models.Index(fields=['id'], name='cookbook_co_id_553a6d_idx'), + ), + migrations.AddIndex( + model_name='cooklog', + index=models.Index(fields=['recipe'], name='cookbook_co_recipe__8ec719_idx'), + ), + migrations.AddIndex( + model_name='cooklog', + index=models.Index(fields=['-created_at'], name='cookbook_co_created_f6e244_idx'), + ), + migrations.AddIndex( + model_name='cooklog', + index=models.Index(fields=['rating'], name='cookbook_co_rating_aa7662_idx'), + ), + migrations.AddIndex( + model_name='cooklog', + index=models.Index(fields=['created_by'], name='cookbook_co_created_7ea086_idx'), + ), + migrations.AddIndex( + model_name='cooklog', + index=models.Index(fields=['created_by', 'rating'], name='cookbook_co_created_f5ccd7_idx'), + ), + migrations.AddIndex( + model_name='food', + index=models.Index(fields=['id'], name='cookbook_fo_id_3c379b_idx'), + ), + migrations.AddIndex( + model_name='food', + index=models.Index(fields=['name'], name='cookbook_fo_name_c848b6_idx'), + ), + migrations.AddIndex( + model_name='ingredient', + index=models.Index(fields=['id'], name='cookbook_in_id_2c1f57_idx'), + ), + migrations.AddIndex( + model_name='recipe', + index=django.contrib.postgres.indexes.GinIndex(fields=['name_search_vector'], name='cookbook_re_name_se_5dbbd5_gin'), + ), + migrations.AddIndex( + model_name='recipe', + index=django.contrib.postgres.indexes.GinIndex(fields=['desc_search_vector'], name='cookbook_re_desc_se_fdee30_gin'), + ), + migrations.AddIndex( + model_name='recipe', + index=models.Index(fields=['id'], name='cookbook_re_id_b2bdcf_idx'), + ), + migrations.AddIndex( + model_name='recipe', + index=models.Index(fields=['name'], name='cookbook_re_name_b8a027_idx'), + ), + migrations.AddIndex( + model_name='recipebook', + index=models.Index(fields=['name'], name='cookbook_re_name_94cc63_idx'), + ), + migrations.AddIndex( + model_name='viewlog', + index=models.Index(fields=['recipe'], name='cookbook_vi_recipe__ce995d_idx'), + ), + migrations.AddIndex( + model_name='viewlog', + index=models.Index(fields=['-created_at'], name='cookbook_vi_created_bd2b5f_idx'), + ), + migrations.AddIndex( + model_name='viewlog', + index=models.Index(fields=['created_by'], name='cookbook_vi_created_f9385c_idx'), + ), + ] diff --git a/cookbook/models.py b/cookbook/models.py index 5a29ce4c..0408a4dd 100644 --- a/cookbook/models.py +++ b/cookbook/models.py @@ -336,7 +336,7 @@ class SyncLog(models.Model, PermissionModelMixin): class Keyword(ExportModelOperationsMixin('keyword'), TreeModel, PermissionModelMixin): # TODO add find and fix problem functions - node_order_by = ['name'] + # node_order_by = ['name'] name = models.CharField(max_length=64) icon = models.CharField(max_length=16, blank=True, null=True) description = models.TextField(default="", blank=True) @@ -371,7 +371,7 @@ class Unit(ExportModelOperationsMixin('unit'), models.Model, PermissionModelMixi class Food(ExportModelOperationsMixin('food'), TreeModel, PermissionModelMixin): # TODO add find and fix problem functions - node_order_by = ['name'] + # node_order_by = ['name'] name = models.CharField(max_length=128, validators=[MinLengthValidator(1)]) recipe = models.ForeignKey('Recipe', null=True, blank=True, on_delete=models.SET_NULL) supermarket_category = models.ForeignKey(SupermarketCategory, null=True, blank=True, on_delete=models.SET_NULL) @@ -394,7 +394,10 @@ class Food(ExportModelOperationsMixin('food'), TreeModel, PermissionModelMixin): constraints = [ models.UniqueConstraint(fields=['space', 'name'], name='f_unique_name_per_space') ] - indexes = (Index(fields=['id', 'name']),) + indexes = ( + Index(fields=['id']), + Index(fields=['name']), + ) class Ingredient(ExportModelOperationsMixin('ingredient'), models.Model, PermissionModelMixin): @@ -415,7 +418,9 @@ class Ingredient(ExportModelOperationsMixin('ingredient'), models.Model, Permiss class Meta: ordering = ['order', 'pk'] - indexes = (Index(fields=['id', 'food', 'unit']),) + indexes = ( + Index(fields=['id']), + ) class Step(ExportModelOperationsMixin('step'), models.Model, PermissionModelMixin): @@ -504,7 +509,12 @@ class Recipe(ExportModelOperationsMixin('recipe'), models.Model, PermissionModel return self.name class Meta(): - indexes = (GinIndex(fields=["name_search_vector", "desc_search_vector"]), Index(fields=['id', 'name', 'description']),) + indexes = ( + GinIndex(fields=["name_search_vector"]), + GinIndex(fields=["desc_search_vector"]), + Index(fields=['id']), + Index(fields=['name']), + ) class Comment(ExportModelOperationsMixin('comment'), models.Model, PermissionModelMixin): @@ -555,7 +565,7 @@ class RecipeBook(ExportModelOperationsMixin('book'), models.Model, PermissionMod return self.name class Meta(): - indexes = (Index(fields=['name', 'description']),) + indexes = (Index(fields=['name']),) class RecipeBookEntry(ExportModelOperationsMixin('book_entry'), models.Model, PermissionModelMixin): @@ -756,7 +766,14 @@ class CookLog(ExportModelOperationsMixin('cook_log'), models.Model, PermissionMo return self.recipe.name class Meta(): - indexes = (Index(fields=['id', 'recipe', '-created_at', 'rating', 'created_by']),) + indexes = ( + Index(fields=['id']), + Index(fields=['recipe']), + Index(fields=['-created_at']), + Index(fields=['rating']), + Index(fields=['created_by']), + Index(fields=['created_by', 'rating']), + ) class ViewLog(ExportModelOperationsMixin('view_log'), models.Model, PermissionModelMixin): @@ -771,7 +788,12 @@ class ViewLog(ExportModelOperationsMixin('view_log'), models.Model, PermissionMo return self.recipe.name class Meta(): - indexes = (Index(fields=['recipe', '-created_at', 'created_by']),) + indexes = ( + Index(fields=['recipe']), + Index(fields=[ '-created_at']), + Index(fields=['created_by']), + Index(fields=['recipe', '-created_at', 'created_by']), + ) class ImportLog(models.Model, PermissionModelMixin): diff --git a/cookbook/tests/api/test_api_food.py b/cookbook/tests/api/test_api_food.py index 41b36d47..8e53c753 100644 --- a/cookbook/tests/api/test_api_food.py +++ b/cookbook/tests/api/test_api_food.py @@ -429,7 +429,7 @@ def test_root_filter(obj_1, obj_1_1, obj_1_1_1, obj_2, obj_3, u1_s1): assert len(response['results']) == 2 with scopes_disabled(): - obj_2.move(obj_1, 'sorted-child') + obj_2.move(obj_1, 'last-child') # should return direct children of obj_1 (obj_1_1, obj_2), ignoring query filters response = json.loads(u1_s1.get(f'{reverse(LIST_URL)}?root={obj_1.id}').content) assert response['count'] == 2 @@ -439,7 +439,7 @@ def test_root_filter(obj_1, obj_1_1, obj_1_1_1, obj_2, obj_3, u1_s1): def test_tree_filter(obj_1, obj_1_1, obj_1_1_1, obj_2, obj_3, u1_s1): with scopes_disabled(): - obj_2.move(obj_1, 'sorted-child') + obj_2.move(obj_1, 'last-child') # should return full tree starting at obj_1 (obj_1_1_1, obj_2), ignoring query filters response = json.loads(u1_s1.get(f'{reverse(LIST_URL)}?tree={obj_1.id}').content) assert response['count'] == 4 diff --git a/cookbook/tests/api/test_api_keyword.py b/cookbook/tests/api/test_api_keyword.py index 6213ebe4..82cad211 100644 --- a/cookbook/tests/api/test_api_keyword.py +++ b/cookbook/tests/api/test_api_keyword.py @@ -350,7 +350,7 @@ def test_root_filter(obj_1, obj_1_1, obj_1_1_1, obj_2, obj_3, u1_s1): assert len(response['results']) == 2 with scopes_disabled(): - obj_2.move(obj_1, 'sorted-child') + obj_2.move(obj_1, 'last-child') # should return direct children of obj_1 (obj_1_1, obj_2), ignoring query filters response = json.loads(u1_s1.get(f'{reverse(LIST_URL)}?root={obj_1.id}').content) assert response['count'] == 2 @@ -360,7 +360,7 @@ def test_root_filter(obj_1, obj_1_1, obj_1_1_1, obj_2, obj_3, u1_s1): def test_tree_filter(obj_1, obj_1_1, obj_1_1_1, obj_2, obj_3, u1_s1): with scopes_disabled(): - obj_2.move(obj_1, 'sorted-child') + obj_2.move(obj_1, 'last-child') # should return full tree starting at obj_1 (obj_1_1_1, obj_2), ignoring query filters response = json.loads(u1_s1.get(f'{reverse(LIST_URL)}?tree={obj_1.id}').content) assert response['count'] == 4 diff --git a/cookbook/views/api.py b/cookbook/views/api.py index f733553a..63784a3c 100644 --- a/cookbook/views/api.py +++ b/cookbook/views/api.py @@ -191,7 +191,7 @@ class MergeMixin(ViewSetMixin): # TODO update Units to use merge API if isTree: children = source.get_children().exclude(id=target.id) for c in children: - c.move(target, 'sorted-child') + c.move(target, 'last-child') content = {'msg': _(f'{source.name} was merged successfully with {target.name}')} source.delete() return Response(content, status=status.HTTP_200_OK) @@ -226,7 +226,7 @@ class TreeMixin(MergeMixin, FuzzyFilterMixin): self.queryset = self.model.objects.none() else: return super().get_queryset() - return self.queryset.filter(space=self.request.space) + return self.queryset.filter(space=self.request.space).order_by('name') @decorators.action(detail=True, url_path='move/(?P[^/.]+)', methods=['PUT'], ) @decorators.renderer_classes((TemplateHTMLRenderer, JSONRenderer)) @@ -244,7 +244,7 @@ class TreeMixin(MergeMixin, FuzzyFilterMixin): if parent == 0: try: with scopes_disabled(): - child.move(self.model.get_first_root_node(), 'sorted-sibling') + child.move(self.model.get_first_root_node(), 'last-sibling') content = {'msg': _(f'{child.name} was moved successfully to the root.')} return Response(content, status=status.HTTP_200_OK) except (PathOverflow, InvalidMoveToDescendant, InvalidPosition): @@ -262,7 +262,7 @@ class TreeMixin(MergeMixin, FuzzyFilterMixin): try: with scopes_disabled(): - child.move(parent, 'sorted-child') + child.move(parent, 'last-child') content = {'msg': _(f'{child.name} was moved successfully to parent {parent.name}')} return Response(content, status=status.HTTP_200_OK) except (PathOverflow, InvalidMoveToDescendant, InvalidPosition):