Source code for shuup.front.template_helpers.product

# -*- coding: utf-8 -*-
# This file is part of Shuup.
#
# Copyright (c) 2012-2021, Shuup Commerce Inc. All rights reserved.
#
# This source code is licensed under the OSL-3.0 license found in the
# LICENSE file in the root directory of this source tree.
import warnings
from django.conf import settings
from jinja2.utils import contextfunction

from shuup.core.models import (
    AttributeVisibility,
    Product,
    ProductAttribute,
    ProductCrossSell,
    ProductCrossSellType,
    ProductMode,
    ShopProduct,
    Supplier,
    get_person_contact,
)
from shuup.core.utils import context_cache
from shuup.core.utils.product_subscription import ProductSubscriptionContext, get_product_subscription_options
from shuup.front.utils import cache as cache_utils
from shuup.utils.text import force_ascii


[docs]def get_visible_attributes(product): return ProductAttribute.objects.filter( product=product, attribute__visibility_mode=AttributeVisibility.SHOW_ON_PRODUCT_PAGE )
[docs]def get_subscription_options_for_product(shop, product, supplier=None, user=None, **kwargs): context = ProductSubscriptionContext(shop, product, supplier, user) return list(get_product_subscription_options(context, **kwargs))
# Deprecated, see `get_product_cross_sells()` @contextfunction
[docs]def get_products_bought_with(context, product, count=5): warnings.warn("Warning! Products bought with template helper is deprecated.", DeprecationWarning) related_product_cross_sells = set( ProductCrossSell.objects.filter(product1=product, type=ProductCrossSellType.COMPUTED) .values_list("product2_id", flat=True) .order_by("-weight")[: (count * 4)] ) request = context["request"] customer = get_person_contact(request.user) return Product.objects.listed(shop=request.shop, customer=customer).filter(pk__in=related_product_cross_sells)[ :count ]
@contextfunction
[docs]def is_visible(context, product): request = context["request"] shop_product = product.get_shop_instance(shop=request.shop, allow_cache=True) return shop_product.is_visible(request.customer)
@contextfunction # noqa (C901)
[docs]def get_product_cross_sells( context, product, relation_type=ProductCrossSellType.RELATED, count=4, orderable_only=True, use_variation_parents=False, ): request = context["request"] key, products = context_cache.get_cached_value( identifier="product_cross_sells", item=cache_utils.get_cross_sells_cache_item(request.shop), context=request, product=product, relation_type=relation_type, count=count, orderable_only=orderable_only, use_variation_parents=use_variation_parents, ) if products is not None: return products rtype = map_relation_type(relation_type) # if this product is parent, then use all children instead if product.mode in [ProductMode.VARIABLE_VARIATION_PARENT, ProductMode.SIMPLE_VARIATION_PARENT]: # Remember to exclude relations with the same parent cross_sell_products = ProductCrossSell.objects.filter( product1__in=product.variation_children.visible(request.shop, customer=request.customer), type=rtype ).exclude(product2__in=product.variation_children.visible(request.shop, customer=request.customer)) else: cross_sell_products = ProductCrossSell.objects.filter(product1=product, type=rtype) related_product_ids = list( cross_sell_products.order_by("weight")[: (count * 4)].values_list("product2_id", flat=True) ) sorted_related_products = [] for product in Product.objects.filter(id__in=related_product_ids): sort_order = related_product_ids.index(product.pk) # use the variation parent when configured if use_variation_parents and product.variation_parent: product = product.variation_parent try: shop_product = product.get_shop_instance(request.shop, allow_cache=True) except ShopProduct.DoesNotExist: continue if orderable_only: for supplier in Supplier.objects.enabled(shop=request.shop): if shop_product.is_orderable( supplier, request.customer, shop_product.minimum_purchase_quantity, allow_cache=True ): sorted_related_products.append((sort_order, product)) break elif shop_product.is_visible(request.customer): sorted_related_products.append((sort_order, product)) # Order related products by weight. Related product ids is in weight order. # If same related product is linked twice to product then lowest weight stands. sorted_related_products.sort(key=lambda pair: pair[0]) products = [] for sort_order, product in sorted_related_products[:count]: if product not in products: products.append(product) context_cache.set_cached_value(key, products, settings.SHUUP_TEMPLATE_HELPERS_CACHE_DURATION) return products
[docs]def map_relation_type(relation_type): """ Map relation type to enum value. :type relation_type: ProductCrossSellType|str :rtype: ProductCrossSellType :raises: `LookupError` if unknown string is given """ if isinstance(relation_type, ProductCrossSellType): return relation_type attr_name = force_ascii(relation_type).upper() try: return getattr(ProductCrossSellType, attr_name) except AttributeError: raise LookupError("Unknown ProductCrossSellType %r" % (relation_type,))