How can I show in cart items in my index view? - python

I'm working on an ecommerce app, and I have a template that displays all my products.
I want to show how many of the item are in the cart. But I can't seem to figure out how to get that information from my for loop.
I want to check if the current product in my for loop matches with an OrderItem for that user.
I'm able to do this in the detail view but not my catalogue/index
Code:
template
{% for product in products %}
{{product.title}} - {{product.quantity_in_cart}}
{% endfor %}
(As of right now, product.quantity_in_cart does nothing. I would like it to show how many of this product are in the cart)
view
def product_index(request):
title = "My Products"
products = Product.objects.all()
order = get_cart(request)
cart = Order.objects.get(id=order.id, complete=False)
items = cart.orderitem_set.all()
context = {
'title' : title,
'products' : products,
'cart' : cart,
'items': items
}
return render(request, "store/product_index.html", context)
models
class Order(models.Model):
customer = models.ForeignKey(Customer, on_delete=models.SET_NULL, blank=True, null=True)
complete = models.BooleanField(default=False, null=True, blank=False)
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)
class Product(models.Model):
name = models.CharField(max_length=64)
slug = models.SlugField(unique=True, null=True)
description = RichTextField(blank=True)
price = models.DecimalField(max_digits=8, decimal_places=2)
quantity = models.IntegerField()

def product_index(request):
title = "My Products"
products = Product.objects.all()
order = get_cart(request)
cart = Order.objects.get(id=order.id, complete=False)
items = cart.orderitem_set.all()
# Changes
for product in products:
qty = 0
if items.filter(product=product).exists():
qty = items.get(product=product).quantity
setattr(product, 'quantity_in_cart', qty)
context = {
'title' : title,
'products' : products,
'cart' : cart,
'items': items
}
return render(request, "store/product_index.html", context)

Add a session key called 'cart' which can be a list and each item in the list can be a dictionary containing 'name', 'quatity' and price, as the the user is adding to the cart you add you the session variable and in the template you can render the cart using a for like this
{% for product in request.session.carr%}
{%endfor%}

Related

Getting Foreign Key data in Django Admin Add/Change Form

I am trying to customise the Django admin add/change for a project. I have created a model called "Visit" which contains 3 Foreign Key fields: "Customer", "Pet" and "Doctor". The workflow is as follows:
The user creates a customer.
The user creates a pet and associates it with a customer.
The user creates a visit and associates it with a pet and a doctor.
Below is the code for my models.py
class Visit(models.Model):
customer = models.ForeignKey('customer.Customer', on_delete=models.CASCADE)
pet = models.ForeignKey('pet.Pet', on_delete=models.CASCADE)
date = models.DateTimeField()
doctor = models.ForeignKey(
'configuration.Doctor', on_delete=models.DO_NOTHING, null=True, blank=True)
status = models.CharField(
choices=PET_STATUS, max_length=3, null=True, blank=True)
reason = models.CharField(max_length=255)
diagnosis = models.TextField(null=True, blank=True)
treatment = models.TextField(null=True, blank=True)
comment = models.TextField(null=True, blank=True)
prescription = models.TextField(null=True, blank=True)
weight = models.DecimalField(
max_digits=6, decimal_places=2, null=True, blank=True)
class Meta:
ordering = ('-date',)
My issue is that someone using the Django Admin to create a Visit can wrongly choose a Customer and Pet. Hence, the Customer does not own that Pet. I would like to know how can I customise the Django Admin, so that, when the user selects a Customer, only Pets under that particular Customer is displayed in the selectbox.
Below is my admin.py
class VisitAdmin(admin.ModelAdmin):
change_form_template = 'visit/invoice_button.html'
add_form_template = 'visit/add_visit.html'
list_display = ('customer', 'pet', 'date', 'status')
list_filter = ('date', 'customer', 'pet', 'status')
search_fields = ('customer__first_name',
'customer__last_name', 'pet__pet_name')
autocomplete_fields = ['customer', 'pet', 'doctor']
radio_fields = {'status': admin.HORIZONTAL}
fieldsets = (
(None, {
"fields": ('customer', 'pet', 'doctor'),
}),
("Visit Details", {
"fields": ('date', 'reason', 'weight', 'status'),
}),
("Visit Outcome", {
"fields": ('diagnosis', 'treatment', 'comment')
})
)
inlines = [FeeInLine, AttachmentInLine]
actions = ['export_as_csv']
def export_as_csv(self, request, queryset):
meta = self.model._meta
field_names = [field.name for field in meta.fields]
response = HttpResponse(content_type='text/csv')
response['Content-Disposition'] = 'attachment; filename={}.csv'.format(
meta)
writer = csv.writer(response)
writer.writerow(field_names)
for obj in queryset:
row = writer.writerow([getattr(obj, field)
for field in field_names])
return response
export_as_csv.short_description = "Export Selected"
def response_change(self, request, obj):
if "invoice" in request.POST:
return render_pdf(request, obj.pk)
return super().response_change(request, obj)
admin.site.register(Visit, VisitAdmin)
i faced the same issue, in my case i use raw_id_field for my foreign keys and many to many fields in ModelAdmin And override add and change template.
you can use raw_id_field for forign keys and in your templates write javascript to change href of search icon for Pet foreign key field when Customer id field changed, and in href use url lookup to show only Pet which belongs to selected Customer
# stock/models.py
class Category(models.Model):
title = models.CharField(max_length=255, blank=True)
is_active = models.BooleanField(default=True)
class Product(models.Model):
category = models.ForeignKey(
Category, on_delete=models.PROTECT, related_name="product"
)
feature_option = models.ManyToManyField("FeatureOption", blank=True, related_name="product")
class Feature(models.Model):
title = models.CharField(max_length=255, blank=True)
category = models.ManyToManyField(Category, blank=True, related_name="feature")
class FeatureOption(models.Model):
title = models.CharField(max_length=255, blank=True)
feature = models.ForeignKey(
Feature, on_delete=models.CASCADE, related_name="feature_option"
)
# stock/admin.py
class CategoryAdmin(admin.ModelAdmin):
raw_id_fields = ['parent']
class ProductAdmin(admin.ModelAdmin):
add_form_template = 'admin/product_add_form.html'
change_form_template = 'admin/product_change_form.html'
raw_id_fields = ['category', "feature_option"]
class FeatureOptionAdmin(admin.ModelAdmin):
list_filter = (("feature__category", admin.RelatedOnlyFieldListFilter),)
and in my template i use javascript to change the href of FeatureOption search icon for url lookup
<!-- product_add_form.html -->
{% extends "admin/change_form.html" %}
{% load i18n %}
{% block admin_change_form_document_ready %} {{ block.super }}
<script lang="text/javascript">
function changeFeatureOptionPopupUrl() {
const featureOptionBaseUrl = "/admin/stock/featureoption/";
let categoryTextBox = document.getElementById("id_category");
document.getElementById("lookup_id_feature_option").href = featureOptionBaseUrl + "?feature__category__id__exact=" + categoryTextBox.value;
}
document.getElementById("lookup_id_feature_option").onfocus = function () {changeFeatureOptionPopupUrl()}
</script>
{% endblock %}

How can i fetch a specific to a variable

Hello i want to fetch a specific field to a variable
For that I have Order model:
class Order(models.Model):
product = models.ForeignKey(
Product, on_delete=models.CASCADE, related_name="product")
customer = models.ForeignKey(Customer, on_delete=models.CASCADE)
quantity = models.IntegerField(default=1)
fname = models.CharField(max_length=100, null=True)
address = models.CharField(max_length=1000, null=True)
phone = models.CharField(max_length=12, null=True)
price = models.IntegerField()
date = models.DateField(datetime.datetime.today, null=True)
status = models.ForeignKey(
Status, on_delete=models.CASCADE, blank=True, null=True)
payment_method = models.ForeignKey(
PaymentMethod, on_delete=models.CASCADE, blank=True, null=True)
total = models.IntegerField(null=True)
Here I want to fetch total field in a variable.But I am new for that reason I am really confused about this topic
There is another method you can use. You would have to define a method or property for the field and then call it in your templates.
models.py
class Order(models.Model):
total = models.IntegerField(null=True)
#property
def get_total(self):
return self.total
views.py
from django.shortcuts import render
from .models import *
def Index(request):
Orders = Order.objects.all()
context = {"Orders":Orders}
return render(request, "template/page.html", context)
page.html
{% for order in Orders %}
{{order.get_total}}
# You can do the same to other field to get them
{% endfor %}
Its quite easy. Since you have defined your models already. All you have to do is to use it in your view.py and then in your templates. Exaple is the below:
view.py
from django.shortcuts import render
from .models import *
def Index(request):
Orders = Order.objects.all()
context = {"Orders":Orders}
return render(request, "template/page.html", context)
page.html
{% for order in Orders %}
{{order.total}}
{{order.price}}
# You can do the same to other field to get them
{% endfor %}

Django POST request in template (without form or a better solution)

I have a shopping cart and I want to add an object to that shopping cart. There is a button which I press and when that happens I want the request to go through.
This is what I want to do in the POST request (When Button Is Pressed):
Check for Item with the same id as the product I pressed.
Create a CartItem with the same Item as the one I checked for above.
Add that cart item to my shopping cart linked to my profile. (Not started work yet)
Models:
class Item(models.Model):
name = models.CharField(max_length=100, null=True)
info = models.CharField(max_length=100, null=True)
price = models.IntegerField(default=0, null=True)
discount = models.CharField(max_length=100, null=True, blank=True)
discountPrice = models.IntegerField(default=0, null=True)
inStock = models.BooleanField(default=False)
imagefield = models.ImageField()
reviews = models.ManyToManyField(Review, blank=True, related_name="itemreview")
class Meta:
verbose_name_plural = 'Items'
def __str__(self):
return "{name}".format(name=self.name)
class CartItem(models.Model):
item = models.ForeignKey(Item, blank=True, related_name="CartItem", on_delete=models.CASCADE)
quantity = models.IntegerField(default=0, null=True)
price = models.IntegerField(default=0, null=True)
class Meta:
verbose_name_plural = 'Cart Items'
def __str__(self):
return self.item.name
def get_total_item_price(self):
return self.quantity * self.item.price
class ShoppingCart(models.Model):
user = models.OneToOneField(settings.AUTH_USER_MODEL, on_delete=models.CASCADE)
items = models.ManyToManyField(CartItem, blank=True, related_name="orderitem")
class Meta:
verbose_name_plural = 'Shopping Carts'
def __str__(self):
return self.user.username
def get_total(self):
total = 0
for cart_item in self.items.all():
total += cart_item.get_total_item_price()
return total
#receiver(post_save, sender=User)
def create_user_data(sender, update_fields, created, instance, **kwargs):
if created:
user = instance
profile = ShoppingCart.objects.create(user=user)
Views and urls:
def add_to_cart(request, item_id):
if request.method == 'POST':
#create a cartitem
selectedItem = Item.objects.filter(id=item_id)
theQuantity = 1
thePrice = selectedItem.price
cartItem = CartItem(quantity=theQuantity, price=thePrice)
cartItem.save()
cartItem.item.add(selectedItem)
#add that cartitem to the cart
return redirect('landing-page')
urlpatterns = [
path('', views.landingPage, name='landing-page'),
path('product/<int:itemID>', views.singleProduct, name='singleProduct'),
path('cart', views.shoppingCart, name='cart'),
path('add-to-cart/<int:item_id>', views.add_to_cart, name="addToCart"),
path('quantity-up/<int:cartitem_id>', views.change_quantity_up, name="quantity-upp"),
path('quantity-down/<int:cartitem_id>', views.change_quantity_down, name="quantity-down"),
]
This is how I made the request and inside the html its a button with an a href="{% url 'addToCart' item.id %}"
html:
<a class="button primary-btn" href="{% url 'addToCart' item.id %}">Add to Cart</a>

Template filter for generating product by category

I have a django project where I am displaying products in the template. What is the best possible way of displaying the product by category using some sort of template filter. For instance, on the template if I want to display Breads by category Hovis. At the moment all the products in the database will be displayed.
<tr>
<td><h5>{{ product.name }}</h5></td>
<td><p><strong>{{ product.price }}</strong></p></td>
</tr>
Copy of Models.py (as requested):
from django.db import models
from django.core.urlresolvers import reverse
class Category(models.Model):
name = models.CharField(max_length=200, db_index=True)
slug = models.SlugField(max_length=200, db_index=True, unique=True)
class Meta:
ordering = ('name',)
verbose_name = 'category'
verbose_name_plural = 'categories'
def __str__(self):
return self.name
# def get_absolute_url(self):
# return reverse('shop:product_list_by_category', args=[self.slug])
class Product(models.Model):
category = models.ForeignKey(Category, related_name='products')
name = models.CharField(max_length=200, db_index=True)
slug = models.SlugField(max_length=200, db_index=True)
description = models.TextField(blank=True)
price = models.DecimalField(max_digits=10, decimal_places=2)
available = models.BooleanField(default=True)
created = models.DateTimeField(auto_now_add=True)
updated = models.DateTimeField(auto_now=True)
class Meta:
ordering = ('-created',)
index_together = (('id', 'slug'),)
def __str__(self):
return self.name
# def get_absolute_url(self):
I would send the already prepared data from the view to the template to avoid additional logic in the front-end. You could do something like this in the view:
products = Product.objects.all()
all_products_by_cat ={}
for product in products:
if all_products_by_cat.get(product.category):
all_products_by_cat[product.category].append(product)
else:
all_products_by_cat[product.category] = [product]
And in the template you would just do:
{% for product in all_products_by_cat['some_category'] %}
<!-- do something with the product-->
{% endfor %}

Django Categories with subcategories

I was able to show categories for Category Model (Man and Woman).
Now I want to make forwarding for a category to the site with subcategory where will show subcategory with product
This is my models.py
class Category(models.Model):
name = models.CharField(max_length=200,
db_index=True)
slug = models.SlugField(max_length=200,
db_index=True)
class Meta:
ordering = ('name',)
verbose_name = 'category'
verbose_name_plural = 'categories'
def __str__(self):
return self.name
def get_absolute_url(self):
return reverse('shop:product_list_by_category',
args=[self.slug])
class SubcategoryMan(models.Model):
category = models.ForeignKey(Category,
related_name='subcategorymans')
name = models.CharField(max_length=200,
db_index=True)
slug = models.SlugField(max_length=200,
db_index=True)
def __str__(self):
return self.name
class ProductMan(models.Model):
category = models.ForeignKey(SubcategoryMan,
related_name='productsman')
name = models.CharField(max_length=200,
db_index=True)
slug = models.SlugField(max_length=200,
db_index=True)
image = models.ImageField(upload_to='productsman/%Y/%m/%d',
blank=True)
description = models.TextField(blank=True)
price = models.DecimalField(max_digits=10, decimal_places=2)
stock = models.PositiveIntegerField()
available = models.BooleanField(default=True)
created = models.DateTimeField(auto_now_add=True)
updated = models.DateTimeField(auto_now=True)
class Meta:
ordering = ('name',)
index_together = (('id', 'slug'),)
def __str__(self):
return self.name
This is my views.py file
def product_list(request, category_slug=None):
category = None
categories = Category.objects.all()
products = ProductMan.objects.filter(available=True)
if category_slug:
category = get_object_or_404(Category, slug=category_slug)
products = products.filter(category=category)
return render(request,
'shop/product/list.html',
{'category': category,
'categories': categories,
'products': products})
This is my URL (in app shop)
urlpatterns = [
url(r'^$', views.product_list, name='product_list'),
url(r'^(?P<category_slug>[-\w]+)/$',
views.product_list,
name='product_list_by_category')
]
and this is my list.html
{% extends "shop/base.html" %}
{% load static %}
{% block content %}
<ul>
<li>
Wszystkie
</li>
{% for c in categories %}
<li>
{{c.name}}
</li>
{% endfor %}
</ul>
{% endblock %}
I know I must create URL and view for detail, and show product but now I want to know what I can do to create forwarding to the subcategory (create views and URL ? )
I have category Man and Women. I want to when i click on Man forwarding to new site with subcategory ( T-shirt , Shorts etc. )
This is not what happens. Your django application does not actively forward the client somewhere. In fact there is no way the server can send commands to the client. (Okay actually there are ways, but you do not need this in your use case.)
When the client clicks the link you rendered into the previous response a new page will be requested. The information which category was clicked is in the url. Django delegates that request to the correct view depending on the url to render another template. You already defined the url. Now you need a view and a template. The connection between server and client is stateless. That means your django does not know or keep track of which site the client has opened. It just answers to every single request
The django tutorial covers all the code necessary to do exactly this.
A side note:
You do not have not split your categories and products into separate models for each gender. You can just create a model Product with a field gender which enables you to filter every "man category" on the content of this field. Like this:
GENDER_CHOICES = (
('man', 'man'),
('woman', 'woman'),
('kids'), 'kids'), # yeah that's not a gender but you could want to have this
)
class Product(models.Model):
category = models.ForeignKey(Category, related_name='product')
name = models.CharField(max_length=200, db_index=True)
# all the other product fields
gender = models.CharField(max_length=30, choices=GENDER_CHOICES)
If want only the man products in your view you could then filter it like this:
man_products = Product.objects.filter(gender='man')
or this:
# get all men's shoes from the database
man_shoes = Product.object.filter(gender='man', category__slug='shoes')

Categories

Resources