Skip to content

Commit

Permalink
feat: add and test many-to-many relationship between program_area and…
Browse files Browse the repository at this point in the history
… project
  • Loading branch information
del9ra committed Nov 12, 2024
1 parent ee5a0a7 commit 0be6331
Show file tree
Hide file tree
Showing 7 changed files with 127 additions and 7 deletions.
7 changes: 6 additions & 1 deletion app/core/api/serializers.py
Original file line number Diff line number Diff line change
Expand Up @@ -101,6 +101,8 @@ class Meta:
class ProjectSerializer(serializers.ModelSerializer):
"""Used to retrieve project info"""

program_areas = serializers.StringRelatedField(many=True)

class Meta:
model = Project
fields = (
Expand All @@ -117,6 +119,7 @@ class Meta:
"image_logo",
"image_hero",
"image_icon",
"program_areas",
)
read_only_fields = (
"uuid",
Expand Down Expand Up @@ -227,9 +230,11 @@ class Meta:
class ProgramAreaSerializer(serializers.ModelSerializer):
"""Used to retrieve program_area info"""

projects = serializers.StringRelatedField(many=True)

class Meta:
model = ProgramArea
fields = ("uuid", "name", "description", "image")
fields = ("uuid", "name", "description", "image", "projects")
read_only_fields = ("uuid", "created_at", "updated_at")


Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,62 @@
# Generated by Django 4.2.11 on 2024-10-31 22:55

from django.db import migrations, models
import django.db.models.deletion
import uuid


class Migration(migrations.Migration):

dependencies = [("core", "0028_alter_userpermission_project")]

operations = [
migrations.CreateModel(
name="ProjectProgramAreaXref",
fields=[
(
"uuid",
models.UUIDField(
default=uuid.uuid4,
editable=False,
primary_key=True,
serialize=False,
unique=True,
),
),
(
"created_at",
models.DateTimeField(auto_now_add=True, verbose_name="Created at"),
),
(
"updated_at",
models.DateTimeField(auto_now=True, verbose_name="Updated at"),
),
(
"program_area_id",
models.ForeignKey(
on_delete=django.db.models.deletion.CASCADE,
to="core.programarea",
),
),
(
"project_id",
models.ForeignKey(
on_delete=django.db.models.deletion.CASCADE, to="core.project"
),
),
],
options={
"abstract": False,
},
),
migrations.AddField(
model_name="project",
name="program_areas",
field=models.ManyToManyField(
blank=True,
related_name="projects",
through="core.ProjectProgramAreaXref",
to="core.programarea",
),
),
]
2 changes: 1 addition & 1 deletion app/core/migrations/max_migration.txt
Original file line number Diff line number Diff line change
@@ -1 +1 @@
0028_alter_userpermission_project
0029_projectprogramareaxref_project_program_areas
11 changes: 11 additions & 0 deletions app/core/models.py
Original file line number Diff line number Diff line change
Expand Up @@ -144,6 +144,12 @@ class Project(AbstractBaseModel):
image_logo = models.URLField(blank=True)
image_hero = models.URLField(blank=True)
image_icon = models.URLField(blank=True)
program_areas = models.ManyToManyField(
"ProgramArea",
related_name="projects",
blank=True,
through="ProjectProgramAreaXref",
)

def __str__(self):
return f"{self.name}"
Expand Down Expand Up @@ -426,3 +432,8 @@ class SocMajor(AbstractBaseModel):

def __str__(self):
return self.title


class ProjectProgramAreaXref(AbstractBaseModel):
project_id = models.ForeignKey(Project, on_delete=models.CASCADE)
program_area_id = models.ForeignKey(ProgramArea, on_delete=models.CASCADE)
30 changes: 26 additions & 4 deletions app/core/tests/test_api.py
Original file line number Diff line number Diff line change
Expand Up @@ -18,10 +18,11 @@
FAQS_VIEWED_URL = reverse("faq-viewed-list")
AFFILIATE_URL = reverse("affiliate-list")
LOCATION_URL = reverse("location-list")
PROGRAM_AREA_URL = reverse("program-area-list")
PROGRAM_AREAS_URL = reverse("program-area-list")
SKILL_URL = reverse("skill-list")
STACK_ELEMENT_URL = reverse("stack-element-list")
PERMISSION_TYPE = reverse("permission-type-list")
PROJECTS_URL = reverse("project-list")
STACK_ELEMENT_TYPE_URL = reverse("stack-element-type-list")
SDG_URL = reverse("sdg-list")
AFFILIATION_URL = reverse("affiliation-list")
Expand Down Expand Up @@ -258,7 +259,7 @@ def test_create_program_area(auth_client):
"description": "About program area",
"image": "http://www.imageurl.com",
}
res = auth_client.post(PROGRAM_AREA_URL, payload)
res = auth_client.post(PROGRAM_AREAS_URL, payload)
assert res.status_code == status.HTTP_201_CREATED
assert res.data["name"] == payload["name"]

Expand All @@ -271,9 +272,9 @@ def test_list_program_area(auth_client):
"description": "About program area",
"image": "http://www.imageurl.com",
}
res = auth_client.post(PROGRAM_AREA_URL, payload)
res = auth_client.post(PROGRAM_AREAS_URL, payload)

res = auth_client.get(PROGRAM_AREA_URL)
res = auth_client.get(PROGRAM_AREAS_URL)

program_areas = ProgramArea.objects.all()
expected_data = ProgramAreaSerializer(program_areas, many=True).data
Expand Down Expand Up @@ -381,3 +382,24 @@ def test_create_soc_major(auth_client):
res = auth_client.post(SOC_MAJOR_URL, payload)
assert res.status_code == status.HTTP_201_CREATED
assert res.data["title"] == payload["title"]


def test_project_program_area_xref(auth_client, project, program_area):
def get_object(objects, target_uuid):
for obj in objects:
if str(obj["uuid"]) == str(target_uuid):
return obj
return None

project.program_areas.add(program_area)
proj_res = auth_client.get(PROJECTS_URL)
test_proj = get_object(proj_res.data, project.uuid)
assert test_proj is not None
assert len(test_proj["program_areas"]) == 1
assert program_area.name in test_proj["program_areas"]

program_area_res = auth_client.get(PROGRAM_AREAS_URL)
test_program_ar = get_object(program_area_res.data, program_area.uuid)
assert test_program_ar is not None
assert len(test_program_ar["projects"]) == 1
assert project.name in test_program_ar["projects"]
20 changes: 20 additions & 0 deletions app/core/tests/test_models.py
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,8 @@
import pytest

from ..models import Event
from ..models import ProgramArea
from ..models import ProjectProgramAreaXref

pytestmark = pytest.mark.django_db

Expand Down Expand Up @@ -145,3 +147,21 @@ def test_check_type(check_type):

def test_soc_major(soc_major):
assert str(soc_major) == "Test Soc Major"


def test_project_program_area_relationship(project):
workforce_development_program_area = ProgramArea.objects.get(
name="Workforce Development"
)
project.program_areas.add(workforce_development_program_area)
assert project.program_areas.count() == 1
assert project.program_areas.contains(workforce_development_program_area)
assert workforce_development_program_area.projects.contains(project)
workforce_development_program_area_xref = ProjectProgramAreaXref.objects.get(
project_id=project, program_area_id=workforce_development_program_area
)
assert workforce_development_program_area_xref.created_at is not None
project.program_areas.remove(workforce_development_program_area)
assert project.program_areas.count() == 0
assert not workforce_development_program_area.projects.contains(project)
assert not project.program_areas.contains(workforce_development_program_area)
2 changes: 1 addition & 1 deletion scripts/createsuperuser.sh
Original file line number Diff line number Diff line change
Expand Up @@ -8,5 +8,5 @@ set -x

# This command requires the DJANGO_SUPERUSER_USERNAME and
# DJANGO_SUPERUSER_PASSWORD environmental variables to be set when django starts
echo "DJANGO_SUPERUSER_USERNAME: $DJANGO_SUPERUSER_USERNAME"
# echo "DJANGO_SUPERUSER_USERNAME: $DJANGO_SUPERUSER_USERNAME"
docker-compose exec web python manage.py createsuperuser --no-input

0 comments on commit 0be6331

Please sign in to comment.