diff --git a/main/admin.py b/main/admin.py
index fc76b9e4..57e7fe2c 100644
--- a/main/admin.py
+++ b/main/admin.py
@@ -23,6 +23,8 @@
Provider,
Skill,
SkillLevel,
+ Team,
+ TeamMembership,
ToolLanguageMethodology,
User,
UserSkill,
@@ -210,3 +212,13 @@ def has_delete_permission(
) -> bool:
"""Do not allow deleting users from this view."""
return False
+
+
+@admin.register(Team)
+class TeamAdmin(admin.ModelAdmin[Team]):
+ """Admin class for the Team model."""
+
+
+@admin.register(TeamMembership)
+class TeamMembershipAdmin(admin.ModelAdmin[TeamMembership]):
+ """Admin class for the TeamMembership model."""
diff --git a/main/forms.py b/main/forms.py
index 22488df4..37f0b409 100644
--- a/main/forms.py
+++ b/main/forms.py
@@ -16,6 +16,8 @@
RegistrationFormUniqueEmail,
)
+from main import models
+
from .models import Skill, SkillLevel, UserSkill
if TYPE_CHECKING: # pragma: no cover
@@ -267,3 +269,13 @@ def save(self, user: "UserType") -> tuple[list[UserSkill], list[UserSkill]]:
created_skills.append(user_skill)
return created_skills, updated_skills
+
+
+class CreateTeamForm(forms.Form):
+ """Form for creating a new team."""
+
+ class Meta:
+ """Meta options for CreateTeamForm."""
+
+ model = models.Team
+ fields = "__all__"
diff --git a/main/migrations/0029_alter_userproxy_options.py b/main/migrations/0029_alter_userproxy_options.py
new file mode 100644
index 00000000..e8c749ce
--- /dev/null
+++ b/main/migrations/0029_alter_userproxy_options.py
@@ -0,0 +1,17 @@
+# Generated by Django 6.0.3 on 2026-04-30 11:04
+
+from django.db import migrations
+
+
+class Migration(migrations.Migration):
+
+ dependencies = [
+ ('main', '0028_alter_toollanguagemethodology_options_and_more'),
+ ]
+
+ operations = [
+ migrations.AlterModelOptions(
+ name='userproxy',
+ options={'verbose_name': 'User skills profile'},
+ ),
+ ]
diff --git a/main/migrations/0030_team_teammembership.py b/main/migrations/0030_team_teammembership.py
new file mode 100644
index 00000000..01cc77b7
--- /dev/null
+++ b/main/migrations/0030_team_teammembership.py
@@ -0,0 +1,36 @@
+# Generated by Django 6.0.3 on 2026-04-30 11:08
+
+import django.db.models.deletion
+from django.conf import settings
+from django.db import migrations, models
+
+
+class Migration(migrations.Migration):
+
+ dependencies = [
+ ('main', '0029_alter_userproxy_options'),
+ ]
+
+ operations = [
+ migrations.CreateModel(
+ name='Team',
+ fields=[
+ ('id', models.BigAutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
+ ('name', models.CharField(max_length=200, unique=True)),
+ ('description', models.TextField()),
+ ('slug', models.SlugField(max_length=200, unique=True)),
+ ],
+ options={
+ 'abstract': False,
+ },
+ ),
+ migrations.CreateModel(
+ name='TeamMembership',
+ fields=[
+ ('id', models.BigAutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
+ ('manager', models.BooleanField(blank=True, default=False)),
+ ('team', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to='main.team')),
+ ('user', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to=settings.AUTH_USER_MODEL)),
+ ],
+ ),
+ ]
diff --git a/main/models/__init__.py b/main/models/__init__.py
index 2d6dc0a2..0b00643d 100644
--- a/main/models/__init__.py
+++ b/main/models/__init__.py
@@ -1,4 +1,5 @@
"""Init in models."""
from .framework_models import * # noqa: F403
+from .team_models import * # noqa: F403
from .user_models import * # noqa: F403
diff --git a/main/models/team_models.py b/main/models/team_models.py
new file mode 100644
index 00000000..67e80114
--- /dev/null
+++ b/main/models/team_models.py
@@ -0,0 +1,14 @@
+from django.conf import settings
+from django.db import models
+
+from .framework_models import SluggedModel
+
+
+class Team(SluggedModel):
+ pass
+
+
+class TeamMembership(models.Model):
+ user = models.ForeignKey(settings.AUTH_USER_MODEL, on_delete=models.CASCADE)
+ team = models.ForeignKey(Team, on_delete=models.CASCADE)
+ manager = models.BooleanField(blank=True, default=False)
diff --git a/main/models/user_models.py b/main/models/user_models.py
index 1b23295e..9fe20054 100644
--- a/main/models/user_models.py
+++ b/main/models/user_models.py
@@ -30,3 +30,17 @@ class Meta:
constraints = (
models.UniqueConstraint(fields=["user", "skill"], name="unique_user_skill"),
)
+
+
+class Team(models.Model):
+ """Model for representing a team of users working together."""
+
+ name = models.CharField(max_length=255)
+ manager = models.ForeignKey(
+ settings.AUTH_USER_MODEL, on_delete=models.CASCADE, related_name="managed_teams"
+ )
+ members = models.ManyToManyField(settings.AUTH_USER_MODEL, related_name="teams")
+
+ def __str__(self) -> str:
+ """Return the string representation of the team."""
+ return self.name
diff --git a/main/templates/main/pages/create-team.html b/main/templates/main/pages/create-team.html
new file mode 100644
index 00000000..463d4f8e
--- /dev/null
+++ b/main/templates/main/pages/create-team.html
@@ -0,0 +1,14 @@
+{% extends "main/base.account.html" %}
+{% load crispy_forms_tags %}
+{% block content %}
+
+ Create Team
+
+ This page presents the teams you are in in the Digital Research + Competencies Framework. +
+| Team Name | +Description | +Manager | +Members | +
|---|---|---|---|
| + {{ team.name }} + | +{{ team.description }} | +{{ team.manager }} | +
+
|
+