-2

I am coding along with denis ivy to build an e-commerce website, now when i checkout the order get's completed as it is supposed to do but then when i return to the homepage i get this error MultipleObjectsReturned at / get() returned more than one Order -- it returned 2! and then once i delete all the orders i have in my database everything works fine until i checkout again, which means that i ave problem which is i can't have two orders in my database i really can't figure this out any help is much appreciated i'll share my views.py now

from django.shortcuts import render
from .models import *
from django.http import JsonResponse
import json
import datetime

def store(request):    
    if request.user.is_authenticated:
        customer = request.user.customer
        order, created = Order.objects.get_or_create(customer=customer)
        items = order.orderitem_set.all()
        cartItems = order.get_cart_items
    else:
        items = []
        order = {
            'get_cart_total':0,
            'get_cart_items': 0,
            'shipping': False
        }
        cartItems = order['get_cart_items']
    products = Product.objects.all()
    context= {'products': products, 'cartItems': cartItems, }
    return render(request, 'store/store.html', context)

def cart(request):
    if request.user.is_authenticated:
        customer = request.user.customer
        order, created = Order.objects.get_or_create(customer=customer)
        items = order.orderitem_set.all()
        cartItems = order.get_cart_items
    else:
        items = []
        order = {
            'get_cart_total':0,
            'get_cart_items': 0,
            'shipping': False
        }
        cartItems = order['get_cart_items']

    context= {'items':items, 'order':order, 'cartItems': cartItems}
    return render(request, 'store/cart.html', context)

def checkout(request):    
    if request.user.is_authenticated:
        customer = request.user.customer
        order, created = Order.objects.get_or_create(customer=customer)
        items = order.orderitem_set.all()
        cartItems = order.get_cart_items
    else:
        items = []
        order = {
            'get_cart_total':0,
            'get_cart_items': 0,
            'shipping': False
        }
        cartItems = order['get_cart_items']

    context= {'items':items, 'order':order, 'cartItems': cartItems}
    return render(request, 'store/checkout.html', context)

def updateItem(request):
    data = json.loads(request.body)
    productId = data['productId']
    action = data['action']
    print('Action:', action)
    print('Product:', productId)

    customer = request.user.customer
    product = Product.objects.get(id=productId)
    order, created = Order.objects.get_or_create(customer=customer, complete=False)

    #orderItem, created = OrderItem.objects.get_or_create(order=order, product=product)

    if action == 'add':
        orderItem, created = OrderItem.objects.get_or_create(order=order, product=product)
        orderItem.quantity = (orderItem.quantity + 1)
    elif action == 'remove':
        orderItem = OrderItem.objects.filter(order=order, product=product)
        orderItem.quantity = (orderItem.quantity - 1)
        
    orderItem.save()

    if orderItem.quantity <= 0:
        orderItem.delete()

    return JsonResponse('Item was added', safe=False)

def processOrder(request):
    transaction_id = datetime.datetime.now().timestamp()
    data = json.loads(request.body)

    if request.user.is_authenticated:
        customer = request.user.customer
        order, created = Order.objects.get_or_create(customer=customer, complete=False)
        total = float(data['form']['total'])
        order.transaction_id = transaction_id

        if total == float(order.get_cart_total):
            order.complete = True 
        order.save()

        if order.shipping == True:
            ShippingAddress.objects.create(
                customer=customer,
                order=order,
                address=data['shipping']['address'],
                city=data['shipping']['city'],
                state=data['shipping']['state'],
                zipcode=data['shipping']['zipcode'],

            )

    else:
        print('User is not logged in ')

    return JsonResponse('Payment Complete!', safe=False)

my models.py

from django.db import models
from django.contrib.auth.models import User

class Customer(models.Model):
    user = models.OneToOneField(User, null=True, blank=True, on_delete=models.CASCADE)
    name = models.CharField(max_length=200,null=True)
    email = models.CharField(max_length=200, null=True)

    def __str__(self):
        return self.name

class Product(models.Model):
    name = models.CharField(max_length=200)
    price = models.DecimalField(max_digits=10, decimal_places=2)
    digital = models.BooleanField(default=False, null=True, blank=True)
    image = models.ImageField(null=True, blank=True)

    def __str__(self):
        return self.name

    @property
    def imageURL(self):
        try:
            url = self.image.url
        except:
            url = ''
        return url

class Order(models.Model):
    customer = models.ForeignKey(Customer, on_delete=models.SET_NULL, null=True, blank=True)
    date_ordered = models.DateTimeField(auto_now_add=True)
    complete = models.BooleanField(default=False)
    transaction_id = models.CharField(max_length=100, null=True)

    def __str__(self):
        return str(self.id)

    @property 
    def get_cart_total(self):
        orderitems = self.orderitem_set.all()
        total = sum([item.get_total for item in orderitems])
        return total

    @property 
    def get_cart_items(self):
        orderitems = self.orderitem_set.all()
        total = sum([item.quantity for item in orderitems ])
        return total

    @property
    def shipping(self):
        shipping = False
        orderitems = self.orderitem_set.all()
        for i in orderitems:
            if i.product.digital == False:
                shipping = True
        return shipping

class OrderItem(models.Model):
    product = models.ForeignKey(Product, on_delete=models.SET_NULL, null=True)
    order = models.ForeignKey(Order, on_delete=models.SET_NULL, null=True)
    quantity = models.IntegerField(default=0, null=True, blank=True)
    date_added = models.DateTimeField(auto_now_add=True)

    def __str__(self):
        return self.product.name

    @property
    def get_total(self):
        total = self.product.price * self.quantity 
        return total

class ShippingAddress(models.Model):
    customer = models.ForeignKey(Customer, on_delete=models.SET_NULL, null=True)
    order = models.ForeignKey(Order, on_delete=models.SET_NULL, null=True)
    address = models.CharField(max_length=200,null=False)
    city = models.CharField(max_length=200, null=False)
    state = models.CharField(max_length=200, null=False)
    zipcode = models.CharField(max_length=200, null=False)
    date_added = models.DateTimeField(auto_now_add=True)

    def __str__(self):
        return self.address

my checkout.html where much javascript goes

{% extends 'store/main.html' %} {% load static %} {% block content %}
<div class="row">
    <div class="col-lg-6">
        <div class="box-element" id="form-wrapper">
            <form id="form">
                {% csrf_token %}
                <div id="user-info">
                    <div class="form-field">
                        <input required class="form-control" type="text" name="name" placeholder="Name..">
                    </div>
                    <div class="form-field">
                        <input required class="form-control" type="email" name="email" placeholder="Email..">
                    </div>
                </div>

                <div id="shipping-info">
                    <hr>
                    <p>Shipping Information:</p>
                    <hr>
                    <div class="form-field">
                        <input class="form-control" type="text" name="address" placeholder="Address..">
                    </div>
                    <div class="form-field">
                        <input class="form-control" type="text" name="city" placeholder="City..">
                    </div>
                    <div class="form-field">
                        <input class="form-control" type="text" name="state" placeholder="State..">
                    </div>
                    <div class="form-field">
                        <input class="form-control" type="text" name="zipcode" placeholder="Zip code..">
                    </div>
                    <div class="form-field">
                        <input class="form-control" type="text" name="country" placeholder="Zip code..">
                    </div>
                </div>

                <hr>
                <input id="form-button" class="btn btn-success btn-block" type="submit" value="Continue">
            </form>
        </div>

        <br>
        <div class="box-element hidden" id="payment-info">
            <small>Paypal Options</small>
            <button id="make-payment">Make Payment</button>
        </div>

    </div>

    <div class="col-lg-6">
        <div class="box-element">
            <a class="btn btn-outline-dark" href="{% url 'cart' %}">&#x2190; Back to Cart</a>
            <hr>
            <h3>Order Summary</h3>
            <hr> {% for item in items %}
            <div class="cart-row">
                <div style="flex:2"><img class="row-image" src="{{ item.product.imageURL }}"></div>
                <div style="flex:2">
                    <p>{{ item.product.name }}</p>
                </div>
                <div style="flex:1">
                    <p>{{ item.product.price|floatformat:2 }}</p>
                </div>
                <div style="flex:1">
                    <p>x{{item.quantity}}</p>
                </div>
            </div>
            {% endfor %}
            <h5>Items: {{order.get_cart_items}}</h5>
            <h5>Total: C{{order.get_cart_total|floatformat:2}}</h5>
        </div>
    </div>
</div>


<script type="text/javascript">
    var shipping = '{{ order.shipping }}'
    var total = '{{ order.get_cart_total }}'

    if (shipping == 'False') {
        document.getElementById('shipping-info').innerHTML = ''
    }

    if (user != 'AnonymousUser') {
        document.getElementById('user-info').innerHTML = ''
    }

    if (shipping == 'False' && user != 'AnonymousUser') {
        document.getElementById('form-wrapper').classList.add("hidden");
        document.getElementById('payment-info').classList.remove("hidden");
    }

    var form = document.getElementById('form')

    form.addEventListener('submit', function(e) {
        e.preventDefault()
        console.log('Form Submitted....')
        document.getElementById('form-button').classList.add("hidden");
        document.getElementById('payment-info').classList.remove("hidden");
    })

    document.getElementById('make-payment').addEventListener('click', function(e) {
        submitFormData()
    })

    function submitFormData() {
        console.log('Payment button clicked')

        var userFormData = {
            'name': null,
            'email': null,
            'total': total,
        }

        var shippingInfo = {
            'address': null,
            'city': null,
            'state': null,
            'zipcode': null,
        }

        if (shipping != 'False') {
            shippingInfo.address = form.address.value
            shippingInfo.city = form.city.value
            shippingInfo.state = form.state.value
            shippingInfo.zipcode = form.zipcode.value
        }

        if (user == 'AnonymousUser') {
            userFormData.name = form.name.value
            userFormData.email = form.email.value
        }

        var url = '/process_order/'

        fetch(url, {
                method: 'POST',
                headers: {
                    'Content-Type': 'application/json',
                    'X-CSRFToken': csrftoken,
                },
                body: JSON.stringify({
                    'form': userFormData,
                    'shipping': shippingInfo
                })
            })
            .then((response) => response.json())
            .then((data) => {
                console.log('Success:', data);
                alert('Transaction completed');
                window.location.href = "{% url 'store' %}"
            })

    }
</script>

{% endblock content %}

my cart.js where much javascript goes as well

var updateBtns = document.getElementsByClassName('update-cart')

for (i = 0; i < updateBtns.length; i++) {
    updateBtns[i].addEventListener('click', function() {
        var productId = this.dataset.product
        var action = this.dataset.action
        console.log('productId:', productId, 'Action:', action)

        console.log('USER:', user)
        if (user == 'AnonymousUser') {
            console.log('User is not authenticated')
        } else {
            updateUserOrder(productId, action)
        }
    })
}

function updateUserOrder(productId, action) {
    console.log('User is authenticated, sending date...')

    var url = '/update_item/'

    fetch(url, {
            method: 'POST',
            headers: {
                'Content-Type': 'application/json',
                'X-CSRFToken': csrftoken,
            },
            body: JSON.stringify({ 'productId': productId, 'action': action })
        })
        .then((response) => {
            return response.json()
        })
        .then((data) => {
            location.reload()
        });
}

my cart.html which is related to cart.js

{% extends 'store/main.html' %} {% load static %} {% block content %}
<div class="row">
    <div class="col-lg-12">
        <div class="box-element">

            <a class="btn btn-outline-dark" href="{% url 'store' %}">&#x2190; Continue Shopping</a>

            <br>
            <br>
            <table class="table">
                <tr>
                    <th>
                        <h5>Items: <strong>{{ order.get_cart_items }}</strong></h5>
                    </th>
                    <th>
                        <h5>Total:<strong>{{ order.get_cart_total|floatformat:2 }}</strong></h5>
                    </th>
                    <th>
                        <a style="float:right; margin:5px;" class="btn btn-success" href="{% url 'checkout' %}">Checkout</a>
                    </th>
                </tr>
            </table>

        </div>

        <br>
        <div class="box-element">
            <div class="cart-row">
                <div style="flex:2"></div>
                <div style="flex:2"><strong>Item</strong></div>
                <div style="flex:1"><strong>Price</strong></div>
                <div style="flex:1"><strong>Quantity</strong></div>
                <div style="flex:1"><strong>Total</strong></div>
            </div>
            {% for item in items %}
            <div class="cart-row">
                <div style="flex:2"><img class="row-image" src="{{ item.product.imageURL }}"></div>
                <div style="flex:2">
                    <p>{{ item.product.name }}</p>
                </div>
                <div style="flex:1">
                    <p>C{{ item.product.price|floatformat:2 }}</p>
                </div>
                <div style="flex:1">
                    <p class="quantity">{{ item.quantity }}</p>
                    <div class="quantity">
                        <img data-product="{{ item.product.id }}" data-action="add" class="chg-quantity update-cart" src="{% static  'images/arrow-up.png' %}">

                        <img data-product="{{ item.product.id }}" data-action="remove" class="chg-quantity update-cart" src="{% static  'images/arrow-down.png' %}">
                    </div>
                </div>
                <div style="flex:1">
                    <p>C{{ item.get_total }}</p>
                </div>
            </div>
            {% endfor %}
        </div>
    </div>
</div>
{% endblock content %}

if anything else is needed to solve the problem please tell me i'll add it and any help is much appreciated!

AliReda-M
  • 329
  • 1
  • 3
  • 12
  • Does this answer your question? [django - get() returned more than one topic](https://stackoverflow.com/questions/22063748/django-get-returned-more-than-one-topic) – JPG Aug 18 '20 at 07:10
  • i hoped it would, i went over it, it didnt sadly. Thank You. – AliReda-M Aug 18 '20 at 07:15

2 Answers2

1

I solved the problem and the solution was simply to add complete=False in the following statments in my views.py

order, created = Order.objects.get_or_create(customer=customer, complete=False)
AliReda-M
  • 329
  • 1
  • 3
  • 12
0

The method get_or_create checks if one and only one object with certain conditions exists and if not will create it for you. Use this method only if you're sure that in every circumstances just one or zero instance of that filter exists in your database (i.e. unique field or fields in database). Also note that the line order, created = Order.objects.get_or_create(customer=customer, complete=False) may raise an exception as before in future if you create more than one record which satisfies the customer=customer, complete=False. So change your Order constraints (i.e. adding unique or unique_together or even a new field that is a unique field) or change this part to:

orders = Order.objects.filter(customer=customer, complete=False)

if not orders.exists():
    order = Order.objects.create(customer=customer, complete=False)
else:
    order = orders.last()
Roham
  • 1,970
  • 2
  • 6
  • 16
  • Yeah thanks I think this code snippet is more safe.. But I think that the way I implemented it wouldn't cz any problems, because once a user checks out the order's complete field is set to True, therefore it's not possible to have two orders with false statement and same customer unless that's done in the admin page in that case your solution would solve the issue, but if two orders are for the same customer and are both not completed I would want that to raise an error, thank you very much @Roham ! – AliReda-M Aug 20 '20 at 21:32