From c7aa69448c6e7a4381d7c8c0b978e2ca782e4ec9 Mon Sep 17 00:00:00 2001 From: mtyton Date: Sun, 16 Jul 2023 16:22:09 +0200 Subject: [PATCH] [WIP] trying formset attempt --- artel/mailings/admin.py | 2 - artel/store/forms.py | 41 +++++++++----- artel/store/models.py | 16 +++++- .../templates/store/configure_product.html | 21 ++++--- artel/store/tests/test_models.py | 42 ++++++++++++++ artel/store/views.py | 55 ++++++++++++++++--- 6 files changed, 144 insertions(+), 33 deletions(-) diff --git a/artel/mailings/admin.py b/artel/mailings/admin.py index 4440840..3600a42 100644 --- a/artel/mailings/admin.py +++ b/artel/mailings/admin.py @@ -40,12 +40,10 @@ class OutgoingMailAdmin(ModelAdmin): exclude_from_explorer = False list_display = ( "subject", - "to", "sent", ) search_fields = ( "subject", - "to", ) list_filter = ( "subject", diff --git a/artel/store/forms.py b/artel/store/forms.py index 8bc7673..6822fe2 100644 --- a/artel/store/forms.py +++ b/artel/store/forms.py @@ -40,26 +40,39 @@ class CustomerDataForm(forms.Form): ) -class ProductCategoryParamForm(forms.ModelForm): +class ProductCategoryParamFormset(forms.BaseModelFormSet): + ... + + +class ProductCategoryParamValueForm(forms.ModelForm): class Meta: - model = ProductCategoryParam + model = ProductCategoryParamValue fields = ("key", "value") - readonly_fields = ("key", ) - - def __init__(self, instance, *args, **kwargs): - super().__init__(*args, instance=instance, **kwargs) - self.fields["key"].widget.attrs["disabled"] = True - self.fields["value"].choices = [ - (param_value.pk, param_value.value) for param_value in instance.param_values.all() - ] - + + key = forms.CharField(required=True) value = forms.ModelChoiceField( queryset=ProductCategoryParamValue.objects.none(), - widget=forms.RadioSelect(attrs={"class": "form-control"}) + widget=forms.RadioSelect(attrs={"class": "btn-check", "type": "radio", "autocomplete": "off"}), + required=True ) + def _get_instace(self, key: str): + return ProductCategoryParam.objects.get(key=key) + + def __init__(self, *args, **kwargs): + super().__init__(*args, **kwargs) + key = self.initial.get("key") + if not key: + return + self.cat_param = self._get_instace(key) + self.fields["value"].choices = [ + (param_value.pk, param_value.value) for param_value in self.cat_param.param_values.all() + ] + def save(self, *args, **kwargs): - return ProductCategoryParamValue.objects.get( - param=self.instance, + param_value = ProductCategoryParamValue.objects.get( + param__key=str(self.cleaned_data["key"]), value=str(self.cleaned_data["value"]) ) + print(param_value or "DUPSKo") + return param_value diff --git a/artel/store/models.py b/artel/store/models.py index 30d37bc..01c7460 100644 --- a/artel/store/models.py +++ b/artel/store/models.py @@ -171,12 +171,22 @@ class ProductTemplateImage(BaseImageModel): class ProductManager(models.Manager): def get_or_create_by_params(self, params: list[ProductCategoryParamValue], template: ProductTemplate): - product = self.filter(params__in=params).first() + products = self.filter(template=template) + # TODO - other price + price_proposal = products.first().price if products.first() else 4.0 + for param in params: + products = products.filter(params__pk=param.pk) + + # There should be only one + if not products.count() <= 1: + raise ValidationError("There should be only one product with given set of params") + + product = products.first() if not product: product = self.create( name=f"{template.title} - AUTOGENERATED", template=template, - price=..., + price=price_proposal, available=False ) for param in params: @@ -194,6 +204,8 @@ class Product(ClusterableModel): price = models.FloatField() available = models.BooleanField(default=True) + objects = ProductManager() + panels = [ FieldPanel("template"), FieldPanel("price"), diff --git a/artel/store/templates/store/configure_product.html b/artel/store/templates/store/configure_product.html index 05666ff..b67a744 100644 --- a/artel/store/templates/store/configure_product.html +++ b/artel/store/templates/store/configure_product.html @@ -23,16 +23,21 @@
{% csrf_token %} + + {{ formset.management_form }}
- {% for form in forms %} + {% for form in formset.forms %}
-

{{form.instance}}

- {% for value, label in form.value.field.choices %} -
- - -
- {% endfor %} +

{{form.cat_param}}

+ {% if form.value.field.choices %} + + {% for value, label in form.value.field.choices %} +
+ + +
+ {% endfor %} + {% endif %}
{% if forloop.counter|divisibleby:"2" %}
{% endif %} {% endfor %} diff --git a/artel/store/tests/test_models.py b/artel/store/tests/test_models.py index 3b07e79..b29364a 100644 --- a/artel/store/tests/test_models.py +++ b/artel/store/tests/test_models.py @@ -98,6 +98,48 @@ class ProductTestCase(TestCase): product.params.add(sec_param_value) self.assertEqual(product.params.count(), 0) + def test_get_or_create_by_params_success(self): + product = factories.ProductFactory(available=True) + value1 = factories.ProductCategoryParamValueFactory() + value2 = factories.ProductCategoryParamValueFactory() + product.params.add(value1) + product.params.add(value2) + product.save() + prod = store_models.Product.objects.get_or_create_by_params( + params=[value1, value2], template=product.template, + ) + self.assertIsNotNone(prod) + self.assertEqual(prod.pk, product.pk) + self.assertTrue(prod.available) + + def test_get_or_create_by_params_success_not_existing_product(self): + product = factories.ProductFactory(available=True) + value1 = factories.ProductCategoryParamValueFactory() + value2 = factories.ProductCategoryParamValueFactory() + product.params.add(value1) + product.price = 13.0 + product.save() + + prod = store_models.Product.objects.get_or_create_by_params( + params=[value1, value2], template=product.template, + ) + self.assertIsNotNone(prod) + self.assertNotEqual(prod.pk, product.pk) + self.assertFalse(prod.available) + self.assertEqual(prod.price, 13.0) + + def test_get_or_create_by_params_success_not_existing_product_no_other_products(self): + template = factories.ProductTemplateFactory() + value1 = factories.ProductCategoryParamValueFactory() + value2 = factories.ProductCategoryParamValueFactory() + + prod = store_models.Product.objects.get_or_create_by_params( + params=[value1, value2], template=template, + ) + self.assertIsNotNone(prod) + self.assertFalse(prod.available) + self.assertEqual(prod.price, 4.0) + class OrderProductTestCase(TestCase): def setUp(self): diff --git a/artel/store/views.py b/artel/store/views.py index c06db5c..75ff0dd 100644 --- a/artel/store/views.py +++ b/artel/store/views.py @@ -8,6 +8,7 @@ from django.shortcuts import render from django.urls import reverse from django.http import HttpResponseRedirect from django.contrib import messages +from django.forms import modelformset_factory from rest_framework.viewsets import ViewSet from rest_framework.decorators import action from rest_framework.response import Response @@ -19,12 +20,14 @@ from store.serializers import ( ) from store.forms import ( CustomerDataForm, - ProductCategoryParamForm + ProductCategoryParamValueForm, + ProductCategoryParamFormset ) from store.models import ( Order, Product, - ProductTemplate + ProductTemplate, + ProductCategoryParamValue ) @@ -96,13 +99,17 @@ class ConfigureProductView(View): def get_context_data(self, pk: int, **kwargs: Any) -> Dict[str, Any]: template = ProductTemplate.objects.get(pk=pk) category_params = template.category.category_params.all() - - + formset_class = modelformset_factory( + ProductCategoryParamValue, + form=ProductCategoryParamValueForm, + formset=ProductCategoryParamFormset + ) + formset = formset_class(queryset=category_params) context = { "template": template, "available_variants": Product.objects.filter(template__pk=pk), "category_params": category_params, - "forms": [ProductCategoryParamForm(instance=param) for param in category_params] + "formset": formset } return context @@ -112,8 +119,42 @@ class ConfigureProductView(View): return render(request, self.template_name, context) def post(self, request, pk: int, *args, **kwargs): + # first select template + template = ProductTemplate.objects.get(pk=pk) + category_params = template.category.category_params.all() + params_values = [] + formset_class = modelformset_factory( + ProductCategoryParamValue, + form=ProductCategoryParamValueForm, + formset=ProductCategoryParamFormset + ) + formset = formset_class(queryset=category_params, data=request.POST) print(request.POST) - return HttpResponseRedirect(reverse("product-configure-summary", kwargs={"variant_pk": 1})) + if not formset.is_valid(): + print(formset.errors) + messages.error(request, "Niepoprawne dane") + context = self.get_context_data(pk) + context["formset"] = formset + return render(request, self.template_name, context) + + for form in formset.forms: + if not form.is_valid(): + messages.error(request, "Niepoprawne dane") + context = self.get_context_data(pk) + context["formset"] = formset + return render(request, self.template_name, context) + params_values.append(form.save()) + + product_variant = Product.objects.get_or_create_by_params( + template=ProductTemplate.objects.get(pk=pk), params=params_values + ) + if not product_variant: + messages.error(request, "Nie udało się utworzyć wariantu produktu") + return HttpResponseRedirect(reverse("product-configure", kwargs={"pk": pk})) + + return HttpResponseRedirect( + reverse("product-configure-summary", kwargs={"variant_pk": product_variant.pk}) + ) class ConfigureProductSummaryView(View): @@ -123,7 +164,7 @@ class ConfigureProductSummaryView(View): variant = Product.objects.get(pk=variant_pk) context = { "variant": variant, - "category_params": variant.template.category.category_params.all() + "params_values": variant.params.all() } return render(request, self.template_name, context)