From 909a4c0f1a9cab8dbeb259b83bbb1ff7fbf253cf Mon Sep 17 00:00:00 2001 From: Jacob Rief Date: Thu, 23 Jul 2020 11:49:32 +0200 Subject: [PATCH 1/3] add identifier to EmailTemplate Add unique `identifier` field to model `EmailTemplate`. It can be used to access templates from Python code and leaves the `name` field as a human readable representation of that template. This additionally simplifies the unqiue constraints on that model. --- CHANGELOG.md | 7 ++++ post_office/admin.py | 14 +++++-- .../migrations/0010_auto_20200723_1131.py | 37 +++++++++++++++++++ post_office/models.py | 7 +--- 4 files changed, 57 insertions(+), 8 deletions(-) create mode 100644 post_office/migrations/0010_auto_20200723_1131.py diff --git a/CHANGELOG.md b/CHANGELOG.md index f667fbf3..0e2177b4 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,6 +1,13 @@ Changelog ========= +next +---- + +* Add unique `identifier` field to model `EmailTemplate`. It can be used to access templates from Python code + and leaves the `name` field as a human readable representation of that template. + + Version 3.4.1 (2020-05-16) -------------------------- * Allow `tasks.py` to be imported when Celery is not installed. This allows diff --git a/post_office/admin.py b/post_office/admin.py index 16ea6809..5ea5db62 100644 --- a/post_office/admin.py +++ b/post_office/admin.py @@ -234,7 +234,7 @@ class EmailTemplateAdminForm(forms.ModelForm): class Meta: model = EmailTemplate - fields = ['name', 'description', 'subject', 'content', 'html_content', 'language', + fields = ['language', 'name', 'description', 'subject', 'content', 'html_content', 'default_template'] def __init__(self, *args, **kwargs): @@ -243,13 +243,21 @@ def __init__(self, *args, **kwargs): if instance and instance.language: self.fields['language'].disabled = True + def get_initial_for_field(self, field, field_name): + """ + For new translated templates, copy the name from the default email template. + """ + if field_name == 'name' and self.instance and self.instance.id is None and 'default_template' in self.fields: + return self.fields['default_template'].parent_instance.name + return super().get_initial_for_field(field, field_name) + class EmailTemplateInline(admin.StackedInline): form = EmailTemplateAdminForm formset = EmailTemplateAdminFormSet model = EmailTemplate extra = 0 - fields = ('language', 'subject', 'content', 'html_content',) + fields = ['language', 'name', 'description', 'subject', 'content', 'html_content'] formfield_overrides = { models.CharField: {'widget': SubjectField} } @@ -265,7 +273,7 @@ class EmailTemplateAdmin(admin.ModelAdmin): search_fields = ('name', 'description', 'subject') fieldsets = [ (None, { - 'fields': ('name', 'description'), + 'fields': ('name', 'description', 'identifier'), }), (_("Default Content"), { 'fields': ('subject', 'content', 'html_content'), diff --git a/post_office/migrations/0010_auto_20200723_1131.py b/post_office/migrations/0010_auto_20200723_1131.py new file mode 100644 index 00000000..24c25ded --- /dev/null +++ b/post_office/migrations/0010_auto_20200723_1131.py @@ -0,0 +1,37 @@ +# Generated by Django 2.2.10 on 2020-07-23 09:31 + +from django.db import migrations, models + + +class Migration(migrations.Migration): + + dependencies = [ + ('post_office', '0009_requeued_mode'), + ] + + operations = [ + migrations.AddField( + model_name='emailtemplate', + name='identifier', + field=models.CharField(blank=True, help_text='Unique template identifier', max_length=50, null=True, unique=True, verbose_name='Identifier'), + ), + migrations.AlterField( + model_name='attachment', + name='emails', + field=models.ManyToManyField(related_name='attachments', to='post_office.Email', verbose_name='Emails'), + ), + migrations.AlterField( + model_name='email', + name='expires_at', + field=models.DateTimeField(blank=True, help_text="Email won't be sent after this timestamp", null=True, verbose_name='Expires'), + ), + migrations.AlterField( + model_name='email', + name='scheduled_time', + field=models.DateTimeField(blank=True, db_index=True, help_text='The scheduled sending time', null=True, verbose_name='Scheduled Time'), + ), + migrations.AlterUniqueTogether( + name='emailtemplate', + unique_together=set(), + ), + ] diff --git a/post_office/models.py b/post_office/models.py index 7cc15a88..d3b406eb 100644 --- a/post_office/models.py +++ b/post_office/models.py @@ -243,6 +243,8 @@ class EmailTemplate(models.Model): name = models.CharField(_('Name'), max_length=255, help_text=_("e.g: 'welcome_email'")) description = models.TextField(_('Description'), blank=True, help_text=_("Description of this template.")) + identifier = models.CharField(_('Identifier'), max_length=50, unique=True, blank=True, null=True, + help_text=_("Unique template identifier")) created = models.DateTimeField(auto_now_add=True) last_updated = models.DateTimeField(auto_now=True) subject = models.CharField(max_length=255, blank=True, @@ -262,7 +264,6 @@ class EmailTemplate(models.Model): class Meta: app_label = 'post_office' - unique_together = ('name', 'language', 'default_template') verbose_name = _("Email Template") verbose_name_plural = _("Email Templates") ordering = ['name'] @@ -274,10 +275,6 @@ def natural_key(self): return (self.name, self.language, self.default_template) def save(self, *args, **kwargs): - # If template is a translation, use default template's name - if self.default_template and not self.name: - self.name = self.default_template.name - template = super().save(*args, **kwargs) cache.delete(self.name) return template From b13e2ca5e1782f31784a12630c3c0a61467cb1f2 Mon Sep 17 00:00:00 2001 From: Jacob Rief Date: Thu, 23 Jul 2020 12:21:38 +0200 Subject: [PATCH 2/3] In EmailTemplateAdmin, replace description against identifier --- post_office/admin.py | 17 ++++++++++------- 1 file changed, 10 insertions(+), 7 deletions(-) diff --git a/post_office/admin.py b/post_office/admin.py index 5ea5db62..c0993727 100644 --- a/post_office/admin.py +++ b/post_office/admin.py @@ -14,7 +14,7 @@ from django.urls import reverse from django.utils.html import format_html from django.utils.text import Truncator -from django.utils.translation import gettext_lazy as _ +from django.utils.translation import gettext_lazy as _, get_language from .fields import CommaSeparatedEmailField from .models import Attachment, Log, Email, EmailTemplate, STATUS @@ -269,8 +269,8 @@ def get_max_num(self, request, obj=None, **kwargs): @admin.register(EmailTemplate) class EmailTemplateAdmin(admin.ModelAdmin): form = EmailTemplateAdminForm - list_display = ('name', 'description_shortened', 'subject', 'languages_compact', 'created') - search_fields = ('name', 'description', 'subject') + list_display = ['translated_name', 'identifier', 'subject', 'languages_compact', 'created'] + search_fields = ['name', 'identifier', 'description', 'subject'] fieldsets = [ (None, { 'fields': ('name', 'description', 'identifier'), @@ -287,10 +287,13 @@ class EmailTemplateAdmin(admin.ModelAdmin): def get_queryset(self, request): return self.model.objects.filter(default_template__isnull=True) - def description_shortened(self, instance): - return Truncator(instance.description.split('\n')[0]).chars(200) - description_shortened.short_description = _("Description") - description_shortened.admin_order_field = 'description' + def translated_name(self, instance): + try: + return instance.translated_templates.get(language=get_language()).name + except EmailTemplate.DoesNotExist: + return instance.name + translated_name.short_description = _("Name") + translated_name.admin_order_field = 'name' def languages_compact(self, instance): languages = [tt.language for tt in instance.translated_templates.order_by('language')] From 109b38ce8989e701cc14688dfe4cf5228bd94f21 Mon Sep 17 00:00:00 2001 From: Jacob Rief Date: Fri, 24 Jul 2020 11:44:39 +0200 Subject: [PATCH 3/3] fix failing unit tests --- post_office/tests/test_models.py | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/post_office/tests/test_models.py b/post_office/tests/test_models.py index 77ec5202..e3aee817 100644 --- a/post_office/tests/test_models.py +++ b/post_office/tests/test_models.py @@ -311,9 +311,10 @@ def test_attachments_email_message_with_mimetype(self): [('test.txt', 'test file content', 'text/plain')]) def test_translated_template_uses_default_templates_name(self): - template = EmailTemplate.objects.create(name='name') - id_template = template.translated_templates.create(language='id') - self.assertEqual(id_template.name, template.name) + template = EmailTemplate.objects.create(name='name', identifier='identifier') + id_template = template.translated_templates.create(name='Nama', language='id') + self.assertEqual(id_template.name, 'Nama') + self.assertEqual(id_template.identifier, None) def test_models_repr(self): self.assertEqual(repr(EmailTemplate(name='test', language='en')),