How to get average across different models in django? - django

I am trying to get the average of the rating (found in the review model) for each product across all the products within a given category. I am having trouble doing this. The relevant urlconf, view, and template are below:
url(r'^category/(?P<category_id>\d+)/$', 'view_product_category'),
class Productbackup(models.Model):
website = models.CharField('Product name', max_length = 200)
website_url = models.URLField('Product URL')
category = models.ForeignKey(Categories)
def __unicode__(self):
return self.website
class ProductbackupForm(ModelForm):
class Meta:
model = Productbackup
def get_avg_rating(self):
reviews = Reviewbackup.objects.filter(product=self)
count = len(reviews)
sum = 0
for rvw in reviews:
sum += rvw.rating
return (sum/count)
class Reviewbackup(models.Model):
review = models.CharField('Review', max_length = 2000)
rating = models.IntegerField(max_length=200)
product = models.ForeignKey(Product)
def __unicode__(self):
return self.review
class Categories(models.Model):
category = models.CharField('Category_second', max_length = 200)
def __unicode__(self):
return unicode(self.category)
def view_product_category(request, category_id):
allproduct = Productbackup.objects.filter(category_id=category_id)
#get average for each product within a category
return render_to_response('reserve/templates/view_category.html', {'allproduct':allproduct},
context_instance=RequestContext(request))
{% for product in allproduct %}
{{ product.website }}: Direct link<br>
#average rating for that product
{{ product.get_avg_rating }}
{% endfor %}

I will add method on Product model to get avg rating.
class Productbackup(models.Model):
website = models.CharField('Product name', max_length = 200)
website_url = models.URLField('Product URL')
category = models.ForeignKey(Categories)
def __unicode__(self):
return self.website
def get_avg_rating(self):
reviews = Reviewbackup.objects.filter(product=self)
count = len(reviews)
sum = 0
for rvw in reviews:
sum += rvw.rating
return (sum/count)
In template :
{% for product in allproducts %}
{{ product.website }}: Direct link<br>
#average rating for that product
{{ product.get_avg_rating }}
{% endfor %}
Note: passing 'allproducts' not 'product' as in your code. So update the view code accordingly. In your code {% for product in product %} is using same variable name, which isn't correct.

Much effective(without iterations) way to do this is using aggregations with Avg() method,
class Productbackup(models.Model):
website = models.CharField('Product name', max_length = 200)
website_url = models.URLField('Product URL')
category = models.ForeignKey(Categories)
def __unicode__(self):
return self.website
def get_avg_rating(self):
reviews = Reviewbackup.objects.filter(product=self).aggregate(rating_avg=Avg('rating'))
return (reviews['rating_avg'])
In template :
{% for product in allproducts %}
{{ product.website }}: Direct link<br>
#average rating for that product
{{ product.get_avg_rating }}
{% endfor %}

Related

django eccomerce prodcut name not showing in file

i am creating a new django eccomorce website now in product detail page here is my code
the problem is i cant see product name correct in html page problem with first()
when i use first then only product name showing but all products have same name i have 8 producs in my page eight product name same to first just like overwriting also i cant use for loop with first()
i will add some pics
urls.py
path('collection/<str:cate_slug>/<str:prod_slug>',views.product_view,name="productview"),
views.py
def product_view(request,cate_slug,prod_slug):
if (Category.objects.filter(slug=cate_slug, status=0)):
if (Products.objects.filter(slug=prod_slug, status=0)):
products = Products.objects.filter(slug=prod_slug, status=0).first()
context = {'products':products}
else:
messages.error(request,"no such product found")
return redirect("collection")
else:
messages.error(request,"no such category found")
return redirect("collection")
return render(request,"product_view.html",context)
models.py
class Products(models.Model):
category = models.ForeignKey(Category,on_delete=models.CASCADE)
slug = models.CharField(max_length=150, null=False, blank=False)
product_name = models.CharField(max_length=150, null=False, blank=False)
product_image = models.ImageField( upload_to=get_image,null=True,blank=True)
description = models.TextField(max_length=500,null=False,blank=False)
original_price = models.IntegerField(null=False,blank=False)
selling_price = models.IntegerField(null=False,blank=False)
status = models.BooleanField(default=False,help_text="0=default , 1=Hidden")
trending = models.BooleanField(default=False,help_text="0=default , 1=Trending")
meta_title = models.CharField(max_length=150,null=False,blank=False)
meta_keyword = models.CharField(max_length=150,null=False,blank=False)
meta_description = models.CharField(max_length=400,null=False,blank=False)
created_at = models.DateTimeField(auto_now_add=True)
def __str__(self):
return self.product_name
productview.html
{% block content %}
<h1>{{ products.product_name }} </h1>
{% endblock %}
i just want correct product name for every category i stucked here in morning helping are appreciated thank you all for helping till now
I think you need to loop through your produckts:
{% for product in products %}
<h1>{{ product.product_name }}</h1>
{% endfor %}
view:
def product_view(request,cate_slug,prod_slug):
if (Category.objects.filter(slug=cate_slug, status=0).exists()):
if (Products.objects.filter(slug=prod_slug, status=0).exists()):
products = Products.objects.filter(slug=prod_slug, status=0)
context = {'products':products}
else:
messages.error(request,"no such product found")
return redirect("collection")
else:
messages.error(request,"no such category found")
return redirect("collection")
return render(request,"product_view.html",context)
Note that usually the model name is singular, so it should be class Products(models.Model):

How to separate a category in a django template

I get this error:
ValueError at /product/
The QuerySet value for an exact lookup must be limited to one result using slicing.
this is my models :
class Categorie (models.Model):
title = models.CharField(max_length=50)
category_slug = models.SlugField(blank=True)
def __str__(self):
return self.category_slug
class Products(models.Model):
category = models.ForeignKey(Categorie,on_delete=models.CASCADE,null=True,
related_name="product")
product_slug = models.SlugField(blank=True)
product_title = models.CharField(max_length=50 , null=True)
product_name = models.CharField(max_length=100 , null=True )
product_describe = models.CharField(max_length=200 , null=True)
product_picture = models.ImageField(upload_to='img_pro' , null=True)
product_created_at = models.DateField(auto_now_add=True)
product_updated_at = models.DateField(auto_now=True)
def __str__(self):
return self.product_slug
this is my view:
def index(requset):
category = Categorie.objects.all()
product = Products.objects.filter(category_slug=category)
context = {
'category':category,
'product ':product ,
}
return render( requset , 'product/index.html' , context)
this is my template:
{% for cat in category %}
<div class="fix single_content">
<h2>{{cat.title}}</h2>
<div class="fix">
{% for pro in product %}
<ul>
<li>
<div class="entry_name"><a href=""><img src="{{pro.product_picture.url}}" alt=""><span>
{{pro.product_title}}</span></a><h6>{{pro.product_name}}</h6></div>
</li>
</ul>
{% endfor %}
</div>
{% endfor %}
this is myapp.urls:
from django.urls import path
from . import views
urlpatterns = [
path('' , views.index , name='index' ),
]
Can anyone help me with that please?
in you index view you are passing a everything you have in vategory
category = Categorie.objects.all()
while it is expecting just one thing
category = Categorie.objects.get(id=pk) # pk or category_id or slug or whatever you named it in your url
so your view should look like
def index(requset,pk):
category = Categorie.objects.get(id=pk)
product = Products.objects.filter(category=category)
context = {
'category':category,
'product ':product ,
}
return render( requset , 'product/index.html' , context)
If you want to get products for a single category use this:
category = Categorie.objects.filter(category_slug='<slug-to-search>').first()
product = Products.objects.filter(category=category)
But if you want the product for multiple categories use this:
categories = Categorie.objects.all()
products = Products.objects.filter(category__in=categories)

How to get a single model object with a relation to the template in Django

I am working on this project that I can add two or more forms in a template. I am able to get the two forms in the template but when I submit the form, I get the objects for the rentalproperty model and not the contract model. I have created two different solution but the two doesn't solve the problem.
The first solution below display both objects multiple times in the detailview but what I want is to display the two model objects just once. The second solution display the rentalproperty object once but the contract objects multiple times. Could someone point me in the right direction? Thanks.
First solution:
views.py
class DetailView(generic.DetailView):
model = RentalProperty
template_name = 'rental/detail.html'
context_object_name = 'property'
def new_rental(request, pk):
if request.method == 'POST':
rental_form = NewRentalPropertyForm(request.POST, request.FILES, prefix = "rentals")
contract_form = NewContractForm(request.POST, prefix = "contracts")
if rental_form.is_valid() and contract_form.is_valid():
print ("all validation passed")
rentalproperty = rental_form.save()
contract_form.cleaned_data["rentalproperty"] = rentalproperty
print(contract_form)
contract = contract_form.save(commit=False)
contract.rentalproperty = rentalproperty
contract = contract_form.save()
return HttpResponseRedirect(reverse("home"))
else:
messages.error(request, "Error")
contract = Contract.objects.get(pk=pk)
else:
rental_form = NewRentalPropertyForm(prefix = "rentals")
contract_form = NewContractForm(prefix = "contracts")
contract = Contract.objects.get(pk=pk)
return render(request, 'rental/new_rental.html', {
#'rentalproperty': rentalproperty,
'rental_form': rental_form,
'contract_form': contract_form,
'contract': contract,
})
detail.html
<h1>This is the detail view</h1>
<h3>From landlord</h3>
<p>Landlord: {{property.created_by}}</p>
<p>address: {{property.landlord.address}}</p>
<h3>From Rental property</h3>
<ul>
{% for rental in property.landlord.rentalpropertys.all %}
<br>
<li>Title: {{property.title}}</li>
<img src="{{property.image.url}}" height="200" alt=""/>
<li>created at: {{property.created_at}}</li>
<li>Type of property: {{property.type_of_property_listing}}</li>
<li>Street: {{property.street}}</li>
<li>Borough: {{property.borough}}</li>
<ul>
{% for contract in rental.contracts.all %}
<li> Insurance required: {{contract.insurance_required}}</li>
<li> other terms: {{contract.other_terms}}</li>
{% endfor %}
</ul>
{% endfor %}
</ul>
Second solution:
views.py
class DetailView(generic.DetailView):
model = RentalProperty
template_name = 'rental/detail.html'
context_object_name = 'property'
def get_context_data(self, **kwargs):
context = super().get_context_data(**kwargs)
context['landlord']= Landlord.objects.all()
context['contract']= Contract.objects.filter(rentalproperty__title=title).order_by('created_at')
return context
The post function is the same.
detail.html
<h1>This is the detail view</h1>
<h3>From landlord</h3>
<p>Landlord: {{property.created_by}}</p>
<p>address: {{property.landlord.address}}</p>
<h3>From Rental property</h3>
<ul>
<li>Title: {{property.title}}</li>
<img src="{{property.image.url}}" height="200" alt=""/>
<li>created at: {{property.created_at}}</li>
<li>Type of property: {{property.type_of_property_listing}}</li>
<li>Street: {{property.street}}</li>
<li>Borough: {{property.borough}}</li>
</ul>
<ul>
{% for data in contract %}
<li> insurance : {{data.insurance_required}}</li>
<li> other terms: {{data.other_terms}}</li>
{% endfor %}
</ul>
My model:
class Landlord(models.Model):
user = models.OneToOneField(UserModel, on_delete=models.CASCADE)
address = models.CharField(max_length=255)
def __str__(self):
return str(self.address)
class RentalProperty(models.Model):
landlord = models.ForeignKey("Landlord", related_name='rentalpropertys', on_delete=models.CASCADE)
created_by = models.ForeignKey(UserModel, related_name='rentalpropertys', on_delete=models.CASCADE)
title = models.TextField(unique=True)
created_at = models.DateTimeField(auto_now_add=True)
PROPERTY_LISTING_CHOICES = Choices(
('APARTMENT', _('Apartment')),
('HOLIDAY_HOME', _('Holiday home')),
('SINGLE_FAMILY_HOME', _('Single family home')),
('COMMERCIAL', _('Commercial')),
)
type_of_property_listing = models.CharField(
max_length = 50,
choices = PROPERTY_LISTING_CHOICES,
default = PROPERTY_LISTING_CHOICES.APARTMENT,)
street = models.CharField(max_length=255)
borough = models.CharField(max_length=255)
image = models.ImageField(upload_to='images/', null=True, blank=True,)
def __str__(self):
return str(self.title)
class Contract(models.Model):
rentalproperty = models.ForeignKey("RentalProperty", related_name='contracts', on_delete=models.CASCADE)
insurance_required = models.BooleanField(default=True)
other_terms = models.TextField(blank=True)
def __str__(self):
return str(self.insurance_required)
first solution output:
address: Helsinki
title: build apps
insurance: Yes
- This repeats itself multiple times.
Second solution output:
address: Helsinki
title: build apps
insurance: Yes
- insurance repeats itself multiple times
What I expect:
address: Helsinki
title: build apps
insurance: Yes
Thanks for your time :)
With the help of #Timmy O'Mahony answer, I did {% for contract in property.contracts.all %} in my template using the first solutions and it works. The contracts is the related name to the rental property. Thanks all.

django - can't filter object and transfer to template

I've got a model which I'm trying to filter according to an argument passed in the url, then display the filtered object via a template, but I don't know what I'm doing wrong.
Here's the urls.py:
url(r'^courses/(?P<course_code>\w+)/$', views.course, name="course"),
Here's the view:
from website.models import Course
def course(request, course_code):
current_course = Course.objects.filter(short_title='course_code')
template = loader.get_template('website/course.html')
context = Context({
'current_course': current_course,
})
return HttpResponse(template.render(context))
Here's the model:
class Course(models.Model):
title = models.CharField(max_length=200)
short_title = models.CharField(max_length=5)
course_type = models.CharField(max_length=100)
start_date = models.DateTimeField()
end_date = models.DateTimeField()
fee = models.IntegerField()
places = models.IntegerField()
venue = models.CharField(max_length=200)
description = models.TextField()
short_description = models.TextField()
age_low = models.IntegerField()
age_high = models.IntegerField()
And here's the template:
{% if current_course %}
{% for course in current_course %}
{{ current_course.title }}
{% endfor %}
{% else %}
<p>Sorry, that course doesn't exist.</p>
{% endif %}
And when I load the page /courses/CR1 (the course with short_title="CR1" definitely exists because it renders fine on another template where I'm not filtering but just displaying all the courses), it gives me "Sorry, that course doesn't exist."
Can anyone see what I'm doing wrong?
In this line:
current_course = Course.objects.filter(short_title='course_code')
You're checking for course titles with the exact text 'course_code'. You mean to use the value of the variable course_code:
current_course = Course.objects.filter(short_title=course_code)

How do I get one to many/foreign key to work in django?

Here is the code I've written:
models.py:
class Name_overall(models.Model):
rank = models.IntegerField()
name = models.CharField(max_length=50)
frequency = models.IntegerField()
def __unicode__(self):
return self.name
class Name_state(models.Model):
gender = models.CharField(max_length=1, choices=GENDER_CHOICES)
name_overall = models.ForeignKey(Name_overall, db_column='name')
frequency = models.IntegerField()
rank = models.IntegerField()
state = models.CharField(max_length=2, choices=STATE_CHOICES)
def __unicode__(self):
return self.state
views.py:
def single_name(request, baby_name):
baby_list = get_list_or_404(Name_overall, name=baby_name)
return render_to_response('names/single_name.html', {'baby_list': baby_list})
single_name.html:
{{ baby_list.name_state_set.all }}
Nothing shows up in the single_name.html template. If I change it to {{ baby_list }}, there is an object there, but I am not able to access the Name_state class. I thought I should be able to have access to Name_state because of the foreign key. What am I missing?
The baby_list context variable is a QuerySet. You need to iterate it and access the ForeignKey in the loop.
{% for item in baby_list %}
{{item.name_state_set.all}}
#iterate the name_state_set
{% for obj in item.name_state_set.all %}
{{obj}}
{% endfor %}
{% endfor %}