Moved property to the inside of items

This commit is contained in:
Raphael Michel
2015-10-08 12:45:19 +02:00
parent b99f541adf
commit 974c5cee79
25 changed files with 649 additions and 603 deletions

View File

@@ -5,7 +5,9 @@ from itertools import product
from django import forms
from django.core.exceptions import ValidationError
from django.db import transaction
from django.forms import BaseInlineFormSet
from django.forms import (
BaseInlineFormSet, BaseModelFormSet, ModelForm, modelformset_factory,
)
from django.forms.widgets import flatatt
from django.utils.encoding import force_text
from django.utils.html import format_html
@@ -21,6 +23,7 @@ class I18nInlineFormSet(BaseInlineFormSet):
This is equivalent to a normal BaseInlineFormset, but cares for the special needs
of I18nForms (see there for more information).
"""
def __init__(self, *args, **kwargs):
self.event = kwargs.pop('event', None)
super().__init__(*args, **kwargs)
@@ -30,6 +33,32 @@ class I18nInlineFormSet(BaseInlineFormSet):
return super()._construct_form(i, **kwargs)
class I18nFormSet(BaseModelFormSet):
"""
This is equivalent to a normal BaseModelFormset, but cares for the special needs
of I18nForms (see there for more information).
"""
def __init__(self, *args, **kwargs):
self.event = kwargs.pop('event', None)
super().__init__(*args, **kwargs)
def _construct_form(self, i, **kwargs):
kwargs['event'] = self.event
return super()._construct_form(i, **kwargs)
@property
def empty_form(self):
form = self.form(
auto_id=self.auto_id,
prefix=self.add_prefix('__prefix__'),
empty_permitted=True,
event=self.event
)
self.add_fields(form, None)
return form
class TolerantFormsetModelForm(VersionedModelForm):
"""
This is equivalent to a normal VersionedModelForm, but works around a problem that
@@ -407,7 +436,6 @@ class VariationsField(forms.ModelMultipleChoiceField):
class ExtFileField(forms.FileField):
def __init__(self, *args, **kwargs):
ext_whitelist = kwargs.pop("ext_whitelist")
self.ext_whitelist = [i.lower() for i in ext_whitelist]
@@ -422,3 +450,108 @@ class ExtFileField(forms.FileField):
if ext not in self.ext_whitelist:
raise forms.ValidationError(_("Filetype not allowed!"))
return data
class BaseNestedFormset(I18nFormSet):
def add_fields(self, form, index):
# allow the super class to create the fields as usual
super().add_fields(form, index)
form.nested = []
for f in self.nested_formset_class:
inner_formset = f(
instance=form.instance,
data=form.data if form.is_bound else None,
prefix='%s-%s' % (form.prefix, f.get_default_prefix()),
queryset=form.instance.values.all(),
event=self.event
)
form.nested.append(inner_formset)
def is_valid(self):
result = super(BaseNestedFormset, self).is_valid()
if self.is_bound:
# look at any nested formsets, as well
for form in self.forms:
if not self._should_delete_form(form):
for n in form.nested:
result = result and n.is_valid()
return result
def save(self, commit=True):
result = super(BaseNestedFormset, self).save(commit=commit)
for form in self.forms:
if not self._should_delete_form(form):
for n in form.nested:
n.save(commit=commit)
return result
def nestedformset_factory(model, nested_formset, form=ModelForm,
formset=BaseNestedFormset, fk_name=None, fields=None,
exclude=None, extra=3, can_order=False,
can_delete=True, max_num=None,
formfield_callback=None, widgets=None,
validate_max=False, localized_fields=None,
labels=None, help_texts=None, error_messages=None):
kwargs = {
'form': form,
'formfield_callback': formfield_callback,
'formset': formset,
'extra': extra,
'can_delete': can_delete,
'can_order': can_order,
'fields': fields,
'exclude': exclude,
'max_num': max_num,
'widgets': widgets,
'validate_max': validate_max,
'localized_fields': localized_fields,
'labels': labels,
'help_texts': help_texts,
'error_messages': error_messages,
}
nfs_class = modelformset_factory(model, **kwargs)
nfs_class.nested_formset_class = []
for f in nested_formset:
nfs_class.nested_formset_class.append(f)
return nfs_class
class NestedInnerI18nInlineFormSet(I18nFormSet):
"""A formset for child objects related to a parent."""
def __init__(self, data=None, files=None, instance=None,
save_as_new=False, prefix=None, queryset=None, **kwargs):
if instance is None:
self.instance = self.fk.rel.to()
else:
self.instance = instance
self.save_as_new = save_as_new
if queryset is None:
if self.instance is not None:
queryset = getattr(self.instance, self.fk.related_query_name()).all()
else:
queryset = self.model._default_manager
if self.instance.pk is not None:
qs = queryset
else:
qs = self.model._default_manager.none()
super().__init__(data, files, prefix=prefix, queryset=qs, **kwargs)
@property
def empty_form(self):
form = self.form(
auto_id=self.auto_id,
prefix=self.add_prefix('__inner_prefix__'),
empty_permitted=True,
event=self.event
)
self.add_fields(form, None)
return form

View File

@@ -124,7 +124,6 @@ class ItemFormGeneral(VersionedModelForm):
def __init__(self, *args, **kwargs):
super().__init__(*args, **kwargs)
self.fields['category'].queryset = self.instance.event.categories.current.all()
self.fields['properties'].queryset = self.instance.event.properties.current.all()
class Meta:
model = Item
@@ -138,7 +137,6 @@ class ItemFormGeneral(VersionedModelForm):
'picture',
'default_price',
'tax_rate',
'properties',
]

View File

@@ -79,12 +79,6 @@
{% trans "Categories" %}
</a>
</li>
<li>
<a href="{% url 'control:event.items.properties' organizer=request.event.organizer.slug event=request.event.slug %}"
{% if "event.items.properties" in url_name %}class="active"{% endif %}>
{% trans "Properties" %}
</a>
</li>
<li>
<a href="{% url 'control:event.items.questions' organizer=request.event.organizer.slug event=request.event.slug %}"
{% if "event.items.questions" in url_name %}class="active"{% endif %}>

View File

@@ -1,13 +1,14 @@
{% extends "pretixcontrol/event/base.html" %}
{% load i18n %}
{% block title %}{{ item.name }} :: {% trans "Product" %}{% endblock %}
{% block title %}{{ object.name }} :: {% trans "Product" %}{% endblock %}
{% block content %}
{% if item.identity %}
<h1>{% trans "Modify product:" %} {{ item.name }}</h1>
{% if object.identity %}
<h1>{% trans "Modify product:" %} {{ object.name }}</h1>
<ul class="nav nav-pills">
<li {% if "event.item" == url_name %}class="active"{% endif %}><a href="{% url 'control:event.item' organizer=request.event.organizer.slug event=request.event.slug item=item.identity %}">{% trans "General information" %}</a></li>
<li {% if "event.item.variations" == url_name %}class="active"{% endif %}><a href="{% url 'control:event.item.variations' organizer=request.event.organizer.slug event=request.event.slug item=item.identity %}">{% trans "Variations" %}</a></li>
<li {% if "event.item.restrictions" == url_name %}class="active"{% endif %}><a href="{% url 'control:event.item.restrictions' organizer=request.event.organizer.slug event=request.event.slug item=item.identity %}">{% trans "Restrictions" %}</a></li>
<li {% if "event.item" == url_name %}class="active"{% endif %}><a href="{% url 'control:event.item' organizer=request.event.organizer.slug event=request.event.slug item=object.identity %}">{% trans "General information" %}</a></li>
<li {% if "event.item.properties" == url_name %}class="active"{% endif %}><a href="{% url 'control:event.item.properties' organizer=request.event.organizer.slug event=request.event.slug item=object.identity %}">{% trans "Properties" %}</a></li>
<li {% if "event.item.variations" == url_name %}class="active"{% endif %}><a href="{% url 'control:event.item.variations' organizer=request.event.organizer.slug event=request.event.slug item=object.identity %}">{% trans "Variations" %}</a></li>
<li {% if "event.item.restrictions" == url_name %}class="active"{% endif %}><a href="{% url 'control:event.item.restrictions' organizer=request.event.organizer.slug event=request.event.slug item=object.identity %}">{% trans "Restrictions" %}</a></li>
</ul>
{% else %}
<h1>{% trans "Create product" %}</h1>
@@ -15,7 +16,7 @@
You will be able to adjust further settings in the next step.
{% endblocktrans %}</p>
{% endif %}
{% if item.identity and not item.quotas.exists %}
{% if object.identity and not object.quotas.exists %}
<div class="alert alert-warning">
{% blocktrans trimmed %}
Please note, that your product will <strong>not</strong> be available for sale until you added your

View File

@@ -18,10 +18,6 @@
{% bootstrap_field form.default_price layout="horizontal" %}
{% bootstrap_field form.tax_rate layout="horizontal" %}
</fieldset>
<fieldset>
<legend>{% trans "Advanced settings" %}</legend>
{% bootstrap_field form.properties layout="horizontal" %}
</fieldset>
<div class="form-group submit-group">
<button type="submit" class="btn btn-primary btn-save">
{% trans "Save" %}

View File

@@ -0,0 +1,147 @@
{% extends "pretixcontrol/item/base.html" %}
{% load i18n %}
{% load bootstrap3 %}
{% load formset_tags %}
{% block inside %}
<form class="form-horizontal branches" method="post" action="">
{% csrf_token %}
{% if state %}
<div class="alert alert-{{ state.class }}">{{ state.message|safe }}</div>
{% endif %}
<div class="formset" data-formset data-formset-prefix="{{ formset.prefix }}">
{{ formset.management_form }}
<div data-formset-body>
{% for form in formset %}
<div class="panel panel-default" data-formset-form>
<div class="sr-only">
{{ form.id }}
{% bootstrap_field form.DELETE form_group_class="" layout="inline" %}
</div>
<div class="panel-heading">
<h4 class="panel-title">
{% bootstrap_field form.name layout='inline' form_group_class="" %}
</h4>
</div>
<div class="panel-body">
<div class="nested-formset" data-formset-prefix="{{ form.nested.0.prefix }}">
<div data-nested-formset-body>
{{ form.nested.0.management_form }}
{% for f in form.nested.0 %}
<div class="form-group" data-nested-formset-form>
{{ f.id }}
<div class="col-sm-9">
{% bootstrap_field f.value form_group_class="" layout="inline" %}
</div>
<div class="sr-only">
{% bootstrap_field f.ORDER form_group_class="" layout="inline" %}
{% bootstrap_field f.DELETE form_group_class="" layout="inline" %}
</div>
<div class="col-sm-3 text-right">
<button type="button" class="btn btn-default"
data-nested-formset-move-up-button>
<i class="fa fa-arrow-up"></i></button>
<button type="button" class="btn btn-default"
data-nested-formset-move-down-button>
<i class="fa fa-arrow-down"></i></button>
<button type="button" class="btn btn-danger" data-nested-formset-delete-button>
<i class="fa fa-trash"></i></button>
</div>
</div>
{% endfor %}
</div>
<script type="form-template" data-nested-formset-empty-form>
{% escapescript %}
<div class="form-group" data-nested-formset-form>
{{ form.nested.0.empty_form.id }}
<div class="col-sm-9">
{% bootstrap_field form.nested.0.empty_form.value form_group_class="" layout="inline" %}
</div>
<div class="sr-only">
{% bootstrap_field form.nested.0.empty_form.ORDER form_group_class="" layout="inline" %}
{% bootstrap_field form.nested.0.empty_form.DELETE form_group_class="" layout="inline" %}
</div>
<div class="col-sm-3 text-right">
<button type="button" class="btn btn-default"
data-nested-formset-move-up-button>
<i class="fa fa-arrow-up"></i></button>
<button type="button" class="btn btn-default"
data-nested-formset-move-down-button>
<i class="fa fa-arrow-down"></i></button>
<button type="button" class="btn btn-danger" data-nested-formset-delete-button>
<i class="fa fa-trash"></i></button>
</div>
</div>
{% endescapescript %}
</script>
<p style="margin-top: 10px">
<button type="button" class="btn btn-default" data-nested-formset-add>
<i class="fa fa-plus"></i> {% trans "Add a new value" %}</button>
</p>
</div>
</div>
</div>
{% endfor %}
</div>
<script type="form-template" data-formset-empty-form>
{% escapescript %}
<div class="panel panel-default" data-formset-form>
<div class="sr-only">
{{ formset.empty_form.id }}
{% bootstrap_field formset.empty_form.DELETE form_group_class="" layout="inline" %}
</div>
<div class="panel-heading">
<h4 class="panel-title">
{% bootstrap_field formset.empty_form.name layout='inline' form_group_class="" %}
</h4>
</div>
<div class="panel-body">
<div class="nested-formset" data-formset-prefix="{{ formset.empty_form.nested.0.prefix }}">
<div data-nested-formset-body>
{{ formset.empty_form.nested.0.management_form }}
</div>
{{ '<script type="form-template" data-nested-formset-empty-form>' }}
<div class="form-group" data-nested-formset-form>
{{ formset.empty_form.nested.0.empty_form.id }}
<div class="col-sm-9">
{% bootstrap_field formset.empty_form.nested.0.empty_form.value form_group_class="" layout="inline" %}
</div>
<div class="sr-only">
{% bootstrap_field formset.empty_form.nested.0.empty_form.ORDER form_group_class="" layout="inline" %}
{% bootstrap_field formset.empty_form.nested.0.empty_form.DELETE form_group_class="" layout="inline" %}
</div>
<div class="col-sm-3 text-right">
<button type="button" class="btn btn-default"
data-nested-formset-move-up-button>
<i class="fa fa-arrow-up"></i></button>
<button type="button" class="btn btn-default"
data-nested-formset-move-down-button>
<i class="fa fa-arrow-down"></i></button>
<button type="button" class="btn btn-danger" data-nested-formset-delete-button>
<i class="fa fa-trash"></i></button>
</div>
</div>
{{ '</script>' }}
<p style="margin-top: 10px">
<button type="button" class="btn btn-default" data-nested-formset-add>
<i class="fa fa-plus"></i> {% trans "Add a new value" %}</button>
</p>
</div>
</div>
</div>
{% endescapescript %}
</script>
<p>
<button type="button" class="btn btn-default" data-formset-add>
<i class="fa fa-plus"></i> {% trans "Add a new property" %}</button>
</p>
</div>
<div class="form-group submit-group">
<button type="submit" class="btn btn-primary btn-save">
{% trans "Save" %}
</button>
</div>
</form>
{% endblock %}

View File

@@ -1,30 +0,0 @@
{% extends "pretixcontrol/items/base.html" %}
{% load i18n %}
{% block title %}{% trans "Product properties" %}{% endblock %}
{% block inside %}
<h1>{% trans "Product properties" %}</h1>
<p>
<a href="{% url "control:event.items.properties.add" organizer=request.event.organizer.slug event=request.event.slug %}" class="btn btn-default"><i class="fa fa-plus"></i> {% trans "Create new property" %}</a>
</p>
<div class="table-responsive">
<table class="table table-hover">
<thead>
<tr>
<th>{% trans "Product properties" %}</th>
<th></th>
</tr>
</thead>
<tbody>
{% for p in properties %}
<tr>
<td><strong><a href="
{% url "control:event.items.properties.edit" organizer=request.event.organizer.slug event=request.event.slug property=p.identity %}">{{ p.name }}</a></strong></td>
<td class="text-right"><a href="
{% url "control:event.items.properties.delete" organizer=request.event.organizer.slug event=request.event.slug property=p.identity %}" class="btn btn-danger btn-sm"><i class="fa fa-trash"></i></a></td>
</tr>
{% endfor %}
</tbody>
</table>
</div>
{% include "pretixcontrol/pagination.html" %}
{% endblock %}

View File

@@ -1,65 +0,0 @@
{% extends "pretixcontrol/items/base.html" %}
{% load i18n %}
{% load bootstrap3 %}
{% load formset_tags %}
{% block title %}{% trans "Product property" %}{% endblock %}
{% block inside %}
<h1>{% trans "Product property" %}</h1>
<form action="" method="post" class="form-horizontal">
{% csrf_token %}
<fieldset>
<legend>{% trans "General information" %}</legend>
{% bootstrap_field form.name layout="horizontal" %}
</fieldset>
<fieldset>
<legend>{% trans "Values" %}</legend>
<div data-formset data-formset-prefix="{{ formset.prefix }}">
<div data-formset-body>
{{ formset.management_form }}
{% for f in formset %}
<div class="form-group" data-formset-form>
{{ f.id }}
<div class="col-sm-9">
{% bootstrap_field f.value form_group_class="" layout="inline" %}
</div>
<div class="sr-only">
{% bootstrap_field f.ORDER form_group_class="" layout="inline" %}
{% bootstrap_field f.DELETE form_group_class="" layout="inline" %}
</div>
<div class="col-sm-3 text-right">
<button type="button" class="btn btn-default" data-formset-move-up-button><i class="fa fa-arrow-up"></i></button>
<button type="button" class="btn btn-default" data-formset-move-down-button><i class="fa fa-arrow-down"></i></button>
<button type="button" class="btn btn-danger" data-formset-delete-button><i class="fa fa-trash"></i></button>
</div>
</div>
{% endfor %}
</div>
<script type="form-template" data-formset-empty-form>
{% escapescript %}
<div class="form-group" data-formset-form>
{{ formset.empty_form.id }}
<div class="col-sm-9">
{% bootstrap_field formset.empty_form.value form_group_class="" layout="inline" %}
</div>
<div class="sr-only">
{% bootstrap_field formset.empty_form.ORDER form_group_class="" layout="inline" %}
{% bootstrap_field formset.empty_form.DELETE form_group_class="" layout="inline" %}
</div>
<div class="col-sm-3 text-right">
<button type="button" class="btn btn-default" data-formset-move-up-button><i class="fa fa-arrow-up"></i></button>
<button type="button" class="btn btn-default" data-formset-move-down-button><i class="fa fa-arrow-down"></i></button>
<button type="button" class="btn btn-danger" data-formset-delete-button><i class="fa fa-trash"></i></button>
</div>
</div>
{% endescapescript %}
</script>
<button type="button" class="btn btn-default" data-formset-add><i class="fa fa-plus"></i> {% trans "Add a new value" %}</button>
</div>
</fieldset>
<div class="form-group submit-group">
<button type="submit" class="btn btn-primary btn-save">
{% trans "Save" %}
</button>
</div>
</form>
{% endblock %}

View File

@@ -1,28 +0,0 @@
{% extends "pretixcontrol/items/base.html" %}
{% load i18n %}
{% load bootstrap3 %}
{% block title %}{% trans "Delete product property" %}{% endblock %}
{% block inside %}
<h1>{% trans "Delete product property" %}</h1>
{% if not possible %}
<p>{% blocktrans %}You can not delete the property <strong>{{ property }}</strong> as long as the following products use it:{% endblocktrans %}</p>
<ul>
{% for item in dependent %}
<li><a href="{% url "control:event.item" organizer=request.event.organizer.slug event=request.event.slug item=item.pk %}">{{ item.name }}</a></li>
{% endfor %}
</ul>
{% else %}
<form action="" method="post" class="form-horizontal">
{% csrf_token %}
<p>{% blocktrans %}Are you sure you want to delete the property <strong>{{ property }}</strong>?{% endblocktrans %}</p>
<div class="form-group submit-group">
<a href="{% url "control:event.items.properties" organizer=request.event.organizer.slug event=request.event.slug %}" class="btn btn-default btn-cancel">
{% trans "Cancel" %}
</a>
<button type="submit" class="btn btn-danger btn-save">
{% trans "Delete" %}
</button>
</div>
</form>
{% endif %}
{% endblock %}

View File

@@ -32,6 +32,8 @@ urlpatterns = [
name='event.item.variations'),
url(r'^items/(?P<item>[0-9a-f-]+)/restrictions$', item.ItemRestrictions.as_view(),
name='event.item.restrictions'),
url(r'^items/(?P<item>[0-9a-f-]+)/properties$', item.ItemProperties.as_view(),
name='event.item.properties'),
url(r'^items/(?P<item>[0-9a-f-]+)/up$', item.item_move_up, name='event.items.up'),
url(r'^items/(?P<item>[0-9a-f-]+)/down$', item.item_move_down, name='event.items.down'),
url(r'^items/(?P<item>[0-9a-f-]+)/delete$', item.ItemDelete.as_view(), name='event.items.delete'),
@@ -50,12 +52,6 @@ urlpatterns = [
url(r'^questions/(?P<question>[0-9a-f-]+)/$', item.QuestionUpdate.as_view(),
name='event.items.questions.edit'),
url(r'^questions/add$', item.QuestionCreate.as_view(), name='event.items.questions.add'),
url(r'^properties/$', item.PropertyList.as_view(), name='event.items.properties'),
url(r'^properties/(?P<property>[0-9a-f-]+)/$', item.PropertyUpdate.as_view(),
name='event.items.properties.edit'),
url(r'^properties/(?P<property>[0-9a-f-]+)/delete$', item.PropertyDelete.as_view(),
name='event.items.properties.delete'),
url(r'^properties/add$', item.PropertyCreate.as_view(), name='event.items.properties.add'),
url(r'^quotas/$', item.QuotaList.as_view(), name='event.items.quotas'),
url(r'^quotas/(?P<quota>[0-9a-f-]+)/$', item.QuotaUpdate.as_view(), name='event.items.quotas.edit'),
url(r'^quotas/(?P<quota>[0-9a-f-]+)/delete$', item.QuotaDelete.as_view(),

View File

@@ -17,7 +17,10 @@ from pretix.base.models import (
Item, ItemCategory, ItemVariation, Property, PropertyValue, Question,
Quota,
)
from pretix.control.forms import I18nInlineFormSet, VariationsField
from pretix.control.forms import (
I18nInlineFormSet, NestedInnerI18nInlineFormSet, VariationsField,
nestedformset_factory,
)
from pretix.control.forms.item import (
CategoryForm, ItemFormGeneral, ItemVariationForm, PropertyForm,
PropertyValueForm, QuestionForm, QuotaForm,
@@ -219,177 +222,6 @@ def category_move_down(request, organizer, event, category):
event=request.event.slug)
class PropertyList(ListView):
model = Property
context_object_name = 'properties'
paginate_by = 30
template_name = 'pretixcontrol/items/properties.html'
def get_queryset(self):
return Property.objects.current.filter(
event=self.request.event
)
class PropertyUpdate(EventPermissionRequiredMixin, UpdateView):
model = Property
form_class = PropertyForm
template_name = 'pretixcontrol/items/property.html'
permission = 'can_change_items'
context_object_name = 'property'
def get_object(self, queryset=None) -> Property:
try:
return self.request.event.properties.current.get(
identity=self.kwargs['property']
)
except Property.DoesNotExist:
raise Http404(_("The requested property does not exist."))
def get_success_url(self) -> str:
return reverse('control:event.items.properties.edit', kwargs={
'organizer': self.request.event.organizer.slug,
'event': self.request.event.slug,
'property': self.kwargs['property']
})
def get_formset(self):
formsetclass = inlineformset_factory(
Property, PropertyValue,
form=PropertyValueForm,
formset=I18nInlineFormSet,
can_order=True,
extra=0,
)
kwargs = self.get_form_kwargs()
kwargs['queryset'] = self.object.values.current.all()
formset = formsetclass(**kwargs)
return formset
def get_context_data(self, *args, **kwargs) -> dict:
context = super().get_context_data(*args, **kwargs)
context['formset'] = self.get_formset()
return context
@transaction.atomic()
def form_valid(self, form, formset):
for f in formset.deleted_forms:
f.instance.delete()
f.instance.pk = None
for i, f in enumerate(formset.ordered_forms):
f.save(commit=False)
f.instance.position = i
f.instance.save()
messages.success(self.request, _('Your changes have been saved.'))
return super().form_valid(form)
def post(self, request, *args, **kwargs):
self.object = self.get_object()
form_class = self.get_form_class()
form = self.get_form(form_class)
formset = self.get_formset()
if form.is_valid() and formset.is_valid():
return self.form_valid(form, formset)
else:
return self.form_invalid(form)
class PropertyCreate(EventPermissionRequiredMixin, CreateView):
model = Property
form_class = PropertyForm
template_name = 'pretixcontrol/items/property.html'
permission = 'can_change_items'
context_object_name = 'property'
def get_success_url(self) -> str:
return reverse('control:event.items.properties', kwargs={
'organizer': self.request.event.organizer.slug,
'event': self.request.event.slug,
})
def get_formset(self):
formsetclass = inlineformset_factory(
Property, PropertyValue,
form=PropertyValueForm,
formset=I18nInlineFormSet,
can_order=True,
extra=3,
)
formset = formsetclass(**self.get_form_kwargs())
return formset
def get_context_data(self, *args, **kwargs) -> dict:
self.object = None
context = super().get_context_data(*args, **kwargs)
context['formset'] = self.get_formset()
return context
@transaction.atomic()
def form_valid(self, form, formset):
form.instance.event = self.request.event
resp = super().form_valid(form)
for i, f in enumerate(formset.ordered_forms):
f.instance.position = i
f.instance.prop = form.instance
f.instance.save()
messages.success(self.request, _('The new property has been created.'))
return resp
def post(self, request, *args, **kwargs):
form_class = self.get_form_class()
form = self.get_form(form_class)
formset = self.get_formset()
if form.is_valid() and formset.is_valid():
return self.form_valid(form, formset)
else:
return self.form_invalid(form)
class PropertyDelete(EventPermissionRequiredMixin, DeleteView):
model = Property
form_class = PropertyForm
template_name = 'pretixcontrol/items/property_delete.html'
permission = 'can_change_items'
context_object_name = 'property'
def get_context_data(self, *args, **kwargs) -> dict:
context = super().get_context_data(*args, **kwargs)
context['dependent'] = self.get_object().items.current.all()
context['possible'] = self.is_allowed()
return context
def is_allowed(self) -> bool:
return self.get_object().items.current.count() == 0
def get_object(self, queryset=None) -> Property:
if not hasattr(self, 'object') or not self.object:
try:
self.object = self.request.event.properties.current.get(
identity=self.kwargs['property']
)
except Property.DoesNotExist:
raise Http404(_("The requested property does not exist."))
return self.object
def delete(self, request, *args, **kwargs):
if self.is_allowed():
success_url = self.get_success_url()
self.get_object().delete()
messages.success(request, _('The selected property has been deleted.'))
return HttpResponseRedirect(success_url)
else:
messages.error(request, _('The selected property can not be deleted.'))
return HttpResponseRedirect(self.get_success_url())
def get_success_url(self) -> str:
return reverse('control:event.items.properties', kwargs={
'organizer': self.request.event.organizer.slug,
'event': self.request.event.slug,
})
class QuestionList(ListView):
model = Question
context_object_name = 'questions'
@@ -676,6 +508,75 @@ class ItemUpdateGeneral(ItemDetailMixin, EventPermissionRequiredMixin, UpdateVie
return super().form_valid(form)
class ItemProperties(ItemDetailMixin, EventPermissionRequiredMixin, TemplateView):
permission = 'can_change_items'
template_name = 'pretixcontrol/item/properties.html'
def get_success_url(self) -> str:
return reverse('control:event.item.properties', kwargs={
'organizer': self.request.event.organizer.slug,
'event': self.request.event.slug,
'item': self.get_object().identity,
})
def get_inner_formset_class(self):
formsetclass = inlineformset_factory(
Property, PropertyValue,
form=PropertyValueForm,
formset=NestedInnerI18nInlineFormSet,
can_order=True, extra=0
)
return formsetclass
def get_outer_formset(self):
formsetclass = nestedformset_factory(
Property, [self.get_inner_formset_class()],
form=PropertyForm, can_order=False, can_delete=True, extra=0
)
formset = formsetclass(self.request.POST if self.request.method == "POST" else None,
queryset=Property.objects.current.filter(item=self.object).prefetch_related('values'),
event=self.request.event)
return formset
def get_context_data(self, **kwargs):
self.object = self.get_object()
ctx = super().get_context_data(**kwargs)
ctx['formset'] = self.get_outer_formset()
return ctx
@transaction.atomic()
def form_valid(self, formset):
for f in formset:
f.instance.event = self.request.event
f.instance.item = self.get_object()
f.instance.save()
print(f.instance)
for n in f.nested:
print(n.deleted_forms, n.ordered_forms, n.extra_forms)
for fn in n.deleted_forms:
fn.instance.delete()
fn.instance.pk = None
for i, fn in enumerate(n.ordered_forms + [ef for ef in n.extra_forms if ef not in n.ordered_forms]):
fn.instance.position = i
fn.instance.prop = f.instance
fn.save()
n.save_new_objects()
messages.success(self.request, _('Your changes have been saved.'))
return redirect(self.get_success_url())
def post(self, request, *args, **kwargs):
self.object = self.get_object()
formset = self.get_outer_formset()
if formset.is_valid():
return self.form_valid(formset)
else:
return self.get(request, *args, **kwargs)
class ItemVariations(ItemDetailMixin, EventPermissionRequiredMixin, TemplateView):
permission = 'can_change_items'
@@ -782,7 +683,7 @@ class ItemVariations(ItemDetailMixin, EventPermissionRequiredMixin, TemplateView
grids.append({'row': val1, 'forms': formrow})
forms.append({'row': ", ".join([value.value for value in gridrow]), 'forms': grids})
forms.append({'row': ", ".join([str(value.value) for value in gridrow]), 'forms': grids})
return forms, forms_flat