Django Error: объект 'dict' не имеет атрибута 'availability'

У меня есть представление корзины, где я пытаюсь проверить, есть ли у товаров, добавленных в корзину, хотя бы один элемент в списке, у которого product.availability установлен в False и работать соответственно в Template, проблема заключается в доступе к доступности продукта в списке объектов корзины, Итак, как мне проверить доступность продуктов, которые люди добавили в корзину?

P.S Я сократил код для утилиты, я добавлю больше, если это необходимо для понимания

Модель

class Product(models.Model):
    availability = models.BooleanField()

Utils

def cookieCart(request):
    try:
        cart = json.loads(request.COOKIES['cart'])
    except:
        cart = {}
    items = []
    for i in cart:
        try:
            product = Product.objects.get(id=i)
            item = {
                'product':{
                    'id':product.id,
                    'name':product.name,
                    'final_price':product.final_price,
                    'image_URL':product.image_URL,
                    'availability':product.availability,
                },
                }
            items.append(item)
        except:
            pass

    return {"items": items}

def cartData(request):
    if request.user.is_authenticated:
        customer = request.user.customer
        order, created = Order.objects.get_or_create(customer=customer, complete=False)
        items = order.orderitem_set.all()
    else:
        cookieData = cookieCart(request)
        items = cookieData['items']
    return {'items':items}

Виды

def cart(request):
    data = cartData(request)
    #products added to cart
    items = data['items']

    #Checking if even one product added to cart has availability set to False
    available = all(x.availability for x in items)

    context = {'items': items, 'available': available}

Шаблон

<p>{{items.product.name}}</p>
{% if available %}
    <a href="#">Checkout</a>
{% else %}
    <p>Out of stock</p>
{% endif %}

Traceback

Traceback (most recent call last):
  File "D:\test\lib\site-packages\django\core\handlers\exception.py", line 47, in inner
    response = get_response(request)
  File "D:\test\lib\site-packages\django\core\handlers\base.py", line 181, in _get_response
    response = wrapped_callback(request, *callback_args, **callback_kwargs)
  File "D:\test\shop\views.py", line 101, in cart
    available = all(x.availability for x in items)
  File "D:\test\shop\views.py", line 101, in <genexpr>
    available = all(x.availability for x in items)

Exception Type: AttributeError at /shop/cart
Exception Value: 'dict' object has no attribute 'availability'

Все очень просто, в функции cookieCart вы добавляете к списку товаров словарь, а не объект Product.

Изменить на:

def cookieCart(request):
    try:
        cart = json.loads(request.COOKIES['cart'])
    except:
        cart = {}
    items = []
    for i in cart:
        try:
            product = Product.objects.get(id=i)            
            items.append(product)
        except:
            pass

    return {"items": items}

Вместо того, чтобы создавать ad-hoc dict, который действительно имеет необходимый availability атрибут, вы должны просто вернуть QuerySet из Product объектов после фильтрации товаров по ID:

def cookieCart(request):
    try:
        cart = json.loads(request.COOKIES['cart'])
    except:
        cart = {}
    return {"items": Product.objects.filter(id__in=cart)}

Эта часть создает проблему

def cookieCart(request):
    try:
        cart = json.loads(request.COOKIES['cart'])
    except:
        cart = {}
    items = []
    for i in cart:
        try:
            product = Product.objects.get(id=i)
            item = {
                'product':{
                    'id':product.id,
                    'name':product.name,
                    'final_price':product.final_price,
                    'image_URL':product.image_URL,
                    'availability':product.availability,
                },
                }
            items.append(item)
        except:
            pass

    return {"items": items} # here your items is like this {"items": [{'product':{'id':product.id,'name':product.name,'final_price':product.final_price,'image_URL':product.image_URL,'availability':product.availability}}]}

Поскольку ошибка предполагает, что вы выбрали элемент из COOKIES

Теперь, поскольку вы выполняете цикл all(x.availability for x in items) x будет выглядеть как {price: {...}}, который является объектом dict, поэтому он говорит, что объект dict не имеет доступности атрибутов

Для решения этой проблемы используйте SimpleNameSpace следующим образом

from types import SimpleNamespace

def cookieCart(request):
    try:
        cart = json.loads(request.COOKIES['cart'])
    except:
        cart = {}
    items = []
    for i in cart:
        try:
            product = Product.objects.get(id=i)
            item = SimpleNamespace(**{
                    'id':product.id,
                    'name':product.name,
                    'final_price':product.final_price,
                    'image_URL':product.image_URL,
                    'availability':product.availability,
                })
            items.append(item)
        except:
            pass

    return {"items": items}

Для оптимизированного способа вы можете проверить решение @blhsing

Ошибка говорит о том, что вы пытаетесь получить доступ к элементу dict как к атрибуту (x.y вместо x['y']).

Это должно быть:

available = all(x['availability'] for x in items)

При этом, как упоминал @blhsing, лучше делать запросы в одном наборе запросов

products = Product.objects.filter(pk__in=cart)
items = [
    {
        'product': p,
        'quantity': cart[p.pk],
        'get_total': p.final_price * cart[p.pk],
    }
    for p in products
}
available = all(x['product'].availability for x in items)
# or 
# available = all(p.availability for p in products)
# or
# available = not products.filter(availability=False).exists()
return {'items': items, 'available': available}

Кроме того, вы должны убедиться, что в обоих случаях вашего if/else в cartData(), items установлена одна и та же структура данных. В данном случае это не так, поскольку в if, items является QuerySet, тогда как в else, items является списком словарей.

Вернуться на верх