Getting my head around Django and followed the tango with Django book, but this last issue gets me after adding subcategories, which are not included in that tutorial.
I have the following:
models.py
class Category(models.Model):
"""Category"""
name = models.CharField(max_length=50)
slug = models.SlugField()
def save(self, *args, **kwargs):
#self.slug = slugify(self.name)
self.slug = slugify(self.name)
super(Category, self).save(*args, **kwargs)
def __unicode__(self):
return self.name
class SubCategory(models.Model):
"""Sub Category"""
category = models.ForeignKey(Category)
name = models.CharField(max_length=50)
slug = models.SlugField()
def save(self, *args, **kwargs):
self.slug = slugify(self.name)
super(SubCategory, self).save(*args, **kwargs)
def __unicode__(self):
return self.name
and
urls.py
(r'^links/$', 'rango.views.links'),
(r'^links/(?P<category_name_slug>[\w\-]+)/$', 'rango.views.category'),
(r'^links/(?P<category_name_slug>[\w\-]+)/(?P<subcategory_name_slug>[\w\-]+)/$', 'rango.views.subcategory'),
and
views.py
@require_GET
def links(request):
"""Linkdirectory Page"""
category_list = Category.objects.order_by('name')
context_dict = {'categories': category_list}
return render(request, 'links.html', context_dict)
@require_GET
def category(request, category_name_slug):
"""Category Page"""
category = Category.objects.get(slug=category_name_slug)
subcategory_list = SubCategory.objects.filter(category=category)
context_dict = {'subcategories': subcategory_list}
return render(request, 'category.html', context_dict)
@require_GET
def subcategory(request, subcategory_name_slug, category_name_slug):
"""SubCategory Page"""
context_dict = {}
try:
subcategory = SubCategory.objects.get(slug=subcategory_name_slug)
context_dict['subcategory_name'] = subcategory.name
websites = Website.objects.filter(sub_categories=subcategory)
context_dict['websites'] = websites
context_dict['subcategory'] = subcategory
except SubCategory.DoesNotExist:
return render(request, 'subcategory.html', context_dict)
This all works nicely up to the point I add subcategories with the same name, e.g. the subcategory "other" for multiple categories.
I understand why, when I reach "def subcategory" my slug will return multiple subcategories so I need to limit these to the related category in some way, like a
"SELECT
subcategory = SubCategory.objects.get(slug=subcategory_name_slug)
WHERE
subcategory = SubCategory.objects.filter(category=subcategory)
CLAUSE"
or something ;)
Not sure what's the best route to take on this and how to filter these
Given that you may have two different SubCategory
objects with the same name for two different Category
objects, as you suggested you can add the Category
as an additional filter.
To achieve this I see you have a view that takes slugs for both SubCategory
and Category
, that you defined like this subcategory(request, subcategory_name_slug, category_name_slug)
. Those are sufficient to filter:
subcategory = SubCategory.objects.get(
slug=subcategory_name_slug,
category__slug=category_name_slug
)
^
|__ # This "double" underscore category__slug is a way to filter
# a related object (SubCategory.category)
# So effectively it's like filtering for SubCategory objects where
# SubCategory.category.slug is category_name_slug
You see above I've used SubCateogry.objects.get(...)
to get a single object instead of `SubCategory.objects.filter(...) which can return many objects.
To do this safely with get()
, there needs to be a guarantee that for any given Category, there will no more than one Subcategory with the same name
You can enforce this condition with unique_together
class SubCategory(models.Model):
class Meta:
unique_together = (
('category', 'name'), # since slug is based on name,
# we are sure slug will be unique too
)