Django add to cart and cart view error

2020-05-09 06:36发布

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

1条回答
家丑人穷心不美
2楼-- · 2020-05-09 07:13

In Cart.new_or_get, here:

if qs.count() == 1:
   # ...
else:
   # ...
   return cart_obj, new_obj

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:

 cart_obj, new_obj = Cart.objects.new_or_get(request)

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).

查看更多
登录 后发表回答