I have two apps "orders" and "carts". In my models for carts I got:
class CartItem(models.Model):
cart = models.ForeignKey("Cart")
item = models.ForeignKey(Variation)
quantity = models.PositiveIntegerField(default=1)
line_item_total = models.DecimalField(max_digits=10, decimal_places=2)
def __unicode__(self):
return self.item.title
def remove(self):
return self.item.remove_from_cart()
def cart_item_pre_save_receiver(sender, instance, *args, **kwargs):
qty = instance.quantity
if qty >= 1:
price = instance.item.get_price()
line_item_total = Decimal(qty) * Decimal(price)
instance.line_item_total = line_item_total
pre_save.connect(cart_item_pre_save_receiver, sender=CartItem)
def cart_item_post_save_receiver(sender, instance, *args, **kwargs):
instance.cart.update_subtotal()
post_save.connect(cart_item_post_save_receiver, sender=CartItem)
post_delete.connect(cart_item_post_save_receiver, sender=CartItem)
class Cart(models.Model):
user = models.ForeignKey(settings.AUTH_USER_MODEL, null=True, blank=True)
items = models.ManyToManyField(Variation, through=CartItem)
timestamp = models.DateTimeField(auto_now_add=True, auto_now=False)
updated = models.DateTimeField(auto_now_add=False, auto_now=True)
subtotal = models.DecimalField(max_digits=50, decimal_places=2, default=25.00)
tax_percentage = models.DecimalField(max_digits=10, decimal_places=5, default=0.085)
tax_total = models.DecimalField(max_digits=50, decimal_places=2, default=25.00)
total = models.DecimalField(max_digits=50, decimal_places=2, default=25.00)
def __unicode__(self):
def update_subtotal(self):
print "updating..."
subtotal = 0
items = self.cartitem_set.all()
for item in items:
subtotal += item.line_item_total
self.subtotal = "%.2f" %(subtotal)
self.save()
def do_tax_and_total_receiver(sender, instance, *args, **kwargs):
subtotal = Decimal(instance.subtotal)
tax_total = round(subtotal * Decimal(instance.tax_percentage), 2) #8.5%
print instance.tax_percentage
total = round(subtotal + Decimal(tax_total), 2)
instance.tax_total = "%.2f" %(tax_total)
instance.total = "%.2f" %(total)
pre_save.connect(do_tax_and_total_receiver, sender=Cart)
In my carts Views.py:
from orders.forms import GuestCheckoutForm
from orders.mixins import CartOrderMixin
from orders.models import UserCheckout, Order, UserAddress
from products.models import Variation
from .models import Cart, CartItem
if settings.DEBUG:
braintree.Configuration.configure(braintree.Environment.Sandbox,
merchant_id=settings.BRAINTREE_MERCHANT_ID,
public_key=settings.BRAINTREE_PUBLIC,
private_key=settings.BRAINTREE_PRIVATE)
class ItemCountView(View):
def get(self, request, *args, **kwargs):
if request.is_ajax():
cart_id = self.request.session.get("cart_id")
if cart_id == None:
count = 0
else:
cart = Cart.objects.get(id=cart_id)
count = cart.items.count()
request.session["cart_item_count"] = count
return JsonResponse({"count": count})
else:
raise Http404
class CartView(SingleObjectMixin, View):
model = Cart
template_name = "carts/view.html"
def get_object(self, *args, **kwargs):
self.request.session.set_expiry(0) #5 minutes
cart_id = self.request.session.get("cart_id")
if cart_id == None:
cart = Cart()
cart.tax_percentage = 0.075
cart.save()
self.request.session["cart_id"] = cart_id
cart = Cart.objects.get(id=cart_id)
if self.request.user.is_authenticated():
cart.user = self.request.user
cart.save()
return cart
def get(self, request, *args, **kwargs):
cart = self.get_object()
item_id = request.GET.get("item")
delete_item = request.GET.get("delete", False)
flash_message = ""
item_added = False
if item_id:
item_instance = get_object_or_404(Variation, id=item_id)
qty = request.GET.get("qty", 1)
try:
if int(qty) < 1:
delete_item = True
except:
raise Http404
cart_item, created = CartItem.objects.get_or_create(cart=cart, item=item_instance)
if created:
flash_message = "Successfully added to the cart"
item_added = True
if delete_item:
flash_message = "Item removed successfully."
cart_item.delete()
else:
if not created:
flash_message = "Quantity has been updated successfully."
cart_item.quantity = qty
cart_item.save()
if not request.is_ajax():
return HttpResponseRedirect(reverse("cart"))
#return cart_item.cart.get_absolute_url()
if request.is_ajax():
try:
total = cart_item.line_item_total
except:
total = None
try:
subtotal = cart_item.cart.subtotal
except:
subtotal = None
try:
cart_total = cart_item.cart.total
except:
cart_total = None
try:
tax_total = cart_item.cart.tax_total
except:
tax_total = None
try:
total_items = cart_item.cart.items.count()
except:
total_items = 0
data = {
"deleted": delete_item,
"item_added": item_added,
"line_total": total,
"subtotal": subtotal,
"cart_total": cart_total,
"tax_total": tax_total,
"flash_message": flash_message,
"total_items": total_items
}
return JsonResponse(data)
context = {
"object": self.get_object()
}
template = self.template_name
return render(request, template, context)
class CheckoutView(CartOrderMixin, FormMixin, DetailView):
model = Cart
template_name = "carts/checkout_view.html"
form_class = GuestCheckoutForm
def get_object(self, *args, **kwargs):
cart = self.get_cart()
if cart == None:
return None
return cart
def get_context_data(self, *args, **kwargs):
context = super(CheckoutView, self).get_context_data(*args, **kwargs)
user_can_continue = False
user_check_id = self.request.session.get("user_checkout_id")
if self.request.user.is_authenticated():
user_can_continue = True
user_checkout, created = UserCheckout.objects.get_or_create(email=self.request.user.email)
user_checkout.user = self.request.user
user_checkout.save()
context["client_token"] = user_checkout.get_client_token()
elif not self.request.user.is_authenticated() and user_check_id == None:
context["login_form"] = AuthenticationForm()
context["next_url"] = self.request.build_absolute_uri()
else:
pass
if user_check_id != None:
user_can_continue = True
if not self.request.user.is_authenticated(): #GUEST USER
user_checkout_2 = UserCheckout.objects.get(id=user_check_id)
context["client_token"] = user_checkout_2.get_client_token()
#if self.get_cart() is not None:
context["order"] = self.get_order()
context["user_can_continue"] = user_can_continue
context["form"] = self.get_form()
return context
def post(self, request, *args, **kwargs):
self.object = self.get_object()
form = self.get_form()
if form.is_valid():
email = form.cleaned_data.get("email")
user_checkout, created = UserCheckout.objects.get_or_create(email=email)
return self.form_valid(form)
else:
return self.form_invalid(form)
def get_success_url(self):
return reverse("checkout")
def get(self, request, *args, **kwargs):
get_data = super(CheckoutView, self).get(request, *args, **kwargs)
cart = self.get_object()
if cart == None:
return redirect("cart")
new_order = self.get_order()
user_checkout_id = request.session.get("user_checkout_id")
if user_checkout_id != None:
user_checkout = UserCheckout.objects.get(id=user_checkout_id)
if new_order.billing_address == None or new_order.shipping_address == None:
return redirect("order_address")
new_order.user = user_checkout
new_order.save()
return get_data
class CheckoutFinalView(CartOrderMixin, View):
def post(self, request, *args, **kwargs):
order = self.get_order()
order_total = order.order_total
nonce = request.POST.get("payment_method_nonce")
if nonce:
result = braintree.Transaction.sale({
"amount": order_total,
"payment_method_nonce": nonce,
"billing": {
"postal_code": "%s" %(order.billing_address.zipcode),
},
"options": {
"submit_for_settlement": True
}
})
if result.is_success:
messages.success(request, "Thank you for your order.")
del request.session["cart_id"]
del request.session["order_id"]
else:
#messages.success(request, "There was a problem with your order.")
messages.success(request, "%s" %(result.message))
return redirect("checkout")
return redirect("order_detail", pk=order.pk)
def get(self, request, *args, **kwargs):
return redirect("checkout")
My orders app models.py:
import braintree
if settings.DEBUG:
braintree.Configuration.configure(braintree.Environment.Sandbox,
merchant_id=settings.BRAINTREE_MERCHANT_ID,
public_key=settings.BRAINTREE_PUBLIC,
private_key=settings.BRAINTREE_PRIVATE)
class UserCheckout(models.Model):
user = models.OneToOneField(settings.AUTH_USER_MODEL, null=True, blank=True) #not required
email = models.EmailField(unique=True) #--> required
braintree_id = models.CharField(max_length=120, null=True, blank=True)
def __unicode__(self): #def __str__(self):
return self.email
@property
def get_braintree_id(self,):
instance = self
if not instance.braintree_id:
result = braintree.Customer.create({
"email": instance.email,
})
if result.is_success:
instance.save()
return instance.braintree_id
def get_client_token(self):
customer_id = self.get_braintree_id
if customer_id:
client_token = braintree.ClientToken.generate({
"customer_id": customer_id
})
return client_token
return None
def update_braintree_id(sender, instance, *args, **kwargs):
if not instance.braintree_id:
instance.get_braintree_id
post_save.connect(update_braintree_id, sender=UserCheckout)
ADDRESS_TYPE = (
('billing', 'Billing'),
('shipping', 'Shipping'),
)
class UserAddress(models.Model):
user = models.ForeignKey(UserCheckout)
type = models.CharField(max_length=120, choices=ADDRESS_TYPE)
street = models.CharField(max_length=120)
city = models.CharField(max_length=120)
state = models.CharField(max_length=120)
zipcode = models.CharField(max_length=120)
def __unicode__(self):
return self.street
def get_address(self):
return "%s, %s, %s %s" %(self.street, self.city, self.state, self.zipcode)
ORDER_STATUS_CHOICES = (
('created', 'Created'),
('paid', 'Paid'),
('shipped', 'Shipped'),
('refunded', 'Refunded'),
)
class Order(models.Model):
status = models.CharField(max_length=120, choices=ORDER_STATUS_CHOICES, default='created')
cart = models.ForeignKey(Cart)
user = models.ForeignKey(UserCheckout, null=True)
billing_address = models.ForeignKey(UserAddress, related_name='billing_address', null=True)
shipping_address = models.ForeignKey(UserAddress, related_name='shipping_address', null=True)
shipping_total_price = models.DecimalField(max_digits=50, decimal_places=2, default=5.99)
order_total = models.DecimalField(max_digits=50, decimal_places=2, )
order_id = models.CharField(max_length=20, null=True, blank=True)
def __unicode__(self):
class Meta:
ordering = ['-id']
def get_absolute_url(self):
return reverse("order_detail", kwargs={"pk":
self.pk})
def mark_completed(self, order_id=None):
self.status = "paid"
if order_id and not self.order_id:
self.order_id = order_id
self.save()
def order_pre_save(sender, instance, *args, **kwargs):
shipping_total_price = instance.shipping_total_price
cart_total = instance.cart.total
order_total = Decimal(shipping_total_price) + Decimal(cart_total)
instance.order_total = order_total
pre_save.connect(order_pre_save, sender=Order)
My orders views.py:
class OrderDetail(DetailView):
model = Order
def dispatch(self, request, *args, **kwargs):
try:
user_check_id = self.request.session.get("user_checkout_id")
user_checkout = UserCheckout.objects.get(id=user_check_id)
except UserCheckout.DoesNotExist:
user_checkout = UserCheckout.objects.get(user=request.user)
except:
user_checkout = None
obj = self.get_object()
if obj.user == user_checkout and user_checkout is not None:
return super(OrderDetail, self).dispatch(request, *args, **kwargs)
else:
raise Http404
class OrderList(ListView):
queryset = Order.objects.all()
def get_queryset(self):
user_checkout = UserCheckout.objects.get(id=user_check_id)
return super(OrderList, self).get_queryset().filter(user=user_checkout)
class UserAddressCreateView(CreateView):
form_class = UserAddressForm
template_name = "forms.html"
success_url = "/checkout/address/"
def get_checkout_user(self):
user_check_id = self.request.session.get("user_checkout_id")
user_checkout = UserCheckout.objects.get(id=user_check_id)
return user_checkout
def form_valid(self, form, *args, **kwargs):
form.instance.user = self.get_checkout_user()
return super(UserAddressCreateView, self).form_valid(form, *args, **kwargs)
class AddressSelectFormView(CartOrderMixin, FormView):
form_class = AddressForm
template_name = "orders/address_select.html"
def dispatch(self, *args, **kwargs):
b_address, s_address = self.get_addresses()
if b_address.count() == 0:
messages.success(self.request, "Please add a billing address before continuing")
return redirect("user_address_create")
elif s_address.count() == 0:
messages.success(self.request, "Please add a shipping address before continuing")
return redirect("user_address_create")
else:
return super(AddressSelectFormView, self).dispatch(*args, **kwargs)
def get_addresses(self, *args, **kwargs):
user_check_id = self.request.session.get("user_checkout_id")
user_checkout = UserCheckout.objects.get(id=user_check_id)
b_address = UserAddress.objects.filter(
user=user_checkout,
type='billing',
)
s_address = UserAddress.objects.filter(
user=user_checkout,
type='shipping',
)
return b_address, s_address
def get_form(self, *args, **kwargs):
form = super(AddressSelectFormView, self).get_form(*args, **kwargs)
b_address, s_address = self.get_addresses()
form.fields["billing_address"].queryset = b_address
form.fields["shipping_address"].queryset = s_address
return form
def form_valid(self, form, *args, **kwargs):
billing_address = form.cleaned_data["billing_address"]
shipping_address = form.cleaned_data["shipping_address"]
order = self.get_order()
order.billing_address = billing_address
order.shipping_address = shipping_address
order.save()
return super(AddressSelectFormView, self).form_valid(form, *args, **kwargs)
def get_success_url(self, *args, **kwargs):
return "/checkout/"
Error I get when checking out is:
Environment:
Request Method: GET
Django Version: 1.8.5
Python Version: 2.7.9
Installed Applications:
('django.contrib.admin',
'django.contrib.auth',
'django.contrib.contenttypes',
'django.contrib.sessions',
'django.contrib.sites',
'django.contrib.messages',
'django.contrib.staticfiles',
'blog',
'products',
'orders',
'carts',
'newsletter',
'crispy_forms',
'registration',
'colorfield',
'hitcount')
Installed Middleware:
('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 "C:\Users\Mudassar\dressikarepo\lib\site-packages\django\core\handlers\base.py" in get_response
132. response = wrapped_callback(request, *callback_args, **callback_kwargs)
File "C:\Users\Mudassar\dressikarepo\lib\site-packages\django\views\generic\base.py" in view
71. return self.dispatch(request, *args, **kwargs)
File "C:\Users\Mudassar\dressikarepo\lib\site-packages\django\views\generic\base.py" in dispatch
89. return handler(request, *args, **kwargs)
File "C:\Users\Mudassar\dressikarepo\src\carts\views.py" in get
188. get_data = super(CheckoutView, self).get(request, *args, **kwargs)
File "C:\Users\Mudassar\dressikarepo\lib\site-packages\django\views\generic\detail.py" in get
116. context = self.get_context_data(object=self.object)
File "C:\Users\Mudassar\dressikarepo\src\carts\views.py" in get_context_data
167. context["order"] = self.get_order()
File "C:\Users\Mudassar\dressikarepo\src\orders\mixins.py" in get_order
22. new_order = Order.objects.create(cart=cart)
File "C:\Users\Mudassar\dressikarepo\lib\site-packages\django\db\models\manager.py" in manager_method
127. return getattr(self.get_queryset(), name)(*args, **kwargs)
File "C:\Users\Mudassar\dressikarepo\lib\site-packages\django\db\models\query.py" in create
348. obj.save(force_insert=True, using=self.db)
File "C:\Users\Mudassar\dressikarepo\lib\site-packages\django\db\models\base.py" in save
734. force_update=force_update, update_fields=update_fields)
File "C:\Users\Mudassar\dressikarepo\lib\site-packages\django\db\models\base.py" in save_base
762. updated = self._save_table(raw, cls, force_insert, force_update, using, update_fields)
File "C:\Users\Mudassar\dressikarepo\lib\site-packages\django\db\models\base.py" in _save_table
846. result = self._do_insert(cls._base_manager, using, fields, update_pk, raw)
File "C:\Users\Mudassar\dressikarepo\lib\site-packages\django\db\models\base.py" in _do_insert
885. using=using, raw=raw)
File "C:\Users\Mudassar\dressikarepo\lib\site-packages\django\db\models\manager.py" in manager_method
127. return getattr(self.get_queryset(), name)(*args, **kwargs)
File "C:\Users\Mudassar\dressikarepo\lib\site-packages\django\db\models\query.py" in _insert
920. return query.get_compiler(using=using).execute_sql(return_id)
File "C:\Users\Mudassar\dressikarepo\lib\site-packages\django\db\models\sql\compiler.py" in execute_sql
974. cursor.execute(sql, params)
File "C:\Users\Mudassar\dressikarepo\lib\site-packages\django\db\backends\utils.py" in execute
79. return super(CursorDebugWrapper, self).execute(sql, params)
File "C:\Users\Mudassar\dressikarepo\lib\site-packages\django\db\backends\utils.py" in execute
64. return self.cursor.execute(sql, params)
File "C:\Users\Mudassar\dressikarepo\lib\site-packages\django\db\utils.py" in __exit__
97. six.reraise(dj_exc_type, dj_exc_value, traceback)
File "C:\Users\Mudassar\dressikarepo\lib\site-packages\django\db\backends\utils.py" in execute
64. return self.cursor.execute(sql, params)
File "C:\Users\Mudassar\dressikarepo\lib\site-packages\django\db\backends\sqlite3\base.py" in execute
318. return Database.Cursor.execute(self, query, params)
Exception Type: OperationalError at /checkout/
Exception Value: table orders_order has no column named order_id
...........
My migrations table for orders shows:
class Migration(migrations.Migration):
dependencies = [
migrations.swappable_dependency(settings.AUTH_USER_MODEL),
('carts', '0001_initial'),
]
operations = [
migrations.CreateModel(
name='Order',
fields=[
('id', models.AutoField(verbose_name='ID', serialize=False, auto_created=True, primary_key=True)),
('status', models.CharField(default=b'created', max_length=120, choices=[(b'created', b'Created'), (b'paid', b'Paid'), (b'shipped', b'Shipped'), (b'refunded', b'Refunded')])),
('shipping_total_price', models.DecimalField(default=5.99, max_digits=50, decimal_places=2)),
('order_total', models.DecimalField(max_digits=50, decimal_places=2)),
('order_id', models.CharField(max_length=20, null=True, blank=True)),
],
options={
'ordering': ['-id'],
},
),
migrations.CreateModel(
name='UserAddress',
fields=[
('id', models.AutoField(verbose_name='ID', serialize=False, auto_created=True, primary_key=True)),
('type', models.CharField(max_length=120, choices=[(b'billing', b'Billing'), (b'shipping', b'Shipping')])),
('street', models.CharField(max_length=120)),
('city', models.CharField(max_length=120)),
('state', models.CharField(max_length=120)),
('zipcode', models.CharField(max_length=120)),
],
),
migrations.CreateModel(
name='UserCheckout',
fields=[
('id', models.AutoField(verbose_name='ID', serialize=False, auto_created=True, primary_key=True)),
('email', models.EmailField(unique=True, max_length=254)),
('braintree_id', models.CharField(max_length=120, null=True, blank=True)),
('user', models.OneToOneField(null=True, blank=True, to=settings.AUTH_USER_MODEL)),
],
),
migrations.AddField(
model_name='useraddress',
name='user',
field=models.ForeignKey(to='orders.UserCheckout'),
),
migrations.AddField(
model_name='order',
name='billing_address',
field=models.ForeignKey(related_name='billing_address', to='orders.UserAddress', null=True),
),
migrations.AddField(
model_name='order',
name='cart',
field=models.ForeignKey(to='carts.Cart'),
),
migrations.AddField(
model_name='order',
name='shipping_address',
field=models.ForeignKey(related_name='shipping_address', to='orders.UserAddress', null=True),
),
migrations.AddField(
model_name='order',
name='user',
field=models.ForeignKey(to='orders.UserCheckout', null=True),
),
]
It creates a cart but isn't processing orders due to the error above. Please advise