Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[7776] django upgrade to 4.2 (LTS) #1504

Merged
merged 9 commits into from
Dec 4, 2023
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
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
# Generated by Django 4.2 on 2023-11-27 12:54

from django.db import migrations


class Migration(migrations.Migration):
dependencies = [
("a4actions", "0008_set_action_obj_comment_creator"),
]

operations = [
migrations.RenameIndex(
model_name="action",
new_name="a4actions_a_obj_con_ee317e_idx",
old_fields=("obj_content_type", "obj_object_id"),
),
]
2 changes: 1 addition & 1 deletion adhocracy4/actions/models.py
Original file line number Diff line number Diff line change
Expand Up @@ -99,7 +99,7 @@ class Action(models.Model):

class Meta:
ordering = ("-timestamp",)
index_together = [("obj_content_type", "obj_object_id")]
indexes = [models.Index(fields=["obj_content_type", "obj_object_id"])]

def __str__(self):
ctx = {
Expand Down
6 changes: 0 additions & 6 deletions adhocracy4/categories/fields.py
Original file line number Diff line number Diff line change
@@ -1,7 +1,6 @@
from django.db import models
from django.utils.translation import gettext_lazy as _

from adhocracy4.categories.form_fields import CategoryChoiceField
from adhocracy4.categories.models import Category


Expand All @@ -17,8 +16,3 @@ def __init__(self, *args, **kwargs):
}
defaults.update(kwargs)
super().__init__(**defaults)

def formfield(self, **kwargs):
form_class = kwargs.get("form_class", CategoryChoiceField)
kwargs["form_class"] = form_class
return super().formfield(**kwargs)
42 changes: 6 additions & 36 deletions adhocracy4/categories/form_fields.py
Original file line number Diff line number Diff line change
@@ -1,34 +1,10 @@
from collections import abc

from django import forms
from django.conf import settings
from django.forms import widgets
from django.utils.functional import cached_property

from adhocracy4.categories import get_category_icon_url


class CategoryIconDict(abc.Mapping):
def __init__(self, field):
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'm not really sure why this was needed so I removed it, I think we should point out to testers to verify everything still works as intended with the icons

self.field = field
self.queryset = field.queryset

@cached_property
def _icons(self):
return {
self.field.prepare_value(obj): getattr(obj, "icon", None)
for obj in self.queryset.all()
}

def __getitem__(self, key):
return self._icons.__getitem__(key)

def __iter__(self):
return self._icons.__iter__()

def __len__(self):
return self._icons.__len__()


class CategorySelectWidget(widgets.Select):
def get_context(self, name, value, attrs):
context = super().get_context(name, value, attrs)
Expand All @@ -40,30 +16,24 @@ def get_context(self, name, value, attrs):

def create_option(self, name, value, label, selected, index, **kwargs):
option = super().create_option(name, value, label, selected, index, **kwargs)
if hasattr(value, "value") and value.value in self.icons:
icon_url = get_category_icon_url(self.icons[value.value])
if value in self.icons:
icon_url = get_category_icon_url(value)
option["attrs"]["data-icon-src"] = icon_url

return option


class CategoryChoiceField(forms.ModelChoiceField):
class CategoryChoiceField(forms.ChoiceField):
widget = CategorySelectWidget

def __init__(self, *args, **kwargs):
super().__init__(*args, **kwargs)
self.widget.icons = self.icons
self.choices = getattr(settings, "A4_CATEGORY_ICONS", [])

@property
def icons(self):
return CategoryIconDict(self)

# Update the icons if the queryset is updated
def _set_queryset(self, queryset):
super()._set_queryset(queryset)
self.widget.icons = self.icons

queryset = property(forms.ModelChoiceField._get_queryset, _set_queryset)
return dict(self.choices)


class IconSelectWidget(widgets.Select):
Expand Down
2 changes: 2 additions & 0 deletions adhocracy4/categories/forms.py
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@
from adhocracy4.modules import models as module_models

from . import has_icons
from .form_fields import CategoryChoiceField


class CategorizableFieldMixin:
Expand Down Expand Up @@ -50,6 +51,7 @@ def __init__(self, module, *args, **kwargs):
del self.fields["icon"]

name = forms.CharField(widget=forms.TextInput(attrs={"placeholder": _("Category")}))
icon = CategoryChoiceField()

class Media:
js = ("category_formset.js",)
Expand Down
10 changes: 0 additions & 10 deletions adhocracy4/categories/models.py
Original file line number Diff line number Diff line change
@@ -1,4 +1,3 @@
from django.conf import settings
from django.db import models
from django.utils.translation import gettext_lazy as _

Expand All @@ -13,15 +12,6 @@ def __init__(self, *args, **kwargs):
kwargs["blank"] = True
super().__init__(*args, **kwargs)

def contribute_to_class(self, cls, name, **kwargs):
"""Initialize icon choices from the settings if they exist."""

if hasattr(settings, "A4_CATEGORY_ICONS"):
self.choices = settings.A4_CATEGORY_ICONS

# Call the super method at last so that choices are already initialized
super().contribute_to_class(cls, name, **kwargs)

def formfield(self, **kwargs):
form_class = kwargs.get("choices_form_class", IconChoiceField)
kwargs["choices_form_class"] = form_class
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
# Generated by Django 4.2 on 2023-11-27 12:54

from django.db import migrations


class Migration(migrations.Migration):
dependencies = [
("a4comments", "0013_set_project"),
]

operations = [
migrations.RenameIndex(
model_name="comment",
new_name="a4comments__content_ff606b_idx",
old_fields=("content_type", "object_pk"),
),
]
13 changes: 10 additions & 3 deletions adhocracy4/comments/models.py
Original file line number Diff line number Diff line change
Expand Up @@ -47,7 +47,7 @@ class Meta:
verbose_name = pgettext_lazy("noun", "Comment")
verbose_name_plural = _("Comments")
ordering = ("created",)
index_together = [("content_type", "object_pk")]
indexes = [models.Index(fields=["content_type", "object_pk"])]

def __init__(self, *args, **kwargs):
super().__init__(*args, **kwargs)
Expand All @@ -60,17 +60,24 @@ def __str__(self):
else:
return "{}".format(self.comment)

def save(self, *args, **kwargs):
def save(self, update_fields=None, *args, **kwargs):
"""Change comment.comment if comment was marked removed or censored."""
self.comment = transforms.clean_html_all(self.comment)
update_project = False
if not self.project:
self.project = self.content_object.module.project
update_project = True

if self.is_removed or self.is_censored:
self.comment = self._former_comment = ""
self.comment_categories = ""

super(Comment, self).save(*args, **kwargs)
if update_fields:
update_fields = {"comment"}.union(update_fields)
if update_project:
update_fields = {"project"}.union(update_fields)

super().save(update_fields=update_fields, *args, **kwargs)

def get_absolute_url(self):
if hasattr(self.content_object, "get_absolute_url"):
Expand Down
2 changes: 1 addition & 1 deletion adhocracy4/dashboard/views.py
Original file line number Diff line number Diff line change
Expand Up @@ -168,7 +168,7 @@ def post(self, request, *args, **kwargs):
def get_next(self):
if "referrer" in self.request.POST:
return self.request.POST["referrer"]
elif "HTTP_REFERER" in self.request.META:
elif "referer" in self.request.headers:
return self.request.headers["Referer"]

return reverse(
Expand Down
23 changes: 23 additions & 0 deletions adhocracy4/maps/migrations/0003_alter_areasettings_module.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
# Generated by Django 4.2 on 2023-11-29 12:49

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


class Migration(migrations.Migration):
dependencies = [
("a4modules", "0008_alter_module_blueprint_type"),
("a4maps", "0002_change_help_text"),
]

operations = [
migrations.AlterField(
model_name="areasettings",
name="module",
field=models.OneToOneField(
on_delete=django.db.models.deletion.CASCADE,
related_name="%(class)s_settings",
to="a4modules.module",
),
),
]
6 changes: 4 additions & 2 deletions adhocracy4/models/base.py
Original file line number Diff line number Diff line change
Expand Up @@ -20,10 +20,12 @@ class TimeStampedModel(models.Model):
class Meta:
abstract = True

def save(self, ignore_modified=False, *args, **kwargs):
def save(self, ignore_modified=False, update_fields=None, *args, **kwargs):
if self.pk is not None and not ignore_modified:
self.modified = timezone.now()
super(TimeStampedModel, self).save(*args, **kwargs)
if update_fields:
update_fields = {"modified"}.union(update_fields)
super().save(update_fields=update_fields, *args, **kwargs)


class UserGeneratedContentModel(TimeStampedModel):
Expand Down
1 change: 1 addition & 0 deletions adhocracy4/modules/admin.py
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,7 @@ class ItemAdmin(admin.ModelAdmin):

class ModuleAdmin(admin.ModelAdmin):
inlines = [phase_admin.PhaseInline]
search_fields = ["name"]
list_filter = ("project__organisation", "project")
list_display = ("__str__", "name")
if hasattr(settings, "A4_BLUEPRINT_TYPES"):
Expand Down
17 changes: 17 additions & 0 deletions adhocracy4/modules/migrations/0008_alter_module_blueprint_type.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
# Generated by Django 4.2 on 2023-11-29 12:49

from django.db import migrations, models


class Migration(migrations.Migration):
dependencies = [
("a4modules", "0007_verbose_name_created_modified"),
]

operations = [
migrations.AlterField(
model_name="module",
name="blueprint_type",
field=models.CharField(blank=True, max_length=255),
),
]
4 changes: 1 addition & 3 deletions adhocracy4/modules/models.py
Original file line number Diff line number Diff line change
Expand Up @@ -11,8 +11,6 @@
from adhocracy4.models import base
from adhocracy4.projects import models as project_models

from .fields import BlueprintTypeField


class ModulesQuerySet(models.QuerySet):
def annotate_module_start(self):
Expand Down Expand Up @@ -95,7 +93,7 @@ class Module(models.Model):

objects = ModulesQuerySet.as_manager()

blueprint_type = BlueprintTypeField(
blueprint_type = models.CharField(
max_length=255,
blank=True,
)
Copy link
Contributor Author

@m4ra m4ra Nov 28, 2023

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

then we delete the fields.py with the custom BlueprintType?
Also the new migration cancels the 0008. If we remove 0008, it won't generate 0009 here
@goapunk

Expand Down
4 changes: 2 additions & 2 deletions adhocracy4/polls/models.py
Original file line number Diff line number Diff line change
Expand Up @@ -70,7 +70,7 @@ def clean(self, *args, **kwargs):
)
}
)
elif self.choices.count() > 0:
elif Choice.objects.filter(question=self.pk).count() > 0:
raise ValidationError(
{
"is_open": _(
Expand Down Expand Up @@ -227,7 +227,7 @@ def save(self, *args, **kwargs):
return super().save(*args, **kwargs)

def validate_unique(self, exclude=None):
super(Vote, self).validate_unique(exclude)
super().validate_unique(exclude)
validators.single_vote_per_user(self.creator, self.choice, self.pk)

@property
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
# Generated by Django 4.2 on 2023-11-29 13:18

import adhocracy4.projects.fields
from django.db import migrations, models


class Migration(migrations.Migration):
dependencies = [
("a4projects", "0041_ckeditor5_iframes"),
]

operations = [
migrations.CreateModel(
name="Topic",
fields=[
(
"id",
models.AutoField(
auto_created=True,
primary_key=True,
serialize=False,
verbose_name="ID",
),
),
("code", models.CharField(blank=True, max_length=10)),
("name", models.CharField(max_length=120, verbose_name="Topic")),
],
),
migrations.AddField(
model_name="project",
name="m2mtopics",
field=models.ManyToManyField(to="a4projects.topic"),
),
]
Original file line number Diff line number Diff line change
@@ -0,0 +1,38 @@
# Generated by Django 4.2 on 2023-11-29 13:20

from django.db import migrations
from django.conf import settings


def add_topics_to_m2m_table(apps, schema_editor):
if hasattr(settings, "A4_PROJECT_TOPICS"):
topicsenum = settings.A4_PROJECT_TOPICS
project = apps.get_model("a4projects", "Project")
for project in project.objects.all():
for topic_code in project.topics:
project.m2mtopics.create(
code=topic_code,
name=[item[1] for item in topicsenum if item[0] == topic_code][0],
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

topicsenum -> topicenum

)
else:
pass


def reverse_func(apps, schema_editor):
if hasattr(settings, "A4_PROJECT_TOPICS"):
project = apps.get_model("a4projects", "Project")
for project in project.objects.all():
for topic in project.m2mtopics.all():
project.m2mtopics.remove(topic)
else:
pass


class Migration(migrations.Migration):
dependencies = [
("a4projects", "0042_topic_alter_project_topics_project_m2mtopics"),
]

operations = [
migrations.RunPython(add_topics_to_m2m_table, reverse_func),
]
Loading
Loading