diff --git a/doc/development/models.rst b/doc/development/models.rst
index d4c1ed9f1..4105490d8 100644
--- a/doc/development/models.rst
+++ b/doc/development/models.rst
@@ -8,26 +8,6 @@ Pretix provides the following data(base) models. Every model and every model met
documented here is considered private and should not be used by third-party plugins, as it may change
without advance notice.
-.. IMPORTANT::
- pretix's models are built with `cleanerversion`_, which extends the default Django ORM by adding versioning
- information to the database. There are basically three things you absolutely need to know about cleanerversion:
-
- * When querying the database, make sure you only get the current versions::
-
- queryset = Model.objects.current.filter(…)
-
- * Before you modify an object, clone it::
-
- obj = Model.objects.current.get(identity=1) # Prefer identities over primary keys
- obj = obj.clone() # Saves the old version to the database and creates the new one
- obj.foo = 'bar'
- obj.save()
-
- * Beware of batch operations, use ``queryset.update()``, ``queryset.delete()`` etc. only if
- you know what you're doing.
-
- There is one exception: The ``User`` model is a classic Django model!
-
User model
----------
diff --git a/src/pretix/base/exporter.py b/src/pretix/base/exporter.py
index 2be7444d3..b66a447b2 100644
--- a/src/pretix/base/exporter.py
+++ b/src/pretix/base/exporter.py
@@ -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')
]
}
}
diff --git a/src/pretix/base/forms/__init__.py b/src/pretix/base/forms/__init__.py
index e8dd49ec8..339b3fe37 100644
--- a/src/pretix/base/forms/__init__.py
+++ b/src/pretix/base/forms/__init__.py
@@ -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
diff --git a/src/pretix/base/migrations/0001_initial.py b/src/pretix/base/migrations/0001_initial.py
index 79df63142..91e65ce6c 100644
--- a/src/pretix/base/migrations/0001_initial.py
+++ b/src/pretix/base/migrations/0001_initial.py
@@ -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),
]
diff --git a/src/pretix/base/migrations/0002_auto_20151021_1412.py b/src/pretix/base/migrations/0002_auto_20151021_1412.py
deleted file mode 100644
index 69380c7e0..000000000
--- a/src/pretix/base/migrations/0002_auto_20151021_1412.py
+++ /dev/null
@@ -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),
- ),
- ]
diff --git a/src/pretix/base/migrations/0002_auto_20151212_1123.py b/src/pretix/base/migrations/0002_auto_20151212_1123.py
new file mode 100644
index 000000000..45708bd5c
--- /dev/null
+++ b/src/pretix/base/migrations/0002_auto_20151212_1123.py
@@ -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'),
+ ),
+ ]
diff --git a/src/pretix/base/migrations/0003_event_is_public.py b/src/pretix/base/migrations/0003_event_is_public.py
deleted file mode 100644
index 3383f8672..000000000
--- a/src/pretix/base/migrations/0003_event_is_public.py
+++ /dev/null
@@ -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'),
- ),
- ]
diff --git a/src/pretix/base/migrations/0004_auto_20151024_0848.py b/src/pretix/base/migrations/0004_auto_20151024_0848.py
deleted file mode 100644
index 50d566d44..000000000
--- a/src/pretix/base/migrations/0004_auto_20151024_0848.py
+++ /dev/null
@@ -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),
- ),
- ]
diff --git a/src/pretix/base/migrations/0005_auto_20151206_1652.py b/src/pretix/base/migrations/0005_auto_20151206_1652.py
deleted file mode 100644
index 999262345..000000000
--- a/src/pretix/base/migrations/0005_auto_20151206_1652.py
+++ /dev/null
@@ -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'),
- ),
- ]
diff --git a/src/pretix/base/models/__init__.py b/src/pretix/base/models/__init__.py
index b9fedaa16..de3224402 100644
--- a/src/pretix/base/models/__init__.py
+++ b/src/pretix/base/models/__init__.py
@@ -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',
diff --git a/src/pretix/base/models/base.py b/src/pretix/base/models/base.py
index 9dfc0fd4e..3d822a2f9 100644
--- a/src/pretix/base/models/base.py
+++ b/src/pretix/base/models/base.py
@@ -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):
diff --git a/src/pretix/base/models/event.py b/src/pretix/base/models/event.py
index d9069835f..248ae8ed0 100644
--- a/src/pretix/base/models/event.py
+++ b/src/pretix/base/models/event.py
@@ -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()
diff --git a/src/pretix/base/models/items.py b/src/pretix/base/models/items.py
index ec707375a..9a9b4835a 100644
--- a/src/pretix/base/models/items.py
+++ b/src/pretix/base/models/items.py
@@ -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())
diff --git a/src/pretix/base/models/orders.py b/src/pretix/base/models/orders.py
index b9ff5c503..bb017e57b 100644
--- a/src/pretix/base/models/orders.py
+++ b/src/pretix/base/models/orders.py
@@ -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 '{% trans "Modify product:" %} {{ object.name }}
{% else %}
{% trans "Create product" %}
@@ -15,7 +15,7 @@
You will be able to adjust further settings in the next step.
{% endblocktrans %}