diff --git a/bsmain/management/commands/__init__.py b/bsmain/management/commands/__init__.py index e69de29..f0cacf8 100644 --- a/bsmain/management/commands/__init__.py +++ b/bsmain/management/commands/__init__.py @@ -0,0 +1,20 @@ +from django.db import models + +IGNORED_ATTRS = ['verbose_name', 'help_text'] + +original_deconstruct = models.Field.deconstruct + + +def new_deconstruct(self): + # works around the non-fix of https://code.djangoproject.com/ticket/21498 (I don't agree with the reasoning that + # "in principle any field could influence the database schema"; you must be _insane_ if verbose_name or help_text + # actually do, and the cost of the migrations is real) + # solution from https://stackoverflow.com/a/39801321/339144 + name, path, args, kwargs = original_deconstruct(self) + for attr in IGNORED_ATTRS: + kwargs.pop(attr, None) + return name, path, args, kwargs + + +def monkey_patch_deconstruct(): + models.Field.deconstruct = new_deconstruct diff --git a/bsmain/management/commands/makemigrations.py b/bsmain/management/commands/makemigrations.py new file mode 100644 index 0000000..2cd10e7 --- /dev/null +++ b/bsmain/management/commands/makemigrations.py @@ -0,0 +1,8 @@ +from django.core.management.commands.makemigrations import Command as OriginalCommand + +from . import monkey_patch_deconstruct +monkey_patch_deconstruct() + + +class Command(OriginalCommand): + pass # no changes, except the monkey patch above diff --git a/bsmain/management/commands/migrate.py b/bsmain/management/commands/migrate.py index 513b5c0..ff080de 100644 --- a/bsmain/management/commands/migrate.py +++ b/bsmain/management/commands/migrate.py @@ -1,6 +1,9 @@ import time from django.core.management.commands.migrate import Command as DjangoMigrateCommand +from . import monkey_patch_deconstruct +monkey_patch_deconstruct() # needed for migrate.py to avoid the warning about non-reflected changes + class Command(DjangoMigrateCommand): # We override the default Django migrate command to add the elapsed time for each migration. (This could in theory @@ -10,8 +13,7 @@ class Command(DjangoMigrateCommand): # We care more about the elapsed time for each migration than the average Django user because sqlite takes such a # prominent role in our architecture, and because migrations are run out of our direct control ("self hosted"). # - # AFAIU, "just dropping a file called migrate.py in one of our apps" is good enough to be the override (and if it - # isn't, it's not critical, since all we do is add a bit more info to the output). + # AFAIU, "just dropping a file called migrate.py in one of our apps" is good enough to be the override. def migration_progress_callback(self, action, migration=None, fake=False): # Django 4.2's method, with a single change diff --git a/projects/forms.py b/projects/forms.py index fcc08fc..2bc2861 100644 --- a/projects/forms.py +++ b/projects/forms.py @@ -3,7 +3,6 @@ from django.contrib.auth import get_user_model from django.template.defaultfilters import yesno from django.urls import reverse from django.utils.translation import gettext_lazy as _ -from django.utils.translation import pgettext_lazy from django.utils.html import format_html from bugsink.utils import assert_ @@ -47,8 +46,6 @@ class MyProjectMembershipForm(forms.ModelForm): super().__init__(*args, **kwargs) assert_(self.instance is not None, "This form is only implemented for editing") - self.fields['role'].label = _("Role") - if not edit_role: del self.fields['role'] @@ -66,7 +63,6 @@ class MyProjectMembershipForm(forms.ModelForm): sea_default = self.instance.user.send_email_alerts empty_label = _('Default (%s, as per %s settings)') % (yesno(sea_default).capitalize(), sea_defined_at) - self.fields['send_email_alerts'].label = _("Send email alerts") self.fields['send_email_alerts'].empty_label = empty_label self.fields['send_email_alerts'].widget.choices[0] = ("unknown", empty_label) @@ -87,10 +83,6 @@ class ProjectForm(forms.ModelForm): team_qs = kwargs.pop("team_qs", None) super().__init__(*args, **kwargs) - self.fields["name"].label = pgettext_lazy("Project", "Name") - self.fields["visibility"].label = _("Visibility") - - self.fields["retention_max_event_count"].label = _("Retention max event count") self.fields["retention_max_event_count"].help_text = _("The maximum number of events to store before evicting.") if self.instance is not None and self.instance.pk is not None: # for editing, we disallow changing the team. consideration: it's somewhat hard to see what the consequences diff --git a/projects/models.py b/projects/models.py index bc3c486..bcf34c4 100644 --- a/projects/models.py +++ b/projects/models.py @@ -3,7 +3,7 @@ import uuid from django.db import models from django.conf import settings from django.utils.text import slugify -from django.utils.translation import gettext_lazy as _ +from django.utils.translation import gettext_lazy as _, pgettext_lazy from bugsink.app_settings import get_settings from bugsink.transaction import delay_on_commit @@ -76,7 +76,7 @@ class Project(models.Model): team = models.ForeignKey("teams.Team", blank=False, null=True, on_delete=models.SET_NULL) - name = models.CharField(max_length=255, blank=False, null=False, unique=True) + name = models.CharField(pgettext_lazy("Project", "Name"), max_length=255, blank=False, null=False, unique=True) slug = models.SlugField(max_length=50, blank=False, null=False, unique=True) is_deleted = models.BooleanField(default=False) @@ -109,7 +109,7 @@ class Project(models.Model): # visibility visibility = models.IntegerField( - choices=ProjectVisibility.choices, default=ProjectVisibility.TEAM_MEMBERS, + _("Visibility"), choices=ProjectVisibility.choices, default=ProjectVisibility.TEAM_MEMBERS, help_text=_("Which users can see this project and its issues?")) # ingestion/digestion quota @@ -117,7 +117,7 @@ class Project(models.Model): next_quota_check = models.PositiveIntegerField(null=False, default=0) # retention - retention_max_event_count = models.PositiveIntegerField(default=10_000) + retention_max_event_count = models.PositiveIntegerField(_("Retention max event count"), default=10_000) def __str__(self): return self.name @@ -179,9 +179,9 @@ class ProjectMembership(models.Model): project = models.ForeignKey(Project, on_delete=models.DO_NOTHING) user = models.ForeignKey(settings.AUTH_USER_MODEL, on_delete=models.CASCADE) - send_email_alerts = models.BooleanField(default=None, null=True) + send_email_alerts = models.BooleanField(_("Send email alerts"), default=None, null=True) - role = models.IntegerField(choices=ProjectRole.choices, default=ProjectRole.MEMBER) + role = models.IntegerField(_("Role"), choices=ProjectRole.choices, default=ProjectRole.MEMBER) accepted = models.BooleanField(default=False) def __str__(self): diff --git a/teams/forms.py b/teams/forms.py index 8e9fad6..dbf0bd3 100644 --- a/teams/forms.py +++ b/teams/forms.py @@ -2,7 +2,6 @@ from django import forms from django.contrib.auth import get_user_model from django.template.defaultfilters import yesno from django.utils.translation import gettext_lazy as _ -from django.utils.translation import pgettext_lazy from bugsink.utils import assert_ from .models import TeamRole, TeamMembership, Team @@ -43,8 +42,6 @@ class MyTeamMembershipForm(forms.ModelForm): super().__init__(*args, **kwargs) assert_(self.instance is not None, "This form is only implemented for editing") - self.fields['role'].label = _("Role") - if not edit_role: del self.fields['role'] @@ -52,7 +49,6 @@ class MyTeamMembershipForm(forms.ModelForm): global_send_email_alerts_text = yesno(global_send_email_alerts).capitalize() empty_label = _("User-default (%s)") % global_send_email_alerts_text - self.fields['send_email_alerts'].label = _("Send email alerts") self.fields['send_email_alerts'].empty_label = empty_label self.fields['send_email_alerts'].widget.choices[0] = ("unknown", empty_label) @@ -69,8 +65,3 @@ class TeamForm(forms.ModelForm): class Meta: model = Team fields = ["name", "visibility"] - - def __init__(self, *args, **kwargs): - super().__init__(*args, **kwargs) - self.fields['name'].label = pgettext_lazy("Team", "Name") - self.fields['visibility'].label = _("Visibility") diff --git a/teams/models.py b/teams/models.py index a725846..5cc41ec 100644 --- a/teams/models.py +++ b/teams/models.py @@ -3,7 +3,7 @@ import uuid from django.db import models from django.conf import settings -from django.utils.translation import gettext_lazy as _ +from django.utils.translation import gettext_lazy as _, pgettext_lazy class TeamRole(models.IntegerChoices): @@ -27,9 +27,10 @@ class TeamVisibility(models.IntegerChoices): class Team(models.Model): id = models.UUIDField(primary_key=True, default=uuid.uuid4, editable=False) - name = models.CharField(max_length=255, blank=False, null=False, unique=True) + name = models.CharField(pgettext_lazy("Team", "Name"), max_length=255, blank=False, null=False, unique=True) visibility = models.IntegerField( + _("Visibility"), choices=TeamVisibility.choices, default=TeamVisibility.DISCOVERABLE, help_text=_("Which users can see this team and its issues?")) @@ -49,8 +50,8 @@ class TeamMembership(models.Model): team = models.ForeignKey(Team, on_delete=models.CASCADE) user = models.ForeignKey(settings.AUTH_USER_MODEL, on_delete=models.CASCADE) - send_email_alerts = models.BooleanField(default=None, null=True, blank=True) - role = models.IntegerField(choices=TeamRole.choices, default=TeamRole.MEMBER) + send_email_alerts = models.BooleanField(_("Send email alerts"), default=None, null=True, blank=True) + role = models.IntegerField(_("Role"), choices=TeamRole.choices, default=TeamRole.MEMBER) accepted = models.BooleanField(default=False) def __str__(self):