Skip to content

Commit

Permalink
docs(add-model): create tips for many-to-many relationships
Browse files Browse the repository at this point in the history
  • Loading branch information
dmartin4820 committed Sep 19, 2024
1 parent 5e10bf5 commit 8eed634
Showing 1 changed file with 146 additions and 0 deletions.
146 changes: 146 additions & 0 deletions docs/how-to/add-model-and-api-endpoints.md
Original file line number Diff line number Diff line change
Expand Up @@ -51,6 +51,23 @@ Let's start!
assert str(recurring_event) == payload["name"]
```

For testing many-to-many relationships, we can add

```python title="app/core/tests/test_models.py" linenums="1"
def test_project_recurring_event_relationship(project):
recurring_event = RecurringEvent.objects.get(name="{Name of Recurring Event}")

project.recurring_events.add(recurring_event)
assert project.recurring_events.count() == 1
assert project.recurring_events.contains(recurring_event)
assert recurring_event.projects.contains(project)

project.sdgs.remove(recurring_event)
assert project.recurring_events.count() == 0
assert not project.recurring_events.contains(recurring_event)
assert not recurring_event.projects.contains(project)
```

1. See it fail

```bash
Expand Down Expand Up @@ -99,6 +116,45 @@ class RecurringEvent(AbstractBaseModel): # (1)!
1. Try to add the relationships to non-existent models, but comment them out. Another developer will complete them when they go to implement those models.
1. Always override the `__str__` function to output something more meaningful than the default. It lets us do a quick test of the model by calling `str([model])`. It's also useful for the admin site model list view.

??? note "Updating models.py for many-to-many relationships"
For adding many-to-many relationships with additional fields, such as `ended_on`, we can add

```python title="app/core/tests/test_models.py" linenums="1"
class Project(AbstractBaseModel):
...
recurring_events = models.ManyToManyField(
"RecurringEvent",
related_name="projects",
blank=True,
through="ProjectRecurringEventXref",
)
...


class ProjectRecurringEventXref(AbstractBaseModel):
"""
Joins a recurring event to a project
"""

recurring_event_id = models.ForeignKey(RecurringEvent, on_delete=models.CASCADE)
project_id = models.ForeignKey(Project, on_delete=models.CASCADE)
ended_on = models.DateField("Ended on", null=True, blank=True)
```

For adding many-to-many relationships without additional fields, we can just add

```python title="app/core/tests/test_models.py" linenums="1"
class Project(AbstractBaseModel):
...
recurring_events = models.ManyToManyField(
"RecurringEvent",
related_name="projects",
blank=True,
through="ProjectRecurringEventXref",
)
...
```

### Run migrations

This generates the database migration files
Expand Down Expand Up @@ -247,6 +303,67 @@ This is code that serializes objects into strings for the API endpoints, and des

In `app/core/api/serializers.py`

??? note "Updating serializers.py for many-to-many relationships"
Following the many-to-many relationship between project and recurring event from above,

Update the existing serializer classes

```python title="app/core/api/serializers.py" linenums="1"
class ProjectSerializer(serializers.ModelSerializer):
"""Used to retrieve project info"""

recurring_events = serializers.StringRelatedField(many=True)

class Meta:
model = Project
fields = (
"uuid",
"name",
"description",
"created_at",
"updated_at",
"completed_at",
"github_org_id",
"github_primary_repo_id",
"hide",
"google_drive_id",
"image_logo",
"image_hero",
"image_icon",
"recurring_events",
)
read_only_fields = (
"uuid",
"created_at",
"updated_at",
"completed_at",
)


class RecurringEventSerializer(serializers.ModelSerializer):
"""Used to retrieve recurring_event info"""

projects = serializers.StringRelatedField(many=True)

class Meta:
model = RecurringEvent
fields = (
"uuid",
"name",
"start_time",
"duration_in_min",
"video_conference_url",
"additional_info",
"project",
"projects",
)
read_only_fields = (
"uuid",
"created_at",
"updated_at",
)
```

1. Import the new model

```python title="app/core/api/serializers.py" linenums="1"
Expand Down Expand Up @@ -491,6 +608,35 @@ In `app/core/api/urls.py`
./scripts/test.sh
```
??? note "Test many-to-many relationships"
In `app/core/tests/test_api.py`
1. Import API URL
```python title="app/core/tests/test_api.py" linenums="1"
PROJECT_URL = reverse("project-list")
```
1. Add test case
```python title="app/core/tests/test_api.py" linenums="1"
def test_project_sdg_xref(auth_client, project, sdg):
project.sdgs.add(sdg)
project.save()
proj_res = auth_client.get(PROJECT_URL)
sdg_res = auth_client.get(SDG_URL)
assert filter(lambda proj: str(proj["uuid"]) == str(project.pk), proj_res.data)
assert filter(lambda _sdg: str(_sdg["uuid"]) == str(sdg.pk), sdg_res)
```
1. Run the test script to show it passing
```bash
./scripts/test.sh
```
??? note "Check and commit"
This is a good place to pause, check, and commit progress.
Expand Down

0 comments on commit 8eed634

Please sign in to comment.