Compare commits

...
4 changed files with 126 additions and 0 deletions
+10
View File
@@ -28,6 +28,7 @@ class PageSerializer(BaseSerializer):
# Many to many
label_ids = serializers.ListField(child=serializers.UUIDField(), required=False)
project_ids = serializers.ListField(child=serializers.UUIDField(), required=False)
sort_order = serializers.SerializerMethodField()
class Meta:
model = Page
@@ -51,6 +52,7 @@ class PageSerializer(BaseSerializer):
"logo_props",
"label_ids",
"project_ids",
"sort_order",
]
read_only_fields = ["workspace", "owned_by"]
@@ -121,6 +123,14 @@ class PageSerializer(BaseSerializer):
return super().update(instance, validated_data)
def get_sort_order(self, obj):
"""Get sort_order from prefetched ProjectPage for the specific project"""
if hasattr(obj, "current_project_page") and obj.current_project_page:
return obj.current_project_page[0].sort_order
# If project_id is not available, return default sort_order
return Page.DEFAULT_SORT_ORDER
class PageDetailSerializer(PageSerializer):
description_html = serializers.CharField()
+10
View File
@@ -15,6 +15,7 @@ from django.db.models import (
Case,
When,
IntegerField,
Prefetch,
)
from django.http import StreamingHttpResponse
from django.contrib.postgres.aggregates import ArrayAgg
@@ -98,6 +99,15 @@ class PageViewSet(BaseViewSet):
.annotate(is_favorite=Exists(subquery))
.order_by(self.request.GET.get("order_by", "-created_at"))
.prefetch_related("labels")
.prefetch_related(
Prefetch(
"project_pages",
queryset=ProjectPage.objects.filter(
project_id=self.kwargs.get("project_id"), deleted_at__isnull=True
),
to_attr="current_project_page",
)
)
.order_by("-is_favorite", "-created_at")
.annotate(
project=Exists(
@@ -0,0 +1,89 @@
# Generated by Django 4.2.22 on 2025-12-15 09:34
from typing import Any
from django.db import migrations, models
from django.db.models import Count, Max
def set_page_sort_order(apps, schema_editor):
Page = apps.get_model("db", "Page")
DEFAULT = 65535
BATCH_SIZE = 3000
INCREMENT_VALUE = 10000
ProjectPage = apps.get_model("db", "ProjectPage")
# move all the parent id sort order to their project page model.
project_pages = ProjectPage.objects.filter(page__parent_id__isnull=False).select_related("page")
project_pages_to_update = []
for project_page in project_pages.iterator(chunk_size=BATCH_SIZE):
project_page.sort_order = project_page.page.sort_order
project_pages_to_update.append(project_page)
if len(project_pages_to_update) >= BATCH_SIZE:
ProjectPage.objects.bulk_update(project_pages_to_update, ["sort_order"], batch_size=BATCH_SIZE)
project_pages_to_update = []
# Update remaining items
if project_pages_to_update:
ProjectPage.objects.bulk_update(project_pages_to_update, ["sort_order"], batch_size=BATCH_SIZE)
# Step 1: Find workspaces with duplicate DEFAULT_SORT_ORDER pages
workspace_ids = (
Page.objects
.filter(sort_order=DEFAULT)
.values("workspace_id")
.annotate(cnt=Count("id"))
.filter(cnt__gt=1)
.values_list("workspace_id", flat=True)
)
for workspace_id in workspace_ids.iterator(chunk_size=500):
# Step 2: get max sort_order in the workspace
max_sort = (
Page.objects
.filter(workspace_id=workspace_id)
.aggregate(m=Max("sort_order"))["m"] or 0
)
# Step 3: fetch duplicate pages
pages = list[Any](
Page.objects
.filter(workspace_id=workspace_id, sort_order=DEFAULT)
.only("id", "sort_order")
)
# Step 4: assign unique increasing sort orders
next_sort = max_sort + INCREMENT_VALUE
for page in pages:
page.sort_order = next_sort
next_sort += INCREMENT_VALUE
Page.objects.bulk_update(pages, ["sort_order"], batch_size=BATCH_SIZE)
def reverse_set_page_sort_order(apps, schema_editor):
ProjectPage = apps.get_model("db", "ProjectPage")
DEFAULT_SORT_ORDER = 65535
ProjectPage.objects.update(sort_order=DEFAULT_SORT_ORDER)
class Migration(migrations.Migration):
dependencies = [
('db', '0112_auto_20251124_0603'),
]
operations = [
migrations.AddField(
model_name='projectpage',
name='sort_order',
field=models.FloatField(default=65535),
),
migrations.RunPython(
set_page_sort_order, reverse_code=reverse_set_page_sort_order
),
]
+17
View File
@@ -132,6 +132,7 @@ class ProjectPage(BaseModel):
project = models.ForeignKey("db.Project", on_delete=models.CASCADE, related_name="project_pages")
page = models.ForeignKey("db.Page", on_delete=models.CASCADE, related_name="project_pages")
workspace = models.ForeignKey("db.Workspace", on_delete=models.CASCADE, related_name="project_pages")
sort_order = models.FloatField(default=Page.DEFAULT_SORT_ORDER)
class Meta:
unique_together = ["project", "page", "deleted_at"]
@@ -149,6 +150,22 @@ class ProjectPage(BaseModel):
def __str__(self):
return f"{self.project.name} {self.page.name}"
def _set_sort_for_root_page(self):
"""
Set sort_order for project pages when the associated page has no parent in the workspace.
"""
largest_sort_order = ProjectPage.objects.filter(project=self.project).aggregate(
largest=models.Max("sort_order")
)["largest"]
if largest_sort_order is not None:
self.sort_order = largest_sort_order + 10000
def save(self, *args, **kwargs):
if self._state.adding:
self._set_sort_for_root_page()
super(ProjectPage, self).save(*args, **kwargs)
class PageVersion(BaseModel):