diff --git a/registrasion/forms.py b/registrasion/forms.py index dc32d7b186038a3099b4fc257de991e9d028531a..34ccc878b063e5acb67c0935734ddbab8d24cde1 100644 --- a/registrasion/forms.py +++ b/registrasion/forms.py @@ -3,11 +3,46 @@ import models as rego from django import forms -class ProductItemForm(forms.Form): - product = forms.ModelChoiceField(queryset=None, empty_label=None) - quantity = forms.IntegerField() - - def __init__(self, category, *a, **k): - super(ProductItemForm, self).__init__(*a, **k) - products = rego.Product.objects.filter(category=category) - self.fields['product'].queryset = products +def CategoryForm(category): + + PREFIX = "product_" + + def field_name(product): + return PREFIX + ("%d" % product.id) + + class _CategoryForm(forms.Form): + + @staticmethod + def initial_data(product_quantities): + ''' Prepares initial data for an instance of this form. + product_quantities is a sequence of (product,quantity) tuples ''' + initial = {} + for product, quantity in product_quantities: + initial[field_name(product)] = quantity + + return initial + + def product_quantities(self): + ''' Yields a sequence of (product, quantity) tuples from the + cleaned form data. ''' + for name, value in self.cleaned_data.items(): + if name.startswith(PREFIX): + product_id = int(name[len(PREFIX):]) + yield (product_id, value, name) + + def disable_product(self, product): + ''' Removes a given product from this form. ''' + del self.fields[field_name(product)] + + products = rego.Product.objects.filter(category=category).order_by("order") + for product in products: + + help_text = "$%d -- %s" % (product.price, product.description) + + field = forms.IntegerField( + label=product.name, + help_text=help_text, + ) + _CategoryForm.base_fields[field_name(product)] = field + + return _CategoryForm diff --git a/registrasion/templates/product_category.html b/registrasion/templates/product_category.html index 54a5bb7b304e9c8fd802b33997eb1e8dbf78c48c..9822cfcd1f5136ce66fee4694b386c79d00cd703 100644 --- a/registrasion/templates/product_category.html +++ b/registrasion/templates/product_category.html @@ -9,12 +9,14 @@
{% csrf_token %} + - {{ formset }} + {{ form }}
+ {% endblock %} diff --git a/registrasion/views.py b/registrasion/views.py index d13028ac106cc79b0418371e26f6b7358ceb5bd4..9f54f46eb3e1052f88ce57b723803b6256bcab3b 100644 --- a/registrasion/views.py +++ b/registrasion/views.py @@ -6,11 +6,10 @@ from registrasion.controllers.product import ProductController from django.contrib.auth.decorators import login_required from django.core.exceptions import ObjectDoesNotExist +from django.core.exceptions import ValidationError from django.db import transaction -from django.forms import formset_factory from django.shortcuts import redirect from django.shortcuts import render -from functools import partial, wraps @login_required @@ -20,50 +19,61 @@ def product_category(request, category_id): category_id = int(category_id) # Routing is [0-9]+ category = rego.Category.objects.get(pk=category_id) - ProductItemFormForCategory = ( - wraps(forms.ProductItemForm) - (partial(forms.ProductItemForm, category=category))) - ProductItemFormSet = formset_factory(ProductItemFormForCategory, extra=0) + CategoryForm = forms.CategoryForm(category) + + products = rego.Product.objects.filter(category=category) + products = products.order_by("order") if request.method == "POST": - formset = ProductItemFormSet(request.POST, request.FILES) - if formset.is_valid(): + cat_form = CategoryForm(request.POST, request.FILES) + if cat_form.is_valid(): current_cart = CartController.for_user(request.user) - with transaction.atomic(): - for form in formset.forms: - data = form.cleaned_data - # TODO set form error instead of failing completely - current_cart.set_quantity( - data["product"], data["quantity"], batched=True) - current_cart.end_batch() + try: + with transaction.atomic(): + for product_id, quantity, field_name \ + in cat_form.product_quantities(): + product = rego.Product.objects.get(pk=product_id) + try: + current_cart.set_quantity( + product, quantity, batched=True) + except ValidationError as ve: + cat_form.add_error(field_name, ve) + if cat_form.errors: + raise ValidationError("Cannot add that stuff") + current_cart.end_batch() + except ValidationError as ve: + pass + else: # Create initial data for each of products in category - initial = [] - products = rego.Product.objects.filter(category=category) items = rego.ProductItem.objects.filter(product__category=category) - products = products.order_by("order") + quantities = [] for product in products: # Only add items that are enabled. prod = ProductController(product) - if not prod.can_add_with_enabling_conditions(request.user, 0): - continue - try: quantity = items.get(product=product).quantity except ObjectDoesNotExist: quantity = 0 - data = {"product": product, "quantity": quantity} - initial.append(data) + quantities.append((product, quantity)) + + initial = CategoryForm.initial_data(quantities) + cat_form = CategoryForm(initial=initial) - formset = ProductItemFormSet(initial=initial) + for product in products: + # Remove fields that do not have an enabling condition. + prod = ProductController(product) + if not prod.can_add_with_enabling_conditions(request.user, 0): + cat_form.disable_product(product) data = { "category": category, - "formset": formset, + "form": cat_form, } return render(request, "product_category.html", data) + @login_required def checkout(request): ''' Runs checkout for the current cart of items, ideally generating an