mirror of
https://github.com/pretix/pretix.git
synced 2026-05-05 15:14:04 +00:00
Add meta_data for items (#1576)
* PoC for ItemMetaProperties/Values * Missing is_valid * ItemMetaProperties/Values in editable via API, cloneable * Tests * Add Docs * Fix import order * Fix another import sorting... * Typeahead for ItemMetaValues * Test for editing event-objects * Fix typeahead permission checks * Further access restriction Co-authored-by: Raphael Michel <mail@raphaelmichel.de>
This commit is contained in:
@@ -10,9 +10,9 @@ from .event import (
|
||||
from .giftcards import GiftCard, GiftCardAcceptance, GiftCardTransaction
|
||||
from .invoices import Invoice, InvoiceLine, invoice_filename
|
||||
from .items import (
|
||||
Item, ItemAddOn, ItemBundle, ItemCategory, ItemVariation, Question,
|
||||
QuestionOption, Quota, SubEventItem, SubEventItemVariation,
|
||||
itempicture_upload_to,
|
||||
Item, ItemAddOn, ItemBundle, ItemCategory, ItemMetaProperty, ItemMetaValue,
|
||||
ItemVariation, Question, QuestionOption, Quota, SubEventItem,
|
||||
SubEventItemVariation, itempicture_upload_to,
|
||||
)
|
||||
from .log import LogEntry
|
||||
from .notifications import NotificationSetting
|
||||
|
||||
@@ -515,7 +515,7 @@ class Event(EventMixin, LoggedModel):
|
||||
), tz)
|
||||
|
||||
def copy_data_from(self, other):
|
||||
from . import ItemAddOn, ItemCategory, Item, Question, Quota
|
||||
from . import ItemAddOn, ItemCategory, Item, Question, Quota, ItemMetaValue
|
||||
from ..signals import event_copy_data
|
||||
|
||||
self.plugins = other.plugins
|
||||
@@ -540,6 +540,14 @@ class Event(EventMixin, LoggedModel):
|
||||
c.save()
|
||||
c.log_action('pretix.object.cloned')
|
||||
|
||||
item_meta_properties_map = {}
|
||||
for imp in other.item_meta_properties.all():
|
||||
item_meta_properties_map[imp.pk] = imp
|
||||
imp.pk = None
|
||||
imp.event = self
|
||||
imp.save()
|
||||
imp.log_action('pretix.object.cloned')
|
||||
|
||||
item_map = {}
|
||||
variation_map = {}
|
||||
for i in Item.objects.filter(event=other).prefetch_related('variations'):
|
||||
@@ -561,6 +569,12 @@ class Event(EventMixin, LoggedModel):
|
||||
v.item = i
|
||||
v.save()
|
||||
|
||||
for imv in ItemMetaValue.objects.filter(item__event=other).prefetch_related('item', 'property'):
|
||||
imv.pk = None
|
||||
imv.property = item_meta_properties_map[imv.property.pk]
|
||||
imv.item = item_map[imv.item.pk]
|
||||
imv.save()
|
||||
|
||||
for ia in ItemAddOn.objects.filter(base_item__event=other).prefetch_related('base_item', 'addon_category'):
|
||||
ia.pk = None
|
||||
ia.base_item = item_map[ia.base_item.pk]
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
import sys
|
||||
import uuid
|
||||
from collections import Counter
|
||||
from collections import Counter, OrderedDict
|
||||
from datetime import date, datetime, time
|
||||
from decimal import Decimal, DecimalException
|
||||
from typing import Tuple
|
||||
@@ -9,6 +9,7 @@ import dateutil.parser
|
||||
import pytz
|
||||
from django.conf import settings
|
||||
from django.core.exceptions import ValidationError
|
||||
from django.core.validators import RegexValidator
|
||||
from django.db import models
|
||||
from django.db.models import F, Func, Q, Sum
|
||||
from django.utils import formats
|
||||
@@ -591,6 +592,16 @@ class Item(LoggedModel):
|
||||
if from_date > until_date:
|
||||
raise ValidationError(_('The item\'s availability cannot end before it starts.'))
|
||||
|
||||
@property
|
||||
def meta_data(self):
|
||||
data = {p.name: p.default for p in self.event.item_meta_properties.all()}
|
||||
if hasattr(self, 'meta_values_cached'):
|
||||
data.update({v.property.name: v.value for v in self.meta_values_cached})
|
||||
else:
|
||||
data.update({v.property.name: v.value for v in self.meta_values.select_related('property').all()})
|
||||
|
||||
return OrderedDict((k, v) for k, v in sorted(data.items(), key=lambda k: k[0]))
|
||||
|
||||
|
||||
class ItemVariation(models.Model):
|
||||
"""
|
||||
@@ -1541,3 +1552,57 @@ class Quota(LoggedModel):
|
||||
else:
|
||||
if subevent:
|
||||
raise ValidationError(_('The subevent does not belong to this event.'))
|
||||
|
||||
|
||||
class ItemMetaProperty(LoggedModel):
|
||||
"""
|
||||
An event can have ItemMetaProperty objects attached to define meta information fields
|
||||
for its items. This information can be re-used for example in ticket layouts.
|
||||
|
||||
:param event: The event this property is defined for.
|
||||
:type event: Event
|
||||
:param name: Name
|
||||
:type name: Name of the property, used in various places
|
||||
:param default: Default value
|
||||
:type default: str
|
||||
"""
|
||||
event = models.ForeignKey(Event, related_name="item_meta_properties", on_delete=models.CASCADE)
|
||||
name = models.CharField(
|
||||
max_length=50, db_index=True,
|
||||
help_text=_(
|
||||
"Can not contain spaces or special characters except underscores"
|
||||
),
|
||||
validators=[
|
||||
RegexValidator(
|
||||
regex="^[a-zA-Z0-9_]+$",
|
||||
message=_("The property name may only contain letters, numbers and underscores."),
|
||||
),
|
||||
],
|
||||
verbose_name=_("Name"),
|
||||
)
|
||||
default = models.TextField(blank=True)
|
||||
|
||||
|
||||
class ItemMetaValue(LoggedModel):
|
||||
"""
|
||||
A meta-data value assigned to an item.
|
||||
|
||||
:param item: The item this metadata is valid for
|
||||
:type item: Item
|
||||
:param property: The property this value belongs to
|
||||
:type property: ItemMetaProperty
|
||||
:param value: The actual value
|
||||
:type value: str
|
||||
"""
|
||||
item = models.ForeignKey('Item', on_delete=models.CASCADE, related_name='meta_values')
|
||||
property = models.ForeignKey('ItemMetaProperty', on_delete=models.CASCADE, related_name='item_values')
|
||||
value = models.TextField()
|
||||
|
||||
class Meta:
|
||||
unique_together = ('item', 'property')
|
||||
|
||||
def delete(self, *args, **kwargs):
|
||||
super().delete(*args, **kwargs)
|
||||
|
||||
def save(self, *args, **kwargs):
|
||||
super().save(*args, **kwargs)
|
||||
|
||||
Reference in New Issue
Block a user