Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
17 changes: 17 additions & 0 deletions apps/roles/admin.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
"""Admin configuration for role management."""

from __future__ import annotations

from django.contrib import admin

from apps.roles.models import Role


@admin.register(Role)
class RoleAdmin(admin.ModelAdmin):
"""Admin interface for StudyBuddy roles."""

list_display = ("display_name", "slug", "created_at", "updated_at")
search_fields = ("display_name", "slug", "description")
prepopulated_fields = {"slug": ("display_name",)}
readonly_fields = ("created_at", "updated_at")
3 changes: 2 additions & 1 deletion apps/roles/apps.py
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
"""Role application configuration."""
"""Application configuration for the roles app."""

from __future__ import annotations

Expand All @@ -10,3 +10,4 @@ class RolesConfig(AppConfig):

default_auto_field = "django.db.models.BigAutoField"
name = "apps.roles"
label = "roles"
47 changes: 47 additions & 0 deletions apps/roles/migrations/0003_rename_role_name_and_add_timestamps.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,47 @@
# Generated by Django 5.2.13 on 2026-05-05

import django.utils.timezone
from django.db import migrations, models


class Migration(migrations.Migration):

dependencies = [
("roles", "0002_initial"),
]

operations = [
migrations.RenameField(
model_name="role",
old_name="name",
new_name="display_name",
),
migrations.AlterModelOptions(
name="role",
options={"ordering": ("display_name",)},
),
migrations.AlterField(
model_name="role",
name="display_name",
field=models.CharField(max_length=120, unique=True),
),
migrations.AlterField(
model_name="role",
name="slug",
field=models.SlugField(unique=True),
),
migrations.AddField(
model_name="role",
name="created_at",
field=models.DateTimeField(
auto_now_add=True,
default=django.utils.timezone.now,
),
preserve_default=False,
),
migrations.AddField(
model_name="role",
name="updated_at",
field=models.DateTimeField(auto_now=True),
),
]
17 changes: 11 additions & 6 deletions apps/roles/models.py
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
"""Role models for StudyBuddy users."""
"""Role models for StudyBuddy access control."""

from __future__ import annotations

Expand All @@ -7,19 +7,24 @@


class Role(models.Model):
"""Named role that can be assigned to users."""
"""Named role used to support role-aware behaviour in the product."""

name = models.CharField(max_length=80, unique=True)
slug = models.SlugField(max_length=100, unique=True)
slug = models.SlugField(unique=True)
display_name = models.CharField(max_length=120, unique=True)
description = models.TextField(blank=True)
created_at = models.DateTimeField(auto_now_add=True)
updated_at = models.DateTimeField(auto_now=True)
users = models.ManyToManyField(
settings.AUTH_USER_MODEL,
blank=True,
related_name="studybuddy_roles",
)

class Meta:
ordering = ["name"]
"""Model metadata for roles."""

ordering = ("display_name",)

def __str__(self) -> str:
return self.name
"""Return the human-readable role name."""
return self.display_name
4 changes: 2 additions & 2 deletions tests/test_models.py
Original file line number Diff line number Diff line change
Expand Up @@ -108,7 +108,7 @@ def test_custom_user_display_name_falls_back_to_email() -> None:
@pytest.mark.django_db
def test_role_string_uses_name() -> None:
"""Roles display with their human-readable name."""
role = Role.objects.create(name="Student", slug="student")
role = Role.objects.create(display_name="Student", slug="student")

assert str(role) == "Student"

Expand All @@ -121,7 +121,7 @@ def test_role_can_be_assigned_to_user() -> None:
email="grace@example.com",
password="password123",
)
role = Role.objects.create(name="Tutor", slug="tutor")
role = Role.objects.create(display_name="Tutor", slug="tutor")

role.users.add(user)

Expand Down
Loading