简体   繁体   中英

Django - Object of type Decimal is not JSON serializable

I got an error:

TypeError at /cart/
Object of type Decimal is not JSON serializable
Request Method: GET
Request URL:    http://127.0.0.1:8000/cart/
Django Version: 4.0.4
Exception Type: TypeError
Exception Value:    
Object of type Decimal is not JSON serializable

The fact is that I store my favorites and the basket in the same session, and when one of these is not filled (favorites / basket), this error appears. When the products are in both lists everything is fine.

allow_nan   
    True
    check_circular  
    True
    cls 
    <class 'json.encoder.JSONEncoder'>
    default 
    None
    ensure_ascii    
    True
    indent  
    None
    kw  
    {}
    obj 
    {'cart': {'2': {'price': Decimal('123123.12'),
                    'product': <Product: test1>,
                    'quantity': 2,
                    'total_price': Decimal('246246.24'),
                    'update_quantity_form': <CartAddProductForm bound=False, valid=Unknown, fields=(quantity;update)>}},
     'favorites': {}}
    separators  
    (',', ':')
    skipkeys    
    False
    sort_keys   
    False

If I add product in favorites and remove items from the cart, I already get this error

TypeError at /favorites/
Object of type Product is not JSON serializable
Request Method: GET
Request URL:    http://127.0.0.1:8000/favorites/
Django Version: 4.0.4
Exception Type: TypeError
Exception Value:    
Object of type Product is not JSON serializable
Exception Location: E:\Python\lib\json\encoder.py, line 179, in default

There is the cart code:

from decimal import Decimal
from django.conf import settings
from main.models import Product


class Cart(object):

    def __init__(self, request):

        self.session = request.session
        cart = self.session.get(settings.CART_SESSION_ID)
        if not cart:
            cart = self.session[settings.CART_SESSION_ID] = {}
        self.cart = cart

    def __iter__(self):

        product_ids = self.cart.keys()
        products = Product.objects.filter(id__in=product_ids)

        cart = self.cart.copy()
        for product in products:
            cart[str(product.id)]['product'] = product

        for item in cart.values():
            item['price'] = Decimal(item['price'])
            item['total_price'] = item['price'] * item['quantity']
            yield item

    def __len__(self):

        return sum(item['quantity'] for item in self.cart.values())

    def add(self, product, quantity=1, update_quantity=False):

        product_id = str(product.id)
        if product_id not in self.cart:
            self.cart[product_id] = {'quantity': 0,
                                     'price': str(product.price)}
        if update_quantity:
            self.cart[product_id]['quantity'] = quantity
        else:
            self.cart[product_id]['quantity'] += quantity
        if quantity == 0:
            del self.cart[product_id]
        self.save()

    def save(self):
        self.session.modified = True

    def remove(self, product):

        product_id = str(product.id)
        if product_id in self.cart:
            del self.cart[product_id]
            self.save()

    def get_total_price(self):

        return sum(Decimal(item['price']) * item['quantity'] for item in self.cart.values())

    def clear(self):

        del self.session[settings.CART_SESSION_ID]
        self.save()

cart/views.py:

import datetime
from django.shortcuts import render, redirect, get_object_or_404
from django.views.decorators.http import require_POST
from main.models import Product, OrderItems, Order
from .cart import Cart
from .forms import CartAddProductForm, OrderForm


@require_POST
def cart_add(request, product_id):
    cart = Cart(request)
    product = get_object_or_404(Product, id=product_id)
    form = CartAddProductForm(request.POST)
    cd = form.cleaned_data
    cart.add(product=product,
             quantity=1,
             update_quantity=cd['update'])
    return redirect('cart:cart_detail')


def cart_remove(request, product_id):
    cart = Cart(request)
    product = get_object_or_404(Product, id=product_id)
    cart.remove(product)
    return redirect('cart:cart_detail')


def cart_detail(request):
    form = OrderForm()
    cart = Cart(request)
    for item in cart:
        item['update_quantity_form'] = CartAddProductForm(initial={'quantity': item['quantity'],
                                                                   'update': True})
    if request.method == 'POST':
        data = Order(data_time=datetime.date.today(),
                     user=request.user.username,
                     total_cost=cart.get_total_price())
        form = OrderForm(request.POST, instance=data)
        if form.is_valid():
            order = form.save()
            data.save()
            for item in cart:
                OrderItems.objects.create(Order=order,
                                          Product=item['product'],
                                          Price=item['price'],
                                          Quantity=item['quantity'])
            cart.clear()
            return redirect('home')
    return render(request, 'main/cart.html', {'cart': cart, 'form': form})
 

cart/urls.py:

from django.urls import path
from . import views

app_name = 'cart'

urlpatterns = [
    path('', views.cart_detail, name='cart_detail'),
    path('add/<int:product_id>/',
         views.cart_add,
         name='cart_add'),
    path('remove/<int:product_id>/',
         views.cart_remove,
         name='cart_remove'),
]

Favorites are same, but with no quantity and price:

from django.conf import settings
from main.models import Product


class Favorites(object):

    def __init__(self, request):

        self.session = request.session
        favorites = self.session.get(settings.FAVORITES_SESSION_ID)
        if not favorites:
            favorites = self.session[settings.FAVORITES_SESSION_ID] = {}
        self.favorites = favorites

    def __len__(self):
        Fsum = 0
        for item in self.favorites.values():
            Fsum += 1
        return Fsum

    def __iter__(self):
        product_ids = self.favorites.keys()
        products = Product.objects.filter(id__in=product_ids)
        favorites = self.favorites.copy()
        for product in products:
            favorites[str(product.id)]['product'] = product
        for item in favorites.values():
            yield item

    def remove(self, product):
        product_id = str(product.id)
        if product_id in self.favorites:
            del self.favorites[product_id]
            self.save()

    def add(self, product):
        product_id = str(product.id)
        if product_id not in self.favorites:
            self.favorites[product_id] = {}
        else:
            del self.favorites[product_id]
        self.save()

    def save(self):
        self.session.modified = True

    def clear(self):
        del self.session[settings.FAVORITES_SESSION_ID]
        self.save()

views.py and urls.py are the same as in the cart

I also used different SESSION_ID for each

And i have context_processors.py:

from .cart import Cart


def cart(request):
    return {'cart': Cart(request)}

favorites context processors are the same

This is how I display them:

  <div class="catalog__wrap">
    <div class="catalog__items items" itemscope="" itemtype="https://schema.org/ItemList">
        {% for item in featured %}
            {% with product=item.product %}
                <div class="item" itemprop="itemListElement" itemscope="" itemtype="https://schema.org/Product" data-element-id="21977">
                    <img class="item__img" itemprop="image" src="{{ product.multiple_images.first.images.url}}" alt="{{ product.name }}">
                    <div class="item__info">
                        <h3 class="item__name">{{ product.name }}</h3>
                            <div class="item__offers" itemprop="offers" itemscope="" itemtype="https://schema.org/Offer">
                                <p class="item__price item__price">
                                Опт <span class="item__sum" itemprop="price">{{ product.price }} ₽</span>
                                </p>
                                <p class="item__price item__price--red">
                                Розница <span class="item__sum" itemprop="price">{{ product.price }} ₽</span>
                                </p>
                            </div>
                        <div class="item__buttons">
                        <form action="{% url 'cart:cart_add' product.id %}" method="post">
                        <button class="item__buy" type="submit" data-modal="sizes" style="width: 95px; height: 40px">В корзину</button>
                        {% csrf_token %}
                        </form>
                        </button>
                        </div>
                    </div>
                    <a class="item__link" itemprop="url" href="{% url 'product_page' product.cat.slug product.slug %}" title="{{ product.name }}"></a>
                </div>
            {% endwith %}
        {% endfor %}
    </div>

How can i fix it?

use

return render(request, 'main/cart.html', {'cart': json.dumps(cart,cls=DjangoJsonEncoder), 'form': form})

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM