Django: How to get total cart item in ecommerce - django

I am trying to add the total item in the cart, but I keep getting
<property object at 0x000001CFFDED8228>:
could anybody help?
View.py
class checkout(ListView):
model = OrderItem
template_name = 'product/checkout.html'
def get_context_data(self, **kwargs):
context = super(checkout, self).get_context_data(**kwargs)
context['orderQty'] = Order.get_cart_items
return context
model.py
class Order(models.Model):
customer = models.ForeignKey(Customer, on_delete=models.SET_NULL, blank=True, null=True)
date_ordered = models.DateTimeField(auto_now_add=True)
complete = models.BooleanField(default=False, null = True, blank=False)
transaction_id = models.CharField(max_length= 200, null=True)
def __str__(self):
return str(self.id)
#property
def get_cart_items(self):
orderitems = self.orderitem_set.all()
total = sum(item.quantity for item in orderitems)
return total
size_choices = (('small', 'S'),
('medium', 'M'),
('large', 'L'))
class OrderItem(models.Model):
product = models.ForeignKey(Product, on_delete=models.SET_NULL, blank=True, null=True)
order = models.ForeignKey(Order, on_delete=models.SET_NULL, blank=True, null=True)
date_added = models.DateTimeField(auto_now_add=True)
quantity = models.IntegerField(default=0, blank=True, null=True)
size = models.CharField(max_length= 200,choices= size_choices, default=0)
#property
def get_total(self):
total = self.product.price *self.quantity
return total
template.html:
<div class="row-1">
<div style="flex:2;"><strong>Total Items</strong>:<span style="float: right;">{{orderQty}}</span></div>
<hr>
<div style="flex:2;"><strong>SubTotal</strong>:<span style="float: right;">$387</span></div>
<hr>
<div style="flex:2; margin-top:10px"><strong>Shipping</strong><span style="float: right;">$54</span></div>
<hr>
</div>
<div>
<div style="flex:2; text-align:center"><h6>Total: $387</h6></div>
<input id="form-button" class="btn btn-success btn-block" type="submit" value="Pay">
</div
I believe these files contain the problem but from researching online I can't seem to pinpoint exactly what it is. The function based view of my website was working fine until I moved to class-based view my views.py file. Any assistance would be greatly appreciated.

I believe the problem is with this line of code:
def get_context_data(self, **kwargs):
context = super(checkout, self).get_context_data(**kwargs)
context['orderQty'] = Order.get_cart_items # <----
return context
You are just creating function object instead of calling it. Changing that line to this should solve the problem:
context['orderQty'] = Order.get_cart_items()
EDIT When you define #property, your function becomes no longer function object but property object. Since property is descriptor and is not callable, it will throw that error. For more information, check this documentation. So, I would suggest you to remove #property at function definition to resolve this issue.

Related

How to get the previous and next related post in django?

views.py
def post_details(request,pk):
post = Post.objects.get(id=pk)
# next_post = Post.objects.filter(id=pk)
context={'post':post,'next':next_post}
return render(request, 'blog/post_detail.html', context)
blog-detail
<div class="s-content__pagenav group">
<div class="prev-nav">
<a href="#" rel="prev">
<span>Previous</span>
Tips on Minimalist Design
</a>
</div>
<div class="next-nav">
<a href="#" rel="next">
<span>Next</span>
Less Is More
</a>
</div>
</div>
models
# this is my model
class User(AbstractUser):
# pass
name = models.CharField(max_length=200)
bio = models.TextField(null=True)
email = models.EmailField(unique=True, null=True)
avatar = models.ImageField( null=True, upload_to='blog_media', default="images/avatar.svg")
facebook = models.URLField(blank=True, null=True)
twitter = models.URLField(blank=True, null=True)
dribbble = models.URLField(blank=True, null=True)
instagram = models.URLField(blank=True, null=True)
class Category(models.Model):
name = models.CharField(max_length=20)
class Meta:
verbose_name = 'Category'
verbose_name_plural = 'Categories'
def __str__(self):
return self.name
class Post(models.Model):
author = models.ForeignKey(User, on_delete=models.CASCADE)
category = models.ManyToManyField(Category)
title = models.CharField(max_length=200, blank=False);
description = models.TextField(null=True,blank=True)
image = models.ImageField(upload_to='blog_media')
url = models.URLField(null=True, blank=True)
body = HTMLField()
created = models.DateTimeField(auto_now=True)
updated = models.DateTimeField(auto_now_add=True)
def __str__(self):
return self.title
Based on your comments, I'm assuming that you would like to get two related posts that have the same category as the current post.
If I'm correct, then one method you could use is to filter the queryset for the same category belonging to the current post then you could choose the next and previous posts of the current post from the retrieved queryset. For example:
def post_details(request, pk):
current_post = Post.objects.get(pk=pk) # retrieving the current post...
# filtering for related posts only by using the category of the current post
# using -> category_in=post.category.all() since it's a ManyToMany field
related_posts = Post.objects.filter(category_in=current_post.category.all())
# next -> get posts with id greater than the current post id, then get the first instance 'next post'
# previous -> get posts with id less than the current post id, then get the first instance 'previous post'
context = {
'post': current_post,
'next': related_posts.filter(id__gt=current_post.id).order_by('id').first(),
'previous': related_posts.filter(id__lt=current_post.id).order_by('-id').first()
}
return render(request, 'blog/post_detail.html', context)
Ideally, that should work.
A quick recommendation here as well... Instead of using Post.objects.get(pk=pk), I'd suggest using get_object_or_404() as this will handle any potential error that Post.objects.get(pk=pk) will throw. So a small update...
from django.shortcuts import get_object_or_404
def post_details(request, pk):
current_post = get_object_or_404(Post, pk=pk) # retrieving the current post...
# the rest of the code follows...

Make change to selected object in many to many fields

I have a Order model and item model with many to many relationship, but I just want to make change to selected items for each Order, How can I do that? As when I get order.items.all will affect all the items in the Order.
Model.py:
class OrderItem(models.Model):
user = models.ForeignKey(settings.AUTH_USER_MODEL, on_delete=models.CASCADE, blank=True, null=True)
item = models.ForeignKey(Product, on_delete=models.CASCADE)
quantity = models.IntegerField(default=1)
ordered = models.BooleanField(default=False)
being_delivered = models.BooleanField(default=False)
being_delivered_date = models.DateTimeField(null=True)
Received = models.BooleanField(default=False)
Received_date = models.DateTimeField(null=True)
def __str__(self):
return f"{self.quantity} of {self.item.product_name}"
class Order(models.Model):
user = models.ForeignKey(settings.AUTH_USER_MODEL, on_delete=models.CASCADE, blank=True, null=True)
items = models.ManyToManyField(OrderItem)
ordered = models.BooleanField(default=False)
being_delivered = models.BooleanField(default=False)
being_delivered_date = models.DateTimeField(null=True)
Received = models.BooleanField(default=False)
Received_date = models.DateTimeField(null=True)
def __str__(self):
return self.user.username
Views.py
def update_to_recevied(request, id):
order_item = get_object_or_404(OrderItem, id=id)
order_item.Received = True
order_item.Received_date = timezone.now()
order_item.save()
return redirect(...)
html:
{% for item in order.items.all %}
Received<br><span>{{ item.item.product_name }}</span>
{% endfor %}
you can get the selected item by using post form then send it to the views.py then resend it to the template as dictionary
I am not sure if I understand your goal correctly, But if you want to update only selected items you can use Jquery. Use a checkbox instead of a button,give an id to your checkbox
<input type="checkbox" id="checkbox">Received</input>
you can check whether that item is checked or not like this :
<script>
$("#checkbox").change(function() {
if(this.checked) {
$.post("{% url 'update_to_recevied' item.id %}", {
received: "True",
},
});
}
});
</script>
or simply use :checked selector
https://api.jquery.com/checked-selector/
and you can take look at Jquery Post requests here
https://www.w3schools.com/jquery/jquery_ajax_get_post.asp

Template not showing context variable

I created an app called marketing app which customizes messages to be written on top of home page. My problem is that these messages are not showing when everything configured and I don't know why is that might be the template because {{ marketing_message.message }} is only not showing
This is the model:
class MarketingMessage(models.Model):
message = models.CharField(max_length=120)
active = models.BooleanField(default=False)
featured = models.BooleanField(default=False)
timestamp = models.DateTimeField(auto_now_add=True, auto_now=False)
updated = models.DateTimeField(auto_now_add=False, auto_now=True)
start_date = models.DateTimeField(
auto_now_add=False, auto_now=False, null=True, blank=True)
end = models.DateTimeField(
auto_now_add=False, auto_now=False, null=True, blank=True)
def __str__(self):
return str(self.message[:12])
This is the view:
from marketing.models import MarketingMessage
class HomeView(ListView):
model = Item
paginate_by = 10
template_name = "home.html"
def get_context_data(self, **kwargs):
context = super(HomeView, self).get_context_data(**kwargs)
context['marketing_message'] = MarketingMessage.objects.all()
return context
This is the template:
{% if marketing_message %}
<div id="top-alert"class="alert alert-light" style="padding-top:85px; margin-bottom:-24px;">
×
<div class="container" style="text-align:center">
<strong> Marketing Message ! : </strong> {{ marketing_message.message}}
</div>
</div>
{% endif %}
marketing_message is a QuerySet, not a model instance.
You probably only want the most recent "active" message.
context['marketing_message'] = MarketingMessage.objects.filter(active=True).latest('timestamp')
Or the most recently updated:
context['marketing_message'] = MarketingMessage.objects.filter(active=True).latest('updated')
However latest() will fail if there is no object, which is likely not what you want, but you can do this:
try:
context['marketing_message'] = MarketingMessage.objects.filter(active=True).latest('updated')
except MarketingMessage.DoesNotExist:
context['marketing_message'] = None
Looking at your data, though, it seems incorrect. You have a start and end dates, but also active. You can imply if something is active if it falls within the start and end dates.
Also, since you have this, it seems like you could have multiple messages at once, in which case you should drop the .latest() and iterate over the ones within the start and end dates.

Django: Problem with filtering products in order

I'm trying to make account view in my django-shop. I want to display information about the order and the ordered goods. I have a ProductInOrder model with foreign key to Order. Now I want to filter the ordered goods by order. But something is going wrong.
class Order(models.Model):
user = models.ForeignKey(settings.AUTH_USER_MODEL, on_delete=models.CASCADE)
ref_code = models.CharField(max_length=15)
items = models.ForeignKey(Cart, null=True ,on_delete=models.CASCADE, verbose_name='Cart')
total = models.DecimalField(max_digits=9, decimal_places=2, default=0.00)
first_name = models.CharField(max_length=100)
last_name = models.CharField(max_length=100, blank=True)
phone = models.CharField(max_length=20)
buying_type = models.CharField(max_length=40, choices=BUYING_TYPE_CHOICES,
default='Доставка')
address = models.CharField(max_length=250, blank=True)
date_created = models.DateTimeField(default=timezone.now)
date_delivery = models.DateTimeField(default=one_day_hence)
comments = models.TextField(blank=True)
status = models.CharField(max_length=100, choices=ORDER_STATUS_CHOICES,
default='Принят в обработку')
class ProductInOrder(models.Model):
order = models.ForeignKey(Order, on_delete=models.CASCADE)
product = models.ForeignKey(Product, on_delete=models.CASCADE)
quantity = models.IntegerField(default=1)
item_cost = models.DecimalField(max_digits=10, decimal_places=2, default=0.00)
all_items_cost = models.DecimalField(max_digits=10, decimal_places=2, default=0.00)
And views.py
def account_view(request):
order = Order.objects.filter(user=request.user).order_by('-id')
products_in_order = ProductInOrder.objects.filter(order__in=order)
categories = Category.objects.all()
instance = get_object_or_404(Profile, user=request.user)
if request.method == 'POST':
image_profile = ProfileImage(request.POST, request.FILES, instance=instance)
if image_profile.is_valid():
avatar = image_profile.save(commit=False)
avatar.user = request.user
avatar.save()
messages.success(request,
f'Ваш аватар был успешно обновлен!')
return redirect('ecomapp:account')
else:
image_profile = ProfileImage()
context = {
'image_profile': image_profile,
'order': order,
'products_in_order': products_in_order,
'categories': categories,
'instance': instance,
}
return render(request, 'ecomapp/account.html', context)
This line products_in_order = ProductInOrder.objects.filter(order__in=order) doesn't work.
Any help please.
Unless you explicityly mention order_by in ProductInOrder queryset, it will order by its default setup, which is mentioned in ProductInOrder model's meta class(if its not mentioned, then default ordering is pk). So using following line should resolve your issue:
ProductInOrder.objects.filter(order__in=order).order_by('-order')
But an improved answer is like this:
products_in_order = ProductInOrder.objects.filter(order__user=request.user).order_by('-order')
In this way, you can remove line order = Order.objects.filter(user=request.user).order_by('-id') from your code. Whats happening here is that, django allows nested filtering, so you can filter by order__user which will allow you order by user from Order model. You don't need to make a filter for Order separately.
Update:
I am not sure, probably you are looking for this:(in template)
{% for o in order %}
{% for po in o.productinorder_set.all %}
{{ po.product }}
{{ po.item_cost }}
{% endfor %}
{% endfor %}
Here I am using reverse relation between Order and ProductInOrder here.

Detailview Object Relations

;TLDR - After some solutions discovered, my final question is how can I, if at all, access models related to models related to the main detailview model?
I'm trying to use a generic detailview to return an object and it's related object. In this example, a company like mcdonalds would have any sites (or locations). What I want the detailview to be able to show is the company detail, and the site detail related to the company. I'm stuck though. Dispite my efforts in not asking for help, I have not been able to pull the data from the model referencing the company sites. Where am I going wrong? I have sort of proven this to work in the django shell with SiteModel.objects.filter(company=5) showing all of the company with an ID of 5's site names.
models.py
'''
The company model consists of the base company information
'''
class CompanyModel(models.Model):
name = models.CharField(_('Company Name'), max_length=255, blank=False)
website = models.URLField(_('Company Website'), blank=True)
since = models.DateField(auto_now_add=True)
rate = models.DecimalField(max_digits=5, decimal_places=2, blank=False)
def __str__(self):
return '%s' % (self.name)
class Meta:
ordering = ['name']
verbose_name = 'Company'
verbose_name_plural = 'Companies'
'''
The site model consists of sites of a company as
some companies have several sites that we will work from.
'''
class SiteModel(models.Model):
company = models.ForeignKey(CompanyModel, on_delete=models.PROTECT)
address = models.ForeignKey(AddressModel, on_delete=models.PROTECT)
phone = models.ForeignKey(PhoneModel, blank=True, null=True, on_delete=models.PROTECT)
distance = models.SmallIntegerField(blank=True)
def __str__(self):
return '%s - %s, %s' % (self.company, self.address.city, self.address.state)
class Meta:
ordering = ['company']
verbose_name = 'Company Site Information'
verbose_name_plural = 'Company Sites'
views.py
class CompanyDetailView(DetailView):
model = CompanyModel
template_name = 'customers/detail.html'
def get_context_data(self, **kwargs):
context = super(CompanyDetailView, self).get_context_data(**kwargs)
context['sites'] = SiteModel.objects.filter(id=self.kwargs['pk'])
return context
urls.py
url(r'^customer/(?P<pk>[0-9a-z-]+)/detail/$', CompanyDetailView.as_view(),
name='customer-detail'),
Update 1:
My template is showing the correct company, but only 1 site, and the site is not related to the company. Arg. It's showing both the company who's ID is 5, and the site who's ID is 5. How do I connect the dots correctly here?
template
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<title>Customer Detail</title>
</head>
<body>
<div class="container">
{{ object.name }}
{% for site in sites %}
{{ site }}
{% endfor %}
</div>
</body>
</html>
Update 2:
I was able to sort this out by not supering get_context_data, and just itterating through the _set suffix of the related model's name. Django Documentation Reference
template
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<title>Customer Detail</title>
</head>
<body>
<div class="container">
{{ company.name }}
{% for site in company.sites.all %}
{{ site }}
{% endfor %}
</div>
</body>
</html>
The follow up to this, however, is how do I go more than one layer deep? Following up with the above models, I also have a "reports" model. But when I use the same method as above, it seems to break down after the first model. i.e. I can't just use company.sites.reports.
models.py
class ServiceReportModel(models.Model):
report_number = models.UUIDField(primary_key=True, default=uuid.uuid4, editable=False)
site = models.ForeignKey(customers_models.SiteModel, on_delete=models.PROTECT, related_name='reports')
request_number = models.ForeignKey(ServiceRequestModel,
on_delete=models.PROTECT,
null=True,
blank=True,
related_name='s_report_number'
)
reported_by = models.ForeignKey(main_models.MyUser, related_name='reports')
reported_date = models.DateTimeField(auto_now_add=True)
updated_by = models.ForeignKey(main_models.MyUser, blank=True, null=True, related_name='+')
updated_date = models.DateTimeField(auto_now=True)
equipment = models.ForeignKey(customers_models.EquipmentModel, on_delete=models.PROTECT)
report_reason = models.CharField(max_length=255, null=True)
time_in = models.DateTimeField(blank=True, null=True)
time_out = models.DateTimeField(blank=True, null=True)
actions_taken = models.TextField(null=False, blank=False)
recommendations = models.TextField(null=True, blank=True)
def get_absolute_url(self):
return reverse('service-report', kwargs={'pk': self.pk})
def __str__(self):
return '%s - %s, %s' % (self.site.company, self.reported_date.strftime('%d %B %Y'), self.equipment.name)
class Meta:
ordering = ['reported_date']
verbose_name = 'Service Report'
verbose_name_plural = 'Service Reports'
I was able to get a solution with some help. I went back to super'ing the get_context_data method, and following this documentation regarding spanning relationships using filters and double underscore notation.
class CompanyDetailView(DetailView):
model = CompanyModel
context_object_name = 'company'
template_name = 'customers/detail.html'
def get_context_data(self, **kwargs):
context = super(CompanyDetailView, self).get_context_data(**kwargs)
context['sites'] = SiteModel.objects.filter(company=self.get_object())
context['reports'] = ServiceReportModel.objects.filter(site__company=self.get_object())
return context