I get a 'NoneType' object is not iterable error when I add 1 object to the cart via the scan_to_cart view and want to add a second object. Also I get the same error when I want to view my cart when there are actually objects in it. I could not find a common problem with a solution... Is the python version I work with the issue, or is there a logic or code error?
Thanks in advance for suggestions/advice!
models:
from manageinv.models import Child
User = settings.AUTH_USER_MODEL
class CartManager(models.Manager):
def new_or_get(self, request):
cart_id = request.session.get("cart_id", None)
qs = self.get_queryset().filter(id=cart_id)
if qs.count() == 1:
new_obj = False
cart_obj = qs.first()
if request.user.is_authenticated() and cart_obj.user is None:
cart_obj.user = request.user
cart_obj.save()
else:
cart_obj = Cart.objects.new(user=request.user)
new_obj = True
request.session['cart_id'] = cart_obj.id
return cart_obj, new_obj
def new(self, user=None):
user_obj = None
if user is not None:
if user.is_authenticated():
user_obj = user
return self.model.objects.create(user=user_obj)
class Cart(models.Model):
user = models.ForeignKey(User, null=True, blank=True)
products = models.ManyToManyField(Child, blank=True)
subtotal = models.DecimalField(default=0.00, max_digits=100, decimal_places=2)
total = models.DecimalField(default=0.00, max_digits=100, decimal_places=2)
updated = models.DateTimeField(auto_now=True)
timestamp = models.DateTimeField(auto_now_add=True)
objects = CartManager()
def __str__(self):
return str(self.id)
def m2m_changed_cart_receiver(sender, instance, action, *args, **kwargs):
if action == 'post_add' or action == 'post_remove' or action == 'post_clear':
products = instance.products.all()
total = 0
for x in products:
total += x.retail_price
if instance.subtotal != total:
instance.subtotal = total
instance.save()
m2m_changed.connect(m2m_changed_cart_receiver, sender=Cart.products.through)
def pre_save_cart_receiver(sender, instance, *args, **kwargs):
instance.total = instance.subtotal
pre_save.connect(pre_save_cart_receiver, sender=Cart)
views:
def cart_home(request):
cart_obj, new_obj = Cart.objects.new_or_get(request)
products = cart_obj.products.all()
return render(request, 'stockscan/scan_session.html', {"cart":
cart_obj})
def scan_to_cart(request):
form = forms.ScanSessionForm()
if request.method == 'POST':
product = None
barcode = request.POST.get('barcode_input')
queryset = Child.objects.filter(product_id_code=barcode)
if queryset.exists():
try:
# the queryset is already filtered by the barcode
# now we apply an extra filter to check if this user has the product
product = queryset.get(user=request.user)
except Child.DoesNotExist:
# here we are sure this product exists, but this user doesnt have it in the stock.
messages.error(request, 'I can\'t find any inventory with this barcode')
else:
# here we know this product doesnt exist
messages.error(request, 'I can\'t find any inventory with this barcode')
if product is not None:
form = forms.ScanSessionForm(request.POST, instance=product)
if form.is_valid():
#####
#ADD TO CART
print(product.id)
product_obj = product.id
cart_obj, new_obj = Cart.objects.new_or_get(request)
products = cart_obj.products.all()
cart_obj.products.add(product_obj)
#####
messages.success(request, '%s - %s was successfully added to cart' % (product.product_name, product.sku))
return HttpResponseRedirect('/scan/stock/')
else:
form = forms.ScanSessionForm()
return render(request, 'stockscan/scan_to_cart.html', {'form': form})
template:
{% if cart.products.exists %}
<table class="table">
<thead>
<tr>
<th scope="col">#</th>
<th scope="col">Product Name</th>
<th scope="col">Product Price</th>
</tr>
</thead>
<tbody>
{% for product in cart.products.all %}
<tr>
<th scope="row">{{ forloop.counter }}</th>
<td>{{ product.product_name }} <small><a href="#">Remove</a></small></td>
<td>{{ product.retail_price }}</td>
</tr>
{% endfor %}
<tr>
<td colspan="2"></td>
<td><b>Subtotal</b> {{ cart.subtotal }}</td>
</tr>
<tr>
<td colspan="2"></td>
<td><b>Total</b> {{ cart.total }}</td>
</tr>
</tbody>
</table>
{% else %}
Cart is empty
<p>
{% endif %}
Error: TypeError at /scan/stock/
'NoneType' object is not iterable
Request Method: POST
Request URL: http://localhost:8000/scan/stock/
Django Version: 1.11
Exception Type: TypeError
Exception Value:
'NoneType' object is not iterable
Exception Location: /Users/sp_env/stockpilot/src/stockscan/views.py in scan_to_cart, line 41
Python Executable: /Users/sp_env/bin/python
Traceback:
Environment:
Request Method: POST
Request URL: http://localhost:8000/scan/stock/
Django Version: 1.11
Python Version: 2.7.10
Installed Applications:
['django.contrib.admin',
'django.contrib.auth',
'django.contrib.contenttypes',
'django.contrib.sessions',
'django.contrib.messages',
'django.contrib.staticfiles',
'mathfilters',
'bootstrapform',
'colorfield',
'gunicorn',
'crispy_forms',
'storages',
'manageinv',
'categories',
'stockscan',
'orderstock',
'accounts']
Installed Middleware:
['django.middleware.security.SecurityMiddleware',
'whitenoise.middleware.WhiteNoiseMiddleware',
'django.contrib.sessions.middleware.SessionMiddleware',
'django.middleware.common.CommonMiddleware',
'django.middleware.csrf.CsrfViewMiddleware',
'django.contrib.auth.middleware.AuthenticationMiddleware',
'django.contrib.messages.middleware.MessageMiddleware',
'django.middleware.clickjacking.XFrameOptionsMiddleware']
Traceback:
File "/Users/sp_env/lib/python2.7/site-packages/django/core/handlers/exception.py" in inner
41. response = get_response(request)
File "/Users/sp_env/lib/python2.7/site-packages/django/core/handlers/base.py" in _get_response
187. response = self.process_exception_by_middleware(e, request)
File "/Users/sp_env/lib/python2.7/site-packages/django/core/handlers/base.py" in _get_response
185. response = wrapped_callback(request, *callback_args, **callback_kwargs)
File "/Users/sp_env/stockpilot/src/stockscan/views.py" in scan_to_cart
41. cart_obj, new_obj = Cart.objects.new_or_get(request)
Exception Type: TypeError at /scan/stock/
Exception Value: 'NoneType' object is not iterable
In Cart.new_or_get, here:
if a cart exists you do not return anything, so the function ends up returning
None
, which is indeed not iterable, so when trying to unpack the result in your view here:you get this exception.
TL;DR: you should unindent the return statement so it executed for both branches.
This being said, your
Cart.new_or_get
method is plain wrong - model code should not depend on the request, session etc. All this code should live either in your view (if that's the only place it's used) or as an utility function (or in an utility class acting as proxy over the model).