I am trying to make an e-commerce website where "AnonymousUser" or Guest user can order and check out products by providing their name, email, and address. But after clicking the "Make Payment" button, my terminal was having an error that says "store.models.Customer.MultipleObjectsReturned: get() returned more than one Customer -- it returned 2!"
When I try to login and do the process for an authenticated user, it doesn't have an error. It just happen to AnonymousUsers.
Here's my checkout.html:
{% extends 'store/main.html' %}
{% load static %}
{% block content %}
<div >
<div >
<div id="form-wrapper">
<form id="form">
<div id="user-info">
<div >
<input required type="text" name="name" placeholder="Name..">
</div>
<div >
<input required type="email" name="email" placeholder="Email..">
</div>
</div>
<div id="shipping-info">
<hr>
<p>Shipping Information:</p>
<hr>
<div >
<input type="text" name="address" placeholder="Address..">
</div>
<div >
<input type="text" name="city" placeholder="City..">
</div>
<div >
<input type="text" name="state" placeholder="State..">
</div>
<div >
<input type="text" name="zip" placeholder="zip code..">
</div>
</div>
<hr>
<input id="form-button" type="submit" value="Continue">
</form>
</div>
<br>
<div id="payment-info">
<small>Paypal Options</small>
<button id="make-payment">Make Payment</button>
</div>
</div>
<div >
<div >
<a href="{% url 'cart' %}">← Back to Cart</a>
<hr>
<h3>Order Summary</h3>
<hr>
{% for item in items %}
<div >
<div style="flex:2"><img 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: ₱{{order.get_cart_total|floatformat:2}}</h5>
</div>
</div>
</div>
<script type="text/javascript">
var shipping = '{{order.shipping}}'
var user = '{{request.user.is_authenticated}}'
var total = '{{order.get_cart_total|floatformat:2}}'
if(shipping == 'False'){
document.getElementById('shipping-info').innerHTML = ''
}
if (user == "True"){
document.getElementById('user-info').innerHTML = ''
}
if (shipping == 'False' && user == "True"){
//Hide entire form if user is logged in and shipping is false
document.getElementById('form-wrapper').classList.add('hidden');
//Show payment if logged in user wants to buy an item that does not require shipping
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,
'zip':null,
}
if (shipping != 'False'){
shippingInfo.address = form.address.value
shippingInfo.city = form.city.value
shippingInfo.state = form.state.value
shippingInfo.zip = form.zip.value
}
if (user == 'False'){
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');
cart ={}
document.cookie = 'cart=' JSON.stringify(cart) ";domain=;path=/"
window.location.href = "{% url 'store' %}"
})
}
</script>
{% endblock content %}
Here's my models.py:
from django.db import models
from django.contrib.auth.models import User
# Create your models here.
class Customer(models.Model):
user = models.OneToOneField(User, on_delete=models.CASCADE, null=True, blank=True)
name = models.CharField(max_length=200, null=True)
email = models.CharField(max_length=200)
def __str__(self):
return self.name
class Product(models.Model):
name = models.CharField(max_length=200)
price = models.DecimalField(max_digits=9, 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.CASCADE, null=True, blank=True)
date_ordered = models.DateTimeField(auto_now_add=True)
complete = models.BooleanField(default=False)
transaction_id = models.CharField(max_length=20, null=True)
def __str__(self):
return str(self.id)
@property
def shipping(self):
shipping = False
orderitems = self.orderitem_set.all()
for i in orderitems:
if i.product.digital == False:
shipping = True
return shipping
@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
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)
@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)
zip = models.CharField(max_length=200, null=False)
date_added = models.DateTimeField(auto_now_add=True)
def __str__(self):
return self.address
Here's my views.py:
from django.http import JsonResponse
from django.shortcuts import render
from .models import *
import json
import datetime
from .utils import cookieCart, cartData, guestOrder
def store(request):
data = cartData(request)
cartItems = data['cartItems']
products = Product.objects.all()
context = {'products':products, 'cartItems': cartItems}
return render(request, 'store/store.html', context)
def cart(request):
data = cartData(request)
cartItems = data['cartItems']
order = data['order']
items = data['items']
context = {'items':items, 'order':order, 'cartItems': cartItems}
return render(request, 'store/cart.html', context)
def checkout(request):
data = cartData(request)
cartItems = data['cartItems']
order = data['order']
items = data['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.quantity = (orderItem.quantity 1)
elif action == 'remove':
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)
else:
customer, order = guestOrder(request, data)
total = float(data['form']['total'])
order.transaction_id = transaction_id
if total == 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'],
zip=data['shipping']['zip'],
)
return JsonResponse('Payment complete!', safe=False)
And here's my utils.py:
import json
from .models import *
def cookieCart(request):
try:
cart = json.loads(request.COOKIES['cart'])
except:
cart = {}
print('Cart:', cart)
items = []
order = {'get_cart_total':0, 'get_cart_items':0, 'shipping': False}
cartItems = order['get_cart_items']
for i in cart:
try:
cartItems = cart[i]["quantity"]
product = Product.objects.get(id=i)
total = (product.price * cart[i]['quantity'])
order['get_cart_total'] = total
order['get_cart_items'] = cart[i]['quantity']
item = {
'product':{
'id':product.id,
'name':product.name,
'price':product.price,
'imageURL':product.imageURL,
},
'quantity': cart[i]['quantity'],
'get_total': total,
}
items.append(item)
if product.digital == False:
order['shipping'] = True
except:
pass
return {'cartItems': cartItems, 'order': order, 'items': items}
def cartData(request):
if request.user.is_authenticated:
customer = request.user.customer
order, created = Order.objects.get_or_create(customer=customer, complete=False)
items = order.orderitem_set.all()
cartItems = order.get_cart_items
else:
cookieData = cookieCart(request)
cartItems = cookieData['cartItems']
order = cookieData['order']
items = cookieData['items']
return {'cartItems': cartItems, 'order': order, 'items': items}
def guestOrder(request, data):
print('User is not logged in...')
print('COOKIES:', request.COOKIES)
name = data['form']['name']
email = data['form']['email']
cookieData = cookieCart(request)
items = cookieData['items']
customer, created = Customer.objects.get_or_create(
email=email,
)
customer.name = name
customer.save()
order = Order.objects.create(
customer=customer,
complete=False,
)
for item in items:
product=Product.objects.get(id=item['product']['id'])
orderItem = OrderItem.objects.create(
product=product,
order=order,
quantity=item['quantity']
)
return customer, order
How can I get rid of that error and let guest users check out without the need to make an account or login?
EDITED:
When I check all the info I entered in the checkout from in the admin site, I encountered an error that says "str returned non-string (type NoneType)"
Here's the problem:
TypeError at /admin/store/customer/3/change/
__str__ returned non-string (type NoneType)
Request Method: GET
Request URL: http://127.0.0.1:8000/admin/store/customer/3/change/
Django Version: 4.0.2
Exception Type: TypeError
Exception Value:
__str__ returned non-string (type NoneType)
Exception Location: C:\Users\RolfShin025\Desktop\E-COMMERCE\env\lib\site-packages\django\contrib\admin\options.py, line 1645, in _changeform_view
Python Executable: C:\Users\RolfShin025\Desktop\E-COMMERCE\env\Scripts\python.exe
Python Version: 3.10.2
Python Path:
['C:\\Users\\RolfShin025\\Desktop\\E-COMMERCE\\WannaCome',
'C:\\Program Files\\Python310\\python310.zip',
'C:\\Program Files\\Python310\\DLLs',
'C:\\Program Files\\Python310\\lib',
'C:\\Program Files\\Python310',
'C:\\Users\\RolfShin025\\Desktop\\E-COMMERCE\\env',
'C:\\Users\\RolfShin025\\Desktop\\E-COMMERCE\\env\\lib\\site-packages']
Server time: Sat, 26 Feb 2022 19:59:54 0000
Here's the traceback in my Terminal:
Traceback (most recent call last):
File "C:\Users\RolfShin025\Desktop\E-COMMERCE\env\lib\site-packages\django\core\handlers\exception.py", line 47, in inner
response = get_response(request)
File "C:\Users\RolfShin025\Desktop\E-COMMERCE\env\lib\site-packages\django\core\handlers\base.py", line 181, in _get_response
response = wrapped_callback(request, *callback_args, **callback_kwargs)
File "C:\Users\RolfShin025\Desktop\E-COMMERCE\env\lib\site-packages\django\contrib\admin\options.py", line 622, in wrapper
return self.admin_site.admin_view(view)(*args, **kwargs)
File "C:\Users\RolfShin025\Desktop\E-COMMERCE\env\lib\site-packages\django\utils\decorators.py", line 130, in _wrapped_view
response = view_func(request, *args, **kwargs)
File "C:\Users\RolfShin025\Desktop\E-COMMERCE\env\lib\site-packages\django\views\decorators\cache.py", line 57, in _wrapped_view_func
response = view_func(request, *args, **kwargs)
File "C:\Users\RolfShin025\Desktop\E-COMMERCE\env\lib\site-packages\django\contrib\admin\sites.py", line 236, in inner
return view(request, *args, **kwargs)
File "C:\Users\RolfShin025\Desktop\E-COMMERCE\env\lib\site-packages\django\contrib\admin\options.py", line 1673, in change_view
return self.changeform_view(request, object_id, form_url, extra_context)
File "C:\Users\RolfShin025\Desktop\E-COMMERCE\env\lib\site-packages\django\utils\decorators.py", line 43, in _wrapper
return bound_method(*args, **kwargs)
File "C:\Users\RolfShin025\Desktop\E-COMMERCE\env\lib\site-packages\django\utils\decorators.py", line 130, in _wrapped_view
response = view_func(request, *args, **kwargs)
File "C:\Users\RolfShin025\Desktop\E-COMMERCE\env\lib\site-packages\django\contrib\admin\options.py", line 1549, in changeform_view
return self._changeform_view(request, object_id, form_url, extra_context)
File "C:\Users\RolfShin025\Desktop\E-COMMERCE\env\lib\site-packages\django\contrib\admin\options.py", line 1645, in _changeform_view
'subtitle': str(obj) if obj else None,
TypeError: __str__ returned non-string (type NoneType)
CodePudding user response:
Here you are creating Customer
object for AnonymousUser
(simply not logged User
).
customer, created = Customer.objects.get_or_create(
email=email,
)
I think it might find more that one if you have same email used more than once with.