Django add to cart and cart view error

霸气de小男生 提交于 2019-12-31 07:22:09

问题


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:


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



来源:https://stackoverflow.com/questions/51307807/django-add-to-cart-and-cart-view-error

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!