Django Error: 'dict' object has no attribute 'availability'

Question:

I have a cart view where I’m trying to check if the products added to the cart have even one item in the list that has product.availability set to False and work accordingly in Template, the problem is with accessing the product availability in cart object list, So how do I check the availability of products that people added to cart?

P.S I shortened the code for utils, I’ll add more if needed for understanding

Model

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,
                },
                    'quantity':cart[i]["quantity"],
                    'get_total':total,
                }
            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}

Views

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}

Template

<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:testlibsite-packagesdjangocorehandlersexception.py", line 47, in inner
    response = get_response(request)
  File "D:testlibsite-packagesdjangocorehandlersbase.py", line 181, in _get_response
    response = wrapped_callback(request, *callback_args, **callback_kwargs)
  File "D:testshopviews.py", line 101, in cart
    available = all(x.availability for x in items)
  File "D:testshopviews.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'
Asked By: betty_

||

Answers:

Its quite simple, in the cookieCart function you are appending to the items list a dictionary, not a Product object.

Change to:

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}
Answered By: walter zeni

Instead of creating an ad-hoc dict that does have the necessary availability attribute, you should simply return a QuerySet of Product objects after filtering products by IDs:

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

This part is creating issue

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}}]}

Since error is suggesting that you picked item from COOKIES

Now since you are looping as all(x.availability for x in items)
x will look like {price: {...}} which is dict object thatswhy it is saying that dict object has no attribute availability

For resolving this issue use SimpleNameSpace like this

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}

For optimised way
you can check @blhsing solution

Answered By: Deepak Tripathi

What the error tells you is that you are trying to access a dict item as an attribute (x.y instead of x['y']). Also, you missed one level of depth (product).

This should be:

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

That being said, as mentionned by @blhsing, you should rather do your queries in a single queryset

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}

Additionally, you have to make sure that in both cases of your if/else in cartData(), items has a consistent data structure. Which is not the case here, as in the if, items is a QuerySet whereas in the else, items is a list of dictionaries.

Answered By: Antoine Pinsard
Categories: questions Tags: ,
Answers are sorted by their score. The answer accepted by the question owner as the best is marked with
at the top-right corner.