def add_option(request, property_id): """Adds option to property with passed property id. """ property = get_object_or_404(Property, pk=property_id) if request.POST.get("action") == "add": name = request.POST.get("name", "") price = request.POST.get("price", "") try: price = abs(atof(str(price))) except (TypeError, ValueError): price = 0.0 if name != "": option = PropertyOption.objects.create(name=name, price=price, property_id=property_id) message = _(u"Option has been added.") else: message = _(u"Option could not be added.") else: for option_id in request.POST.getlist("option"): try: option = PropertyOption.objects.get(pk=option_id) except PropertyOption.DoesNotExist: pass else: try: price = abs( atof(str(request.POST.get("price-%s" % option_id, "")))) except (TypeError, ValueError): price = 0.0 try: position = int( request.POST.get("position-%s" % option_id, 99)) except ValueError: position = 99 option.position = position option.name = request.POST.get("name-%s" % option_id, "") option.price = price option.save() message = _(u"Options have been updated.") _update_positions(property) # invalidate global properties version number (all product property caches will be invalidated) invalidate_cache_group_id('global-properties-version') html = [["#options", options_inline(request, property_id)]] result = json.dumps({"html": html, "message": message}, cls=LazyEncoder) return HttpResponse(result, content_type='application/json')
def add_option(request, property_id): """Adds option to property with passed property id. """ property = get_object_or_404(Property, pk=property_id) if request.POST.get("action") == "add": name = request.POST.get("name", "") price = request.POST.get("price", "") try: price = abs(atof(str(price))) except (TypeError, ValueError): price = 0.0 if name != "": option = PropertyOption.objects.create(name=name, price=price, property_id=property_id) message = _(u"Option has been added.") else: message = _(u"Option could not be added.") else: for option_id in request.POST.getlist("option"): try: option = PropertyOption.objects.get(pk=option_id) except PropertyOption.DoesNotExist: pass else: try: price = abs(atof(str(request.POST.get("price-%s" % option_id, "")))) except (TypeError, ValueError): price = 0.0 try: position = int(request.POST.get("position-%s" % option_id, 99)) except ValueError: position = 99 option.position = position option.name = request.POST.get("name-%s" % option_id, "") option.price = price option.save() message = _(u"Options have been updated.") _update_positions(property) # invalidate global properties version number (all product property caches will be invalidated) invalidate_cache_group_id('global-properties-version') html = [["#options", options_inline(request, property_id)]] result = json.dumps({ "html": html, "message": message }, cls=LazyEncoder) return HttpResponse(result, content_type='application/json')
def refresh_cart(request): """ Refreshes the cart after some changes has been taken place, e.g.: the amount of a product or shipping/payment method. """ cart = cart_utils.get_cart(request) customer = customer_utils.get_or_create_customer(request) # Update country country_iso = request.POST.get("country") if country_iso: selected_country = Country.objects.get(code=country_iso.lower()) customer.selected_country_id = selected_country.id if customer.selected_shipping_address: customer.selected_shipping_address.country = selected_country customer.selected_shipping_address.save() customer.selected_shipping_address.save() if customer.selected_invoice_address: customer.selected_invoice_address.country = selected_country customer.selected_invoice_address.save() customer.selected_invoice_address.save() # NOTE: The customer has to be saved already here in order to calculate # a possible new valid shippig method below, which coulb be triggered by # the changing of the shipping country. customer.save() # Update Amounts message = "" for item in cart.get_items(): try: value = request.POST.get("amount-cart-item_%s" % item.id, "0.0") amount = core_utils.atof(value) except (TypeError, ValueError): amount = 1.0 if item.product.manage_stock_amount and amount > item.product.stock_amount and not item.product.order_time: amount = item.product.stock_amount if amount < 0: amount = 0 if amount == 0: message = _(u"Sorry, but '%(product)s' is not available anymore." % {"product": item.product.name}) elif amount == 1: message = _(u"Sorry, but '%(product)s' is only one time available." % {"product": item.product.name}) else: message = _(u"Sorry, but '%(product)s' is only %(amount)s times available.") % {"product": item.product.name, "amount": amount} if item.product.get_active_packing_unit(): item.amount = item.product.get_amount_by_packages(float(amount)) else: item.amount = amount if amount == 0: item.delete() else: item.save() # IMPORTANT: We have to send the signal already here, because the valid # shipping methods might be dependent on the price. cart_changed.send(cart, request=request) # Update shipping method shipping_method = get_object_or_404(ShippingMethod, pk=request.POST.get("shipping_method")) customer.selected_shipping_method = shipping_method valid_shipping_methods = shipping_utils.get_valid_shipping_methods(request) if customer.selected_shipping_method not in valid_shipping_methods: customer.selected_shipping_method = shipping_utils.get_default_shipping_method(request) # Update payment method payment_method = get_object_or_404(PaymentMethod, pk=request.POST.get("payment_method")) customer.selected_payment_method = payment_method # Last but not least we save the customer ... customer.save() result = simplejson.dumps({ "html": cart_inline(request), "message": message, }, cls=LazyEncoder) return HttpResponse(result)
def add_to_cart(request, product_id=None): """ Adds the passed product with passed product_id to the cart after some validations have been taken place. The amount is taken from the query string. """ if product_id is None: product_id = request.REQUEST.get("product_id") product = lfs_get_object_or_404(Product, pk=product_id) # Only active and deliverable products can be added to the cart. if not (product.is_active() and product.is_deliverable()): raise Http404() try: value = request.POST.get("quantity", "1.0") quantity = core_utils.atof(value) except (TypeError, ValueError): quantity = 1.0 # Validate properties (They are added below) properties_dict = {} if product.is_configurable_product(): for key, value in request.POST.items(): if key.startswith("property-"): try: property_id = key.split("-")[1] except IndexError: continue try: prop = Property.objects.get(pk=property_id) except Property.DoesNotExist: continue if prop.is_number_field: try: value = lfs.core.utils.atof(value) except ValueError: value = 0.0 properties_dict[property_id] = unicode(value) # validate property's value if prop.is_number_field: if (value < prop.unit_min) or (value > prop.unit_max): msg = _(u"%(name)s must be between %(min)s and %(max)s %(unit)s.") % {"name": prop.title, "min": prop.unit_min, "max": prop.unit_max, "unit": prop.unit} return lfs.core.utils.set_message_cookie( product.get_absolute_url(), msg) # calculate valid steps steps = [] x = prop.unit_min while x < prop.unit_max: steps.append("%.2f" % x) x += prop.unit_step steps.append("%.2f" % prop.unit_max) value = "%.2f" % value if value not in steps: msg = _(u"Your entered value for %(name)s (%(value)s) is not in valid step width, which is %(step)s.") % {"name": prop.title, "value": value, "step": prop.unit_step} return lfs.core.utils.set_message_cookie( product.get_absolute_url(), msg) if product.get_active_packing_unit(): quantity = product.get_amount_by_packages(quantity) cart = cart_utils.get_or_create_cart(request) cart_item = cart.add(product, properties_dict, quantity) cart_items = [cart_item] # Check stock amount message = "" if product.manage_stock_amount and cart_item.amount > product.stock_amount and not product.order_time: if product.stock_amount == 0: message = _(u"Sorry, but '%(product)s' is not available anymore.") % {"product": product.name} elif product.stock_amount == 1: message = _(u"Sorry, but '%(product)s' is only one time available.") % {"product": product.name} else: message = _(u"Sorry, but '%(product)s' is only %(amount)s times available.") % {"product": product.name, "amount": product.stock_amount} cart_item.amount = product.stock_amount cart_item.save() # Add selected accessories to cart for key, value in request.POST.items(): if key.startswith("accessory"): accessory_id = key.split("-")[1] try: accessory = Product.objects.get(pk=accessory_id) except ObjectDoesNotExist: continue # Get quantity quantity = request.POST.get("quantity-%s" % accessory_id, 0) try: quantity = float(quantity) except TypeError: quantity = 1 cart_item = cart.add(product=accessory, amount=quantity) cart_items.append(cart_item) # Store cart items for retrieval within added_to_cart. request.session["cart_items"] = cart_items cart_changed.send(cart, request=request) # Update the customer's shipping method (if appropriate) customer = customer_utils.get_or_create_customer(request) shipping_utils.update_to_valid_shipping_method(request, customer, save=True) # Update the customer's payment method (if appropriate) payment_utils.update_to_valid_payment_method(request, customer, save=True) # Save the cart to update modification date cart.save() try: url_name = settings.LFS_AFTER_ADD_TO_CART except AttributeError: url_name = "lfs_added_to_cart" if message: return lfs.core.utils.set_message_cookie(reverse(url_name), message) else: return HttpResponseRedirect(reverse(url_name))
def save_products(request): """ Saves products with passed ids (by request body). """ products = _get_filtered_products(request) paginator = Paginator( products, request.session.get("product_filters", {}).get('amount', 25)) page = paginator.page(request.REQUEST.get("page", 1)) if request.POST.get("action") == "delete": for key, value in request.POST.items(): if key.startswith("delete-"): id = key.split("-")[1] try: product = Product.objects.get(pk=id) except Product.DoesNotExist: continue else: product.delete() msg = _(u"Products have been deleted.") # switch to the first page because after some products are removed current page might be out of range page = paginator.page(1) elif request.POST.get("action") == "save": for key, value in request.POST.items(): if key.startswith("id-"): id = value try: product = Product.objects.get(pk=id) except Product.DoesNotExist: continue product.name = request.POST.get("name-%s" % id, "") product.sku = request.POST.get("sku-%s" % id, "") product.slug = request.POST.get("slug-%s" % id, "") product.sub_type = request.POST.get("sub_type-%s" % id, 0) try: price = request.POST.get("price-%s" % id, '0') product.price = atof(price) except ValueError: product.price = 0 try: for_sale_price = request.POST.get("for_sale_price-%s" % id, '0') product.for_sale_price = atof(for_sale_price) except ValueError: product.for_sale_price = 0 if request.POST.get("for_sale-%s" % id): product.for_sale = True else: product.for_sale = False if request.POST.get("active-%s" % id): product.active = True else: product.active = False # TODO: remove IntegrityError and apply some kind of form/formset validation try: product.save() except IntegrityError: pass msg = _(u"Products have been saved") html = (("#products-inline", products_inline(request, page, paginator)), ("#pages-inline", pages_inline(request, page, paginator, 0))) result = json.dumps({ "html": html, "message": msg, }, cls=LazyEncoder) return HttpResponse(result, content_type='application/json')
def save_products(request): """ Saves products with passed ids (by request body). """ products = _get_filtered_products(request) paginator = Paginator(products, request.session.get("product_filters", {}).get('amount', 25)) page = paginator.page((request.POST if request.method == 'POST' else request.GET).get("page", 1)) if request.POST.get("action") == "delete": for key, value in request.POST.items(): if key.startswith("delete-"): id = key.split("-")[1] try: product = Product.objects.get(pk=id) except Product.DoesNotExist: continue else: product.delete() msg = _(u"Products have been deleted.") # switch to the first page because after some products are removed current page might be out of range page = paginator.page(1) elif request.POST.get("action") == "save": for key, value in request.POST.items(): if key.startswith("id-"): id = value try: product = Product.objects.get(pk=id) except Product.DoesNotExist: continue product.name = request.POST.get("name-%s" % id, "") product.sku = request.POST.get("sku-%s" % id, "") product.slug = request.POST.get("slug-%s" % id, "") product.sub_type = request.POST.get("sub_type-%s" % id, 0) try: price = request.POST.get("price-%s" % id, '0') product.price = atof(price) except ValueError: product.price = 0 try: for_sale_price = request.POST.get("for_sale_price-%s" % id, '0') product.for_sale_price = atof(for_sale_price) except ValueError: product.for_sale_price = 0 if request.POST.get("for_sale-%s" % id): product.for_sale = True else: product.for_sale = False if request.POST.get("active-%s" % id): product.active = True else: product.active = False # TODO: remove IntegrityError and apply some kind of form/formset validation try: product.save() except IntegrityError: pass msg = _(u"Products have been saved") html = (("#products-inline", products_inline(request, page, paginator)), ("#pages-inline", pages_inline(request, page, paginator, 0)) ) result = json.dumps({ "html": html, "message": msg, }, cls=LazyEncoder) return HttpResponse(result, content_type='application/json')
def refresh_cart(request): """ Refreshes the cart after some changes has been taken place, e.g.: the amount of a product or shipping/payment method. """ cart = cart_utils.get_cart(request) customer = customer_utils.get_or_create_customer(request) # Update country country_iso = request.POST.get("country") if country_iso: selected_country = Country.objects.get(code=country_iso.lower()) customer.selected_country_id = selected_country.id if customer.selected_shipping_address: customer.selected_shipping_address.country = selected_country customer.selected_shipping_address.save() customer.selected_shipping_address.save() if customer.selected_invoice_address: customer.selected_invoice_address.country = selected_country customer.selected_invoice_address.save() customer.selected_invoice_address.save() # NOTE: The customer has to be saved already here in order to calculate # a possible new valid shippig method below, which coulb be triggered by # the changing of the shipping country. customer.save() # Update Amounts message = "" for item in cart.get_items(): try: value = request.POST.get("amount-cart-item_%s" % item.id, "0.0") amount = core_utils.atof(value) except (TypeError, ValueError): amount = 1.0 if item.product.manage_stock_amount and amount > item.product.stock_amount and not item.product.order_time: amount = item.product.stock_amount if amount < 0: amount = 0 if amount == 0: message = _(u"Sorry, but '%(product)s' is not available anymore." % {"product": item.product.name}) elif amount == 1: message = _(u"Sorry, but '%(product)s' is only one time available." % {"product": item.product.name}) else: message = _(u"Sorry, but '%(product)s' is only %(amount)s times available.") % { "product": item.product.name, "amount": amount, } if item.product.get_active_packing_unit(): item.amount = item.product.get_amount_by_packages(float(amount)) else: item.amount = amount if amount == 0: item.delete() else: item.save() # IMPORTANT: We have to send the signal already here, because the valid # shipping methods might be dependent on the price. cart_changed.send(cart, request=request) # Update shipping method shipping_method = get_object_or_404(ShippingMethod, pk=request.POST.get("shipping_method")) customer.selected_shipping_method = shipping_method valid_shipping_methods = shipping_utils.get_valid_shipping_methods(request) if customer.selected_shipping_method not in valid_shipping_methods: customer.selected_shipping_method = shipping_utils.get_default_shipping_method(request) # Update payment method payment_method = get_object_or_404(PaymentMethod, pk=request.POST.get("payment_method")) customer.selected_payment_method = payment_method # Last but not least we save the customer ... customer.save() result = simplejson.dumps({"html": cart_inline(request), "message": message}, cls=LazyEncoder) return HttpResponse(result)
def add_to_cart(request, product_id=None): """ Adds the passed product with passed product_id to the cart after some validations have been taken place. The amount is taken from the query string. """ if product_id is None: product_id = request.REQUEST.get("product_id") product = lfs_get_object_or_404(Product, pk=product_id) # Only active and deliverable products can be added to the cart. if not (product.is_active() and product.is_deliverable()): raise Http404() try: value = request.POST.get("quantity", "1.0") quantity = core_utils.atof(value) except (TypeError, ValueError): quantity = 1.0 # Validate properties (They are added below) properties_dict = {} if product.is_configurable_product(): for key, value in request.POST.items(): if key.startswith("property-"): try: property_id = key.split("-")[1] except IndexError: continue try: prop = Property.objects.get(pk=property_id) except Property.DoesNotExist: continue if prop.is_number_field: try: value = lfs.core.utils.atof(value) except ValueError: value = 0.0 properties_dict[property_id] = unicode(value) # validate property's value if prop.is_number_field: if (value < prop.unit_min) or (value > prop.unit_max): msg = _(u"%(name)s must be between %(min)s and %(max)s %(unit)s.") % { "name": prop.title, "min": prop.unit_min, "max": prop.unit_max, "unit": prop.unit, } return lfs.core.utils.set_message_cookie(product.get_absolute_url(), msg) # calculate valid steps steps = [] x = prop.unit_min while x < prop.unit_max: steps.append("%.2f" % x) x += prop.unit_step steps.append("%.2f" % prop.unit_max) value = "%.2f" % value if value not in steps: msg = _( u"Your entered value for %(name)s (%(value)s) is not in valid step width, which is %(step)s." ) % {"name": prop.title, "value": value, "step": prop.unit_step} return lfs.core.utils.set_message_cookie(product.get_absolute_url(), msg) if product.get_active_packing_unit(): quantity = product.get_amount_by_packages(quantity) cart = cart_utils.get_or_create_cart(request) cart_item = cart.add(product, properties_dict, quantity) cart_items = [cart_item] # Check stock amount message = "" if product.manage_stock_amount and cart_item.amount > product.stock_amount and not product.order_time: if product.stock_amount == 0: message = _(u"Sorry, but '%(product)s' is not available anymore.") % {"product": product.name} elif product.stock_amount == 1: message = _(u"Sorry, but '%(product)s' is only one time available.") % {"product": product.name} else: message = _(u"Sorry, but '%(product)s' is only %(amount)s times available.") % { "product": product.name, "amount": product.stock_amount, } cart_item.amount = product.stock_amount cart_item.save() # Add selected accessories to cart for key, value in request.POST.items(): if key.startswith("accessory"): accessory_id = key.split("-")[1] try: accessory = Product.objects.get(pk=accessory_id) except ObjectDoesNotExist: continue # Get quantity quantity = request.POST.get("quantity-%s" % accessory_id, 0) try: quantity = float(quantity) except TypeError: quantity = 1 cart_item = cart.add(product=accessory, amount=quantity) cart_items.append(cart_item) # Store cart items for retrieval within added_to_cart. request.session["cart_items"] = cart_items cart_changed.send(cart, request=request) # Update the customer's shipping method (if appropriate) customer = customer_utils.get_or_create_customer(request) shipping_utils.update_to_valid_shipping_method(request, customer, save=True) # Update the customer's payment method (if appropriate) payment_utils.update_to_valid_payment_method(request, customer, save=True) # Save the cart to update modification date cart.save() try: url_name = settings.LFS_AFTER_ADD_TO_CART except AttributeError: url_name = "lfs_added_to_cart" if message: return lfs.core.utils.set_message_cookie(reverse(url_name), message) else: return HttpResponseRedirect(reverse(url_name))
def update_variants(request, product_id): """Updates/Deletes variants with passed ids (via request body) dependent on given action (also via request body). """ product = lfs_get_object_or_404(Product, pk=product_id) message = '' action = request.POST.get("action") if action == "delete": message = _(u"Variants have been deleted.") for key in request.POST.keys(): if key.startswith("delete-"): try: prop_id = key.split("-")[1] variant = Product.objects.get(pk=prop_id) except (IndexError, ObjectDoesNotExist): continue else: if product.default_variant == variant: product.default_variant = None product.save() variant.delete() elif action == "update": # TODO: change all of these to formsets or something that will allow for error hangling/messages message = _(u"Variants have been saved.") for key, value in request.POST.items(): if key.startswith("variant-"): prop_id = key.split("-")[1] try: variant = Product.objects.get(pk=prop_id) except ObjectDoesNotExist: continue else: for name in ("sku", "price"): value = request.POST.get("%s-%s" % (name, prop_id)) if value != "": if name == 'price': try: value = abs(atof(str(value))) except (TypeError, ValueError): value = 0.0 setattr(variant, name, value) # handle slug - ensure it is unique slug = request.POST.get("slug-%s" % prop_id) if variant.slug != slug: counter = 1 new_slug = slug[:80] while Product.objects.exclude(pk=variant.pk).filter(slug=new_slug).exists(): new_slug = '%s-%s' % (slug[:(79 - len(str(counter)))], counter) counter += 1 variant.slug = new_slug # name variant.name = request.POST.get("name-%s" % prop_id) # active active = request.POST.get("active-%s" % prop_id) if active: variant.active = True else: variant.active = False # active attributes for name in ("active_price", "active_sku", "active_name"): value = request.POST.get("%s-%s" % (name, prop_id)) if value: setattr(variant, name, True) else: setattr(variant, name, False) # position position = request.POST.get("position-%s" % prop_id) try: variant.variant_position = int(position) except ValueError: variant.variant_position = 10 # default variant try: product.default_variant_id = int(request.POST.get("default_variant")) except TypeError: pass else: product.save() variant.save() elif key.startswith("property"): # properties are marshalled as: property-variant_id|property_group_id|property_id temp = key.split("-")[1] variant_id, property_group_id, property_id = temp.split("|") if property_group_id == '0': # local properties are not bound to property groups property_group_id = None try: variant = Product.objects.get(pk=variant_id) except Product.DoesNotExist: continue prop = Property.objects.get(pk=property_id) ppv = None try: ppv = ProductPropertyValue.objects.get(product=variant, property_id=property_id, property_group_id=property_group_id, type=PROPERTY_VALUE_TYPE_VARIANT) except ProductPropertyValue.DoesNotExist: pass if prop.filterable: # it is possible that multiple values are selected for filter ppv_filterables = ProductPropertyValue.objects.filter(product=variant, property_group_id=property_group_id, property_id=property_id, type=PROPERTY_VALUE_TYPE_FILTER) if value != '': is_changed = True if not ppv: ppv = ProductPropertyValue.objects.create(product=variant, property_group_id=property_group_id, property_id=property_id, type=PROPERTY_VALUE_TYPE_VARIANT, value=value) else: is_changed = ppv.value != value ppv.value = value ppv.save() if prop.filterable and is_changed: ppv_filterables.delete() ProductPropertyValue.objects.create(product=variant, property_group_id=property_group_id, property_id=property_id, value=value, type=PROPERTY_VALUE_TYPE_FILTER) elif ppv: ppv.delete() ppv_filterables.delete() # Refresh variant positions for i, variant in enumerate(product.variants.order_by("variant_position")): variant.variant_position = (i + 1) * 10 variant.save() # Send a signal to update cache product_changed.send(product) pid = product.get_parent().pk invalidate_cache_group_id('properties-%s' % pid) html = ( ("#variants", manage_variants(request, product_id, as_string=True)), ("#selectable-products-inline", _selectable_products_inline(request, product)), ) result = json.dumps({ "html": html, "message": message, }, cls=LazyEncoder) return HttpResponse(result, content_type='application/json')
def update_variants(request, product_id): """Updates/Deletes variants with passed ids (via request body) dependent on given action (also via request body). """ product = lfs_get_object_or_404(Product, pk=product_id) message = '' action = request.POST.get("action") if action == "delete": message = _(u"Variants have been deleted.") for key in request.POST.keys(): if key.startswith("delete-"): try: prop_id = key.split("-")[1] variant = Product.objects.get(pk=prop_id) except (IndexError, ObjectDoesNotExist): continue else: if product.default_variant == variant: product.default_variant = None product.save() variant.delete() elif action == "update": # TODO: change all of these to formsets or something that will allow for error hangling/messages message = _(u"Variants have been saved.") for key, value in request.POST.items(): if key.startswith("variant-"): prop_id = key.split("-")[1] try: variant = Product.objects.get(pk=prop_id) except ObjectDoesNotExist: continue else: for name in ("sku", "price"): value = request.POST.get("%s-%s" % (name, prop_id)) if value != "": if name == 'price': try: value = abs(atof(str(value))) except (TypeError, ValueError): value = 0.0 setattr(variant, name, value) # handle slug - ensure it is unique slug = request.POST.get("slug-%s" % prop_id) if variant.slug != slug: counter = 1 new_slug = slug[:80] while Product.objects.exclude(pk=variant.pk).filter( slug=new_slug).exists(): new_slug = '%s-%s' % (slug[:( 79 - len(str(counter)))], counter) counter += 1 variant.slug = new_slug # name variant.name = request.POST.get("name-%s" % prop_id) # active active = request.POST.get("active-%s" % prop_id) if active: variant.active = True else: variant.active = False # active attributes for name in ("active_price", "active_sku", "active_name"): value = request.POST.get("%s-%s" % (name, prop_id)) if value: setattr(variant, name, True) else: setattr(variant, name, False) # position position = request.POST.get("position-%s" % prop_id) try: variant.variant_position = int(position) except ValueError: variant.variant_position = 10 # default variant try: product.default_variant_id = int( request.POST.get("default_variant")) except TypeError: pass else: product.save() variant.save() elif key.startswith("property"): # properties are marshalled as: property-variant_id|property_group_id|property_id temp = key.split("-")[1] variant_id, property_group_id, property_id = temp.split("|") if property_group_id == '0': # local properties are not bound to property groups property_group_id = None try: variant = Product.objects.get(pk=variant_id) except Product.DoesNotExist: continue prop = Property.objects.get(pk=property_id) ppv = None try: ppv = ProductPropertyValue.objects.get( product=variant, property_id=property_id, property_group_id=property_group_id, type=PROPERTY_VALUE_TYPE_VARIANT) except ProductPropertyValue.DoesNotExist: pass if prop.filterable: # it is possible that multiple values are selected for filter ppv_filterables = ProductPropertyValue.objects.filter( product=variant, property_group_id=property_group_id, property_id=property_id, type=PROPERTY_VALUE_TYPE_FILTER) if value != '': is_changed = True if not ppv: ppv = ProductPropertyValue.objects.create( product=variant, property_group_id=property_group_id, property_id=property_id, type=PROPERTY_VALUE_TYPE_VARIANT, value=value) else: is_changed = ppv.value != value ppv.value = value ppv.save() if prop.filterable and is_changed: ppv_filterables.delete() ProductPropertyValue.objects.create( product=variant, property_group_id=property_group_id, property_id=property_id, value=value, type=PROPERTY_VALUE_TYPE_FILTER) elif ppv: ppv.delete() ppv_filterables.delete() # Refresh variant positions for i, variant in enumerate(product.variants.order_by("variant_position")): variant.variant_position = (i + 1) * 10 variant.save() # Send a signal to update cache product_changed.send(product) pid = product.get_parent().pk invalidate_cache_group_id('properties-%s' % pid) html = ( ("#variants", manage_variants(request, product_id, as_string=True)), ("#selectable-products-inline", _selectable_products_inline(request, product)), ) result = json.dumps({ "html": html, "message": message, }, cls=LazyEncoder) return HttpResponse(result, content_type='application/json')