Removed CleanerVersion layer [backwards-incompatible!]

This commit is contained in:
Raphael Michel
2015-12-12 13:08:33 +01:00
parent 0c9c9dd22c
commit d133d2abff
85 changed files with 712 additions and 1089 deletions

View File

@@ -87,13 +87,13 @@ class JSONExporter(BaseExporter):
},
'categories': [
{
'id': category.identity,
'id': category.id,
'name': str(category.name)
} for category in self.event.categories.current.all()
} for category in self.event.categories.all()
],
'items': [
{
'id': item.identity,
'id': item.id,
'name': str(item.name),
'category': item.category_id,
'price': item.default_price,
@@ -101,20 +101,20 @@ class JSONExporter(BaseExporter):
'active': item.active,
'variations': [
{
'id': variation.identity,
'id': variation.id,
'active': variation.active,
'price': variation.default_price if variation.default_price is not None else item.default_price,
'name': str(variation)
} for variation in item.variations.current.all()
} for variation in item.variations.all()
]
} for item in self.event.items.current.all().prefetch_related('variations')
} for item in self.event.items.all().prefetch_related('variations')
],
'questions': [
{
'id': question.identity,
'id': question.id,
'question': str(question.question),
'type': question.type
} for question in self.event.questions.current.all()
} for question in self.event.questions.all()
],
'orders': [
{
@@ -126,7 +126,7 @@ class JSONExporter(BaseExporter):
'total': order.total,
'positions': [
{
'id': position.identity,
'id': position.id,
'item': position.item_id,
'variation': position.variation_id,
'price': position.price,
@@ -137,19 +137,19 @@ class JSONExporter(BaseExporter):
'answer': answer.answer
} for answer in position.answers.all()
]
} for position in order.positions.current.all()
} for position in order.positions.all()
]
} for order in
self.event.orders.current.all().prefetch_related('positions', 'positions__answers').select_related(
self.event.orders.all().prefetch_related('positions', 'positions__answers').select_related(
'user')
],
'quotas': [
{
'id': quota.identity,
'id': quota.id,
'size': quota.size,
'items': [item.id for item in quota.items.all()],
'variations': [variation.id for variation in quota.variations.all()],
} for quota in self.event.quotas.current.all().prefetch_related('items', 'variations')
} for quota in self.event.quotas.all().prefetch_related('items', 'variations')
]
}
}

View File

@@ -5,11 +5,9 @@ from django import forms
from django.core.files import File
from django.core.files.storage import default_storage
from django.core.files.uploadedfile import UploadedFile
from django.db import models
from django.forms.models import BaseModelForm, ModelFormMetaclass
from django.utils import six
from django.utils.translation import ugettext_lazy as _
from versions.models import Versionable
from pretix.base.i18n import I18nFormField
from pretix.base.models import Event
@@ -30,44 +28,6 @@ class BaseI18nModelForm(BaseModelForm):
field.widget.enabled_langcodes = event.settings.get('locales')
class VersionedBaseModelForm(BaseI18nModelForm):
"""
This is a helperclass to construct VersionedModelForm
"""
def __init__(self, *args, **kwargs):
instance = kwargs.get('instance', None)
self.original_instance = copy.copy(instance) if instance else None
super().__init__(*args, **kwargs)
def save(self, commit=True):
if self.instance.pk is not None and isinstance(self.instance, Versionable):
if self.has_changed() and self.original_instance:
new = self.instance
old = self.original_instance
clone = old.clone()
for f in type(self.instance)._meta.get_fields():
if f.name not in (
'id', 'identity', 'version_start_date', 'version_end_date',
'version_birth_date'
) and not isinstance(f, (
models.ManyToOneRel, models.ManyToManyRel, models.ManyToManyField
)):
setattr(clone, f.name, getattr(new, f.name))
self.instance = clone
return super().save(commit)
class VersionedModelForm(six.with_metaclass(ModelFormMetaclass, VersionedBaseModelForm)):
"""
This is a modified version of I18nModelForm which differs from I18nModelForm in
only one way: It executes the .clone() method of an object before saving it back to
the database, if the model is a sub-class of versions.models.Versionable. You can
safely use this as a base class for all your model forms, it will work out correctly
with both versioned and non-versioned models.
"""
pass
class I18nModelForm(six.with_metaclass(ModelFormMetaclass, BaseI18nModelForm)):
"""
This is a modified version of Django's ModelForm which differs from ModelForm in

View File

@@ -5,12 +5,23 @@ import uuid
import django.core.validators
import django.db.models.deletion
import versions.models
from django.conf import settings
from django.contrib.auth.hashers import make_password
from django.db import migrations, models
import pretix.base.i18n
import pretix.base.models
import pretix.base.models.base
import pretix.base.models.items
import pretix.base.models.orders
def initial_user(apps, schema_editor):
User = apps.get_model("pretixbase", "User")
user = User(email='admin@localhost')
user.is_staff = True
user.is_superuser = True
user.password = make_password('admin')
user.save()
class Migration(migrations.Migration):
@@ -23,20 +34,20 @@ class Migration(migrations.Migration):
migrations.CreateModel(
name='User',
fields=[
('id', models.AutoField(serialize=False, verbose_name='ID', primary_key=True, auto_created=True)),
('id', models.AutoField(verbose_name='ID', primary_key=True, auto_created=True, serialize=False)),
('password', models.CharField(verbose_name='password', max_length=128)),
('last_login', models.DateTimeField(verbose_name='last login', null=True, blank=True)),
('is_superuser', models.BooleanField(default=False, verbose_name='superuser status', help_text='Designates that this user has all permissions without explicitly assigning them.')),
('email', models.EmailField(unique=True, verbose_name='E-mail', blank=True, db_index=True, max_length=254, null=True)),
('givenname', models.CharField(verbose_name='Given name', null=True, max_length=255, blank=True)),
('familyname', models.CharField(verbose_name='Family name', null=True, max_length=255, blank=True)),
('is_active', models.BooleanField(default=True, verbose_name='Is active')),
('is_staff', models.BooleanField(default=False, verbose_name='Is site admin')),
('last_login', models.DateTimeField(verbose_name='last login', blank=True, null=True)),
('is_superuser', models.BooleanField(verbose_name='superuser status', default=False, help_text='Designates that this user has all permissions without explicitly assigning them.')),
('email', models.EmailField(max_length=254, blank=True, unique=True, verbose_name='E-mail', null=True, db_index=True)),
('givenname', models.CharField(verbose_name='Given name', max_length=255, blank=True, null=True)),
('familyname', models.CharField(verbose_name='Family name', max_length=255, blank=True, null=True)),
('is_active', models.BooleanField(verbose_name='Is active', default=True)),
('is_staff', models.BooleanField(verbose_name='Is site admin', default=False)),
('date_joined', models.DateTimeField(verbose_name='Date joined', auto_now_add=True)),
('locale', models.CharField(default='en', verbose_name='Language', choices=[('en', 'English'), ('de', 'German'), ('de-informal', 'German (informal)')], max_length=50)),
('timezone', models.CharField(default='UTC', verbose_name='Timezone', max_length=100)),
('groups', models.ManyToManyField(related_query_name='user', verbose_name='groups', help_text='The groups this user belongs to. A user will get all permissions granted to each of their groups.', blank=True, related_name='user_set', to='auth.Group')),
('user_permissions', models.ManyToManyField(related_query_name='user', verbose_name='user permissions', help_text='Specific permissions for this user.', blank=True, related_name='user_set', to='auth.Permission')),
('locale', models.CharField(verbose_name='Language', default='en', choices=[('en', 'English'), ('de', 'German'), ('de-informal', 'German (informal)')], max_length=50)),
('timezone', models.CharField(verbose_name='Timezone', default='UTC', max_length=100)),
('groups', models.ManyToManyField(to='auth.Group', blank=True, related_query_name='user', verbose_name='groups', help_text='The groups this user belongs to. A user will get all permissions granted to each of their groups.', related_name='user_set')),
('user_permissions', models.ManyToManyField(to='auth.Permission', blank=True, related_query_name='user', verbose_name='user permissions', help_text='Specific permissions for this user.', related_name='user_set')),
],
options={
'verbose_name': 'User',
@@ -47,17 +58,17 @@ class Migration(migrations.Migration):
name='CachedFile',
fields=[
('id', models.UUIDField(default=uuid.uuid4, primary_key=True, serialize=False)),
('expires', models.DateTimeField(null=True, blank=True)),
('date', models.DateTimeField(null=True, blank=True)),
('expires', models.DateTimeField(blank=True, null=True)),
('date', models.DateTimeField(blank=True, null=True)),
('filename', models.CharField(max_length=255)),
('type', models.CharField(max_length=255)),
('file', models.FileField(upload_to=pretix.base.models.cachedfile_name, null=True, blank=True)),
('file', models.FileField(blank=True, null=True, upload_to=pretix.base.models.base.cachedfile_name)),
],
),
migrations.CreateModel(
name='CachedTicket',
fields=[
('id', models.AutoField(serialize=False, verbose_name='ID', primary_key=True, auto_created=True)),
('id', models.AutoField(verbose_name='ID', primary_key=True, auto_created=True, serialize=False)),
('provider', models.CharField(max_length=255)),
('cachedfile', models.ForeignKey(to='pretixbase.CachedFile')),
],
@@ -65,39 +76,32 @@ class Migration(migrations.Migration):
migrations.CreateModel(
name='CartPosition',
fields=[
('id', models.CharField(serialize=False, max_length=36, primary_key=True)),
('identity', models.CharField(max_length=36)),
('version_start_date', models.DateTimeField()),
('version_end_date', models.DateTimeField(default=None, null=True, blank=True)),
('version_birth_date', models.DateTimeField()),
('session', models.CharField(verbose_name='Session', null=True, max_length=255, blank=True)),
('price', models.DecimalField(decimal_places=2, verbose_name='Price', max_digits=10)),
('id', models.AutoField(verbose_name='ID', primary_key=True, auto_created=True, serialize=False)),
('cart_id', models.CharField(verbose_name='Cart ID (e.g. session key)', max_length=255, blank=True, null=True)),
('price', models.DecimalField(verbose_name='Price', max_digits=10, decimal_places=2)),
('datetime', models.DateTimeField(verbose_name='Date', auto_now_add=True)),
('expires', models.DateTimeField(verbose_name='Expiration date')),
('attendee_name', models.CharField(verbose_name='Attendee name', null=True, help_text='Empty, if this product is not an admission ticket', max_length=255, blank=True)),
('attendee_name', models.CharField(verbose_name='Attendee name', max_length=255, blank=True, null=True, help_text='Empty, if this product is not an admission ticket')),
],
options={
'verbose_name': 'Cart position',
'verbose_name_plural': 'Cart positions',
},
bases=(pretix.base.models.ObjectWithAnswers, models.Model),
bases=(pretix.base.models.orders.ObjectWithAnswers, models.Model),
),
migrations.CreateModel(
name='Event',
fields=[
('id', models.CharField(serialize=False, max_length=36, primary_key=True)),
('identity', models.CharField(max_length=36)),
('version_start_date', models.DateTimeField()),
('version_end_date', models.DateTimeField(default=None, null=True, blank=True)),
('version_birth_date', models.DateTimeField()),
('id', models.AutoField(verbose_name='ID', primary_key=True, auto_created=True, serialize=False)),
('name', pretix.base.i18n.I18nCharField(verbose_name='Name', max_length=200)),
('slug', models.SlugField(verbose_name='Slug', help_text='Should be short, only contain lowercase letters and numbers, and must be unique among your events. This is being used in addresses and bank transfer references.', validators=[django.core.validators.RegexValidator(regex='^[a-zA-Z0-9.-]+$', message='The slug may only contain letters, numbers, dots and dashes.')])),
('currency', models.CharField(default='EUR', verbose_name='Default currency', max_length=10)),
('slug', models.SlugField(verbose_name='Slug', validators=[django.core.validators.RegexValidator(message='The slug may only contain letters, numbers, dots and dashes.', regex='^[a-zA-Z0-9.-]+$')], help_text='Should be short, only contain lowercase letters and numbers, and must be unique among your events. This is being used in addresses and bank transfer references.')),
('currency', models.CharField(verbose_name='Default currency', default='EUR', max_length=10)),
('date_from', models.DateTimeField(verbose_name='Event start time')),
('date_to', models.DateTimeField(verbose_name='Event end time', null=True, blank=True)),
('presale_end', models.DateTimeField(verbose_name='End of presale', help_text='No products will be sold after this date.', null=True, blank=True)),
('presale_start', models.DateTimeField(verbose_name='Start of presale', help_text='No products will be sold before this date.', null=True, blank=True)),
('plugins', models.TextField(verbose_name='Plugins', null=True, blank=True)),
('date_to', models.DateTimeField(verbose_name='Event end time', blank=True, null=True)),
('is_public', models.BooleanField(verbose_name='Visible in public lists', default=False, help_text="If selected, this event may show up on the ticket system's start page or an organization profile.")),
('presale_end', models.DateTimeField(verbose_name='End of presale', help_text='No products will be sold after this date.', blank=True, null=True)),
('presale_start', models.DateTimeField(verbose_name='Start of presale', help_text='No products will be sold before this date.', blank=True, null=True)),
('plugins', models.TextField(verbose_name='Plugins', blank=True, null=True)),
],
options={
'verbose_name': 'Event',
@@ -108,7 +112,7 @@ class Migration(migrations.Migration):
migrations.CreateModel(
name='EventLock',
fields=[
('event', models.CharField(serialize=False, max_length=36, primary_key=True)),
('event', models.CharField(max_length=36, primary_key=True, serialize=False)),
('date', models.DateTimeField(auto_now=True)),
('token', models.UUIDField(default=uuid.uuid4)),
],
@@ -116,18 +120,14 @@ class Migration(migrations.Migration):
migrations.CreateModel(
name='EventPermission',
fields=[
('id', models.CharField(serialize=False, max_length=36, primary_key=True)),
('identity', models.CharField(max_length=36)),
('version_start_date', models.DateTimeField()),
('version_end_date', models.DateTimeField(default=None, null=True, blank=True)),
('version_birth_date', models.DateTimeField()),
('can_change_settings', models.BooleanField(default=True, verbose_name='Can change event settings')),
('can_change_items', models.BooleanField(default=True, verbose_name='Can change product settings')),
('can_view_orders', models.BooleanField(default=True, verbose_name='Can view orders')),
('can_change_permissions', models.BooleanField(default=True, verbose_name='Can change permissions')),
('can_change_orders', models.BooleanField(default=True, verbose_name='Can change orders')),
('event', versions.models.VersionedForeignKey(to='pretixbase.Event')),
('user', models.ForeignKey(related_name='event_perms', to=settings.AUTH_USER_MODEL)),
('id', models.AutoField(verbose_name='ID', primary_key=True, auto_created=True, serialize=False)),
('can_change_settings', models.BooleanField(verbose_name='Can change event settings', default=True)),
('can_change_items', models.BooleanField(verbose_name='Can change product settings', default=True)),
('can_view_orders', models.BooleanField(verbose_name='Can view orders', default=True)),
('can_change_permissions', models.BooleanField(verbose_name='Can change permissions', default=True)),
('can_change_orders', models.BooleanField(verbose_name='Can change orders', default=True)),
('event', models.ForeignKey(to='pretixbase.Event', related_name='user_perms')),
('user', models.ForeignKey(to=settings.AUTH_USER_MODEL, related_name='event_perms')),
],
options={
'verbose_name': 'Event permission',
@@ -137,35 +137,26 @@ class Migration(migrations.Migration):
migrations.CreateModel(
name='EventSetting',
fields=[
('id', models.CharField(serialize=False, max_length=36, primary_key=True)),
('identity', models.CharField(max_length=36)),
('version_start_date', models.DateTimeField()),
('version_end_date', models.DateTimeField(default=None, null=True, blank=True)),
('version_birth_date', models.DateTimeField()),
('id', models.AutoField(verbose_name='ID', primary_key=True, auto_created=True, serialize=False)),
('key', models.CharField(max_length=255)),
('value', models.TextField()),
('object', versions.models.VersionedForeignKey(related_name='setting_objects', to='pretixbase.Event')),
('object', models.ForeignKey(to='pretixbase.Event', related_name='setting_objects')),
],
options={
'abstract': False,
},
),
migrations.CreateModel(
name='Item',
fields=[
('id', models.CharField(serialize=False, max_length=36, primary_key=True)),
('identity', models.CharField(max_length=36)),
('version_start_date', models.DateTimeField()),
('version_end_date', models.DateTimeField(default=None, null=True, blank=True)),
('version_birth_date', models.DateTimeField()),
('id', models.AutoField(verbose_name='ID', primary_key=True, auto_created=True, serialize=False)),
('name', pretix.base.i18n.I18nCharField(verbose_name='Item name', max_length=255)),
('active', models.BooleanField(default=True, verbose_name='Active')),
('description', pretix.base.i18n.I18nTextField(verbose_name='Description', help_text='This is shown below the product name in lists.', null=True, blank=True)),
('default_price', models.DecimalField(decimal_places=2, verbose_name='Default price', max_digits=7, null=True)),
('tax_rate', models.DecimalField(decimal_places=2, verbose_name='Taxes included in percent', max_digits=7, null=True, blank=True)),
('admission', models.BooleanField(default=False, verbose_name='Is an admission ticket', help_text='Whether or not buying this product allows a person to enter your event')),
('active', models.BooleanField(verbose_name='Active', default=True)),
('description', pretix.base.i18n.I18nTextField(verbose_name='Description', help_text='This is shown below the product name in lists.', blank=True, null=True)),
('default_price', models.DecimalField(verbose_name='Default price', max_digits=7, decimal_places=2, null=True)),
('tax_rate', models.DecimalField(verbose_name='Taxes included in percent', max_digits=7, blank=True, null=True, decimal_places=2)),
('admission', models.BooleanField(verbose_name='Is an admission ticket', default=False, help_text='Whether or not buying this product allows a person to enter your event')),
('position', models.IntegerField(default=0)),
('picture', models.ImageField(upload_to=pretix.base.models.itempicture_upload_to, verbose_name='Product picture', null=True, blank=True)),
('picture', models.ImageField(verbose_name='Product picture', blank=True, null=True, upload_to=pretix.base.models.items.itempicture_upload_to)),
('available_from', models.DateTimeField(verbose_name='Available from', help_text='This product will not be sold before the given date.', blank=True, null=True)),
('available_until', models.DateTimeField(verbose_name='Available until', help_text='This product will not be sold after the given date.', blank=True, null=True)),
],
options={
'verbose_name': 'Product',
@@ -176,32 +167,24 @@ class Migration(migrations.Migration):
migrations.CreateModel(
name='ItemCategory',
fields=[
('id', models.CharField(serialize=False, max_length=36, primary_key=True)),
('identity', models.CharField(max_length=36)),
('version_start_date', models.DateTimeField()),
('version_end_date', models.DateTimeField(default=None, null=True, blank=True)),
('version_birth_date', models.DateTimeField()),
('id', models.AutoField(verbose_name='ID', primary_key=True, auto_created=True, serialize=False)),
('name', pretix.base.i18n.I18nCharField(verbose_name='Category name', max_length=255)),
('position', models.IntegerField(default=0)),
('event', versions.models.VersionedForeignKey(related_name='categories', to='pretixbase.Event')),
('event', models.ForeignKey(to='pretixbase.Event', related_name='categories')),
],
options={
'verbose_name': 'Product category',
'verbose_name_plural': 'Product categories',
'ordering': ('position', 'version_birth_date'),
'ordering': ('position', 'id'),
},
),
migrations.CreateModel(
name='ItemVariation',
fields=[
('id', models.CharField(serialize=False, max_length=36, primary_key=True)),
('identity', models.CharField(max_length=36)),
('version_start_date', models.DateTimeField()),
('version_end_date', models.DateTimeField(default=None, null=True, blank=True)),
('version_birth_date', models.DateTimeField()),
('active', models.BooleanField(default=True, verbose_name='Active')),
('default_price', models.DecimalField(decimal_places=2, verbose_name='Default price', max_digits=7, null=True, blank=True)),
('item', versions.models.VersionedForeignKey(related_name='variations', to='pretixbase.Item')),
('id', models.AutoField(verbose_name='ID', primary_key=True, auto_created=True, serialize=False)),
('active', models.BooleanField(verbose_name='Active', default=True)),
('default_price', models.DecimalField(verbose_name='Default price', max_digits=7, blank=True, null=True, decimal_places=2)),
('item', models.ForeignKey(to='pretixbase.Item', related_name='variations')),
],
options={
'verbose_name': 'Product variation',
@@ -211,25 +194,21 @@ class Migration(migrations.Migration):
migrations.CreateModel(
name='Order',
fields=[
('id', models.CharField(serialize=False, max_length=36, primary_key=True)),
('identity', models.CharField(max_length=36)),
('version_start_date', models.DateTimeField()),
('version_end_date', models.DateTimeField(default=None, null=True, blank=True)),
('version_birth_date', models.DateTimeField()),
('id', models.AutoField(verbose_name='ID', primary_key=True, auto_created=True, serialize=False)),
('code', models.CharField(verbose_name='Order code', max_length=16)),
('status', models.CharField(verbose_name='Status', choices=[('n', 'pending'), ('p', 'paid'), ('e', 'expired'), ('c', 'cancelled'), ('r', 'refunded')], max_length=3)),
('email', models.EmailField(verbose_name='E-mail', null=True, max_length=254, blank=True)),
('locale', models.CharField(verbose_name='Locale', null=True, max_length=32, blank=True)),
('secret', models.CharField(default=pretix.base.models.generate_secret, max_length=32)),
('status', models.CharField(verbose_name='Status', max_length=3, choices=[('n', 'pending'), ('p', 'paid'), ('e', 'expired'), ('c', 'cancelled'), ('r', 'refunded')])),
('email', models.EmailField(verbose_name='E-mail', max_length=254, blank=True, null=True)),
('locale', models.CharField(verbose_name='Locale', max_length=32, blank=True, null=True)),
('secret', models.CharField(default=pretix.base.models.orders.generate_secret, max_length=32)),
('datetime', models.DateTimeField(verbose_name='Date')),
('expires', models.DateTimeField(verbose_name='Expiration date')),
('payment_date', models.DateTimeField(verbose_name='Payment date', null=True, blank=True)),
('payment_provider', models.CharField(verbose_name='Payment provider', null=True, max_length=255, blank=True)),
('payment_fee', models.DecimalField(decimal_places=2, default=0, verbose_name='Payment method fee', max_digits=10)),
('payment_info', models.TextField(verbose_name='Payment information', null=True, blank=True)),
('payment_manual', models.BooleanField(default=False, verbose_name='Payment state was manually modified')),
('total', models.DecimalField(decimal_places=2, verbose_name='Total amount', max_digits=10)),
('event', versions.models.VersionedForeignKey(verbose_name='Event', related_name='orders', to='pretixbase.Event')),
('payment_date', models.DateTimeField(verbose_name='Payment date', blank=True, null=True)),
('payment_provider', models.CharField(verbose_name='Payment provider', max_length=255, blank=True, null=True)),
('payment_fee', models.DecimalField(verbose_name='Payment method fee', default=0, max_digits=10, decimal_places=2)),
('payment_info', models.TextField(verbose_name='Payment information', blank=True, null=True)),
('payment_manual', models.BooleanField(verbose_name='Payment state was manually modified', default=False)),
('total', models.DecimalField(verbose_name='Total amount', max_digits=10, decimal_places=2)),
('event', models.ForeignKey(to='pretixbase.Event', verbose_name='Event', related_name='orders')),
],
options={
'verbose_name': 'Order',
@@ -240,33 +219,25 @@ class Migration(migrations.Migration):
migrations.CreateModel(
name='OrderPosition',
fields=[
('id', models.CharField(serialize=False, max_length=36, primary_key=True)),
('identity', models.CharField(max_length=36)),
('version_start_date', models.DateTimeField()),
('version_end_date', models.DateTimeField(default=None, null=True, blank=True)),
('version_birth_date', models.DateTimeField()),
('price', models.DecimalField(decimal_places=2, verbose_name='Price', max_digits=10)),
('attendee_name', models.CharField(verbose_name='Attendee name', null=True, help_text='Empty, if this product is not an admission ticket', max_length=255, blank=True)),
('item', versions.models.VersionedForeignKey(verbose_name='Item', related_name='positions', to='pretixbase.Item')),
('order', versions.models.VersionedForeignKey(verbose_name='Order', related_name='positions', to='pretixbase.Order')),
('variation', versions.models.VersionedForeignKey(verbose_name='Variation', blank=True, null=True, to='pretixbase.ItemVariation')),
('id', models.AutoField(verbose_name='ID', primary_key=True, auto_created=True, serialize=False)),
('price', models.DecimalField(verbose_name='Price', max_digits=10, decimal_places=2)),
('attendee_name', models.CharField(verbose_name='Attendee name', max_length=255, blank=True, null=True, help_text='Empty, if this product is not an admission ticket')),
('item', models.ForeignKey(to='pretixbase.Item', on_delete=django.db.models.deletion.PROTECT, verbose_name='Item', related_name='positions')),
('order', models.ForeignKey(to='pretixbase.Order', on_delete=django.db.models.deletion.PROTECT, verbose_name='Order', related_name='positions')),
('variation', models.ForeignKey(blank=True, to='pretixbase.ItemVariation', on_delete=django.db.models.deletion.PROTECT, verbose_name='Variation', null=True)),
],
options={
'verbose_name': 'Order position',
'verbose_name_plural': 'Order positions',
},
bases=(pretix.base.models.ObjectWithAnswers, models.Model),
bases=(pretix.base.models.orders.ObjectWithAnswers, models.Model),
),
migrations.CreateModel(
name='Organizer',
fields=[
('id', models.CharField(serialize=False, max_length=36, primary_key=True)),
('identity', models.CharField(max_length=36)),
('version_start_date', models.DateTimeField()),
('version_end_date', models.DateTimeField(default=None, null=True, blank=True)),
('version_birth_date', models.DateTimeField()),
('id', models.AutoField(verbose_name='ID', primary_key=True, auto_created=True, serialize=False)),
('name', models.CharField(verbose_name='Name', max_length=200)),
('slug', models.SlugField(verbose_name='Slug', help_text='Should be short, only contain lowercase letters and numbers, and must be unique among your events. This is being used in addresses and bank transfer references.', validators=[django.core.validators.RegexValidator(regex='^[a-zA-Z0-9.-]+$', message='The slug may only contain letters, numbers, dots and dashes.')])),
('slug', models.SlugField(verbose_name='Slug', validators=[django.core.validators.RegexValidator(message='The slug may only contain letters, numbers, dots and dashes.', regex='^[a-zA-Z0-9.-]+$')], help_text='Should be short, only contain lowercase letters and numbers, and must be unique among your events. This is being used in addresses and bank transfer references.')),
],
options={
'verbose_name': 'Organizer',
@@ -277,14 +248,10 @@ class Migration(migrations.Migration):
migrations.CreateModel(
name='OrganizerPermission',
fields=[
('id', models.CharField(serialize=False, max_length=36, primary_key=True)),
('identity', models.CharField(max_length=36)),
('version_start_date', models.DateTimeField()),
('version_end_date', models.DateTimeField(default=None, null=True, blank=True)),
('version_birth_date', models.DateTimeField()),
('can_create_events', models.BooleanField(default=True, verbose_name='Can create events')),
('organizer', versions.models.VersionedForeignKey(to='pretixbase.Organizer')),
('user', models.ForeignKey(related_name='organizer_perms', to=settings.AUTH_USER_MODEL)),
('id', models.AutoField(verbose_name='ID', primary_key=True, auto_created=True, serialize=False)),
('can_create_events', models.BooleanField(verbose_name='Can create events', default=True)),
('organizer', models.ForeignKey(to='pretixbase.Organizer', related_name='user_perms')),
('user', models.ForeignKey(to=settings.AUTH_USER_MODEL, related_name='organizer_perms')),
],
options={
'verbose_name': 'Organizer permission',
@@ -294,30 +261,19 @@ class Migration(migrations.Migration):
migrations.CreateModel(
name='OrganizerSetting',
fields=[
('id', models.CharField(serialize=False, max_length=36, primary_key=True)),
('identity', models.CharField(max_length=36)),
('version_start_date', models.DateTimeField()),
('version_end_date', models.DateTimeField(default=None, null=True, blank=True)),
('version_birth_date', models.DateTimeField()),
('id', models.AutoField(verbose_name='ID', primary_key=True, auto_created=True, serialize=False)),
('key', models.CharField(max_length=255)),
('value', models.TextField()),
('object', versions.models.VersionedForeignKey(related_name='setting_objects', to='pretixbase.Organizer')),
('object', models.ForeignKey(to='pretixbase.Organizer', related_name='setting_objects')),
],
options={
'abstract': False,
},
),
migrations.CreateModel(
name='Property',
fields=[
('id', models.CharField(serialize=False, max_length=36, primary_key=True)),
('identity', models.CharField(max_length=36)),
('version_start_date', models.DateTimeField()),
('version_end_date', models.DateTimeField(default=None, null=True, blank=True)),
('version_birth_date', models.DateTimeField()),
('id', models.AutoField(verbose_name='ID', primary_key=True, auto_created=True, serialize=False)),
('name', pretix.base.i18n.I18nCharField(verbose_name='Property name', max_length=250)),
('event', versions.models.VersionedForeignKey(related_name='properties', to='pretixbase.Event')),
('item', versions.models.VersionedForeignKey(blank=True, related_name='properties', null=True, to='pretixbase.Item')),
('event', models.ForeignKey(to='pretixbase.Event', related_name='properties')),
('item', models.ForeignKey(blank=True, to='pretixbase.Item', null=True, related_name='properties')),
],
options={
'verbose_name': 'Product property',
@@ -327,34 +283,26 @@ class Migration(migrations.Migration):
migrations.CreateModel(
name='PropertyValue',
fields=[
('id', models.CharField(serialize=False, max_length=36, primary_key=True)),
('identity', models.CharField(max_length=36)),
('version_start_date', models.DateTimeField()),
('version_end_date', models.DateTimeField(default=None, null=True, blank=True)),
('version_birth_date', models.DateTimeField()),
('id', models.AutoField(verbose_name='ID', primary_key=True, auto_created=True, serialize=False)),
('value', pretix.base.i18n.I18nCharField(verbose_name='Value', max_length=250)),
('position', models.IntegerField(default=0)),
('prop', versions.models.VersionedForeignKey(related_name='values', to='pretixbase.Property')),
('prop', models.ForeignKey(to='pretixbase.Property', related_name='values')),
],
options={
'verbose_name': 'Property value',
'verbose_name_plural': 'Property values',
'ordering': ('position', 'version_birth_date'),
'ordering': ('position', 'id'),
},
),
migrations.CreateModel(
name='Question',
fields=[
('id', models.CharField(serialize=False, max_length=36, primary_key=True)),
('identity', models.CharField(max_length=36)),
('version_start_date', models.DateTimeField()),
('version_end_date', models.DateTimeField(default=None, null=True, blank=True)),
('version_birth_date', models.DateTimeField()),
('id', models.AutoField(verbose_name='ID', primary_key=True, auto_created=True, serialize=False)),
('question', pretix.base.i18n.I18nTextField(verbose_name='Question')),
('type', models.CharField(verbose_name='Question type', choices=[('N', 'Number'), ('S', 'Text (one line)'), ('T', 'Multiline text'), ('B', 'Yes/No')], max_length=5)),
('required', models.BooleanField(default=False, verbose_name='Required question')),
('event', versions.models.VersionedForeignKey(related_name='questions', to='pretixbase.Event')),
('items', versions.models.VersionedManyToManyField(to='pretixbase.Item', verbose_name='Products', help_text='This question will be asked to buyers of the selected products', related_name='questions', blank=True)),
('type', models.CharField(verbose_name='Question type', max_length=5, choices=[('N', 'Number'), ('S', 'Text (one line)'), ('T', 'Multiline text'), ('B', 'Yes/No')])),
('required', models.BooleanField(verbose_name='Required question', default=False)),
('event', models.ForeignKey(to='pretixbase.Event', related_name='questions')),
('items', models.ManyToManyField(verbose_name='Products', help_text='This question will be asked to buyers of the selected products', blank=True, to='pretixbase.Item', related_name='questions')),
],
options={
'verbose_name': 'Question',
@@ -364,33 +312,22 @@ class Migration(migrations.Migration):
migrations.CreateModel(
name='QuestionAnswer',
fields=[
('id', models.CharField(serialize=False, max_length=36, primary_key=True)),
('identity', models.CharField(max_length=36)),
('version_start_date', models.DateTimeField()),
('version_end_date', models.DateTimeField(default=None, null=True, blank=True)),
('version_birth_date', models.DateTimeField()),
('id', models.AutoField(verbose_name='ID', primary_key=True, auto_created=True, serialize=False)),
('answer', models.TextField()),
('cartposition', models.ForeignKey(blank=True, related_name='answers', null=True, to='pretixbase.CartPosition')),
('orderposition', models.ForeignKey(blank=True, related_name='answers', null=True, to='pretixbase.OrderPosition')),
('question', versions.models.VersionedForeignKey(related_name='answers', to='pretixbase.Question')),
('cartposition', models.ForeignKey(blank=True, to='pretixbase.CartPosition', null=True, related_name='answers')),
('orderposition', models.ForeignKey(blank=True, to='pretixbase.OrderPosition', null=True, related_name='answers')),
('question', models.ForeignKey(to='pretixbase.Question', related_name='answers')),
],
options={
'abstract': False,
},
),
migrations.CreateModel(
name='Quota',
fields=[
('id', models.CharField(serialize=False, max_length=36, primary_key=True)),
('identity', models.CharField(max_length=36)),
('version_start_date', models.DateTimeField()),
('version_end_date', models.DateTimeField(default=None, null=True, blank=True)),
('version_birth_date', models.DateTimeField()),
('id', models.AutoField(verbose_name='ID', primary_key=True, auto_created=True, serialize=False)),
('name', models.CharField(verbose_name='Name', max_length=200)),
('size', models.PositiveIntegerField(verbose_name='Total capacity')),
('event', versions.models.VersionedForeignKey(verbose_name='Event', related_name='quotas', to='pretixbase.Event')),
('items', versions.models.VersionedManyToManyField(to='pretixbase.Item', verbose_name='Item', related_name='quotas', blank=True)),
('variations', pretix.base.models.VariationsField(to='pretixbase.ItemVariation', verbose_name='Variations', related_name='quotas', blank=True)),
('size', models.PositiveIntegerField(verbose_name='Total capacity', help_text='Leave empty for an unlimited number of tickets.', blank=True, null=True)),
('event', models.ForeignKey(to='pretixbase.Event', verbose_name='Event', related_name='quotas')),
('items', models.ManyToManyField(verbose_name='Item', to='pretixbase.Item', blank=True, related_name='quotas')),
('variations', pretix.base.models.items.VariationsField(verbose_name='Variations', to='pretixbase.ItemVariation', blank=True, related_name='quotas')),
],
options={
'verbose_name': 'Quota',
@@ -400,51 +337,52 @@ class Migration(migrations.Migration):
migrations.AddField(
model_name='organizer',
name='permitted',
field=models.ManyToManyField(related_name='organizers', to=settings.AUTH_USER_MODEL, through='pretixbase.OrganizerPermission'),
field=models.ManyToManyField(to=settings.AUTH_USER_MODEL, through='pretixbase.OrganizerPermission', related_name='organizers'),
),
migrations.AddField(
model_name='itemvariation',
name='values',
field=versions.models.VersionedManyToManyField(related_name='variations', to='pretixbase.PropertyValue'),
field=models.ForeignKey(to='pretixbase.PropertyValue', related_name='variations'),
),
migrations.AddField(
model_name='item',
name='category',
field=versions.models.VersionedForeignKey(verbose_name='Category', blank=True, to='pretixbase.ItemCategory', related_name='items', null=True, on_delete=django.db.models.deletion.PROTECT),
field=models.ForeignKey(blank=True, to='pretixbase.ItemCategory', on_delete=django.db.models.deletion.PROTECT, verbose_name='Category', null=True, related_name='items'),
),
migrations.AddField(
model_name='item',
name='event',
field=versions.models.VersionedForeignKey(verbose_name='Event', related_name='items', to='pretixbase.Event', on_delete=django.db.models.deletion.PROTECT),
field=models.ForeignKey(to='pretixbase.Event', on_delete=django.db.models.deletion.PROTECT, verbose_name='Event', related_name='items'),
),
migrations.AddField(
model_name='event',
name='organizer',
field=versions.models.VersionedForeignKey(related_name='events', to='pretixbase.Organizer', on_delete=django.db.models.deletion.PROTECT),
field=models.ForeignKey(on_delete=django.db.models.deletion.PROTECT, to='pretixbase.Organizer', related_name='events'),
),
migrations.AddField(
model_name='event',
name='permitted',
field=models.ManyToManyField(related_name='events', to=settings.AUTH_USER_MODEL, through='pretixbase.EventPermission'),
field=models.ManyToManyField(to=settings.AUTH_USER_MODEL, through='pretixbase.EventPermission', related_name='events'),
),
migrations.AddField(
model_name='cartposition',
name='event',
field=versions.models.VersionedForeignKey(verbose_name='Event', to='pretixbase.Event'),
field=models.ForeignKey(verbose_name='Event', to='pretixbase.Event'),
),
migrations.AddField(
model_name='cartposition',
name='item',
field=versions.models.VersionedForeignKey(verbose_name='Item', to='pretixbase.Item'),
field=models.ForeignKey(verbose_name='Item', to='pretixbase.Item'),
),
migrations.AddField(
model_name='cartposition',
name='variation',
field=versions.models.VersionedForeignKey(verbose_name='Variation', blank=True, null=True, to='pretixbase.ItemVariation'),
field=models.ForeignKey(blank=True, to='pretixbase.ItemVariation', verbose_name='Variation', null=True),
),
migrations.AddField(
model_name='cachedticket',
name='order',
field=versions.models.VersionedForeignKey(to='pretixbase.Order'),
field=models.ForeignKey(to='pretixbase.Order'),
),
migrations.RunPython(initial_user),
]

View File

@@ -1,19 +0,0 @@
# -*- coding: utf-8 -*-
from __future__ import unicode_literals
from django.db import migrations, models
class Migration(migrations.Migration):
dependencies = [
('pretixbase', '0001_initial'),
]
operations = [
migrations.AlterField(
model_name='quota',
name='size',
field=models.PositiveIntegerField(help_text='Leave empty for an unlimited number of tickets.', verbose_name='Total capacity', blank=True, null=True),
),
]

View File

@@ -0,0 +1,23 @@
# -*- coding: utf-8 -*-
from __future__ import unicode_literals
from django.db import migrations, models
class Migration(migrations.Migration):
dependencies = [
('pretixbase', '0001_initial'),
]
operations = [
migrations.RemoveField(
model_name='itemvariation',
name='values',
),
migrations.AddField(
model_name='itemvariation',
name='values',
field=models.ManyToManyField(related_name='variations', to='pretixbase.PropertyValue'),
),
]

View File

@@ -1,19 +0,0 @@
# -*- coding: utf-8 -*-
from __future__ import unicode_literals
from django.db import migrations, models
class Migration(migrations.Migration):
dependencies = [
('pretixbase', '0002_auto_20151021_1412'),
]
operations = [
migrations.AddField(
model_name='event',
name='is_public',
field=models.BooleanField(help_text="If selected, this event may show up on the ticket system's start page or an organization profile.", default=False, verbose_name='Visible in public lists'),
),
]

View File

@@ -1,24 +0,0 @@
# -*- coding: utf-8 -*-
from __future__ import unicode_literals
from django.db import migrations, models
class Migration(migrations.Migration):
dependencies = [
('pretixbase', '0003_event_is_public'),
]
operations = [
migrations.RenameField(
model_name='cartposition',
old_name='session',
new_name='cart_id'
),
migrations.AlterField(
model_name='cartposition',
name='cart_id',
field=models.CharField(blank=True, verbose_name='Cart ID (e.g. session key)', max_length=255, null=True),
),
]

View File

@@ -1,24 +0,0 @@
# -*- coding: utf-8 -*-
from __future__ import unicode_literals
from django.db import migrations, models
class Migration(migrations.Migration):
dependencies = [
('pretixbase', '0004_auto_20151024_0848'),
]
operations = [
migrations.AddField(
model_name='item',
name='available_from',
field=models.DateTimeField(null=True, help_text='This product will not be sold before the given date.', blank=True, verbose_name='Available from'),
),
migrations.AddField(
model_name='item',
name='available_until',
field=models.DateTimeField(null=True, help_text='This product will not be sold after the given date.', blank=True, verbose_name='Available to'),
),
]

View File

@@ -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',

View File

@@ -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):

View File

@@ -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()

View File

@@ -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())

View File

@@ -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
)

View File

@@ -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()

View File

@@ -258,9 +258,7 @@ class BasePaymentProvider:
If the payment is completed, you should call ``pretix.base.services.orders.mark_order_paid(order, provider, info)``
with ``provider`` being your :py:attr:`identifier` and ``info`` being any string
you might want to store for later usage. Please note, that if you want to store
something inside ``order.payment_info``, please do it after the ``mark_order_paid`` call,
as this call does a object clone for you. Please also note that ``mark_order_paid`` might
you might want to store for later usage. Please note that ``mark_order_paid`` might
raise a ``Quota.QuotaExceededException`` if (and only if) the payment term of this
order is over and some of the items are sold out. You should use the exception message
to display a meaningful error to the user.
@@ -403,7 +401,7 @@ class FreeOrderProvider(BasePaymentProvider):
pass
def payment_is_valid_session(self, request: HttpRequest) -> bool:
return CartPosition.objects.current.filter(
return CartPosition.objects.filter(
Q(cart_id=request.session.session_key) & Q(event=request.event)
).aggregate(sum=Sum('price'))['sum'] == 0
@@ -446,7 +444,7 @@ class FreeOrderProvider(BasePaymentProvider):
messages.success(request, _('The order has been marked as refunded.'))
def is_allowed(self, request: HttpRequest) -> bool:
return CartPosition.objects.current.filter(
return CartPosition.objects.filter(
cart_id=request.session.session_key, event=request.event
).aggregate(sum=Sum('price'))['sum'] == 0

View File

@@ -35,7 +35,7 @@ def _extend_existing(event: Event, cart_id: str, expiry: datetime) -> None:
# Extend this user's cart session to 30 minutes from now to ensure all items in the
# cart expire at the same time
# We can extend the reservation of items which are not yet expired without risk
CartPosition.objects.current.filter(
CartPosition.objects.filter(
Q(cart_id=cart_id) & Q(event=event) & Q(expires__gt=now())
).update(expires=expiry)
@@ -44,7 +44,7 @@ def _re_add_expired_positions(items: List[CartPosition], event: Event, cart_id:
positions = set()
# For items that are already expired, we have to delete and re-add them, as they might
# be no longer available or prices might have changed. Sorry!
expired = CartPosition.objects.current.filter(
expired = CartPosition.objects.filter(
Q(cart_id=cart_id) & Q(event=event) & Q(expires__lte=now())
)
for cp in expired:
@@ -55,7 +55,7 @@ def _re_add_expired_positions(items: List[CartPosition], event: Event, cart_id:
def _delete_expired(expired: List[CartPosition]) -> None:
for cp in expired:
if cp.version_end_date is None:
if cp.expires <= now():
cp.delete()
@@ -66,19 +66,19 @@ def _check_date(event: Event) -> None:
raise CartError(error_messages['ended'])
def _add_new_items(event: Event, items: List[Tuple[str, Optional[str], int]],
def _add_new_items(event: Event, items: List[Tuple[int, Optional[int], int]],
cart_id: str, expiry: datetime) -> Optional[str]:
err = None
# Fetch items from the database
items_query = Item.objects.current.filter(event=event, identity__in=[i[0] for i in items]).prefetch_related(
items_query = Item.objects.filter(event=event, id__in=[i[0] for i in items]).prefetch_related(
"quotas")
items_cache = {i.identity: i for i in items_query}
variations_query = ItemVariation.objects.current.filter(
items_cache = {i.id: i for i in items_query}
variations_query = ItemVariation.objects.filter(
item__event=event,
identity__in=[i[1] for i in items if i[1] is not None]
id__in=[i[1] for i in items if i[1] is not None]
).select_related("item", "item__event").prefetch_related("quotas", "values", "values__prop")
variations_cache = {v.identity: v for v in variations_query}
variations_cache = {v.id: v for v in variations_query}
for i in items:
# Check whether the specified items are part of what we just fetched from the database
@@ -115,7 +115,7 @@ def _add_new_items(event: Event, items: List[Tuple[str, Optional[str], int]],
for k in range(quota_ok):
if len(i) > 3 and i[2] == 1:
# Recreating
cp = i[3].clone()
cp = i[3]
cp.expires = expiry
cp.price = item.default_price if variation is None else (
variation.default_price if variation.default_price is not None else item.default_price)
@@ -131,10 +131,10 @@ def _add_new_items(event: Event, items: List[Tuple[str, Optional[str], int]],
return err
def _add_items_to_cart(event: Event, items: List[Tuple[str, Optional[str], int]], cart_id: str=None) -> None:
def _add_items_to_cart(event: Event, items: List[Tuple[int, Optional[int], int]], cart_id: str=None) -> None:
with event.lock():
_check_date(event)
existing = CartPosition.objects.current.filter(Q(cart_id=cart_id) & Q(event=event)).count()
existing = CartPosition.objects.filter(Q(cart_id=cart_id) & Q(event=event)).count()
if sum(i[2] for i in items) + existing > int(event.settings.max_items_per_order):
# TODO: i18n plurals
raise CartError(error_messages['max_items'] % event.settings.max_items_per_order)
@@ -152,7 +152,7 @@ def _add_items_to_cart(event: Event, items: List[Tuple[str, Optional[str], int]]
raise CartError(err)
def add_items_to_cart(event: str, items: List[Tuple[str, Optional[str], int]], cart_id: str=None) -> None:
def add_items_to_cart(event: int, items: List[Tuple[int, Optional[int], int]], cart_id: str=None) -> None:
"""
Adds a list of items to a user's cart.
:param event: The event ID in question
@@ -160,21 +160,21 @@ def add_items_to_cart(event: str, items: List[Tuple[str, Optional[str], int]], c
:param session: Session ID of a guest
:raises CartError: On any error that occured
"""
event = Event.objects.current.get(identity=event)
event = Event.objects.get(id=event)
try:
_add_items_to_cart(event, items, cart_id)
except EventLock.LockTimeoutException:
raise CartError(error_messages['busy'])
def remove_items_from_cart(event: str, items: List[Tuple[str, Optional[str], int]], cart_id: str=None) -> None:
def remove_items_from_cart(event: int, items: List[Tuple[int, Optional[int], int]], cart_id: int=None) -> None:
"""
Removes a list of items from a user's cart.
:param event: The event ID in question
:param items: A list of tuple of the form (item id, variation id or None, number)
:param session: Session ID of a guest
"""
event = Event.objects.current.get(identity=event)
event = Event.objects.get(id=event)
for item, variation, cnt in items:
cw = Q(cart_id=cart_id) & Q(item_id=item) & Q(event=event)
@@ -182,7 +182,7 @@ def remove_items_from_cart(event: str, items: List[Tuple[str, Optional[str], int
cw &= Q(variation_id=variation)
else:
cw &= Q(variation__isnull=True)
for cp in CartPosition.objects.current.filter(cw).order_by("-price")[:cnt]:
for cp in CartPosition.objects.filter(cw).order_by("-price")[:cnt]:
cp.delete()
@@ -190,8 +190,8 @@ if settings.HAS_CELERY:
from pretix.celery import app
@app.task(bind=True, max_retries=5, default_retry_delay=2)
def add_items_to_cart_task(self, event: str, items: List[Tuple[str, Optional[str], int]], cart_id: str):
event = Event.objects.current.get(identity=event)
def add_items_to_cart_task(self, event: int, items: List[Tuple[int, Optional[int], int]], cart_id: str):
event = Event.objects.get(id=event)
try:
_add_items_to_cart(event, items, cart_id)
except EventLock.LockTimeoutException:

View File

@@ -7,7 +7,7 @@ from pretix.base.signals import register_data_exporters
def export(event: str, fileid: str, provider: str, form_data: Dict[str, Any]) -> None:
event = Event.objects.current.get(identity=event)
event = Event.objects.get(id=event)
file = CachedFile.objects.get(id=fileid)
responses = register_data_exporters.send(event)
for receiver, response in responses:

View File

@@ -64,13 +64,13 @@ def lock_event_db(event):
for i in range(retries):
with transaction.atomic():
dt = now()
l, created = EventLock.objects.get_or_create(event=event.identity)
l, created = EventLock.objects.get_or_create(event=event.id)
if created:
event._lock = l
return True
elif l.date < now() - timedelta(seconds=LOCK_TIMEOUT):
newtoken = uuid.uuid4()
updated = EventLock.objects.filter(event=event.identity, token=l.token).update(date=dt, token=newtoken)
updated = EventLock.objects.filter(event=event.id, token=l.token).update(date=dt, token=newtoken)
if updated:
l.token = newtoken
event._lock = l
@@ -84,7 +84,7 @@ def release_event_db(event):
if not hasattr(event, '_lock') or not event._lock:
raise EventLock.LockReleaseException('Lock is not owned by this thread')
try:
lock = EventLock.objects.get(event=event.identity, token=event._lock.token)
lock = EventLock.objects.get(event=event.id, token=event._lock.token)
lock.delete()
event._lock = None
except EventLock.DoesNotExist:
@@ -97,7 +97,7 @@ def redis_lock_from_event(event):
if not hasattr(event, '_lock') or not event._lock:
rc = get_redis_connection("redis")
event._lock = Lock(redis=rc, name='pretix_event_%s' % event.identity, timeout=LOCK_TIMEOUT)
event._lock = Lock(redis=rc, name='pretix_event_%s' % event.id, timeout=LOCK_TIMEOUT)
return event._lock

View File

@@ -33,8 +33,8 @@ error_messages = {
def mark_order_paid(order: Order, provider: str=None, info: str=None, date: datetime=None, manual: bool=None,
force: bool=False) -> Order:
"""
Marks an order as paid. This clones the order object, sets the payment provider,
info and date and returns the cloned order object.
Marks an order as paid. This sets the payment provider, info and date and returns
the order object.
:param provider: The payment provider that marked this as paid
:type provider: str
@@ -52,7 +52,6 @@ def mark_order_paid(order: Order, provider: str=None, info: str=None, date: date
can_be_paid = order._can_be_paid()
if not force and can_be_paid is not True:
raise Quota.QuotaExceededException(can_be_paid)
order = order.clone()
order.payment_provider = provider or order.payment_provider
order.payment_info = info or order.payment_info
order.payment_date = date or now()
@@ -110,7 +109,6 @@ def _check_positions(event: Event, dt: datetime, positions: List[CartPosition]):
cp.delete()
continue
if price != cp.price:
cp = cp.clone()
positions[i] = cp
cp.price = price
cp.save()
@@ -125,7 +123,6 @@ def _check_positions(event: Event, dt: datetime, positions: List[CartPosition]):
quota_ok = False
break
if quota_ok:
cp = cp.clone()
positions[i] = cp
cp.expires = now() + timedelta(
minutes=event.settings.get('reservation_time', as_type=int))
@@ -163,7 +160,7 @@ def _create_order(event: Event, email: str, positions: List[CartPosition], dt: d
def _perform_order(event: str, payment_provider: str, position_ids: List[str],
email: str, locale: str):
event = Event.objects.current.get(identity=event)
event = Event.objects.get(id=event)
responses = register_payment_providers.send(event)
pprov = None
for receiver, response in responses:
@@ -175,8 +172,8 @@ def _perform_order(event: str, payment_provider: str, position_ids: List[str],
dt = now()
with event.lock():
positions = list(CartPosition.objects.current.filter(
identity__in=position_ids).select_related('item', 'variation'))
positions = list(CartPosition.objects.filter(
id__in=position_ids).select_related('item', 'variation'))
if len(position_ids) != len(positions):
raise OrderError(error_messages['internal'])
_check_positions(event, dt, positions)
@@ -197,7 +194,7 @@ def _perform_order(event: str, payment_provider: str, position_ids: List[str],
},
event, locale=order.locale
)
return order.identity
return order.id
def perform_order(event: str, payment_provider: str, positions: List[str],

View File

@@ -37,28 +37,28 @@ def order_overview(event: Event) -> Tuple[List[Tuple[ItemCategory, List[Item]]],
num_total = {
(p['item'], p['variation']): (p['cnt'], p['price'])
for p in (OrderPosition.objects.current
for p in (OrderPosition.objects
.filter(order__event=event)
.values('item', 'variation')
.annotate(cnt=Count('id'), price=Sum('price')).order_by())
}
num_cancelled = {
(p['item'], p['variation']): (p['cnt'], p['price'])
for p in (OrderPosition.objects.current
for p in (OrderPosition.objects
.filter(order__event=event, order__status=Order.STATUS_CANCELLED)
.values('item', 'variation')
.annotate(cnt=Count('id'), price=Sum('price')).order_by())
}
num_refunded = {
(p['item'], p['variation']): (p['cnt'], p['price'])
for p in (OrderPosition.objects.current
for p in (OrderPosition.objects
.filter(order__event=event, order__status=Order.STATUS_REFUNDED)
.values('item', 'variation')
.annotate(cnt=Count('id'), price=Sum('price')).order_by())
}
num_pending = {
(p['item'], p['variation']): (p['cnt'], p['price'])
for p in (OrderPosition.objects.current
for p in (OrderPosition.objects
.filter(order__event=event,
order__status__in=(Order.STATUS_PENDING, Order.STATUS_EXPIRED))
.values('item', 'variation')
@@ -66,7 +66,7 @@ def order_overview(event: Event) -> Tuple[List[Tuple[ItemCategory, List[Item]]],
}
num_paid = {
(p['item'], p['variation']): (p['cnt'], p['price'])
for p in (OrderPosition.objects.current
for p in (OrderPosition.objects
.filter(order__event=event, order__status=Order.STATUS_PAID)
.values('item', 'variation')
.annotate(cnt=Count('id'), price=Sum('price')).order_by())
@@ -76,12 +76,12 @@ def order_overview(event: Event) -> Tuple[List[Tuple[ItemCategory, List[Item]]],
item.all_variations = sorted(item.get_all_variations(),
key=lambda vd: vd.ordered_values())
for var in item.all_variations:
variid = var['variation'].identity if 'variation' in var else None
var.num_total = num_total.get((item.identity, variid), (0, 0))
var.num_pending = num_pending.get((item.identity, variid), (0, 0))
var.num_cancelled = num_cancelled.get((item.identity, variid), (0, 0))
var.num_refunded = num_refunded.get((item.identity, variid), (0, 0))
var.num_paid = num_paid.get((item.identity, variid), (0, 0))
variid = var['variation'].id if 'variation' in var else None
var.num_total = num_total.get((item.id, variid), (0, 0))
var.num_pending = num_pending.get((item.id, variid), (0, 0))
var.num_cancelled = num_cancelled.get((item.id, variid), (0, 0))
var.num_refunded = num_refunded.get((item.id, variid), (0, 0))
var.num_paid = num_paid.get((item.id, variid), (0, 0))
item.has_variations = (len(item.all_variations) != 1
or not item.all_variations[0].empty())
item.num_total = tuplesum(var.num_total for var in item.all_variations)
@@ -100,7 +100,8 @@ def order_overview(event: Event) -> Tuple[List[Tuple[ItemCategory, List[Item]]],
# insert categories into a set for uniqueness
# a set is unsorted, so sort again by category
],
key=lambda group: (group[0].position, group[0].identity) if group[0] is not None else (0, "")
key=lambda group: (group[0].position, group[0].id) if (
group[0] is not None and group[0].id is not None) else (0, 0)
)
for c in items_by_category:
@@ -116,35 +117,35 @@ def order_overview(event: Event) -> Tuple[List[Tuple[ItemCategory, List[Item]]],
payment_items = []
num_total = {
o['payment_provider']: (o['cnt'], o['payment_fee'])
for o in (Order.objects.current
for o in (Order.objects
.filter(event=event)
.values('payment_provider')
.annotate(cnt=Count('id'), payment_fee=Sum('payment_fee')).order_by())
}
num_cancelled = {
o['payment_provider']: (o['cnt'], o['payment_fee'])
for o in (Order.objects.current
for o in (Order.objects
.filter(event=event, status=Order.STATUS_CANCELLED)
.values('payment_provider')
.annotate(cnt=Count('id'), payment_fee=Sum('payment_fee')).order_by())
}
num_refunded = {
o['payment_provider']: (o['cnt'], o['payment_fee'])
for o in (Order.objects.current
for o in (Order.objects
.filter(event=event, status=Order.STATUS_REFUNDED)
.values('payment_provider')
.annotate(cnt=Count('id'), payment_fee=Sum('payment_fee')).order_by())
}
num_pending = {
o['payment_provider']: (o['cnt'], o['payment_fee'])
for o in (Order.objects.current
for o in (Order.objects
.filter(event=event, status__in=(Order.STATUS_PENDING, Order.STATUS_EXPIRED))
.values('payment_provider')
.annotate(cnt=Count('id'), payment_fee=Sum('payment_fee')).order_by())
}
num_paid = {
o['payment_provider']: (o['cnt'], o['payment_fee'])
for o in (Order.objects.current
for o in (Order.objects
.filter(event=event, status=Order.STATUS_PAID)
.values('payment_provider')
.annotate(cnt=Count('id'), payment_fee=Sum('payment_fee')).order_by())

View File

@@ -9,7 +9,7 @@ from pretix.base.signals import register_ticket_outputs
def generate(order: str, provider: str):
order = Order.objects.current.select_related('event').get(identity=order)
order = Order.objects.select_related('event').get(id=order)
ct = CachedTicket.objects.get_or_create(order=order, provider=provider)[0]
if not ct.cachedfile:
cf = CachedFile()

View File

@@ -8,7 +8,6 @@ from django.core.files import File
from django.core.files.storage import default_storage
from django.db.models import Model
from typing import Any, Callable, Dict, Optional, TypeVar, Union
from versions.models import Versionable
DEFAULTS = {
'max_items_per_order': {
@@ -115,7 +114,7 @@ class SettingsProxy:
def _cache(self) -> Dict[str, Any]:
if self._cached_obj is None:
self._cached_obj = {}
for setting in self._obj.setting_objects.current.all():
for setting in self._obj.setting_objects.all():
self._cached_obj[setting.key] = setting
return self._cached_obj
@@ -146,8 +145,6 @@ class SettingsProxy:
return dateutil.parser.parse(value).date()
elif as_type == time:
return dateutil.parser.parse(value).time()
elif as_type is not None and issubclass(as_type, Versionable):
return as_type.objects.current.get(identity=value)
elif as_type is not None and issubclass(as_type, Model):
return as_type.objects.get(pk=value)
return value
@@ -162,8 +159,6 @@ class SettingsProxy:
return json.dumps(value)
elif isinstance(value, datetime) or isinstance(value, date) or isinstance(value, time):
return value.isoformat()
elif isinstance(value, Versionable):
return value.identity
elif isinstance(value, Model):
return value.pk
elif isinstance(value, File):
@@ -213,7 +208,6 @@ class SettingsProxy:
def set(self, key: str, value: Any) -> None:
if key in self._cache():
s = self._cache()[key]
s = s.clone()
else:
s = self._type(object=self._obj, key=key)
s.value = self._serialize(value)
@@ -237,7 +231,7 @@ class SettingsSandbox:
prefixes for you.
"""
def __init__(self, type: str, key: str, event: Versionable):
def __init__(self, type: str, key: str, event: Model):
self._event = event
self._type = type
self._key = key