I want to show top_categories in the homepage. I have written top_categories function to list the categories that has most number of products. But I have written this function in Product Model. I am confused on where should I write this. Here is my code
class Category(models.Model):
name = models.CharField(max_length=50)
slug = models.SlugField(max_length=50, unique=True)
class Product(models.Model):
name = models.CharField(max_length=200, unique=True,
blank=False, null=False)
categories = models.ManyToManyField(Category, related_name='products')
def top_categories(self):
product = Product.objects.values('id').annotate(
categories_count=models.Count('categories')).order_by('-categories_count')
return product
def home(request):
categories = Category.objects.all()
companies = Company.objects.all()[:12]
context = {
'categories': categories,
'companies': companies
}
return render(request, 'company/home.html', context)
Now there is a confusion, Do I have to implement top_categories function in Category modal or the way I am doing is fine? Because the job of showing the content in homepage is the role of home view.
You can do it in views.py
def home(request):
# arrange your category on basis of product_count
categories = Category.objects.annotate(product_count = Count('products')).order_by('-product_count')
# if you want only top 10 categories
# categories = categories[:10]
companies = Company.objects.all()[:12]
context = {
'categories': categories,
'companies': companies
}
return render(request, 'company/home.html', context)
In home.html
{% for category in categories %}
<h3> category name:</h3>{{category.name}}
<h3> Total product::</h3>{{category.product_count}}
{% endfor %}
Well think I would do something like this :
class Category(models.Model):
name = models.CharField(max_length=50)
slug = models.SlugField(max_length=50, unique=True)
#staticmethod
def top_category():
return models.Count('categories')).order_by('-categories_count')
Made it a static method cause it's not about an instance. An even better way of doing it would be to extends the Category manager and add a method get_top_category to it so you can call a Category.objects.get_top_category(). Then just call it in your Product model :
def top_products(self):
count_category = Category.top_category() # or Category.objects.get_top_category() if you have done it this way
product = Product.objects.values('id').annotate(count_category)
return product
Some doc to override managers
I suggest you to using templatetags, because if you handle this case with views you will create similiar popular filter for another page.
# yourapp/templatetags/popcategories.py
from django import template
from yourapp.models import (Category, Product)
register = template.Library()
#register.assignment_tag
def popular_categories():
categories = Category.objects.all()
get_total = lambda c: Product.objects.filter(categories__slug=c.slug).count()
tags_list = [{'category': category, 'total': get_total(category)} for category in categories]
tags_list.sort(key=lambda x: int(x['total']), reverse=True)
return tags_list[:10] # return 10 top
And then, you can use it in your template company/home.html with;
{% load popcategories %}
{% popular_categories as categories_list %}
{% for category in categories_list %}
<a href="">
{{ category.name }} - {{ category.slug }}
</a>
{% empty %}
<p>No categories yet!</p>
{% endfor %}
Related
I am trying to make specific queries by using some model entry fields.
I have the following model entry:
models.py
class Work(models.Model):
categories =(
('cat1', 'cat1'),
('cat2', 'cat2'),
('cat3', 'cat3'),
('cat4', 'cat4'),
('cat5', 'cat5'),
)
title = models.CharField(max_length=200)
description = RichTextUploadingField(config_name='awesome_ckeditor')
date = models.DateTimeField(default=timezone.now)
category = models.CharField(max_length=200, choices = categories, default = 'projects')
thumb = models.ImageField(upload_to = 'works/thumbs', blank = True)
content = models.FileField(upload_to = 'works/content_media', blank = True)
published = models.BooleanField()
def __str__(self):
return self.title
def get_absolute_url(self):
return reverse("work_detail",kwargs={'pk':self.pk})
#property
def thumb_url(self):
if self.thumb and hasattr(self.thumb, 'url'):
return self.thumb.url
#property
def content_url(self):
if self.content and hasattr(self.content, 'url'):
return self.content.url
here is the view:
views.py
class WorksListView(ListView):
template_name = 'template.html'
model = Work
def get_queryset(self):
return Work.objects.filter(published=True).order_by('-date')
and I am trying to query first by the category field then by entry in the following template:
template.html
{% for category in works_list.category %}
<ul data-category-name={{category.name}}>
{% for work in category.works %}
<li data-thumbnail-path={{thumbnail.url}} data-url={{content.url}} >
<div>
<p class="gallery1DecHeader">{{work.title}}</p>
<p class="gallery1DescP">{{work.description}}</p>
</div>
</li>
{% endfor %}
{% endfor %}
what do I need to change?
Okay, from what I can see there are a few problems. First, try adding context_object_name = 'works_list' That way you will be able to refer to the object_list as works_list like you do in the template outer for loop. The bigger problem is you are iterating over works_list.category, which according to your Work model is a Charlist(). I think you might be getting confused about what the choices kwarg does and expecting {% for category in works_list.category %} to iterate over your choices and giving you the list of cats you defined in categories. As far as I know, that's not how choices works.
If you go to your admin panel and add a new entry for your Work model, you'll see that category has a dropdown list that contains your list of cats. So, choices defines a set of legal category options for new Work objects, not a list in existing Work objects.
I think what you actually want is an additional model Category which defines: work = models.ForeignKey(Work, on_delete=models.CASCADE) as a one-to-many relationship. Basically, you want is for Work to have a subset of Category objects that you can iterate over. This will involve redesigning the way you structure and access your data though.
You need to change at least your views.py and template.html. Add a context_object_name and an extra context(Doc Link)
views.py
class WorksListView(ListView):
template_name = 'template.html'
model = Work
context_object_name = 'work_list'
def get_queryset(self):
return Work.objects.filter(published=True).order_by('-date')
def get_context_data(self, **kwargs):
# Call the base implementation first to get a context
context = super(WorksListView, self).get_context_data(**kwargs)
# Insert categories so that it can be used in template
context['categories'] = Work.categories
return context
template.html
{% for category in categories%}
<ul data-category-name={{category.0}}>
{% for work in work_list %}
{% if category.0 == work.category %}
<li data-thumbnail-path={{work.thumb_url}} data-url={{work.content_url}} >
<div>
<p class="gallery1DecHeader">{{work.title}}</p>
<p class="gallery1DescP">{{work.description}}</p>
</div>
</li>
{% endif %}
{% endfor %}
</ul>
{% endfor %}
Models:
class Category(models.Model):
name = models.CharField(max_length=20)
slug = models.SlugField(max_length=200, unique=True )
def __unicode__(self):
return self.name
class Post(models.Model):
title = models.CharField(max_length = 100)
content = models.TextField(max_length = 600, default = 'cool' )
date_of_creating = models.DateTimeField(auto_now=False, auto_now_add=True)
image = models.ImageField(
upload_to=upload_location,
null=True,
blank=True,
height_field="height_field",
width_field="width_field"
)
height_field = models.IntegerField(default=0)
width_field = models.IntegerField(default=0)
category = models.ForeignKey('Category')
slug = models.SlugField(unique=True, blank=False)
def __unicode__(self):
return self.title
Views:
def category(reguest, slug):
category = Category.objects.get(slug=slug)
post = Post.objects.filter(category=category)
html = 'category.html'
context = {
'category': category,
'post': post,
}
return render(reguest, html, context)
def listofposts(request):
query_set_list = Post.objects.all()
context = {
"list" : query_set_list,
}
html = 'base.html'
return render(request, html, context)
I dont know what should I write in template to filter my posts by categories.
I need to choose category and display posts of this category. How can I filter it by category?
I believe you are asking how to show your posts, arranged by categories? If that is what you are after, your template should look something like this:
template.html
{% if category %}
{% if post %}
{% for c in category %}
{% for p in post %}
{% if p.category == c %}
<div>{{ p.title }}</div> <!-- and whatever else you want to display and however you want to style it I am just giving an example -->
{% endif %}
{% endfor %}
{% endfor %}
{% endif %}
{% endif %}
Based on what you do in your views, this is one way to display by category. Note there are better ways of how you can traverse the data, and group them up, but you are asking for this. So I hope this helps you out!
EDIT 1
After reading your question again (i believe there was an edit) I saw that you are asking how to show the posts for the selected category. I am assuming that since you have a slug the category selected is in the URL. So indeed in the view you are selecting the correct posts. So in order to display the posts from the selected category you simply have to do this in your template:
{% if post %}
{% for p in post %}
<div>{{ p.title }}</div> <!-- and whatever else you want to display and however you want to style it I am just giving an example -->
{% endfor %}
{% endif %}
Hope this helps!
You can try to use Ajax to filter by category (Tutorial here).
Make a selection box containing all the categories, then when a new option is selected, trigger the Ajax query to select all posts from that category.
I have been trying to do this nested loop for couple of hours, but so far none of my results worked. Here is what i have so far:
index.html
{% for category in categories %}
<div class="col-md-12 column category list-group">
<p><b>{{ category.name }}</b></p>
{% for subcategory in subcategories %}
<div class="list-group-item">
<h5 class="mb-1">{{ subcategory.name }}</h5>
<small>{{ subcategory.description }}</small>
<small>{{ subcategory.count_threads }}</small>
</div>
{% endfor %}
</div>
{% endfor %}
views.py
class IndexView(ListView):
template_name = 'myForum/index.html'
context_object_name = 'SubCategory_list'
queryset = SubCategory.objects.all()
def get_context_data(self, **kwargs):
context = super(IndexView, self).get_context_data(**kwargs)
context['categories'] = Category.objects.all()
context['subcategories'] = SubCategory.objects.all()
return context
models.py
class Category(models.Model):
name = models.CharField(max_length=255)
def __str__(self):
return self.name
class SubCategory(models.Model):
name = models.CharField(max_length=255)
description = models.CharField(max_length=255)
category = models.ForeignKey('Category', default=0)
def __str__(self):
return self.name
Output
Output
My problem is that SubCategory - News, belongs to Infromation, category Off-Topic belongs to General. For some reason loop displays all of subcategories, and i can't figure out how to narrow it to current Category.
You can change the inner loop to:
{% for subcategory in category.subcategory_set.all %}
This will loop over the subcategories for the current category.
Since you are looping over categories in the outer loop, you can change the queryset in the list view to use the Category model. You can cut down the number of queries by prefetching the subcategories.
It also looks as if you can remove the get_context_data method, since the list view already makes the queryset available in the template context.
class IndexView(ListView):
template_name = 'myForum/index.html'
context_object_name = 'catrgories'
queryset = Category.objects.all().prefetch_related('subcategory_set')
I am a amature django developer. I have a model with two class called "Post" and "Catagory". I want to read Category items in my template. How can I import my category in my template and show it's data in my page?
models.py
from django.db import models
from taggit.managers import TaggableManager
class Category(models.Model):
title = models.CharField(max_length=40)
def __unicode__(self):
return self.title
class Post (models.Model):
title = models.CharField(max_length=150)
body = models.TextField()
date = models.DateTimeField()
tags = TaggableManager ()
cats = models.ManyToManyField(Category)
def __unicode__ (self):
return self.title
Thank you.
If you are using class based views and want to list all categories you could do:
# urls.py
url(regex=r'^category/$',
view=CategoriesListView.as_view(),
name='category_list_all'),
# views.py
class CategoriesListView(ListView):
model = Category
# category_list.html
<h2>Category list</h2>
<ul>
{% for cat in category_list %}
<li>
{{ cat.category }}
</li>
{% endfor %}
</ul>
You would place the html file in <project_route>/<app_name>/templates/<app_name>/ or <project_route>/templates/<app_name>/
If you have an existing function based view that's working with the Post model then you can just do something like:
# views.py
...
post = get_object_or_404(Post, pk=pass_in_pk)
return render(request, 'post.html', {'post': post})
# post.html
<h2>Category list</h2>
<ul>
{% for category in post.cats %}
<li>{{ category.title }}</li>
{% endfor %}
</ul>
If you have a class based view based on a Post model using the DetailView then you can also use the above html, just place it in post_detail.html in the appropriate folder.
It`s like get the category value and assign into settings and passed into view html will work
def viewfuncion(request):
template_vars = {}
settings = Category.objects.get(pk=1)
template_vars['title_show'] = settings.title
t = loader.get_template('view.html')
c = Context(template_vars)
return HttpResponse(t.render(c), content_type = "application/xhtml")
So in your HTML { title_show } will print the content
my models are
class Product(models.Model):
name = models.CharField(max_length=50)
desc = models.CharField(max_length=50)
def __unicode__(self):
return self.name
class ProductModels(models.Model):
product = models.ForeignKey(Product)
name = models.CharField(max_length=50)
price = IntegerField(max_length=50)
def __unicode__(self):
return self.name
i can easily print in python , product and it's releated models , but in django template , i am unable to figure out how to print them .
I want data to be seen in html pages like this :
product1 modelpm1
modelpm2
product2 modelpm3
modelpm4
modelpm5
and so on .....
Of course I have created table and all the html related tags properly, but I am unable to figure out how to print this way in template.
Its the same in the template:
<ul>
{% for product in objects %}
<li>{{ product }}
<ul>
{% for product_model in product.productmodel_set.all %}
<li>{{ product_model }}</li>
{% endfor %}
</ul></li>
{% endfor %}
</ul>
Use the following view:
def product_list(request):
return render(request, 'template.html', {'objects': Product.objects.all()})
Or, if you prefer, use the generic views:
class ProductList(ListView):
template = 'template.html'
queryset = Product.objects.all()