Skip to content

Commit 450bf7f

Browse files
Julian Dehmm4ra
authored andcommitted
apps/projects: add topics as m2m relation
1 parent 540fb14 commit 450bf7f

File tree

12 files changed

+161
-17
lines changed

12 files changed

+161
-17
lines changed

Makefile

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -45,11 +45,15 @@ lint:
4545
$(VIRTUAL_ENV)/bin/isort --diff -c $(SOURCE_DIRS) || EXIT_STATUS=$$?; \
4646
$(VIRTUAL_ENV)/bin/flake8 $(SOURCE_DIRS) --exclude migrations,settings || EXIT_STATUS=$$?; \
4747
npm run lint || EXIT_STATUS=$$?; \
48+
$(VIRTUAL_ENV)/bin/python manage.py makemigrations --dry-run --check --noinput || EXIT_STATUS=$$?; \
49+
exit $${EXIT_STATUS}
4850

4951
.PHONY: lint-quick
5052
lint-quick:
5153
EXIT_STATUS=0; \
5254
npm run lint-staged || EXIT_STATUS=$$?; \
55+
$(VIRTUAL_ENV)/bin/python manage.py makemigrations --dry-run --check --noinput || EXIT_STATUS=$$?; \
56+
exit $${EXIT_STATUS}
5357

5458
.PHONY: lint-python-files
5559
lint-python-files:

adhocracy4/projects/fields.py

Lines changed: 4 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,10 @@
1-
from django.conf import settings
21
from multiselectfield import MultiSelectField
32

43

54
class TopicField(MultiSelectField):
5+
"""Deprecated, don't use"""
6+
7+
# TODO: remove once topic migrations are rolled out
68
def __init__(self, *args, **kwargs):
79
kwargs["max_length"] = 254
810
kwargs["max_choices"] = 2
@@ -11,11 +13,7 @@ def __init__(self, *args, **kwargs):
1113
super().__init__(*args, **kwargs)
1214

1315
def contribute_to_class(self, cls, name, **kwargs):
14-
"""Initialize the choices from the project's settings if they exist."""
15-
if hasattr(settings, "A4_PROJECT_TOPICS"):
16-
self.choices = settings.A4_PROJECT_TOPICS
17-
else:
18-
self.choices = ()
16+
self.choices = ()
1917

2018
# Call the super method at last so that choices are already initialized
2119
super().contribute_to_class(cls, name, **kwargs)
Lines changed: 33 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,33 @@
1+
# Generated by Django 4.2 on 2023-11-29 13:18
2+
3+
import adhocracy4.projects.fields
4+
from django.db import migrations, models
5+
6+
7+
class Migration(migrations.Migration):
8+
dependencies = [
9+
("a4projects", "0041_ckeditor5_iframes"),
10+
]
11+
12+
operations = [
13+
migrations.CreateModel(
14+
name="Topic",
15+
fields=[
16+
(
17+
"id",
18+
models.AutoField(
19+
auto_created=True,
20+
primary_key=True,
21+
serialize=False,
22+
verbose_name="ID",
23+
),
24+
),
25+
("code", models.CharField(blank=True, max_length=10, unique=True)),
26+
],
27+
),
28+
migrations.AddField(
29+
model_name="project",
30+
name="m2mtopics",
31+
field=models.ManyToManyField(to="a4projects.topic"),
32+
),
33+
]
Lines changed: 40 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,40 @@
1+
# Generated by Django 4.2 on 2023-11-29 13:20
2+
import sys
3+
import importlib
4+
5+
from django.db import migrations
6+
from django.conf import settings
7+
8+
9+
def add_topics_to_m2m_table(apps, schema_editor):
10+
if hasattr(settings, "A4_PROJECT_TOPICS"):
11+
project = apps.get_model("a4projects", "Project")
12+
topic = apps.get_model("a4projects", "Topic")
13+
for project in project.objects.all():
14+
for topic_code in project.topics:
15+
proj_topic, _ = topic.objects.get_or_create(
16+
code=topic_code,
17+
)
18+
project.m2mtopics.add(proj_topic)
19+
else:
20+
pass
21+
22+
23+
def reverse_func(apps, schema_editor):
24+
if hasattr(settings, "A4_PROJECT_TOPICS"):
25+
project = apps.get_model("a4projects", "Project")
26+
for project in project.objects.all():
27+
for topic in project.m2mtopics.all():
28+
project.m2mtopics.remove(topic)
29+
else:
30+
pass
31+
32+
33+
class Migration(migrations.Migration):
34+
dependencies = [
35+
("a4projects", "0042_topic_alter_project_topics_project_m2mtopics"),
36+
]
37+
38+
operations = [
39+
migrations.RunPython(add_topics_to_m2m_table, reverse_func),
40+
]
Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,16 @@
1+
# Generated by Django 4.2 on 2023-11-29 15:03
2+
3+
from django.db import migrations, models
4+
5+
6+
class Migration(migrations.Migration):
7+
dependencies = [
8+
("a4projects", "0043_migrate_topics_to_m2m_topics"),
9+
]
10+
11+
operations = [
12+
migrations.RemoveField(
13+
model_name="project",
14+
name="topics",
15+
),
16+
]
Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,15 @@
1+
# Generated by Django 4.2 on 2023-11-29 15:14
2+
3+
from django.db import migrations, models
4+
5+
6+
class Migration(migrations.Migration):
7+
dependencies = [
8+
("a4projects", "0044_remove_project_field_topics"),
9+
]
10+
11+
operations = [
12+
migrations.RenameField(
13+
model_name="project", old_name="m2mtopics", new_name="topics"
14+
),
15+
]

adhocracy4/projects/models.py

Lines changed: 14 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@
88
from django.urls import reverse
99
from django.utils import timezone
1010
from django.utils.functional import cached_property
11+
from django.utils.module_loading import import_string
1112
from django.utils.translation import gettext_lazy as _
1213
from django_ckeditor_5.fields import CKEditor5Field
1314
from django_enumfield.enum import EnumField
@@ -19,11 +20,20 @@
1920
from adhocracy4.models import base
2021

2122
from .enums import Access
22-
from .fields import TopicField
2323
from .utils import get_module_clusters
2424
from .utils import get_module_clusters_dict
2525

2626

27+
class Topic(models.Model):
28+
code = models.CharField(blank=True, max_length=10, unique=True)
29+
30+
def __str__(self):
31+
if hasattr(settings, "A4_PROJECT_TOPICS"):
32+
topics_enum = import_string(settings.A4_PROJECT_TOPICS)
33+
return str(topics_enum(self.code).label)
34+
return self.code
35+
36+
2737
class ProjectManager(models.Manager):
2838
def get_by_natural_key(self, name):
2939
return self.get(name=name)
@@ -259,9 +269,8 @@ class Project(
259269
"dashboard."
260270
),
261271
)
262-
topics = TopicField(
263-
verbose_name=_("Project topics"), help_text=_("Add topics to your project.")
264-
)
272+
topics = models.ManyToManyField(Topic)
273+
265274
project_type = models.CharField(
266275
blank=True, max_length=256, default="a4projects.Project"
267276
)
@@ -321,8 +330,7 @@ def has_moderator(self, user):
321330
@cached_property
322331
def topic_names(self):
323332
if hasattr(settings, "A4_PROJECT_TOPICS"):
324-
choices = dict(settings.A4_PROJECT_TOPICS)
325-
return [choices[topic] for topic in self.topics]
333+
return [topic.name for topic in self.topics.all()]
326334
return []
327335

328336
@cached_property

changelog/0007.md

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,6 @@
1+
# Changed
2+
3+
- apps/projects:
4+
add topics model/table
5+
make topics a m2m relation to projects
6+
stop making use of django-multiselectfield as it's not maintained

changelog/0008.md

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
### Changed
2+
3+
- apps/projects: topics should be first created and then added to project in migration

changelog/0009.md

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
### Fixed
2+
3+
- apps/projects: project's topics_name property should iterate through m2m relation

0 commit comments

Comments
 (0)