def override_filter(module, chain, request, products, key, value, attribute, operator=None, **kwargs): try: value = attribute.parse(value) except ValueError: return products if operator is None: q = product_q(attribute, value) | product_q( attribute, value, through='base_product') else: qargs = { operator: value } q = (product_q(attribute, **qargs) | product_q(attribute, through='base_product', **qargs)) return products.filter(q)
def save(self, *args, **kwargs): old = None if self.pk: old = _attribute.models.Attribute.objects.get(pk=self.pk) super(Attribute, self).save(*args, **kwargs) if self.variates or old and old.variates: products = _product.models.Product.objects.filter( product_q(attribute=self, through='base_product')) _variation.models.Variation.objects.invalidate( products=products)
def save(self, *args, **kwargs): old = None if self.pk: old = _attribute.models.Attribute.objects.get(pk=self.pk) super(Attribute, self).save(*args, **kwargs) if self.variates or old and old.variates: products = _product.models.Product.objects.filter( product_q( attribute=self, through='base_product' ) ) _variation.models.Variation.objects.invalidate( products=products )
def sync(self, product): product = product.downcast() logger.info("Syncing variations for %s" % (product)) # Get all variating attributes for this product attributes = _attribute.models.Attribute.objects.which_variate_product( product) # Do we have an attribute which groups ? group = attributes.filter(groups=True).first() # CONVERT TO LIST FOR PERFORMANCE AND INDEXING attributes = list(attributes) if attributes: # Create all possible variation combinations combinations = itertools.product( *[ ( row['value'] for row in _attribute.models.Value.objects.which_variate_product( product).for_attribute(attribute) .smart_values('value') .distinct() .smart_sort(attribute, product) ) for attribute in attributes ] ) else: combinations = [] # Create variations sort_order = 0 created = [] for combination in combinations: # Find all values which could be in this combination. # Values can be explicitly assigned to a variant, or # they could variate this product. q = Q() for attribute, value in zip(attributes, combination): q |= value_q(attribute, value) all_values = ( _attribute.models.Value.objects.which_variate_product(product) .filter(q) ) # Filter out implicits and explicits implicits = all_values.filter(variates=True) explicits = all_values.filter(variates=False) # Find all remaining variants which can be matched # against one ore more explicit values in this combination. variants = product.variants.filter( pk__in=( explicits.order_by('product').distinct().values('product') ) ) # Find most explicit value combination grouped_explicitly = None most_explicit = _attribute.models.Value.objects.none() for variant in variants: current = explicits.filter(product=variant) # Make sure this variant does not belong to a different combination if current.count() != variant.values.which_variate( product ).filter(variates=False).count(): continue if current.count() == len(combination): # This must be the most explicit combination most_explicit = current break else: # If variations are grouped, ignore the grouping # attribute when determening most explicit variant. a = current.exclude(attribute=group).count() b = most_explicit.exclude(attribute=group).count() # Try keep track of an explicit grouped value # We only allow value combinations containing a single # grouped value. if group and not grouped_explicitly and a == 0: try: grouped_explicitly = current.get(attribute=group) except _attribute.models.Value.DoesNotExist: pass if most_explicit.count() == 0 or a > b: # Found more explicit match.. override most_explicit = current explicits = most_explicit if explicits.count() > 0: # A variant did match variant = explicits[0].product.downcast() else: # If no variants are matched, variant equals product variant = product values = _attribute.models.Value.objects.none() # Resolve actual values if variant == product: # All values are implicit since we don't have a variant values = implicits else: # Collect all values by combining implicit values and values # in the explicits queryset implicits = implicits.exclude( attribute__in=explicits.values('attribute') ) values = all_values.filter( Q(pk__in=implicits) | Q( pk__in=explicits ) ) # Now account for grouping behaviour if group and grouped_explicitly and values.filter( attribute=group ).count() == 0: # Seem like the grouped value was left out due to a more # explicit value combination, filter again values = all_values.filter( Q(pk__in=implicits) | Q( pk__in=explicits ) | Q(pk=grouped_explicitly.pk) ) # Make sure this combination actually exists if values.count() != len(combination): continue # Generate a unique key (uses slug format) for this variation variation_key = values_slug( values, prefix=variant.slug, full=True ) # Generate description (does not use all values only implicits). # This because unicode(variant) already includes explicit values. variation_description = values_description( implicits, prefix=unicode(variant) ) try: # See if variation already exists variation = _variation.models.Variation.objects.get( id=variation_key, product=product ) except _variation.models.Variation.DoesNotExist: # Create variation = _variation.models.Variation.objects.create( id=variation_key, description=variation_description, product=product, variant=variant, sort_order=sort_order ) else: # Update variation.variant = variant variation.sort_order = sort_order variation.description = variation_description variation.save() variation.values.add(*values) sort_order += 1 # Make sure this variation does not get deleted created.append(variation_key) # Handle grouping if group: # We need to find a single variant which is common across all # variations in this group for value in [ row['value'] for row in _attribute.models.Value.objects.which_variate_product( product ).for_attribute(attribute=group) .smart_values('value').distinct().order_by() ]: # Get variations for this grouped attribute / value combination qargs = { 'values__attribute': group, 'values__{0}'.format( group.get_type().get_value_field_name( ) ): value, } variations = _variation.models.Variation.objects.filter( product=product ).filter(**qargs) # Get variant qargs = [product_q(group, value)] if variations.count() > 1: # Get single variant common across all variations for attribute in attributes: if attribute != group: qargs.append(~product_q(attribute)) try: variant = product.variants.get(*qargs) except _product.models.Product.DoesNotExist: variant = product except _product.models.Product.MultipleObjectsReturned: variant = product logger.warning( "Product {product} has multiple variants " "which conflict with the following " "attribute/value combinations: " "{attribute}/{value}".format( product=product, attribute=group, value=value ) ) variations.update(group_variant=variant) # Delete any stale variations stale = self.filter(product=product) \ .exclude(pk__in=created) stale.delete() # Finally update product product.variations_synced = True product.save() variations_synced.send(self, product=product)