mirror of
https://github.com/makeplane/plane
synced 2024-06-14 14:31:34 +00:00
[WEB - 1552]chore: attach pages to multiple projects (#4741)
* dev: pages migrations * dev: page models * dev: api migrations * chore: apis for pages migrations * chore: dropped project id from page label and logs * dev: pages logger exception --------- Co-authored-by: NarayanBavisetti <narayan3119@gmail.com> Co-authored-by: sriram veeraghanta <veeraghanta.sriram@gmail.com>
This commit is contained in:
parent
61d8586f7f
commit
cb593538e4
@ -8,6 +8,8 @@ from plane.db.models import (
|
||||
PageLog,
|
||||
PageLabel,
|
||||
Label,
|
||||
ProjectPage,
|
||||
Project,
|
||||
)
|
||||
|
||||
|
||||
@ -18,6 +20,7 @@ class PageSerializer(BaseSerializer):
|
||||
write_only=True,
|
||||
required=False,
|
||||
)
|
||||
project = serializers.UUIDField(read_only=True)
|
||||
|
||||
class Meta:
|
||||
model = Page
|
||||
@ -33,17 +36,16 @@ class PageSerializer(BaseSerializer):
|
||||
"is_locked",
|
||||
"archived_at",
|
||||
"workspace",
|
||||
"project",
|
||||
"created_at",
|
||||
"updated_at",
|
||||
"created_by",
|
||||
"updated_by",
|
||||
"view_props",
|
||||
"logo_props",
|
||||
"project",
|
||||
]
|
||||
read_only_fields = [
|
||||
"workspace",
|
||||
"project",
|
||||
"owned_by",
|
||||
]
|
||||
|
||||
@ -57,11 +59,23 @@ class PageSerializer(BaseSerializer):
|
||||
project_id = self.context["project_id"]
|
||||
owned_by_id = self.context["owned_by_id"]
|
||||
description_html = self.context["description_html"]
|
||||
|
||||
# Get the workspace id from the project
|
||||
project = Project.objects.get(pk=project_id)
|
||||
|
||||
page = Page.objects.create(
|
||||
**validated_data,
|
||||
description_html=description_html,
|
||||
project_id=project_id,
|
||||
owned_by_id=owned_by_id,
|
||||
workspace_id=project.workspace_id,
|
||||
)
|
||||
|
||||
ProjectPage.objects.create(
|
||||
workspace_id=page.workspace_id,
|
||||
project_id=project_id,
|
||||
page_id=page.id,
|
||||
created_by_id=page.created_by_id,
|
||||
updated_by_id=page.updated_by_id,
|
||||
)
|
||||
|
||||
if labels is not None:
|
||||
@ -70,7 +84,6 @@ class PageSerializer(BaseSerializer):
|
||||
PageLabel(
|
||||
label=label,
|
||||
page=page,
|
||||
project_id=project_id,
|
||||
workspace_id=page.workspace_id,
|
||||
created_by_id=page.created_by_id,
|
||||
updated_by_id=page.updated_by_id,
|
||||
@ -90,7 +103,6 @@ class PageSerializer(BaseSerializer):
|
||||
PageLabel(
|
||||
label=label,
|
||||
page=instance,
|
||||
project_id=instance.project_id,
|
||||
workspace_id=instance.workspace_id,
|
||||
created_by_id=instance.created_by_id,
|
||||
updated_by_id=instance.updated_by_id,
|
||||
@ -120,7 +132,6 @@ class SubPageSerializer(BaseSerializer):
|
||||
fields = "__all__"
|
||||
read_only_fields = [
|
||||
"workspace",
|
||||
"project",
|
||||
"page",
|
||||
]
|
||||
|
||||
@ -141,6 +152,5 @@ class PageLogSerializer(BaseSerializer):
|
||||
fields = "__all__"
|
||||
read_only_fields = [
|
||||
"workspace",
|
||||
"project",
|
||||
"page",
|
||||
]
|
@ -6,7 +6,7 @@ from django.core.serializers.json import DjangoJSONEncoder
|
||||
|
||||
# Django imports
|
||||
from django.db import connection
|
||||
from django.db.models import Exists, OuterRef, Q
|
||||
from django.db.models import Exists, OuterRef, Q, Subquery
|
||||
from django.utils.decorators import method_decorator
|
||||
from django.views.decorators.gzip import gzip_page
|
||||
from django.http import StreamingHttpResponse
|
||||
@ -15,6 +15,7 @@ from django.http import StreamingHttpResponse
|
||||
from rest_framework import status
|
||||
from rest_framework.response import Response
|
||||
|
||||
|
||||
from plane.app.permissions import ProjectEntityPermission
|
||||
from plane.app.serializers import (
|
||||
PageLogSerializer,
|
||||
@ -27,6 +28,7 @@ from plane.db.models import (
|
||||
PageLog,
|
||||
UserFavorite,
|
||||
ProjectMember,
|
||||
ProjectPage,
|
||||
)
|
||||
|
||||
# Module imports
|
||||
@ -66,28 +68,31 @@ class PageViewSet(BaseViewSet):
|
||||
user=self.request.user,
|
||||
entity_type="page",
|
||||
entity_identifier=OuterRef("pk"),
|
||||
project_id=self.kwargs.get("project_id"),
|
||||
workspace__slug=self.kwargs.get("slug"),
|
||||
)
|
||||
project_subquery = ProjectPage.objects.filter(
|
||||
page_id=OuterRef("id"), project_id=self.kwargs.get("project_id")
|
||||
).values_list("project_id", flat=True)[:1]
|
||||
return self.filter_queryset(
|
||||
super()
|
||||
.get_queryset()
|
||||
.filter(workspace__slug=self.kwargs.get("slug"))
|
||||
.filter(project_id=self.kwargs.get("project_id"))
|
||||
.filter(
|
||||
project__project_projectmember__member=self.request.user,
|
||||
project__project_projectmember__is_active=True,
|
||||
project__archived_at__isnull=True,
|
||||
projects__project_projectmember__member=self.request.user,
|
||||
projects__project_projectmember__is_active=True,
|
||||
projects__archived_at__isnull=True,
|
||||
)
|
||||
.filter(parent__isnull=True)
|
||||
.filter(Q(owned_by=self.request.user) | Q(access=0))
|
||||
.select_related("project")
|
||||
.prefetch_related("projects")
|
||||
.select_related("workspace")
|
||||
.select_related("owned_by")
|
||||
.annotate(is_favorite=Exists(subquery))
|
||||
.order_by(self.request.GET.get("order_by", "-created_at"))
|
||||
.prefetch_related("labels")
|
||||
.order_by("-is_favorite", "-created_at")
|
||||
.annotate(project=Subquery(project_subquery))
|
||||
.filter(project=self.kwargs.get("project_id"))
|
||||
.distinct()
|
||||
)
|
||||
|
||||
@ -115,7 +120,9 @@ class PageViewSet(BaseViewSet):
|
||||
def partial_update(self, request, slug, project_id, pk):
|
||||
try:
|
||||
page = Page.objects.get(
|
||||
pk=pk, workspace__slug=slug, project_id=project_id
|
||||
pk=pk,
|
||||
workspace__slug=slug,
|
||||
projects__id=project_id,
|
||||
)
|
||||
|
||||
if page.is_locked:
|
||||
@ -127,7 +134,9 @@ class PageViewSet(BaseViewSet):
|
||||
parent = request.data.get("parent", None)
|
||||
if parent:
|
||||
_ = Page.objects.get(
|
||||
pk=parent, workspace__slug=slug, project_id=project_id
|
||||
pk=parent,
|
||||
workspace__slug=slug,
|
||||
projects__id=project_id,
|
||||
)
|
||||
|
||||
# Only update access if the page owner is the requesting user
|
||||
@ -187,7 +196,7 @@ class PageViewSet(BaseViewSet):
|
||||
|
||||
def lock(self, request, slug, project_id, pk):
|
||||
page = Page.objects.filter(
|
||||
pk=pk, workspace__slug=slug, project_id=project_id
|
||||
pk=pk, workspace__slug=slug, projects__id=project_id
|
||||
).first()
|
||||
|
||||
page.is_locked = True
|
||||
@ -196,7 +205,7 @@ class PageViewSet(BaseViewSet):
|
||||
|
||||
def unlock(self, request, slug, project_id, pk):
|
||||
page = Page.objects.filter(
|
||||
pk=pk, workspace__slug=slug, project_id=project_id
|
||||
pk=pk, workspace__slug=slug, projects__id=project_id
|
||||
).first()
|
||||
|
||||
page.is_locked = False
|
||||
@ -211,7 +220,7 @@ class PageViewSet(BaseViewSet):
|
||||
|
||||
def archive(self, request, slug, project_id, pk):
|
||||
page = Page.objects.get(
|
||||
pk=pk, workspace__slug=slug, project_id=project_id
|
||||
pk=pk, workspace__slug=slug, projects__id=project_id
|
||||
)
|
||||
|
||||
# only the owner or admin can archive the page
|
||||
@ -238,7 +247,7 @@ class PageViewSet(BaseViewSet):
|
||||
|
||||
def unarchive(self, request, slug, project_id, pk):
|
||||
page = Page.objects.get(
|
||||
pk=pk, workspace__slug=slug, project_id=project_id
|
||||
pk=pk, workspace__slug=slug, projects__id=project_id
|
||||
)
|
||||
|
||||
# only the owner or admin can un archive the page
|
||||
@ -267,7 +276,7 @@ class PageViewSet(BaseViewSet):
|
||||
|
||||
def destroy(self, request, slug, project_id, pk):
|
||||
page = Page.objects.get(
|
||||
pk=pk, workspace__slug=slug, project_id=project_id
|
||||
pk=pk, workspace__slug=slug, projects__id=project_id
|
||||
)
|
||||
|
||||
# only the owner and admin can delete the page
|
||||
@ -380,7 +389,6 @@ class SubPagesEndpoint(BaseAPIView):
|
||||
pages = (
|
||||
PageLog.objects.filter(
|
||||
page_id=page_id,
|
||||
project_id=project_id,
|
||||
workspace__slug=slug,
|
||||
entity_name__in=["forward_link", "back_link"],
|
||||
)
|
||||
@ -399,7 +407,7 @@ class PagesDescriptionViewSet(BaseViewSet):
|
||||
|
||||
def retrieve(self, request, slug, project_id, pk):
|
||||
page = Page.objects.get(
|
||||
pk=pk, workspace__slug=slug, project_id=project_id
|
||||
pk=pk, workspace__slug=slug, projects__id=project_id
|
||||
)
|
||||
binary_data = page.description_binary
|
||||
|
||||
@ -419,7 +427,7 @@ class PagesDescriptionViewSet(BaseViewSet):
|
||||
|
||||
def partial_update(self, request, slug, project_id, pk):
|
||||
page = Page.objects.get(
|
||||
pk=pk, workspace__slug=slug, project_id=project_id
|
||||
pk=pk, workspace__slug=slug, projects__id=project_id
|
||||
)
|
||||
|
||||
base64_data = request.data.get("description_binary")
|
||||
|
@ -147,9 +147,9 @@ class GlobalSearchEndpoint(BaseAPIView):
|
||||
|
||||
pages = Page.objects.filter(
|
||||
q,
|
||||
project__project_projectmember__member=self.request.user,
|
||||
project__project_projectmember__is_active=True,
|
||||
project__archived_at__isnull=True,
|
||||
projects__project_projectmember__member=self.request.user,
|
||||
projects__project_projectmember__is_active=True,
|
||||
projects__archived_at__isnull=True,
|
||||
workspace__slug=slug,
|
||||
)
|
||||
|
||||
@ -249,7 +249,7 @@ class IssueSearchEndpoint(BaseAPIView):
|
||||
workspace__slug=slug,
|
||||
project__project_projectmember__member=self.request.user,
|
||||
project__project_projectmember__is_active=True,
|
||||
project__archived_at__isnull=True
|
||||
project__archived_at__isnull=True,
|
||||
)
|
||||
|
||||
if workspace_search == "false":
|
||||
|
@ -278,7 +278,6 @@ def create_page_labels(workspace, project, user_id, pages_count):
|
||||
PageLabel(
|
||||
page_id=page,
|
||||
label_id=label,
|
||||
project=project,
|
||||
workspace=workspace,
|
||||
)
|
||||
)
|
||||
|
@ -59,7 +59,6 @@ def page_transaction(new_value, old_value, page_id):
|
||||
entity_identifier=mention["entity_identifier"],
|
||||
entity_name=mention["entity_name"],
|
||||
workspace_id=page.workspace_id,
|
||||
project_id=page.project_id,
|
||||
created_at=timezone.now(),
|
||||
updated_at=timezone.now(),
|
||||
)
|
||||
|
@ -0,0 +1,257 @@
|
||||
# Generated by Django 4.2.11 on 2024-06-07 12:04
|
||||
|
||||
from django.conf import settings
|
||||
from django.db import migrations, models
|
||||
import django.db.models.deletion
|
||||
import uuid
|
||||
|
||||
|
||||
def migrate_pages(apps, schema_editor):
|
||||
ProjectPage = apps.get_model("db", "ProjectPage")
|
||||
Page = apps.get_model("db", "Page")
|
||||
ProjectPage.objects.bulk_create(
|
||||
[
|
||||
ProjectPage(
|
||||
workspace_id=page.get("workspace_id"),
|
||||
project_id=page.get("project_id"),
|
||||
page_id=page.get("id"),
|
||||
created_by_id=page.get("created_by_id"),
|
||||
updated_by_id=page.get("updated_by_id"),
|
||||
)
|
||||
for page in Page.objects.values(
|
||||
"workspace_id",
|
||||
"project_id",
|
||||
"id",
|
||||
"created_by_id",
|
||||
"updated_by_id",
|
||||
)
|
||||
],
|
||||
batch_size=1000,
|
||||
)
|
||||
|
||||
|
||||
class Migration(migrations.Migration):
|
||||
|
||||
dependencies = [
|
||||
("db", "0067_issue_estimate"),
|
||||
]
|
||||
|
||||
operations = [
|
||||
migrations.AddField(
|
||||
model_name="page",
|
||||
name="is_global",
|
||||
field=models.BooleanField(default=False),
|
||||
),
|
||||
migrations.CreateModel(
|
||||
name="ProjectPage",
|
||||
fields=[
|
||||
(
|
||||
"created_at",
|
||||
models.DateTimeField(
|
||||
auto_now_add=True, verbose_name="Created At"
|
||||
),
|
||||
),
|
||||
(
|
||||
"updated_at",
|
||||
models.DateTimeField(
|
||||
auto_now=True, verbose_name="Last Modified At"
|
||||
),
|
||||
),
|
||||
(
|
||||
"id",
|
||||
models.UUIDField(
|
||||
db_index=True,
|
||||
default=uuid.uuid4,
|
||||
editable=False,
|
||||
primary_key=True,
|
||||
serialize=False,
|
||||
unique=True,
|
||||
),
|
||||
),
|
||||
(
|
||||
"created_by",
|
||||
models.ForeignKey(
|
||||
null=True,
|
||||
on_delete=django.db.models.deletion.SET_NULL,
|
||||
related_name="%(class)s_created_by",
|
||||
to=settings.AUTH_USER_MODEL,
|
||||
verbose_name="Created By",
|
||||
),
|
||||
),
|
||||
(
|
||||
"page",
|
||||
models.ForeignKey(
|
||||
on_delete=django.db.models.deletion.CASCADE,
|
||||
related_name="project_pages",
|
||||
to="db.page",
|
||||
),
|
||||
),
|
||||
(
|
||||
"project",
|
||||
models.ForeignKey(
|
||||
on_delete=django.db.models.deletion.CASCADE,
|
||||
related_name="project_pages",
|
||||
to="db.project",
|
||||
),
|
||||
),
|
||||
(
|
||||
"updated_by",
|
||||
models.ForeignKey(
|
||||
null=True,
|
||||
on_delete=django.db.models.deletion.SET_NULL,
|
||||
related_name="%(class)s_updated_by",
|
||||
to=settings.AUTH_USER_MODEL,
|
||||
verbose_name="Last Modified By",
|
||||
),
|
||||
),
|
||||
(
|
||||
"workspace",
|
||||
models.ForeignKey(
|
||||
on_delete=django.db.models.deletion.CASCADE,
|
||||
related_name="project_pages",
|
||||
to="db.workspace",
|
||||
),
|
||||
),
|
||||
],
|
||||
options={
|
||||
"verbose_name": "Project Page",
|
||||
"verbose_name_plural": "Project Pages",
|
||||
"db_table": "project_pages",
|
||||
"ordering": ("-created_at",),
|
||||
"unique_together": {("project", "page")},
|
||||
},
|
||||
),
|
||||
migrations.CreateModel(
|
||||
name="TeamPage",
|
||||
fields=[
|
||||
(
|
||||
"created_at",
|
||||
models.DateTimeField(
|
||||
auto_now_add=True, verbose_name="Created At"
|
||||
),
|
||||
),
|
||||
(
|
||||
"updated_at",
|
||||
models.DateTimeField(
|
||||
auto_now=True, verbose_name="Last Modified At"
|
||||
),
|
||||
),
|
||||
(
|
||||
"id",
|
||||
models.UUIDField(
|
||||
db_index=True,
|
||||
default=uuid.uuid4,
|
||||
editable=False,
|
||||
primary_key=True,
|
||||
serialize=False,
|
||||
unique=True,
|
||||
),
|
||||
),
|
||||
(
|
||||
"created_by",
|
||||
models.ForeignKey(
|
||||
null=True,
|
||||
on_delete=django.db.models.deletion.SET_NULL,
|
||||
related_name="%(class)s_created_by",
|
||||
to=settings.AUTH_USER_MODEL,
|
||||
verbose_name="Created By",
|
||||
),
|
||||
),
|
||||
(
|
||||
"page",
|
||||
models.ForeignKey(
|
||||
on_delete=django.db.models.deletion.CASCADE,
|
||||
related_name="team_pages",
|
||||
to="db.page",
|
||||
),
|
||||
),
|
||||
(
|
||||
"team",
|
||||
models.ForeignKey(
|
||||
on_delete=django.db.models.deletion.CASCADE,
|
||||
related_name="team_pages",
|
||||
to="db.team",
|
||||
),
|
||||
),
|
||||
(
|
||||
"updated_by",
|
||||
models.ForeignKey(
|
||||
null=True,
|
||||
on_delete=django.db.models.deletion.SET_NULL,
|
||||
related_name="%(class)s_updated_by",
|
||||
to=settings.AUTH_USER_MODEL,
|
||||
verbose_name="Last Modified By",
|
||||
),
|
||||
),
|
||||
(
|
||||
"workspace",
|
||||
models.ForeignKey(
|
||||
on_delete=django.db.models.deletion.CASCADE,
|
||||
related_name="team_pages",
|
||||
to="db.workspace",
|
||||
),
|
||||
),
|
||||
],
|
||||
options={
|
||||
"verbose_name": "Team Page",
|
||||
"verbose_name_plural": "Team Pages",
|
||||
"db_table": "team_pages",
|
||||
"ordering": ("-created_at",),
|
||||
"unique_together": {("team", "page")},
|
||||
},
|
||||
),
|
||||
migrations.AddField(
|
||||
model_name="page",
|
||||
name="projects",
|
||||
field=models.ManyToManyField(
|
||||
related_name="pages", through="db.ProjectPage", to="db.project"
|
||||
),
|
||||
),
|
||||
migrations.AddField(
|
||||
model_name="page",
|
||||
name="teams",
|
||||
field=models.ManyToManyField(
|
||||
related_name="pages", through="db.TeamPage", to="db.team"
|
||||
),
|
||||
),
|
||||
migrations.RunPython(migrate_pages),
|
||||
migrations.RemoveField(
|
||||
model_name="page",
|
||||
name="project",
|
||||
),
|
||||
migrations.AlterField(
|
||||
model_name="page",
|
||||
name="workspace",
|
||||
field=models.ForeignKey(
|
||||
on_delete=django.db.models.deletion.CASCADE,
|
||||
related_name="pages",
|
||||
to="db.workspace",
|
||||
),
|
||||
),
|
||||
migrations.RemoveField(
|
||||
model_name="pagelabel",
|
||||
name="project",
|
||||
),
|
||||
migrations.RemoveField(
|
||||
model_name="pagelog",
|
||||
name="project",
|
||||
),
|
||||
migrations.AlterField(
|
||||
model_name="pagelabel",
|
||||
name="workspace",
|
||||
field=models.ForeignKey(
|
||||
on_delete=django.db.models.deletion.CASCADE,
|
||||
related_name="workspace_page_label",
|
||||
to="db.workspace",
|
||||
),
|
||||
),
|
||||
migrations.AlterField(
|
||||
model_name="pagelog",
|
||||
name="workspace",
|
||||
field=models.ForeignKey(
|
||||
on_delete=django.db.models.deletion.CASCADE,
|
||||
related_name="workspace_page_log",
|
||||
to="db.workspace",
|
||||
),
|
||||
),
|
||||
]
|
@ -50,7 +50,7 @@ from .notification import (
|
||||
Notification,
|
||||
UserNotificationPreference,
|
||||
)
|
||||
from .page import Page, PageFavorite, PageLabel, PageLog
|
||||
from .page import Page, PageFavorite, PageLabel, PageLog, ProjectPage
|
||||
from .project import (
|
||||
Project,
|
||||
ProjectBaseModel,
|
||||
|
@ -9,13 +9,17 @@ from django.db import models
|
||||
from plane.utils.html_processor import strip_tags
|
||||
|
||||
from .project import ProjectBaseModel
|
||||
from .base import BaseModel
|
||||
|
||||
|
||||
def get_view_props():
|
||||
return {"full_width": False}
|
||||
|
||||
|
||||
class Page(ProjectBaseModel):
|
||||
class Page(BaseModel):
|
||||
workspace = models.ForeignKey(
|
||||
"db.Workspace", on_delete=models.CASCADE, related_name="pages"
|
||||
)
|
||||
name = models.CharField(max_length=255, blank=True)
|
||||
description = models.JSONField(default=dict, blank=True)
|
||||
description_binary = models.BinaryField(null=True)
|
||||
@ -44,6 +48,13 @@ class Page(ProjectBaseModel):
|
||||
is_locked = models.BooleanField(default=False)
|
||||
view_props = models.JSONField(default=get_view_props)
|
||||
logo_props = models.JSONField(default=dict)
|
||||
is_global = models.BooleanField(default=False)
|
||||
projects = models.ManyToManyField(
|
||||
"db.Project", related_name="pages", through="db.ProjectPage"
|
||||
)
|
||||
teams = models.ManyToManyField(
|
||||
"db.Team", related_name="pages", through="db.TeamPage"
|
||||
)
|
||||
|
||||
class Meta:
|
||||
verbose_name = "Page"
|
||||
@ -56,7 +67,7 @@ class Page(ProjectBaseModel):
|
||||
return f"{self.owned_by.email} <{self.name}>"
|
||||
|
||||
|
||||
class PageLog(ProjectBaseModel):
|
||||
class PageLog(BaseModel):
|
||||
TYPE_CHOICES = (
|
||||
("to_do", "To Do"),
|
||||
("issue", "issue"),
|
||||
@ -81,6 +92,9 @@ class PageLog(ProjectBaseModel):
|
||||
choices=TYPE_CHOICES,
|
||||
verbose_name="Transaction Type",
|
||||
)
|
||||
workspace = models.ForeignKey(
|
||||
"db.Workspace", on_delete=models.CASCADE, related_name="workspace_page_log"
|
||||
)
|
||||
|
||||
class Meta:
|
||||
unique_together = ["page", "transaction"]
|
||||
@ -171,13 +185,18 @@ class PageFavorite(ProjectBaseModel):
|
||||
return f"{self.user.email} <{self.page.name}>"
|
||||
|
||||
|
||||
class PageLabel(ProjectBaseModel):
|
||||
class PageLabel(BaseModel):
|
||||
label = models.ForeignKey(
|
||||
"db.Label", on_delete=models.CASCADE, related_name="page_labels"
|
||||
)
|
||||
page = models.ForeignKey(
|
||||
"db.Page", on_delete=models.CASCADE, related_name="page_labels"
|
||||
)
|
||||
workspace = models.ForeignKey(
|
||||
"db.Workspace",
|
||||
on_delete=models.CASCADE,
|
||||
related_name="workspace_page_label",
|
||||
)
|
||||
|
||||
class Meta:
|
||||
verbose_name = "Page Label"
|
||||
@ -187,3 +206,44 @@ class PageLabel(ProjectBaseModel):
|
||||
|
||||
def __str__(self):
|
||||
return f"{self.page.name} {self.label.name}"
|
||||
|
||||
|
||||
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"
|
||||
)
|
||||
|
||||
class Meta:
|
||||
unique_together = ["project", "page"]
|
||||
verbose_name = "Project Page"
|
||||
verbose_name_plural = "Project Pages"
|
||||
db_table = "project_pages"
|
||||
ordering = ("-created_at",)
|
||||
|
||||
def __str__(self):
|
||||
return f"{self.project.name} {self.page.name}"
|
||||
|
||||
|
||||
class TeamPage(BaseModel):
|
||||
team = models.ForeignKey(
|
||||
"db.Team", on_delete=models.CASCADE, related_name="team_pages"
|
||||
)
|
||||
page = models.ForeignKey(
|
||||
"db.Page", on_delete=models.CASCADE, related_name="team_pages"
|
||||
)
|
||||
workspace = models.ForeignKey(
|
||||
"db.Workspace", on_delete=models.CASCADE, related_name="team_pages"
|
||||
)
|
||||
|
||||
class Meta:
|
||||
unique_together = ["team", "page"]
|
||||
verbose_name = "Team Page"
|
||||
verbose_name_plural = "Team Pages"
|
||||
db_table = "team_pages"
|
||||
ordering = ("-created_at",)
|
||||
|
@ -10,14 +10,13 @@ from sentry_sdk import capture_exception
|
||||
|
||||
|
||||
def log_exception(e):
|
||||
print(e)
|
||||
# Log the error
|
||||
logger = logging.getLogger("plane")
|
||||
logger.error(e)
|
||||
|
||||
# Log traceback if running in Debug
|
||||
if settings.DEBUG:
|
||||
logger.error(traceback.format_exc(e))
|
||||
# Print the traceback if in debug mode
|
||||
traceback.print_exc(e)
|
||||
|
||||
# Capture in sentry if configured
|
||||
capture_exception(e)
|
||||
|
Loading…
Reference in New Issue
Block a user