prompt: You will be provided with a partial code base and an issue statement explaining a problem to resolve.
  <issue>
  Calling get_price_range() on an empty item range
  On the product detail page backend doesn't check existence of product variants.
  If the product requires variants, but they don't exist, Django throws an exception `Calling get_price_range() on an empty item range`.
  
  </issue>
  <code>
  [start of saleor/product/utils.py]
  1 from collections import namedtuple
  2 
  3 from ..cart.utils import get_cart_from_request, get_or_create_cart_from_request
  4 from ..core.utils import to_local_currency
  5 from .forms import get_form_class_for_product
  6 from .models.utils import get_attributes_display_map
  7 from .models import Product
  8 
  9 
  10 def products_visible_to_user(user):
  11     if (user.is_authenticated() and
  12             user.is_active and user.is_staff):
  13         return Product.objects.all()
  14     else:
  15         return Product.objects.get_available_products()
  16 
  17 
  18 def products_with_details(user):
  19     products = products_visible_to_user(user)
  20     products = products.prefetch_related('categories', 'images',
  21                                          'variants__stock',
  22                                          'variants__variant_images__image',
  23                                          'attributes__values',
  24                                          'product_class__variant_attributes__values',
  25                                          'product_class__product_attributes__values')
  26     return products
  27 
  28 
  29 def get_product_images(product):
  30     """
  31     Returns list of product images that will be placed in product gallery
  32     """
  33     return list(product.images.all())
  34 
  35 
  36 def products_with_availability(products, discounts, local_currency):
  37     for product in products:
  38         yield product, get_availability(product, discounts, local_currency)
  39 
  40 
  41 ProductAvailability = namedtuple(
  42     'ProductAvailability', (
  43         'available', 'price_range', 'price_range_undiscounted', 'discount',
  44         'price_range_local_currency', 'discount_local_currency'))
  45 
  46 
  47 def get_availability(product, discounts=None, local_currency=None):
  48     # In default currency
  49     price_range = product.get_price_range(discounts=discounts)
  50     undiscounted = product.get_price_range()
  51     if undiscounted.min_price > price_range.min_price:
  52         discount = undiscounted.min_price - price_range.min_price
  53     else:
  54         discount = None
  55 
  56     # Local currency
  57     if local_currency:
  58         price_range_local = to_local_currency(
  59             price_range, local_currency)
  60         undiscounted_local = to_local_currency(
  61             undiscounted, local_currency)
  62         if (undiscounted_local and
  63                 undiscounted_local.min_price > price_range_local.min_price):
  64             discount_local_currency = (
  65                 undiscounted_local.min_price - price_range_local.min_price)
  66         else:
  67             discount_local_currency = None
  68     else:
  69         price_range_local = None
  70         discount_local_currency = None
  71 
  72     is_available = product.is_in_stock() and product.is_available()
  73 
  74     return ProductAvailability(
  75         available=is_available,
  76         price_range=price_range,
  77         price_range_undiscounted=undiscounted,
  78         discount=discount,
  79         price_range_local_currency=price_range_local,
  80         discount_local_currency=discount_local_currency)
  81 
  82 
  83 def handle_cart_form(request, product, create_cart=False):
  84     if create_cart:
  85         cart = get_or_create_cart_from_request(request)
  86     else:
  87         cart = get_cart_from_request(request)
  88 
  89     form_class = get_form_class_for_product(product)
  90     form = form_class(cart=cart, product=product,
  91                       data=request.POST or None, discounts=request.discounts)
  92     return form, cart
  93 
  94 
  95 def products_for_cart(user):
  96     products = products_visible_to_user(user)
  97     products = products.prefetch_related(
  98         'variants', 'variants__variant_images__image')
  99     return products
  100 
  101 
  102 def get_variant_picker_data(product, discounts=None):
  103     availability = get_availability(product, discounts)
  104     variants = product.variants.all()
  105     data = {'variantAttributes': [], 'variants': []}
  106 
  107     variant_attributes = product.product_class.variant_attributes.all()
  108     for attribute in variant_attributes:
  109         data['variantAttributes'].append({
  110             'pk': attribute.pk,
  111             'display': attribute.display,
  112             'name': attribute.name,
  113             'values': [{'pk': value.pk, 'display': value.display}
  114                        for value in attribute.values.all()]})
  115 
  116     for variant in variants:
  117         price = variant.get_price_per_item(discounts)
  118         price_undiscounted = variant.get_price_per_item()
  119         variant_data = {
  120             'id': variant.id,
  121             'price': price.gross,
  122             'priceUndiscounted': price_undiscounted.gross,
  123             'currency': price.currency,
  124             'attributes': variant.attributes}
  125         data['variants'].append(variant_data)
  126 
  127     data['availability'] = {
  128         'discount': price_as_dict(availability.discount),
  129         'priceRange': price_range_as_dict(availability.price_range),
  130         'priceRangeUndiscounted': price_range_as_dict(
  131             availability.price_range_undiscounted)}
  132     return data
  133 
  134 
  135 def get_product_attributes_data(product):
  136     attributes = product.product_class.product_attributes.all()
  137     attributes_map = {attribute.pk: attribute for attribute in attributes}
  138     values_map = get_attributes_display_map(product, attributes)
  139     return {attributes_map.get(attr_pk): value_obj
  140             for (attr_pk, value_obj) in values_map.items()}
  141 
  142 
  143 def price_as_dict(price):
  144     if not price:
  145         return {}
  146     return {'currency': price.currency,
  147             'gross': price.gross,
  148             'net': price.net}
  149 
  150 
  151 def price_range_as_dict(price_range):
  152     return {'maxPrice': price_as_dict(price_range.max_price),
  153             'minPrice': price_as_dict(price_range.min_price)}
  154
  [end of saleor/product/utils.py]
  </code>
  
  I need you to solve this issue by generating a single patch file that I can apply directly to this repository using git apply. Please respond with a single patch file in the following format.
  <patch>
  diff --git a/file.py b/file.py
  --- a/file.py
  +++ b/file.py
  @@ -1,27 +1,35 @@
   def euclidean(a, b):
  -    while b:
  -        a, b = b, a % b
  -    return a
  +    if b == 0:
  +        return a
  +    return euclidean(b, a % b)
   
   
   def bresenham(x0, y0, x1, y1):
       points = []
       dx = abs(x1 - x0)
       dy = abs(y1 - y0)
  -    sx = 1 if x0 < x1 else -1
  -    sy = 1 if y0 < y1 else -1
  -    err = dx - dy
  +    x, y = x0, y0
  +    sx = -1 if x0 > x1 else 1
  +    sy = -1 if y0 > y1 else 1
   
  -    while True:
  -        points.append((x0, y0))
  -        if x0 == x1 and y0 == y1:
  -            break
  -        e2 = 2 * err
  -        if e2 > -dy:
  -            err -= dy
  -            x0 += sx
  -        if e2 < dx:
  -            err += dx
  -            y0 += sy
  +    if dx > dy:
  +        err = dx / 2.0
  +        while x != x1:
  +            points.append((x, y))
  +            err -= dy
  +            if err < 0:
  +                y += sy
  +                err += dx
  +            x += sx
  +    else:
  +        err = dy / 2.0
  +        while y != y1:
  +            points.append((x, y))
  +            err -= dx
  +            if err < 0:
  +                x += sx
  +                err += dy
  +            y += sy
  +
  +    points.append((x, y))
       return points
  
  </patch>
golden_diff: diff --git a/saleor/product/utils.py b/saleor/product/utils.py
  --- a/saleor/product/utils.py
  +++ b/saleor/product/utils.py
  @@ -45,6 +45,15 @@
   
   
   def get_availability(product, discounts=None, local_currency=None):
  +    if not product.variants.exists():
  +        return ProductAvailability(
  +            available=False,
  +            price_range=None,
  +            price_range_undiscounted=None,
  +            discount=None,
  +            price_range_local_currency=None,
  +            discount_local_currency=None)
  +
       # In default currency
       price_range = product.get_price_range(discounts=discounts)
       undiscounted = product.get_price_range()
  @@ -142,12 +151,14 @@
   
   def price_as_dict(price):
       if not price:
  -        return {}
  +        return None
       return {'currency': price.currency,
               'gross': price.gross,
               'net': price.net}
   
   
   def price_range_as_dict(price_range):
  +    if not price_range:
  +        return None
       return {'maxPrice': price_as_dict(price_range.max_price),
               'minPrice': price_as_dict(price_range.min_price)}
  
verification_info: {"golden_diff": "diff --git a/saleor/product/utils.py b/saleor/product/utils.py\n--- a/saleor/product/utils.py\n+++ b/saleor/product/utils.py\n@@ -45,6 +45,15 @@\n \n \n def get_availability(product, discounts=None, local_currency=None):\n+    if not product.variants.exists():\n+        return ProductAvailability(\n+            available=False,\n+            price_range=None,\n+            price_range_undiscounted=None,\n+            discount=None,\n+            price_range_local_currency=None,\n+            discount_local_currency=None)\n+\n     # In default currency\n     price_range = product.get_price_range(discounts=discounts)\n     undiscounted = product.get_price_range()\n@@ -142,12 +151,14 @@\n \n def price_as_dict(price):\n     if not price:\n-        return {}\n+        return None\n     return {'currency': price.currency,\n             'gross': price.gross,\n             'net': price.net}\n \n \n def price_range_as_dict(price_range):\n+    if not price_range:\n+        return None\n     return {'maxPrice': price_as_dict(price_range.max_price),\n             'minPrice': price_as_dict(price_range.min_price)}\n", "issue": "Calling get_price_range() on an empty item range\nOn the product detail page backend doesn't check existence of product variants.\r\nIf the product requires variants, but they don't exist, Django throws an exception `Calling get_price_range() on an empty item range`.\n", "before_files": [{"content": "from collections import namedtuple\n\nfrom ..cart.utils import get_cart_from_request, get_or_create_cart_from_request\nfrom ..core.utils import to_local_currency\nfrom .forms import get_form_class_for_product\nfrom .models.utils import get_attributes_display_map\nfrom .models import Product\n\n\ndef products_visible_to_user(user):\n    if (user.is_authenticated() and\n            user.is_active and user.is_staff):\n        return Product.objects.all()\n    else:\n        return Product.objects.get_available_products()\n\n\ndef products_with_details(user):\n    products = products_visible_to_user(user)\n    products = products.prefetch_related('categories', 'images',\n                                         'variants__stock',\n                                         'variants__variant_images__image',\n                                         'attributes__values',\n                                         'product_class__variant_attributes__values',\n                                         'product_class__product_attributes__values')\n    return products\n\n\ndef get_product_images(product):\n    \"\"\"\n    Returns list of product images that will be placed in product gallery\n    \"\"\"\n    return list(product.images.all())\n\n\ndef products_with_availability(products, discounts, local_currency):\n    for product in products:\n        yield product, get_availability(product, discounts, local_currency)\n\n\nProductAvailability = namedtuple(\n    'ProductAvailability', (\n        'available', 'price_range', 'price_range_undiscounted', 'discount',\n        'price_range_local_currency', 'discount_local_currency'))\n\n\ndef get_availability(product, discounts=None, local_currency=None):\n    # In default currency\n    price_range = product.get_price_range(discounts=discounts)\n    undiscounted = product.get_price_range()\n    if undiscounted.min_price > price_range.min_price:\n        discount = undiscounted.min_price - price_range.min_price\n    else:\n        discount = None\n\n    # Local currency\n    if local_currency:\n        price_range_local = to_local_currency(\n            price_range, local_currency)\n        undiscounted_local = to_local_currency(\n            undiscounted, local_currency)\n        if (undiscounted_local and\n                undiscounted_local.min_price > price_range_local.min_price):\n            discount_local_currency = (\n                undiscounted_local.min_price - price_range_local.min_price)\n        else:\n            discount_local_currency = None\n    else:\n        price_range_local = None\n        discount_local_currency = None\n\n    is_available = product.is_in_stock() and product.is_available()\n\n    return ProductAvailability(\n        available=is_available,\n        price_range=price_range,\n        price_range_undiscounted=undiscounted,\n        discount=discount,\n        price_range_local_currency=price_range_local,\n        discount_local_currency=discount_local_currency)\n\n\ndef handle_cart_form(request, product, create_cart=False):\n    if create_cart:\n        cart = get_or_create_cart_from_request(request)\n    else:\n        cart = get_cart_from_request(request)\n\n    form_class = get_form_class_for_product(product)\n    form = form_class(cart=cart, product=product,\n                      data=request.POST or None, discounts=request.discounts)\n    return form, cart\n\n\ndef products_for_cart(user):\n    products = products_visible_to_user(user)\n    products = products.prefetch_related(\n        'variants', 'variants__variant_images__image')\n    return products\n\n\ndef get_variant_picker_data(product, discounts=None):\n    availability = get_availability(product, discounts)\n    variants = product.variants.all()\n    data = {'variantAttributes': [], 'variants': []}\n\n    variant_attributes = product.product_class.variant_attributes.all()\n    for attribute in variant_attributes:\n        data['variantAttributes'].append({\n            'pk': attribute.pk,\n            'display': attribute.display,\n            'name': attribute.name,\n            'values': [{'pk': value.pk, 'display': value.display}\n                       for value in attribute.values.all()]})\n\n    for variant in variants:\n        price = variant.get_price_per_item(discounts)\n        price_undiscounted = variant.get_price_per_item()\n        variant_data = {\n            'id': variant.id,\n            'price': price.gross,\n            'priceUndiscounted': price_undiscounted.gross,\n            'currency': price.currency,\n            'attributes': variant.attributes}\n        data['variants'].append(variant_data)\n\n    data['availability'] = {\n        'discount': price_as_dict(availability.discount),\n        'priceRange': price_range_as_dict(availability.price_range),\n        'priceRangeUndiscounted': price_range_as_dict(\n            availability.price_range_undiscounted)}\n    return data\n\n\ndef get_product_attributes_data(product):\n    attributes = product.product_class.product_attributes.all()\n    attributes_map = {attribute.pk: attribute for attribute in attributes}\n    values_map = get_attributes_display_map(product, attributes)\n    return {attributes_map.get(attr_pk): value_obj\n            for (attr_pk, value_obj) in values_map.items()}\n\n\ndef price_as_dict(price):\n    if not price:\n        return {}\n    return {'currency': price.currency,\n            'gross': price.gross,\n            'net': price.net}\n\n\ndef price_range_as_dict(price_range):\n    return {'maxPrice': price_as_dict(price_range.max_price),\n            'minPrice': price_as_dict(price_range.min_price)}\n", "path": "saleor/product/utils.py"}]}