Django Categories with subcategories - python

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')

Related

How can i select from 2 tables some relationed fields in django?

Im new in python-django and i really need some help. This is my first question here. I´ve searched a lot on similar questions here but didnt find my needs. I am working on a restaurant website, developing in python and django and im facing very difficulties to make the restaurant menu. This data of the menu stays in a mysql db.
I searched for select_related and prefetch related a lot, i imagine one of these will help, but its not working.
So, i have 2 tables, category_menu and menu. I have to list all categories and all the items of these categories.
I have 2 models, category_menu and menu:
class category_menu(models.Model):
name_category = models.CharField(max_length=50, null=False)
class Meta:
default_related_name = 'categories'
def __str__(self):
return self.name_category
def get_absolute_url(self):
return reverse('category_menu_edit', kwargs={'pk': self.pk})
class menu(models.Model):
item = models.CharField(max_length=50, null=False)
description = models.CharField(max_length=300, null=True)
price = models.DecimalField(max_digits=10, decimal_places=2)
category = models.ForeignKey(category_menu,related_name='categories', null=True, on_delete=models.CASCADE)
class Meta:
default_related_name = 'menus'
def __str__(self):
return self.item
def get_absolute_url(self):
return reverse('menu_edit', kwargs={'pk': self.pk})
Im my views, i have the following:
query = category_menu.objects.prefetch_related('categories').all().distinct
datamenu = {'object_list': query}
return render(request, template_name, datamenu)
In my html, just the categories are listing, but the relationed items of the menu are not.
Could you help me?
You have used "categories" in both category.Meta.default_related_name and in menu.category.related_name. That could be causing some problems.
Try removing related_name='categories' from menu.category so that it looks like this:
category = models.ForeignKey(category_menu, null=True, on_delete=models.CASCADE)
Fix the indentation of your menu model. Meta, __str__() and get_absolute_url() should all be indented the same as they are on the category_menu model, e.g.:
class menu(models.Model):
item = models.CharField(max_length=50, null=False)
description = models.CharField(max_length=300, null=True)
price = models.DecimalField(max_digits=10, decimal_places=2)
category = models.ForeignKey(category_menu, null=True, on_delete=models.CASCADE)
# These have been indented
class Meta:
default_related_name = 'menus'
def __str__(self):
return self.item
def get_absolute_url(self):
return reverse('menu_edit', kwargs={'pk': self.pk})
Migrate the database, then attempt getting your queryset without prefetch:
query = category_menu.objects.all()
In your template, you can use nested for tags to list the categories and menu items within each category. Something like this:
{% for category in object_list %}
<h2>{{ category }} ({{ category.menus.count }})</h2>
{% for item in category.menus.all %}
<div>
<h3>{{ item }}</h3>
<p>{{ item.description }}</p>
<div>${{ item.price }}</div>
</div>
{% endfor %}
{% endfor %}
If it works, try prefetching using the default_related_name on your menu model:
query = category_menu.objects.prefetch_related('menus').all()
I am not focusing on the answer here, but it may be a step to solution at least.
on menu model, could you please change the field:
category = models.ForeignKey(category_menu, related_name='categories', null=True,
to this one:
category = models.ForeignKey(category_menu, related_name='menus', null=True)

how to split post view on the same page in django

I have no idea if this question make much sense or not but i am so confused about it. I have a post list view and it is rendering some of the post here.
My question is how can I split the sections of the page.something like this.
what should be the approach of making this kind of view.
this is my posts view.py
posts/view.py
class PostListView(ListView):
model = Post
template_name = 'posts/home.html'
context_object_name = 'posts'
ordering = ['-date_posted']
def get_queryset(self):
if not self.request.user.is_authenticated:
return Post.objects.all()[:10]
else :
return super().get_queryset()
posts/models.py
from django.db import models
from django.utils import timezone
from slugger import AutoSlugField
from django.contrib.auth.models import User
from django.urls import reverse
# Create your models here.
def upload_location(instance, filename):
return "%s/%s" %(instance.slug, filename)
class Category(models.Model):
title = models.CharField(max_length= 60)
slug = AutoSlugField(populate_from='title')
parent = models.ForeignKey('self',blank=True, null=True ,related_name='children',on_delete=models.CASCADE)
updated = models.DateTimeField(auto_now=True, auto_now_add=False)
timestamp = models.DateTimeField(auto_now=False, auto_now_add=True)
def __unicode__(self):
return self.title
def __str__(self):
return self.title
class Post(models.Model):
title = models.CharField(max_length=120)
slug = AutoSlugField(populate_from='title')
image = models.ImageField(
upload_to=upload_location,
null=True,
blank=True,
)
category = models.ForeignKey(Category, on_delete=models.CASCADE)
content = models.TextField()
date_posted = models.DateTimeField(default=timezone.now)
author = models.ForeignKey(User, on_delete=models.CASCADE)
def __str__(self):
return self.title
def get_absolute_url(self, slug=None):
return reverse("posts-detail", kwargs={"slug": self.slug})
You have posts assigned to categories. Each post could be assigned only to one category (since you have FK from Post to Category). And you want to display all categories and 10 latest posts under each one.
I see several ways of how to solve that. The easiest one is to extend Category model with property, containing the queryset to retrieve related posts in the way you want them for front page.
class Post(models.Model):
title = models.CharField(max_length=255)
category = models.ForeignKey('Category', on_delete=models.CASCADE, related_name='posts')
date_posted = models.DateTimeField(default=timezone.now)
class Category(models.Model):
title = models.CharField(max_length=255)
#property
def posts_for_frontpage(self):
return self.posts.order_by('-date_posted')[:10]
class FrontpageView(ListView):
model = Category
template_name = 'frontpage.html'
context_object_name = 'categories'
def get_queryset(self):
# select some categories for frontpage
# all by default
return Category.objects.all()
and then in template
{% for category in categories %}
<h1>{{ category.title }}</h1>
<hr />
{% for post in category.posts_for_frontpage %}
<h4>{{ post.title }}</h4>
{% endfor %}
<br />
<br />
{% endfor %}
You could also play with select_related to reduce number of queries and with annotate to get all related posts.

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 %}

how to display the name field of Category model instead of [<Category: hockey>, <Category: run>]

what I'm trying to do is for sports category page display links to hockey and running category pages. I think I'm almost there, but instead of [<Category: hockey>, <Category: run>] I want hockey and run to be there.
class Category(models.Model):
name = models.CharField(max_length=128, unique=True)
parent_cat = models.ForeignKey('self', null=True, blank=True)
In template I have
{{category.category_set.all}}
my url
url(r'^category/(?P<category_name_url>[\w|\W]+)/$', views.category, name='category'),
can someone please help me out here?
Edit:My full model
class Category(models.Model):
name = models.CharField(max_length=128, unique=True)
author = models.ForeignKey(settings.AUTH_USER_MODEL)
parent_cat = models.ForeignKey('self', null=True, blank=True)
hotCat = models.BooleanField(default=False)
active = models.BooleanField(default=True)
sponsored = models.ForeignKey(Sponsored, null=True, blank=True)
objects = CategoryManager()
def __unicode__(self):
return self.name
def get_absolute_url(self):
return "/category/%s/" %self.name
def get_image_url(self):
return "%s%s" %(settings.MEDIA_URL, self.image)
You should use a for in loop in your template, to display the name for each category, try this :
{% for cat in category.category_set.all %}{{ cat.name }} {% endfor %}
But I assumed that category.category_set.all was the way you get the set of categories to traverse. I'm not sure that this is the correct way to do this (if may be a way do to this I don't know though).
Why don't you get all categories from your view and passing the set directly as something like "categories" to your render() call?
With something like this :
def myview(request):
cats = Category.objects.all()
render(request, "mytemplate.html", {"categories" : cats})

How to collect/sort items from a database with the same category name?

I have been creating a Hackernews type clone recently and bumped into some trouble with my database layout and views.
Basically, a user can post a story and choose only one category for each story. Here are my models:
models.py
class Category(models.Model):
category_name = models.CharField(max_length = 50)
created_at = models.DateTimeField(auto_now_add = True)
updated_at = models.DateTimeField(auto_now = True)
def __unicode__(self):
return self.category_name
class Meta:
verbose_name_plural = "categories"
class Story(models.Model):
title = models.CharField(max_length = 200)
url = models.URLField()
points = models.IntegerField(default = 1)
moderator = models.ForeignKey(User, related_name = 'moderated_stories')
category = models.ForeignKey(Category, related_name = 'categories')
voters = models.ManyToManyField(User, related_name = 'liked_stories')
created_at = models.DateTimeField(auto_now_add = True)
updated_at = models.DateTimeField(auto_now = True)
#property
def domain(self):
return urlparse(self.url).netloc
def __unicode__(self):
return self.title
class Meta:
verbose_name_plural = "stories"
Firstly, I would just like to confirm that this is the way it should be layed out?
This then leads onto my second problem. I want to collect all the stories that belong to a particular Category and display them at their respective urls (i.e. localhost/engineering would show all the stories within the engineering category).
Here is my view/urls so far. All it does is assign each category in the database its own url.
url.py
url(r'^(?P<category_id>[0-9]+)/$', 'stories.views.category'),
views.py
def category(request, category_id=1):
template = 'stories/category.html'
category = Category.objects.get(id = category_id)
return render_to_response(template, {
'category': category
})
What could I add to my Category view such that I collect all the stories with the same category to display within my template?
Apologies if something didn't make sense, I'm only quite new to Django.
Yes ,you can use setfor reverse relation (related objects) in the templates too:
In the template
{{ for cat_story in category.story_set.all }}
{{cat_story.title }}
{{cat_story.url }}
...
{{ endfor }}
Update: Ah, you used reated_name, then you should use categories instead of story_set. Try category.categories.all instead of category.story_set.all.
{{ for cat_story in category.categories.all }}
{{cat_story.title }}
{{cat_story.url }}
...
{{ endfor }}
Update: Yes you can, django database api usage is nearly the same in both views and in the templates. You can check related documentation from here
Above page shows below examples:
class Publication(models.Model):
title = models.CharField(max_length=30)
class Article(models.Model):
headline = models.CharField(max_length=100)
publications = models.ManyToManyField(Publication)
article1.publications.all()
publication2.article_set.all()
You can use similar syntax in the templates without the parenthesis
{{ for publication in article1.publications.all }}
{{ for article in publication2.article_set.all }}

Categories

Resources