Skip to content

Commit

Permalink
Test out django-rules for template conditionals in Django (#612)
Browse files Browse the repository at this point in the history
  • Loading branch information
justuswilhelm authored Jan 21, 2025
2 parents 9264405 + 179d747 commit b6131b3
Show file tree
Hide file tree
Showing 11 changed files with 315 additions and 284 deletions.
6 changes: 0 additions & 6 deletions .circleci/config.yml
Original file line number Diff line number Diff line change
Expand Up @@ -86,12 +86,6 @@ jobs:
- run:
name: Poetry check
command: poetry check
- run:
name: Create requirements.txt
command: poetry export --format requirements.txt --output requirements.txt
- run:
name: Check if requirements.txt up to date
command: git diff --exit-code HEAD -- requirements.txt
backend_lint:
executor: python
working_directory: ~/projectify/backend
Expand Down
8 changes: 4 additions & 4 deletions backend/poetry.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

7 changes: 7 additions & 0 deletions backend/projectify/settings/development.py
Original file line number Diff line number Diff line change
Expand Up @@ -148,6 +148,13 @@ class Development(SpectacularSettings, Base):
# Feature flags
ENABLE_DJANGO_DASHBOARD = True

# Enable template debugging
TEMPLATES = Base.TEMPLATES
TEMPLATES[0]["OPTIONS"] = {
**TEMPLATES[0]["OPTIONS"],
"debug": True,
}

@classmethod
def pre_setup(cls) -> None:
"""Load environment variables from .env."""
Expand Down
2 changes: 2 additions & 0 deletions backend/projectify/workspace/admin.py
Original file line number Diff line number Diff line change
Expand Up @@ -85,7 +85,9 @@ class TeamMemberAdmin(admin.ModelAdmin[TeamMember]):
"user_email",
"created",
"modified",
"role",
)
list_filter = ("role",)
list_select_related = (
"workspace",
"user",
Expand Down
34 changes: 6 additions & 28 deletions backend/projectify/workspace/services/task.py
Original file line number Diff line number Diff line change
Expand Up @@ -126,26 +126,6 @@ def task_create_nested(


# Update
def task_update(
*,
who: User,
task: Task,
title: str,
description: Optional[str] = None,
due_date: Optional[datetime] = None,
assignee: Optional[TeamMember] = None,
) -> Task:
"""Add a task to this section."""
validate_perm("workspace.update_task", who, task.workspace)
task.title = title
task.description = description
task.due_date = due_date
task.assignee = assignee
task.save()
return task


# TODO make this method replace the above task_update
@transaction.atomic
def task_update_nested(
*,
Expand All @@ -160,14 +140,12 @@ def task_update_nested(
sub_tasks: Optional[ValidatedData] = None,
) -> Task:
"""Assign labels, assign assignee."""
task = task_update(
who=who,
task=task,
title=title,
description=description,
due_date=due_date,
assignee=assignee,
)
validate_perm("workspace.update_task", who, task.workspace)
task.title = title
task.description = description
task.due_date = due_date
task.assignee = assignee
task.save()
task_assign_labels(task=task, labels=labels)
if sub_tasks:
sub_task_instances = list(task.subtask_set.all())
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
{# SPDX-FileCopyrightText: 2025 JWP Consulting GK #}
{# SPDX-License-Identifier: AGPL-3.0-or-later #}
<div class="font-bold text-utility">
<a href="{% url "dashboard:projects:detail" task.section.project.uuid %}"
class="text-primary underline hover:text-primary-hover active:text-primary-pressed text-base">{{ task.section.project.title }}</a><!--<Anchor>-->
&gt;
<a href="{% url "dashboard:projects:detail" task.section.project.uuid %}#{{ task.section.uuid }}"
class="text-primary underline hover:text-primary-hover active:text-primary-pressed text-base">{{ task.section.title }}</a><!--<Anchor>-->
&gt;
<a href="{% url "dashboard:tasks:detail" task.uuid %}"
class="text-primary underline hover:text-primary-hover active:text-primary-pressed text-base">{{ task.number }}</a><!--<Anchor>-->
</div>
464 changes: 229 additions & 235 deletions backend/projectify/workspace/templates/workspace/task_detail.html

Large diffs are not rendered by default.

Original file line number Diff line number Diff line change
Expand Up @@ -25,15 +25,7 @@
</svg>
</a>
</div>
<div class="font-bold text-utility">
<a href="{% url "dashboard:projects:detail" task.section.project.uuid %}"
class="text-primary underline hover:text-primary-hover active:text-primary-pressed text-base">{{ task.section.project.title }}</a>
&gt;
<a href="{% url "dashboard:projects:detail" task.section.project.uuid %}#{{ section.uuid }}"
class="text-primary underline hover:text-primary-hover active:text-primary-pressed text-base">{{ task.section.title }}</a>
&gt; <a href="{% url "dashboard:tasks:detail" task.uuid %}"
class="text-primary underline hover:text-primary-hover active:text-primary-pressed text-base">{{ task.title }}</a>
</div>
{% include "workspace/task/breadcrumbs.html" with task=task %}
</div>
<div class="flex items-center justify-between gap-4 sm:flex-row">
<input type="submit"
Expand Down
2 changes: 1 addition & 1 deletion backend/pyproject.toml
Original file line number Diff line number Diff line change
Expand Up @@ -32,7 +32,7 @@ pillow = "^10.3.0"
psycopg = {version = "^3.1.18", extras = ["c"]}
python = "~3.12.7"
redis = "^5"
rules = "~3.3"
rules = "^3.3"
stripe = "^8"
uvicorn = "~0.17.6"
websockets = "~10.3"
Expand Down
52 changes: 52 additions & 0 deletions docs/remove-fe-worklog.md
Original file line number Diff line number Diff line change
Expand Up @@ -506,3 +506,55 @@ def view(request: HttpRequest) -> HttpResponse:
Furthermore, I've decided to not implement sub task reordering. I question
the utility of sub task reordering in general, so I want to hold off on that
feature for now.

## 2025-01-20

Today I will try out hiding Projectify UI features using `django-rules`.
Users can have different roles depending on which workspace they belong to.
For example, if a user has the *Observer* role, they can only look at
tasks, but not change them. So, if someone has the Observer role, Projectify
should hide the *Edit task* button. If someone has at least the *Contributor*
role, Projectify should show them the *Edit task* button.

To query `django-rules` inside a template, add the following at the
top of the HTML template file:

```
{% load rules %}
```

Here, I change the `task_detail.html` file and make the *Edit task* button
only visible when a user can update the current task. First, I add
a line that calls the `has_perm` template function from `django_rules.` It
takes a variable number of arguments. Here, it takes the user and the task
in question.

```
{% has_perm "workspace.update_task" user task.workspace as can_update_task %}
{% if can_update_task %}
<a href="{% url "dashboard:tasks:update" task.uuid %}"
class="...">
Edit
</a><!--<button>-->
{% endif %}
```

When checking for permissions, be careful with the following:

1. If you test as a superuser, all permission checks will evaluate to true.
Check as another user.
2. The permission checks are purely visual, any real permission checks
should happen in the services or selectors Python modules.
3. A permission check always takes a user and a workspace instance. If the
current object is a project, dereference `project.workspace`. Dereference
`Section` and other models similarly.

This is, for example, how to check permissions on a project:

```
{% has_perm "workspace.update_project" user project.workspace as can_update_project %}
```

I visually confirmed that the above works by signing in as `guest@localhost`.
While switching this user between Maintainer and Observer, I verified that
the *Edit task* button disappears and re-appears.
2 changes: 1 addition & 1 deletion docs/remove-fe.md
Original file line number Diff line number Diff line change
Expand Up @@ -92,7 +92,7 @@ everything.
- [x] Try out Django forms and see if they fit our use case
- [x] Try overriding Django widget templates
- [x] Completely implement task create site (copy HTML over from frontend)
- [ ] Completely implement task update site (copy HTML over from frontend)
- [x] Completely implement task update site (copy HTML over from frontend)
- [ ] Try conditionally showing UI functionality based on user permissions
- [ ] Find out how to properly integrate icon pack used in frontend

Expand Down

0 comments on commit b6131b3

Please sign in to comment.