Django:
in urls.py
be careful which goes first
app_name = 'boutique'
urlpatterns = [
# show index page
path('', views.IndexView.as_view(), name='index'),
# show a specific item
path('item_<int:item_pk>/', views.ItemDetailView.as_view(), name='item'),
# show categories of products for men or women
path('<slug:gender>/', views.CategoryListView.as_view(), name='show-all'),
# show a specific category for men or women
path('<slug:gender>/cat_<int:category_pk>/', views.CategoryListView.as_view(), name='category'),
# show a specific subcategory under a specific category for men or women
path('<slug:gender>/cat_<int:category_pk>/subcat_<int:subcategory_pk>/', views.CategoryListView.as_view(), name='subcategory'),
]
In this file, if you put item
after subcategory
, item
view will never render the correct page, as it will be hjacked by ListView
s before it.
models - gender choices
example below:
class Category(models.Model):
'''Category for men's and women's items'''
gender = models.IntegerField(choices=[
(1, 'Women'),
(2, 'Men'),
], default=1)
name = models.CharField(max_length=100)
description = models.CharField(max_length=300, blank=True)
uploaded_date = models.DateTimeField(
auto_now_add=True, null=True, blank=True)
class Meta():
verbose_name_plural = 'Categories'
def __str__(self):
return self.get_gender_display() + ' ' + self.name
def get_category_url(self):
return reverse('boutique:category', kwargs={'gender': self.get_gender_display(), 'category_pk': self.pk})
A couple of things to notice in this example:
- gender's choices:
*
choices
is in small letters *choices
is a list with tuples - access gender's choices: you can use
get_FOO_display()
to access a field's choices, checkout this for more details
def get_absolute_url()
in models.py
models.py
from django.urls import reverse
class SomeModel(models.Model):
<---snip--->
# it can be any name you like because it doesn't seem to be inheriting from anything
def get_absolute_url(self):
return reverse('app_name:view_name', kwargs={'key': self.field_name})
# reverse returns a string for href content
When implementing the url into html tag, make sure SomeModel is accessable (either by iteration or it is an object passed through context):
*html
<a href="{{ SomeModel.get_absolute_url }}">link</a>
dynamic handling url: checkout this SO question I posted
based on the answer by Mathias, it's not possible unless installing a django-middleware-global-request
Then you can:
from django_global_request.middleware import get_request
class TestModel(models.Model):
...
def get_absolute_url(self):
request = get_request()
if request.GET.get('whatever'):
return ...
else:
return ...
You just need to make sure, you could still access this method without any available request. So make sure it's fail save and has a fallback in case of the absence of a request (like in a shell, upgrade, etc.)
get_queryset(self)
and get_context_data(self, **kwargs)
in ListView
- CCBV
- _
get_queryset()
is more useful inListView
, because it returns a queryset for templates to render.queryset
s are passed through context automagically! _ -
if a model is defined in the CBV,
get_queryset()
will automatically run and acquiremodel.objects.all()
, if to override it:
class SomeView(ListView):
<---snip--->
# context_object_name represents the result of get_queryset()
# you can directly access this from the template even if you do not set context_object_name
context_object_name = 'coobna'
def get_queryset(self):
# if you still need the default functionality of this function
# it's like to inherite from it's superior, instead of writing a whole new function
queryset = super().get_queryset()
# before returning the queryset, you can print(queryset) to debug or for referencing purposes
return queryset.filter(........)
def get_context_data(self, **kwargs):
# inherite the functionality from its 'superior'
context = super().get_context_data(**kwargs)
# now you can add more context to it
new_context = Category.objects.all()
context['new_context'] = new_context
# you can always print out the context for debugging
print(context)
return context
- An example of a ListView:
class CategoryListView(ListView):
'''display a list of items'''
model = Category
template_name = 'boutique/items.html'
# context_object_name is actually the result of `get_queryset()`
context_object_name = 'category_shown'
# paginate_by = 12
def get_queryset(self):
# get original queryset: Category.objects.all()
qs = super().get_queryset()
# filter men/women
if self.kwargs.get('gender') == 'Women':
qs = qs.filter(gender=1)
elif self.kwargs['gender'] == 'Men':
qs = qs.filter(gender=2)
if self.kwargs.get('category_pk'):
qs = qs.filter(pk=self.kwargs.get('category_pk'))
# print('\nqs= ', qs, '\n')
return qs
def get_context_data(self, **kwargs):
context = super().get_context_data(**kwargs)
# add categories for navbar link texts
context['categories'] = Category.objects.all()
if self.kwargs.get('subcategory_pk'):
context['subcategory_shown'] = get_object_or_404(
SubCategory, pk=self.kwargs.get('subcategory_pk'))
context['item_list'] = Item.objects.filter(
subcategory=self.kwargs.get('subcategory_pk'))
# print('\ncontext with subcat= ', context, '\n')
return context
# Because context_object_name actually represents the result of `get_queryset()`
# Therefore, if context_object_name is set to the same name as the context name
# the following expression can be omitted
# context['category_shown'] = self.get_queryset()
# The benefit of this is you don't need to run get_queryset() again!!
if self.kwargs.get('category_pk'):
context['item_list'] = Item.objects.filter(
category=self.kwargs.get('category_pk'))
# print('\ncontext= ', context, '\n')
return context
DetailView in CCBV
example
class ItemDetailView(DetailView):
'''display an individual item'''
model = Item
template_name = 'boutique/item.html'
# no need to specify as default context_object_name depends on the model
# they are actually the same (with lower case first letter)
# context_object_name = 'item'
A couple of things to note:
- context_object_name (see comment above)
- Two lines are sufficient for displaying standard DetailView of an item ### HOWEVER: One should be very careful about the url value passed into the CBV, checkout my question on StackO. url.py
urlpatterns = [
path('item_<int:pk>/', views.ItemDetailView.as_view(), name='item'),
]
I used path('item_<int:item_pk>/'...)
that's why it didn't work. For DetailView to work, you either have to pass in <pk>
or specify in your CBV of your pk_url_kwarg = 'item_pk'
. docs
Top comments (0)