forked from CGM_Public/pretix_original
Introduce VariationDict to reduce duplicate code
This commit is contained in:
@@ -68,6 +68,8 @@ It is sent out with several arguments:
|
|||||||
the item does not have any properties, the list will contain exactly one empty
|
the item does not have any properties, the list will contain exactly one empty
|
||||||
dictionary. Please not: this is *not* the list of all possible variations, this is
|
dictionary. Please not: this is *not* the list of all possible variations, this is
|
||||||
only the list of all variations the frontend likes to determine the status for.
|
only the list of all variations the frontend likes to determine the status for.
|
||||||
|
Technically, you won't get ``dict`` objects but ``tixlbase.types.VariationDict``
|
||||||
|
objects, which behave exactly the same but add some extra methods.
|
||||||
context
|
context
|
||||||
A yet-to-defined context object containing information about the user and the order
|
A yet-to-defined context object containing information about the user and the order
|
||||||
process. This is required to implement coupon-systems or similar restrictions.
|
process. This is required to implement coupon-systems or similar restrictions.
|
||||||
@@ -158,9 +160,7 @@ In our example, the implementation could look like this::
|
|||||||
# Make up some unique key for this variation
|
# Make up some unique key for this variation
|
||||||
cachekey = 'timerestriction:%d:%s' % (
|
cachekey = 'timerestriction:%d:%s' % (
|
||||||
item.pk,
|
item.pk,
|
||||||
",".join(sorted(
|
v.identify(),
|
||||||
[str(v[1].pk) for v in v.items() if v[0] != 'variation']
|
|
||||||
))
|
|
||||||
)
|
)
|
||||||
|
|
||||||
# Fetch from cache, if available
|
# Fetch from cache, if available
|
||||||
|
|||||||
@@ -7,6 +7,8 @@ from django.utils.translation import ugettext_lazy as _
|
|||||||
from django.template.defaultfilters import date as _date
|
from django.template.defaultfilters import date as _date
|
||||||
from django.core.validators import RegexValidator
|
from django.core.validators import RegexValidator
|
||||||
|
|
||||||
|
from tixlbase.types import VariationDict
|
||||||
|
|
||||||
|
|
||||||
class UserManager(BaseUserManager):
|
class UserManager(BaseUserManager):
|
||||||
"""
|
"""
|
||||||
@@ -571,10 +573,13 @@ class Item(models.Model):
|
|||||||
def get_all_variations(self):
|
def get_all_variations(self):
|
||||||
"""
|
"""
|
||||||
This method returns a list containing all variations of this
|
This method returns a list containing all variations of this
|
||||||
item. The list contains one dictionary per variation, where
|
item. The list contains one VariationDict per variation, where
|
||||||
the Proprty IDs are keys and the PropertyValue objects are
|
the Proprty IDs are keys and the PropertyValue objects are
|
||||||
values. If an ItemVariation object exists, it is available in
|
values. If an ItemVariation object exists, it is available in
|
||||||
the dictionary via the special key 'variation'.
|
the dictionary via the special key 'variation'.
|
||||||
|
|
||||||
|
VariationDicts differ from dicts only by specifying some extra
|
||||||
|
methods.
|
||||||
"""
|
"""
|
||||||
all_variations = self.variations.all().prefetch_related("values")
|
all_variations = self.variations.all().prefetch_related("values")
|
||||||
all_properties = self.properties.all().prefetch_related("values")
|
all_properties = self.properties.all().prefetch_related("values")
|
||||||
@@ -583,20 +588,20 @@ class Item(models.Model):
|
|||||||
key = []
|
key = []
|
||||||
for v in var.values.all():
|
for v in var.values.all():
|
||||||
key.append((v.prop_id, v.pk))
|
key.append((v.prop_id, v.pk))
|
||||||
key = hash(tuple(sorted(key)))
|
key = tuple(sorted(key))
|
||||||
variations_cache[key] = var
|
variations_cache[key] = var
|
||||||
|
|
||||||
result = []
|
result = []
|
||||||
for comb in product(*[prop.values.all() for prop in all_properties]):
|
for comb in product(*[prop.values.all() for prop in all_properties]):
|
||||||
if len(comb) == 0:
|
if len(comb) == 0:
|
||||||
result.append({})
|
result.append(VariationDict())
|
||||||
continue
|
continue
|
||||||
key = []
|
key = []
|
||||||
var = {}
|
var = VariationDict()
|
||||||
for v in comb:
|
for v in comb:
|
||||||
key.append((v.prop.pk, v.pk))
|
key.append((v.prop.pk, v.pk))
|
||||||
var[v.prop.pk] = v
|
var[v.prop.pk] = v
|
||||||
key = hash(tuple(sorted(key)))
|
key = tuple(sorted(key))
|
||||||
if key in variations_cache:
|
if key in variations_cache:
|
||||||
var['variation'] = variations_cache[key]
|
var['variation'] = variations_cache[key]
|
||||||
result.append(var)
|
result.append(var)
|
||||||
|
|||||||
@@ -5,6 +5,7 @@ from tixlbase.models import (
|
|||||||
Event, Organizer, Item, ItemVariation,
|
Event, Organizer, Item, ItemVariation,
|
||||||
Property, PropertyValue
|
Property, PropertyValue
|
||||||
)
|
)
|
||||||
|
from tixlbase.types import VariationDict
|
||||||
|
|
||||||
|
|
||||||
class ItemVariationsTest(TestCase):
|
class ItemVariationsTest(TestCase):
|
||||||
@@ -44,7 +45,7 @@ class ItemVariationsTest(TestCase):
|
|||||||
self.assertEqual(len(v), 3)
|
self.assertEqual(len(v), 3)
|
||||||
values = []
|
values = []
|
||||||
for var in v:
|
for var in v:
|
||||||
self.assertIs(type(var), dict)
|
self.assertIs(type(var), VariationDict)
|
||||||
self.assertIn(p.pk, var)
|
self.assertIn(p.pk, var)
|
||||||
self.assertIs(type(var[p.pk]), PropertyValue)
|
self.assertIs(type(var[p.pk]), PropertyValue)
|
||||||
values.append(var[p.pk].value)
|
values.append(var[p.pk].value)
|
||||||
@@ -59,7 +60,7 @@ class ItemVariationsTest(TestCase):
|
|||||||
values = []
|
values = []
|
||||||
num_variations = 0
|
num_variations = 0
|
||||||
for var in v:
|
for var in v:
|
||||||
self.assertIs(type(var), dict)
|
self.assertIs(type(var), VariationDict)
|
||||||
if 'variation' in var and type(var['variation']) is ItemVariation:
|
if 'variation' in var and type(var['variation']) is ItemVariation:
|
||||||
self.assertEqual(iv.pk, var['variation'].pk)
|
self.assertEqual(iv.pk, var['variation'].pk)
|
||||||
values.append(var['variation'].values.all()[0].value)
|
values.append(var['variation'].values.all()[0].value)
|
||||||
@@ -80,7 +81,7 @@ class ItemVariationsTest(TestCase):
|
|||||||
values = []
|
values = []
|
||||||
num_variations = 0
|
num_variations = 0
|
||||||
for var in v:
|
for var in v:
|
||||||
self.assertIs(type(var), dict)
|
self.assertIs(type(var), VariationDict)
|
||||||
if 'variation' in var:
|
if 'variation' in var:
|
||||||
self.assertEqual(iv.pk, var['variation'].pk)
|
self.assertEqual(iv.pk, var['variation'].pk)
|
||||||
values.append(sorted([ivv.value for ivv in iv.values.all()]))
|
values.append(sorted([ivv.value for ivv in iv.values.all()]))
|
||||||
|
|||||||
71
src/tixlbase/types.py
Normal file
71
src/tixlbase/types.py
Normal file
@@ -0,0 +1,71 @@
|
|||||||
|
class VariationDict(dict):
|
||||||
|
"""
|
||||||
|
A VariationDict object behaves exactle the same as the Python built-in
|
||||||
|
``dict`` does, but adds some special methods. It is used for the dicts
|
||||||
|
returned by ``Item.get_all_variations()`` to avoid duplicate code in the
|
||||||
|
code calling this method.
|
||||||
|
"""
|
||||||
|
|
||||||
|
def relevant_items(self):
|
||||||
|
"""
|
||||||
|
Iterate over all items with numeric keys.
|
||||||
|
|
||||||
|
This is in use because the variation dictionaries use property ids
|
||||||
|
as key and have some special keys like 'variation'.
|
||||||
|
"""
|
||||||
|
for i in self.items():
|
||||||
|
if type(i[0]) is int:
|
||||||
|
yield i
|
||||||
|
|
||||||
|
def relevant_values(self):
|
||||||
|
"""
|
||||||
|
Iterate over all values with numeric keys.
|
||||||
|
|
||||||
|
This is in use because the variation dictionaries use property ids
|
||||||
|
as key and have some special keys like 'variation'.
|
||||||
|
"""
|
||||||
|
for i in self.items():
|
||||||
|
if type(i[0]) is int:
|
||||||
|
yield i[1]
|
||||||
|
|
||||||
|
def identify(self):
|
||||||
|
"""
|
||||||
|
Build an identifier for this dict. This can be any string used to
|
||||||
|
compare one VariationDict to others.
|
||||||
|
|
||||||
|
In the current implementation, it is a string containing a list of
|
||||||
|
the PropertyValue id's, sorted by the Property id's and is therefore
|
||||||
|
unique among one item.
|
||||||
|
"""
|
||||||
|
order_key = lambda i: i[0]
|
||||||
|
return ",".join([
|
||||||
|
str(v[1].pk) for v in sorted(self.relevant_items(), key=order_key)
|
||||||
|
])
|
||||||
|
|
||||||
|
def __eq__(self, other):
|
||||||
|
if type(other) is type(self):
|
||||||
|
return self.identify() == other.identify()
|
||||||
|
else:
|
||||||
|
return super().__eq__(other)
|
||||||
|
|
||||||
|
def ordered_values(self):
|
||||||
|
"""
|
||||||
|
Returns a list of values ordered by their keys
|
||||||
|
"""
|
||||||
|
return [
|
||||||
|
i[1] for i
|
||||||
|
in sorted(
|
||||||
|
[it for it in self.relevant_items()],
|
||||||
|
key=lambda i: i[0]
|
||||||
|
)
|
||||||
|
]
|
||||||
|
|
||||||
|
def copy(self):
|
||||||
|
"""
|
||||||
|
Return a one-level deep copy of this object (create a new
|
||||||
|
VariationDict but make a shallow copy of the dict inside it).
|
||||||
|
"""
|
||||||
|
new = VariationDict()
|
||||||
|
for k, v in self.items():
|
||||||
|
new[k] = v
|
||||||
|
return new
|
||||||
@@ -561,16 +561,6 @@ class ItemVariations(TemplateView, SingleObjectMixin):
|
|||||||
if v.prop.pk != prop2.pk
|
if v.prop.pk != prop2.pk
|
||||||
]
|
]
|
||||||
|
|
||||||
# Given an dictionary like the ones returned by
|
|
||||||
# Item.get_all_variation() this will return a list of PropertyValue
|
|
||||||
# objects sorted by the primary keys of the properties they belong
|
|
||||||
# to.
|
|
||||||
values = lambda variation: [
|
|
||||||
i[1] for i in sorted(
|
|
||||||
[it for it in variation.items() if it[0] != 'variation']
|
|
||||||
)
|
|
||||||
]
|
|
||||||
|
|
||||||
# Given a list of variations, this will sort them by their position
|
# Given a list of variations, this will sort them by their position
|
||||||
# on the x-axis
|
# on the x-axis
|
||||||
sort = lambda v: v[prop2.pk].pk
|
sort = lambda v: v[prop2.pk].pk
|
||||||
@@ -592,7 +582,7 @@ class ItemVariations(TemplateView, SingleObjectMixin):
|
|||||||
selection = selector(gridrow + (val1,))
|
selection = selector(gridrow + (val1,))
|
||||||
# We now iterate over all variations who generate the same
|
# We now iterate over all variations who generate the same
|
||||||
# selector as 'selection'.
|
# selector as 'selection'.
|
||||||
filtered = [v for v in variations if selector(values(v)) == selection]
|
filtered = [v for v in variations if selector(v.relevant_values()) == selection]
|
||||||
for variation in sorted(filtered, key=sort):
|
for variation in sorted(filtered, key=sort):
|
||||||
form = self.get_form(variation, data)
|
form = self.get_form(variation, data)
|
||||||
formrow.append(form)
|
formrow.append(form)
|
||||||
|
|||||||
@@ -59,9 +59,7 @@ def availability_handler(sender, **kwargs):
|
|||||||
# Make up some unique key for this variation
|
# Make up some unique key for this variation
|
||||||
cachekey = 'timerestriction:%d:%s' % (
|
cachekey = 'timerestriction:%d:%s' % (
|
||||||
item.pk,
|
item.pk,
|
||||||
",".join(sorted(
|
v.identify(),
|
||||||
[str(v[1].pk) for v in v.items() if v[0] != 'variation']
|
|
||||||
))
|
|
||||||
)
|
)
|
||||||
|
|
||||||
# Fetch from cache, if available
|
# Fetch from cache, if available
|
||||||
|
|||||||
Reference in New Issue
Block a user