Attempt to create a unique together constraint between Template.name and Site

This commit is contained in:
blag 2025-05-29 15:42:55 -06:00
parent 8e284b54d8
commit 2d99b9683a
No known key found for this signature in database
3 changed files with 104 additions and 7 deletions

View file

@ -1,13 +1,14 @@
import posixpath
from django import forms
from django.contrib import admin
from django.contrib.admin.widgets import FilteredSelectMultiple
from django.core.exceptions import ImproperlyConfigured
from django.utils.translation import gettext_lazy as _
from django.utils.translation import ngettext
from django.utils.safestring import mark_safe
from dbtemplates.conf import settings
from dbtemplates.models import Template, add_template_to_cache, remove_cached_template
from dbtemplates.models import Template, TemplateSite, add_template_to_cache, remove_cached_template
from dbtemplates.utils.template import check_template_syntax
# Check if either django-reversion-compare or django-reversion is installed and
@ -80,6 +81,10 @@ elif settings.DBTEMPLATES_USE_REDACTOR:
TemplateContentTextArea = RedactorEditor
class TemplateSiteInlineAdmin(admin.TabularInline):
model = TemplateSite
class TemplateAdminForm(forms.ModelForm):
"""
@ -89,11 +94,37 @@ class TemplateAdminForm(forms.ModelForm):
widget=TemplateContentTextArea(attrs={'rows': '24'}),
help_text=content_help_text, required=False)
# https://stackoverflow.com/a/11658199
sites = forms.ModelMultipleChoiceField(
queryset=TemplateSite.objects.all(),
required=False,
widget=FilteredSelectMultiple(
verbose_name=_("sites"),
is_stacked=False,
),
)
class Meta:
model = Template
fields = ('name', 'content', 'sites', 'creation_date', 'last_changed')
fields = "__all__"
def __init__(self, *args, **kwargs):
super().__init__(*args, **kwargs)
if self.instance.pk:
self.fields['sites'].initial = self.instance.sites.all()
def save(self, commit=True, **kwargs):
template = super().save(commit=False, **kwargs)
if commit:
template.save()
if template.pk:
template.sites = self.cleaned_data['sites']
self.save_m2m()
return template
class TemplateAdmin(TemplateModelAdmin):
form = TemplateAdminForm
@ -103,17 +134,13 @@ class TemplateAdmin(TemplateModelAdmin):
'fields': ('name', 'content'),
'classes': ('monospace',),
}),
(_('Advanced'), {
'fields': (('sites'),),
}),
(_('Date/time'), {
'fields': (('creation_date', 'last_changed'),),
'classes': ('collapse',),
}),
)
filter_horizontal = ('sites',)
inlines = [TemplateSiteInlineAdmin]
list_display = ('name', 'creation_date', 'last_changed', 'site_list')
list_filter = ('sites',)
save_as = True
search_fields = ('name', 'content')
actions = ['invalidate_cache', 'repopulate_cache', 'check_syntax']

View file

@ -0,0 +1,44 @@
# Generated by Django 5.2 on 2025-05-29 20:49
import django.db.models.deletion
from django.db import migrations, models
class Migration(migrations.Migration):
dependencies = [
('dbtemplates', '0002_alter_template_creation_date_and_more'),
('sites', '0002_alter_domain_unique'),
]
operations = [
migrations.SeparateDatabaseAndState(
database_operations=[],
state_operations=[
migrations.CreateModel(
name='TemplateSite',
fields=[
('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
('site', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to='sites.site')),
('template', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to='dbtemplates.template')),
],
options={
'db_table': 'django_template_sites',
},
),
migrations.AlterField(
model_name='template',
name='sites',
field=models.ManyToManyField(blank=True, through='dbtemplates.TemplateSite', to='sites.site', verbose_name='sites'),
),
migrations.AddConstraint(
model_name='templatesite',
constraint=models.UniqueConstraint(fields=('template', 'site'), name='django_template_sites_template_id_site_id_d00c11a4_uniq'),
),
migrations.AddConstraint(
model_name='templatesite',
constraint=models.UniqueConstraint(fields=('template__name', 'site'), name='template_name_site_uniq'),
),
],
),
]

View file

@ -9,10 +9,36 @@ from django.contrib.sites.managers import CurrentSiteManager
from django.contrib.sites.models import Site
from django.db import models
from django.db.models import signals
from django.db.models.constraints import UniqueConstraint
from django.template import TemplateDoesNotExist
from django.utils.translation import gettext_lazy as _
class TemplateSite(models.Model):
"""
Defines an explicit through model between Template and Site models.
This explicit model allows us to add a unique_together index between
Site and Template.name
"""
template = models.ForeignKey('Template', on_delete=models.CASCADE)
site = models.ForeignKey(Site, on_delete=models.CASCADE)
class Meta:
db_table = 'django_template_sites'
constraints = [
UniqueConstraint(
# Preserve backwards compatibility so we don't have to drop the
# old unique index and recreate this index from scratch
name='django_template_sites_template_id_site_id_d00c11a4_uniq',
fields=['template', 'site'],
),
UniqueConstraint(
name='template_name_site_uniq',
fields=['template__name', 'site'],
),
]
class Template(models.Model):
"""
Defines a template model for use with the database template loader.
@ -24,7 +50,7 @@ class Template(models.Model):
help_text=_("Example: 'flatpages/default.html'"))
content = models.TextField(_('content'), blank=True)
sites = models.ManyToManyField(Site, verbose_name=_('sites'),
blank=True)
blank=True, through=TemplateSite)
creation_date = models.DateTimeField(_('creation date'), auto_now_add=True)
last_changed = models.DateTimeField(_('last changed'), auto_now=True)