Beispiel #1
0
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)
Beispiel #2
0
 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)
Beispiel #3
0
 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
         )
Beispiel #4
0
    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)