|
|
|
@@ -0,0 +1,91 @@
|
|
|
|
|
# Generated by Django 5.2.12 on 2026-04-28 11:34
|
|
|
|
|
import logging
|
|
|
|
|
|
|
|
|
|
from django.db import IntegrityError, migrations, transaction
|
|
|
|
|
from django.db.models import Count, F
|
|
|
|
|
|
|
|
|
|
logger = logging.getLogger(__name__)
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
def fix_cross_organizer_eventmetavalues(apps, schema_editor):
|
|
|
|
|
EventMetaProperty = apps.get_model("pretixbase", "EventMetaProperty")
|
|
|
|
|
EventMetaValue = apps.get_model("pretixbase", "EventMetaValue")
|
|
|
|
|
|
|
|
|
|
cross_org_values = EventMetaValue.objects.filter(
|
|
|
|
|
event__organizer__pk__ne=F('property__organizer__pk')
|
|
|
|
|
).order_by('event__organizer__slug', 'event__slug')
|
|
|
|
|
for emv in cross_org_values:
|
|
|
|
|
logger.warning("%s", f"Fixing cross-organizer EventMetaValue: {emv.event.organizer.slug}/{emv.event.slug}")
|
|
|
|
|
logger.warning(" %s", f"{emv.property.name}({emv.property.id}@{emv.property.organizer.slug}) = {repr(emv.value)}")
|
|
|
|
|
try:
|
|
|
|
|
emv.property = emv.event.organizer.meta_properties.get(name=emv.property.name)
|
|
|
|
|
if EventMetaValue.objects.filter(event=emv.event, property=emv.property).exists():
|
|
|
|
|
correct = EventMetaValue.objects.get(event=emv.event, property=emv.property)
|
|
|
|
|
if correct.value != emv.value:
|
|
|
|
|
logger.warning(" %s", f"WARN: conflicting EventMetaValue with property in correct organizer already exists, deleting the cross-organizer one")
|
|
|
|
|
else:
|
|
|
|
|
logger.warning(" %s", f"OK: same-value EventMetaValue with property in correct organizer already exists, deleting the cross-organizer one")
|
|
|
|
|
logger.warning(" %s", f"keeping: {correct.property.name}({correct.property.id}@{correct.property.organizer.slug}) = {repr(correct.value)}")
|
|
|
|
|
emv.delete()
|
|
|
|
|
else:
|
|
|
|
|
logger.warning(" %s", f"OK: found existing EventMetaProperty in {emv.event.organizer.slug}, updating reference")
|
|
|
|
|
logger.warning(" %s", f"after: {emv.property.name}({emv.property.id}@{emv.property.organizer.slug}) = {repr(emv.value)}")
|
|
|
|
|
emv.save(update_fields=["property"])
|
|
|
|
|
except EventMetaProperty.DoesNotExist:
|
|
|
|
|
meta_prop = emv.property
|
|
|
|
|
meta_prop.pk = None
|
|
|
|
|
meta_prop.organizer = emv.event.organizer
|
|
|
|
|
meta_prop.filter_public = False
|
|
|
|
|
meta_prop.save(force_insert=True)
|
|
|
|
|
logger.warning(" %s", f"WARN: found no matching EventMetaProperty, creating")
|
|
|
|
|
logger.warning(" %s", f"after: {emv.property.name}({emv.property.id}@{emv.property.organizer.slug}) = {repr(emv.value)}")
|
|
|
|
|
emv.save(update_fields=["property"])
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
def make_eventmetaproperties_unique(apps, schema_editor):
|
|
|
|
|
EventMetaProperty = apps.get_model("pretixbase", "EventMetaProperty")
|
|
|
|
|
EventMetaValue = apps.get_model("pretixbase", "EventMetaValue")
|
|
|
|
|
|
|
|
|
|
duplicates = EventMetaProperty.objects.values('organizer', 'organizer__slug', 'name').annotate(count=Count('id')).filter(count__gt=1)
|
|
|
|
|
for dup in duplicates:
|
|
|
|
|
logger.warning("%s", f"Fixup duplicate property {dup['organizer__slug']} {dup['name']}")
|
|
|
|
|
props = list(EventMetaProperty.objects.filter(organizer=dup['organizer'], name=dup['name']))
|
|
|
|
|
|
|
|
|
|
target = props[0]
|
|
|
|
|
invalid = props[1:]
|
|
|
|
|
|
|
|
|
|
try:
|
|
|
|
|
with transaction.atomic():
|
|
|
|
|
affected = EventMetaValue.objects.filter(
|
|
|
|
|
event__organizer=dup['organizer'], property__in=invalid
|
|
|
|
|
).update(
|
|
|
|
|
property=target
|
|
|
|
|
)
|
|
|
|
|
logger.warning("%s", f" Switching {affected} value(s) over to {target.name}({target.id}@{target.organizer.slug})")
|
|
|
|
|
|
|
|
|
|
except IntegrityError as e:
|
|
|
|
|
logger.warning("%s", f" Failed to switch all value(s) over to {target.name}({target.id}@{target.organizer.slug})")
|
|
|
|
|
logger.warning("%s", f" {e}")
|
|
|
|
|
for prop in invalid:
|
|
|
|
|
newname = f'{prop.name}_DUPLICATE_{prop.id}'
|
|
|
|
|
logger.warning("%s", f" Renaming {prop.name}({prop.id}@{prop.organizer.slug}) to {newname}({prop.id}@{prop.organizer.slug})")
|
|
|
|
|
prop.name = newname
|
|
|
|
|
prop.filter_public = False
|
|
|
|
|
prop.save()
|
|
|
|
|
|
|
|
|
|
else:
|
|
|
|
|
for prop in invalid:
|
|
|
|
|
logger.warning("%s", f" Deleting {prop.name}({prop.id}@{prop.organizer.slug})")
|
|
|
|
|
prop.delete()
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
class Migration(migrations.Migration):
|
|
|
|
|
|
|
|
|
|
dependencies = [
|
|
|
|
|
("pretixbase", "0301_reusablemedium_remove_orderposition"),
|
|
|
|
|
]
|
|
|
|
|
|
|
|
|
|
operations = [
|
|
|
|
|
migrations.RunPython(fix_cross_organizer_eventmetavalues, migrations.RunPython.noop),
|
|
|
|
|
migrations.RunPython(make_eventmetaproperties_unique, migrations.RunPython.noop),
|
|
|
|
|
]
|