mirror of
https://github.com/pretix/pretix.git
synced 2026-05-04 15:04:03 +00:00
Removed CleanerVersion layer [backwards-incompatible!]
This commit is contained in:
@@ -1,5 +1,5 @@
|
||||
from .auth import User
|
||||
from .base import CachedFile, Versionable, cachedfile_name
|
||||
from .base import CachedFile, cachedfile_name
|
||||
from .event import Event, EventLock, EventPermission, EventSetting
|
||||
from .items import (
|
||||
Item, ItemCategory, ItemVariation, Property, PropertyValue, Question,
|
||||
@@ -12,7 +12,7 @@ from .orders import (
|
||||
from .organizer import Organizer, OrganizerPermission, OrganizerSetting
|
||||
|
||||
__all__ = [
|
||||
'Versionable', 'User', 'CachedFile', 'Organizer', 'OrganizerPermission', 'Event', 'EventPermission',
|
||||
'User', 'CachedFile', 'Organizer', 'OrganizerPermission', 'Event', 'EventPermission',
|
||||
'ItemCategory', 'Item', 'Property', 'PropertyValue', 'ItemVariation', 'VariationsField', 'Question',
|
||||
'Quota', 'Order', 'CachedTicket', 'QuestionAnswer', 'ObjectWithAnswers', 'OrderPosition',
|
||||
'CartPosition', 'EventSetting', 'OrganizerSetting', 'EventLock', 'cachedfile_name', 'itempicture_upload_to',
|
||||
|
||||
@@ -1,71 +1,12 @@
|
||||
import copy
|
||||
import uuid
|
||||
from datetime import datetime
|
||||
|
||||
import six
|
||||
from django.db import models
|
||||
from django.db.models.signals import post_delete
|
||||
from django.dispatch import receiver
|
||||
from versions.models import Versionable as BaseVersionable, get_utc_now
|
||||
|
||||
|
||||
class Versionable(BaseVersionable):
|
||||
class Meta:
|
||||
abstract = True
|
||||
|
||||
def clone_shallow(self, forced_version_date: datetime=None):
|
||||
"""
|
||||
This behaves like clone(), but misses all the Many2Many-relation-handling. This is
|
||||
a performance optimization for cases in which we have to handle the Many2Many relations
|
||||
by hand anyways.
|
||||
"""
|
||||
if not self.pk: # NOQA
|
||||
raise ValueError('Instance must be saved before it can be cloned')
|
||||
|
||||
if self.version_end_date: # NOQA
|
||||
raise ValueError('This is a historical item and can not be cloned.')
|
||||
|
||||
if forced_version_date: # NOQA
|
||||
if not self.version_start_date <= forced_version_date <= get_utc_now():
|
||||
raise ValueError('The clone date must be between the version start date and now.')
|
||||
else:
|
||||
forced_version_date = get_utc_now()
|
||||
|
||||
earlier_version = self
|
||||
|
||||
later_version = copy.copy(earlier_version)
|
||||
later_version.version_end_date = None
|
||||
later_version.version_start_date = forced_version_date
|
||||
|
||||
# set earlier_version's ID to a new UUID so the clone (later_version) can
|
||||
# get the old one -- this allows 'head' to always have the original
|
||||
# id allowing us to get at all historic foreign key relationships
|
||||
earlier_version.id = six.u(str(uuid.uuid4()))
|
||||
earlier_version.version_end_date = forced_version_date
|
||||
earlier_version.save()
|
||||
|
||||
for field in earlier_version._meta.many_to_many:
|
||||
earlier_version.clone_relations_shallow(later_version, field.attname, forced_version_date)
|
||||
|
||||
if hasattr(earlier_version._meta, 'many_to_many_related'):
|
||||
for rel in earlier_version._meta.many_to_many_related:
|
||||
earlier_version.clone_relations_shallow(later_version, rel.via_field_name, forced_version_date)
|
||||
|
||||
later_version.save()
|
||||
|
||||
return later_version
|
||||
|
||||
def clone_relations_shallow(self, clone, manager_field_name, forced_version_date):
|
||||
# Source: the original object, where relations are currently pointing to
|
||||
source = getattr(self, manager_field_name) # returns a VersionedRelatedManager instance
|
||||
# Destination: the clone, where the cloned relations should point to
|
||||
source.through.objects.filter(**{source.source_field.attname: clone.id}).update(**{
|
||||
source.source_field.attname: self.id, 'version_end_date': forced_version_date
|
||||
})
|
||||
|
||||
|
||||
def cachedfile_name(instance, filename: str) -> str:
|
||||
return 'cachedfiles/%s.%s' % (instance.id, filename.split('.')[-1])
|
||||
return 'cachedfiles/%012d.%s' % (instance.id, filename.split('.')[-1])
|
||||
|
||||
|
||||
class CachedFile(models.Model):
|
||||
|
||||
@@ -9,17 +9,15 @@ from django.template.defaultfilters import date as _date
|
||||
from django.utils.functional import cached_property
|
||||
from django.utils.timezone import now
|
||||
from django.utils.translation import ugettext_lazy as _
|
||||
from versions.models import VersionedForeignKey
|
||||
|
||||
from pretix.base.i18n import I18nCharField
|
||||
from pretix.base.settings import SettingsProxy
|
||||
|
||||
from .auth import User
|
||||
from .base import Versionable
|
||||
from .organizer import Organizer
|
||||
|
||||
|
||||
class Event(Versionable):
|
||||
class Event(models.Model):
|
||||
"""
|
||||
This model represents an event. An event is anything you can buy
|
||||
tickets for.
|
||||
@@ -46,8 +44,7 @@ class Event(Versionable):
|
||||
:type plugins: str
|
||||
"""
|
||||
|
||||
organizer = VersionedForeignKey(Organizer, related_name="events",
|
||||
on_delete=models.PROTECT)
|
||||
organizer = models.ForeignKey(Organizer, related_name="events", on_delete=models.PROTECT)
|
||||
name = I18nCharField(
|
||||
max_length=200,
|
||||
verbose_name=_("Name"),
|
||||
@@ -184,7 +181,7 @@ class Event(Versionable):
|
||||
return locking.LockManager(self)
|
||||
|
||||
|
||||
class EventPermission(Versionable):
|
||||
class EventPermission(models.Model):
|
||||
"""
|
||||
The relation between an Event and an User who has permissions to
|
||||
access an event.
|
||||
@@ -203,8 +200,8 @@ class EventPermission(Versionable):
|
||||
:type can_change_orders: bool
|
||||
"""
|
||||
|
||||
event = VersionedForeignKey(Event)
|
||||
user = models.ForeignKey(User, related_name="event_perms")
|
||||
event = models.ForeignKey(Event, related_name="user_perms", on_delete=models.CASCADE)
|
||||
user = models.ForeignKey(User, related_name="event_perms", on_delete=models.CASCADE)
|
||||
can_change_settings = models.BooleanField(
|
||||
default=True,
|
||||
verbose_name=_("Can change event settings")
|
||||
@@ -237,12 +234,12 @@ class EventPermission(Versionable):
|
||||
}
|
||||
|
||||
|
||||
class EventSetting(Versionable):
|
||||
class EventSetting(models.Model):
|
||||
"""
|
||||
An event settings is a key-value setting which can be set for a
|
||||
specific event
|
||||
"""
|
||||
object = VersionedForeignKey(Event, related_name='setting_objects')
|
||||
object = models.ForeignKey(Event, related_name='setting_objects', on_delete=models.CASCADE)
|
||||
key = models.CharField(max_length=255)
|
||||
value = models.TextField()
|
||||
|
||||
|
||||
@@ -8,16 +8,14 @@ from django.utils.functional import cached_property
|
||||
from django.utils.timezone import now
|
||||
from django.utils.translation import ugettext_lazy as _
|
||||
from typing import List, Tuple
|
||||
from versions.models import VersionedForeignKey, VersionedManyToManyField
|
||||
|
||||
from pretix.base.i18n import I18nCharField, I18nTextField
|
||||
|
||||
from ..types import VariationDict
|
||||
from .base import Versionable
|
||||
from .event import Event
|
||||
|
||||
|
||||
class ItemCategory(Versionable):
|
||||
class ItemCategory(models.Model):
|
||||
"""
|
||||
Items can be sorted into these categories.
|
||||
|
||||
@@ -28,7 +26,7 @@ class ItemCategory(Versionable):
|
||||
:param position: An integer, used for sorting
|
||||
:type position: int
|
||||
"""
|
||||
event = VersionedForeignKey(
|
||||
event = models.ForeignKey(
|
||||
Event,
|
||||
on_delete=models.CASCADE,
|
||||
related_name='categories',
|
||||
@@ -44,7 +42,7 @@ class ItemCategory(Versionable):
|
||||
class Meta:
|
||||
verbose_name = _("Product category")
|
||||
verbose_name_plural = _("Product categories")
|
||||
ordering = ('position', 'version_birth_date')
|
||||
ordering = ('position', 'id')
|
||||
|
||||
def __str__(self):
|
||||
return str(self.name)
|
||||
@@ -61,7 +59,7 @@ class ItemCategory(Versionable):
|
||||
|
||||
@property
|
||||
def sortkey(self):
|
||||
return self.position, self.version_birth_date
|
||||
return self.position, self.id
|
||||
|
||||
def __lt__(self, other) -> bool:
|
||||
return self.sortkey < other.sortkey
|
||||
@@ -69,12 +67,12 @@ class ItemCategory(Versionable):
|
||||
|
||||
def itempicture_upload_to(instance, filename: str) -> str:
|
||||
return '%s/%s/item-%s.%s' % (
|
||||
instance.event.organizer.slug, instance.event.slug, instance.identity,
|
||||
instance.event.organizer.slug, instance.event.slug, instance.id,
|
||||
filename.split('.')[-1]
|
||||
)
|
||||
|
||||
|
||||
class Item(Versionable):
|
||||
class Item(models.Model):
|
||||
"""
|
||||
An item is a thing which can be sold. It belongs to an event and may or may not belong to a category.
|
||||
Items are often also called 'products' but are named 'items' internally due to historic reasons.
|
||||
@@ -104,13 +102,13 @@ class Item(Versionable):
|
||||
|
||||
"""
|
||||
|
||||
event = VersionedForeignKey(
|
||||
event = models.ForeignKey(
|
||||
Event,
|
||||
on_delete=models.PROTECT,
|
||||
related_name="items",
|
||||
verbose_name=_("Event"),
|
||||
)
|
||||
category = VersionedForeignKey(
|
||||
category = models.ForeignKey(
|
||||
ItemCategory,
|
||||
on_delete=models.PROTECT,
|
||||
related_name="items",
|
||||
@@ -222,7 +220,7 @@ class Item(Versionable):
|
||||
for var in all_variations:
|
||||
key = []
|
||||
for v in var.values.all():
|
||||
key.append((v.prop_id, v.identity))
|
||||
key.append((v.prop_id, v.id))
|
||||
key = tuple(sorted(key))
|
||||
variations_cache[key] = var
|
||||
|
||||
@@ -234,8 +232,8 @@ class Item(Versionable):
|
||||
key = []
|
||||
var = VariationDict()
|
||||
for v in comb:
|
||||
key.append((v.prop.identity, v.identity))
|
||||
var[v.prop.identity] = v
|
||||
key.append((v.prop.id, v.id))
|
||||
var[v.prop.id] = v
|
||||
key = tuple(sorted(key))
|
||||
if key in variations_cache:
|
||||
var['variation'] = variations_cache[key]
|
||||
@@ -245,7 +243,7 @@ class Item(Versionable):
|
||||
return result
|
||||
|
||||
def _get_all_generated_variations(self):
|
||||
propids = set([p.identity for p in self.properties.all()])
|
||||
propids = set([p.id for p in self.properties.all()])
|
||||
if len(propids) == 0:
|
||||
variations = [VariationDict()]
|
||||
else:
|
||||
@@ -261,11 +259,11 @@ class Item(Versionable):
|
||||
values = list(var.values.all())
|
||||
# Make sure we don't expose stale ItemVariation objects which are
|
||||
# still around altough they have an old set of properties
|
||||
if set([v.prop.identity for v in values]) != propids:
|
||||
if set([v.prop.id for v in values]) != propids:
|
||||
continue
|
||||
vardict = VariationDict()
|
||||
for v in values:
|
||||
vardict[v.prop.identity] = v
|
||||
vardict[v.prop.id] = v
|
||||
vardict['variation'] = var
|
||||
variations.append(vardict)
|
||||
return variations
|
||||
@@ -325,7 +323,7 @@ class Item(Versionable):
|
||||
key=lambda s: (s[0], s[1] if s[1] is not None else sys.maxsize))
|
||||
|
||||
|
||||
class Property(Versionable):
|
||||
class Property(models.Model):
|
||||
"""
|
||||
A property is a modifier which can be applied to an Item. For example
|
||||
'Size' would be a property associated with the item 'T-Shirt'.
|
||||
@@ -336,11 +334,11 @@ class Property(Versionable):
|
||||
:type name: str
|
||||
"""
|
||||
|
||||
event = VersionedForeignKey(
|
||||
event = models.ForeignKey(
|
||||
Event,
|
||||
related_name="properties"
|
||||
)
|
||||
item = VersionedForeignKey(
|
||||
item = models.ForeignKey(
|
||||
Item, related_name='properties', null=True, blank=True
|
||||
)
|
||||
name = I18nCharField(
|
||||
@@ -366,7 +364,7 @@ class Property(Versionable):
|
||||
self.event.get_cache().clear()
|
||||
|
||||
|
||||
class PropertyValue(Versionable):
|
||||
class PropertyValue(models.Model):
|
||||
"""
|
||||
A value of a property. If the property would be 'T-Shirt size',
|
||||
this could be 'M' or 'L'.
|
||||
@@ -379,7 +377,7 @@ class PropertyValue(Versionable):
|
||||
:type position: int
|
||||
"""
|
||||
|
||||
prop = VersionedForeignKey(
|
||||
prop = models.ForeignKey(
|
||||
Property,
|
||||
on_delete=models.CASCADE,
|
||||
related_name="values"
|
||||
@@ -395,7 +393,7 @@ class PropertyValue(Versionable):
|
||||
class Meta:
|
||||
verbose_name = _("Property value")
|
||||
verbose_name_plural = _("Property values")
|
||||
ordering = ("position", "version_birth_date")
|
||||
ordering = ("position", "id")
|
||||
|
||||
def __str__(self):
|
||||
return "%s: %s" % (self.prop.name, self.value)
|
||||
@@ -412,13 +410,13 @@ class PropertyValue(Versionable):
|
||||
|
||||
@property
|
||||
def sortkey(self) -> Tuple[int, datetime]:
|
||||
return self.position, self.version_birth_date
|
||||
return self.position, self.id
|
||||
|
||||
def __lt__(self, other) -> bool:
|
||||
return self.sortkey < other.sortkey
|
||||
|
||||
|
||||
class ItemVariation(Versionable):
|
||||
class ItemVariation(models.Model):
|
||||
"""
|
||||
A variation is an item combined with values for all properties
|
||||
associated with the item. For example, if your item is 'T-Shirt'
|
||||
@@ -444,11 +442,11 @@ class ItemVariation(Versionable):
|
||||
:param default_price: This variation's default price
|
||||
:type default_price: decimal.Decimal
|
||||
"""
|
||||
item = VersionedForeignKey(
|
||||
item = models.ForeignKey(
|
||||
Item,
|
||||
related_name='variations'
|
||||
)
|
||||
values = VersionedManyToManyField(
|
||||
values = models.ManyToManyField(
|
||||
PropertyValue,
|
||||
related_name='variations',
|
||||
)
|
||||
@@ -495,7 +493,7 @@ class ItemVariation(Versionable):
|
||||
"""
|
||||
vd = VariationDict()
|
||||
for v in self.values.all():
|
||||
vd[v.prop.identity] = v
|
||||
vd[v.prop.id] = v
|
||||
vd['variation'] = self
|
||||
return vd
|
||||
|
||||
@@ -507,14 +505,14 @@ class ItemVariation(Versionable):
|
||||
for pair in pk.split(","):
|
||||
prop, value = pair.split(":")
|
||||
self.values.add(
|
||||
PropertyValue.objects.current.get(
|
||||
identity=value,
|
||||
PropertyValue.objects.get(
|
||||
id=value,
|
||||
prop_id=prop
|
||||
)
|
||||
)
|
||||
|
||||
|
||||
class VariationsField(VersionedManyToManyField):
|
||||
class VariationsField(models.ManyToManyField):
|
||||
"""
|
||||
This is a ManyToManyField using the pretixcontrol.views.forms.VariationsField
|
||||
form field by default.
|
||||
@@ -536,12 +534,12 @@ class VariationsField(VersionedManyToManyField):
|
||||
initial = defaults['initial']
|
||||
if callable(initial):
|
||||
initial = initial()
|
||||
defaults['initial'] = [i.identity for i in initial]
|
||||
defaults['initial'] = [i.id for i in initial]
|
||||
# Skip ManyToManyField in dependency chain
|
||||
return super(RelatedField, self).formfield(**defaults)
|
||||
|
||||
|
||||
class Question(Versionable):
|
||||
class Question(models.Model):
|
||||
"""
|
||||
A question is an input field that can be used to extend a ticket
|
||||
by custom information, e.g. "Attendee age". A question can allow one o several
|
||||
@@ -573,7 +571,7 @@ class Question(Versionable):
|
||||
(TYPE_BOOLEAN, _("Yes/No")),
|
||||
)
|
||||
|
||||
event = VersionedForeignKey(
|
||||
event = models.ForeignKey(
|
||||
Event,
|
||||
related_name="questions"
|
||||
)
|
||||
@@ -589,7 +587,7 @@ class Question(Versionable):
|
||||
default=False,
|
||||
verbose_name=_("Required question")
|
||||
)
|
||||
items = VersionedManyToManyField(
|
||||
items = models.ManyToManyField(
|
||||
Item,
|
||||
related_name='questions',
|
||||
verbose_name=_("Products"),
|
||||
@@ -615,7 +613,7 @@ class Question(Versionable):
|
||||
self.event.get_cache().clear()
|
||||
|
||||
|
||||
class Quota(Versionable):
|
||||
class Quota(models.Model):
|
||||
"""
|
||||
A quota is a "pool of tickets". It is there to limit the number of items
|
||||
of a certain type to be sold. For example, you could have a quota of 500
|
||||
@@ -666,7 +664,7 @@ class Quota(Versionable):
|
||||
AVAILABILITY_RESERVED = 20
|
||||
AVAILABILITY_OK = 100
|
||||
|
||||
event = VersionedForeignKey(
|
||||
event = models.ForeignKey(
|
||||
Event,
|
||||
on_delete=models.CASCADE,
|
||||
related_name="quotas",
|
||||
@@ -681,7 +679,7 @@ class Quota(Versionable):
|
||||
null=True, blank=True,
|
||||
help_text=_("Leave empty for an unlimited number of tickets.")
|
||||
)
|
||||
items = VersionedManyToManyField(
|
||||
items = models.ManyToManyField(
|
||||
Item,
|
||||
verbose_name=_("Item"),
|
||||
related_name="quotas",
|
||||
@@ -745,7 +743,7 @@ class Quota(Versionable):
|
||||
def count_in_cart(self) -> int:
|
||||
from pretix.base.models import CartPosition
|
||||
|
||||
return CartPosition.objects.current.filter(
|
||||
return CartPosition.objects.filter(
|
||||
Q(expires__gte=now())
|
||||
& self._position_lookup
|
||||
).count()
|
||||
@@ -753,7 +751,7 @@ class Quota(Versionable):
|
||||
def count_orders(self) -> dict:
|
||||
from pretix.base.models import Order, OrderPosition
|
||||
|
||||
o = OrderPosition.objects.current.filter(self._position_lookup).aggregate(
|
||||
o = OrderPosition.objects.filter(self._position_lookup).aggregate(
|
||||
paid=Sum(
|
||||
Case(When(order__status=Order.STATUS_PAID, then=1),
|
||||
output_field=models.IntegerField())
|
||||
|
||||
@@ -6,9 +6,8 @@ from django.db import models
|
||||
from django.utils.timezone import now
|
||||
from django.utils.translation import ugettext_lazy as _
|
||||
from typing import List, Union
|
||||
from versions.models import VersionedForeignKey
|
||||
|
||||
from .base import CachedFile, Versionable
|
||||
from .base import CachedFile
|
||||
from .event import Event
|
||||
from .items import Item, ItemVariation, Question, Quota
|
||||
|
||||
@@ -17,7 +16,7 @@ def generate_secret():
|
||||
return ''.join(random.choice(string.ascii_letters + string.digits) for _ in range(16))
|
||||
|
||||
|
||||
class Order(Versionable):
|
||||
class Order(models.Model):
|
||||
"""
|
||||
An order is created when a user clicks 'buy' on his cart. It holds
|
||||
several OrderPositions and is connected to an user. It has an
|
||||
@@ -83,7 +82,7 @@ class Order(Versionable):
|
||||
choices=STATUS_CHOICE,
|
||||
verbose_name=_("Status")
|
||||
)
|
||||
event = VersionedForeignKey(
|
||||
event = models.ForeignKey(
|
||||
Event,
|
||||
verbose_name=_("Event"),
|
||||
related_name="orders"
|
||||
@@ -180,13 +179,11 @@ class Order(Versionable):
|
||||
|
||||
def mark_refunded(self):
|
||||
"""
|
||||
Mark this order as refunded. This clones the order object, sets the payment status and
|
||||
returns the cloned order object.
|
||||
Mark this order as refunded. This sets the payment status and returns the order object.
|
||||
"""
|
||||
order = self.clone()
|
||||
order.status = Order.STATUS_REFUNDED
|
||||
order.save()
|
||||
return order
|
||||
self.status = Order.STATUS_REFUNDED
|
||||
self.save()
|
||||
return self
|
||||
|
||||
def _can_be_paid(self) -> Union[bool, str]:
|
||||
error_messages = {
|
||||
@@ -223,12 +220,12 @@ class Order(Versionable):
|
||||
for quota in quotas:
|
||||
# Lock the quota, so no other thread is allowed to perform sales covered by this
|
||||
# quota while we're doing so.
|
||||
if quota.identity not in quota_cache:
|
||||
quota_cache[quota.identity] = quota
|
||||
if quota.id not in quota_cache:
|
||||
quota_cache[quota.id] = quota
|
||||
quota.cached_availability = quota.availability()[1]
|
||||
else:
|
||||
# Use cached version
|
||||
quota = quota_cache[quota.identity]
|
||||
quota = quota_cache[quota.id]
|
||||
if quota.cached_availability is not None:
|
||||
quota.cached_availability -= 1
|
||||
if quota.cached_availability < 0:
|
||||
@@ -240,12 +237,12 @@ class Order(Versionable):
|
||||
|
||||
|
||||
class CachedTicket(models.Model):
|
||||
order = VersionedForeignKey(Order, on_delete=models.CASCADE)
|
||||
order = models.ForeignKey(Order, on_delete=models.CASCADE)
|
||||
cachedfile = models.ForeignKey(CachedFile, on_delete=models.CASCADE)
|
||||
provider = models.CharField(max_length=255)
|
||||
|
||||
|
||||
class QuestionAnswer(Versionable):
|
||||
class QuestionAnswer(models.Model):
|
||||
"""
|
||||
The answer to a Question, connected to an OrderPosition or CartPosition.
|
||||
|
||||
@@ -268,7 +265,7 @@ class QuestionAnswer(Versionable):
|
||||
'CartPosition', null=True, blank=True,
|
||||
related_name='answers'
|
||||
)
|
||||
question = VersionedForeignKey(
|
||||
question = models.ForeignKey(
|
||||
Question, related_name='answers'
|
||||
)
|
||||
answer = models.TextField()
|
||||
@@ -286,14 +283,14 @@ class ObjectWithAnswers:
|
||||
self.answ[a.question_id] = a.answer
|
||||
self.questions = []
|
||||
for q in self.item.questions.all():
|
||||
if q.identity in self.answ:
|
||||
q.answer = self.answ[q.identity]
|
||||
if q.id in self.answ:
|
||||
q.answer = self.answ[q.id]
|
||||
else:
|
||||
q.answer = ""
|
||||
self.questions.append(q)
|
||||
|
||||
|
||||
class OrderPosition(ObjectWithAnswers, Versionable):
|
||||
class OrderPosition(ObjectWithAnswers, models.Model):
|
||||
"""
|
||||
An OrderPosition is one line of an order, representing one ordered items
|
||||
of a specified type (or variation).
|
||||
@@ -309,20 +306,23 @@ class OrderPosition(ObjectWithAnswers, Versionable):
|
||||
:param attendee_name: The attendee's name, if entered.
|
||||
:type attendee_name: str
|
||||
"""
|
||||
order = VersionedForeignKey(
|
||||
order = models.ForeignKey(
|
||||
Order,
|
||||
verbose_name=_("Order"),
|
||||
related_name='positions'
|
||||
related_name='positions',
|
||||
on_delete=models.PROTECT
|
||||
)
|
||||
item = VersionedForeignKey(
|
||||
item = models.ForeignKey(
|
||||
Item,
|
||||
verbose_name=_("Item"),
|
||||
related_name='positions'
|
||||
related_name='positions',
|
||||
on_delete=models.PROTECT
|
||||
)
|
||||
variation = VersionedForeignKey(
|
||||
variation = models.ForeignKey(
|
||||
ItemVariation,
|
||||
null=True, blank=True,
|
||||
verbose_name=_("Variation")
|
||||
verbose_name=_("Variation"),
|
||||
on_delete=models.PROTECT
|
||||
)
|
||||
price = models.DecimalField(
|
||||
decimal_places=2, max_digits=10,
|
||||
@@ -347,17 +347,16 @@ class OrderPosition(ObjectWithAnswers, Versionable):
|
||||
order=order, item=cartpos.item, variation=cartpos.variation,
|
||||
price=cartpos.price, attendee_name=cartpos.attendee_name
|
||||
)
|
||||
op.save()
|
||||
for answ in cartpos.answers.all():
|
||||
answ = answ.clone()
|
||||
answ.orderposition = op
|
||||
answ.cartposition = None
|
||||
answ.save()
|
||||
op.save()
|
||||
cartpos.delete()
|
||||
ops.append(op)
|
||||
|
||||
|
||||
class CartPosition(ObjectWithAnswers, Versionable):
|
||||
class CartPosition(ObjectWithAnswers, models.Model):
|
||||
"""
|
||||
A cart position is similar to a order line, except that it is not
|
||||
yet part of a binding order but just placed by some user in his or
|
||||
@@ -383,7 +382,7 @@ class CartPosition(ObjectWithAnswers, Versionable):
|
||||
:param attendee_name: The attendee's name, if entered.
|
||||
:type attendee_name: str
|
||||
"""
|
||||
event = VersionedForeignKey(
|
||||
event = models.ForeignKey(
|
||||
Event,
|
||||
verbose_name=_("Event")
|
||||
)
|
||||
@@ -391,14 +390,16 @@ class CartPosition(ObjectWithAnswers, Versionable):
|
||||
max_length=255, null=True, blank=True,
|
||||
verbose_name=_("Cart ID (e.g. session key)")
|
||||
)
|
||||
item = VersionedForeignKey(
|
||||
item = models.ForeignKey(
|
||||
Item,
|
||||
verbose_name=_("Item")
|
||||
verbose_name=_("Item"),
|
||||
on_delete=models.CASCADE
|
||||
)
|
||||
variation = VersionedForeignKey(
|
||||
variation = models.ForeignKey(
|
||||
ItemVariation,
|
||||
null=True, blank=True,
|
||||
verbose_name=_("Variation")
|
||||
verbose_name=_("Variation"),
|
||||
on_delete=models.CASCADE
|
||||
)
|
||||
price = models.DecimalField(
|
||||
decimal_places=2, max_digits=10,
|
||||
@@ -421,3 +422,8 @@ class CartPosition(ObjectWithAnswers, Versionable):
|
||||
class Meta:
|
||||
verbose_name = _("Cart position")
|
||||
verbose_name_plural = _("Cart positions")
|
||||
|
||||
def __str__(self):
|
||||
return '<CartPosition: item %d, variation %d for cart %s>' % (
|
||||
self.item.id, self.variation.id if self.variation else 0, self.cart_id
|
||||
)
|
||||
|
||||
@@ -2,15 +2,13 @@ from django.core.validators import RegexValidator
|
||||
from django.db import models
|
||||
from django.utils.functional import cached_property
|
||||
from django.utils.translation import ugettext_lazy as _
|
||||
from versions.models import VersionedForeignKey
|
||||
|
||||
from pretix.base.settings import SettingsProxy
|
||||
|
||||
from .auth import User
|
||||
from .base import Versionable
|
||||
|
||||
|
||||
class Organizer(Versionable):
|
||||
class Organizer(models.Model):
|
||||
"""
|
||||
This model represents an entity organizing events, e.g. a company, institution,
|
||||
charity, person, …
|
||||
@@ -72,7 +70,7 @@ class Organizer(Versionable):
|
||||
return ObjectRelatedCache(self)
|
||||
|
||||
|
||||
class OrganizerPermission(Versionable):
|
||||
class OrganizerPermission(models.Model):
|
||||
"""
|
||||
The relation between an Organizer and an User who has permissions to
|
||||
access an organizer profile.
|
||||
@@ -86,7 +84,7 @@ class OrganizerPermission(Versionable):
|
||||
:type can_create_events: bool
|
||||
"""
|
||||
|
||||
organizer = VersionedForeignKey(Organizer)
|
||||
organizer = models.ForeignKey(Organizer, related_name="user_perms", on_delete=models.CASCADE)
|
||||
user = models.ForeignKey(User, related_name="organizer_perms")
|
||||
can_create_events = models.BooleanField(
|
||||
default=True,
|
||||
@@ -104,11 +102,11 @@ class OrganizerPermission(Versionable):
|
||||
}
|
||||
|
||||
|
||||
class OrganizerSetting(Versionable):
|
||||
class OrganizerSetting(models.Model):
|
||||
"""
|
||||
An event option is a key-value setting which can be set for an
|
||||
organizer. It will be inherited by the events of this organizer
|
||||
"""
|
||||
object = VersionedForeignKey(Organizer, related_name='setting_objects')
|
||||
object = models.ForeignKey(Organizer, related_name='setting_objects', on_delete=models.CASCADE)
|
||||
key = models.CharField(max_length=255)
|
||||
value = models.TextField()
|
||||
|
||||
Reference in New Issue
Block a user