【发布时间】:2020-05-22 16:44:41
【问题描述】:
我对 CBV 感到非常困惑和羞愧,寻求帮助。
所以我设计了模型结构并确定了如下的url模式,但根本无法编写有效的CBV来促进url:
models.py
class Category(models.Model):
'''Category for men's and women's items'''
men = models.BooleanField()
women = models.BooleanField()
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 ("Men's " + self.name) if self.men else ("Women's " + self.name)
class SubCategory(models.Model):
'''Sub-category for the categories (not mandatory)'''
category = models.ForeignKey(Category, on_delete=models.CASCADE)
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 = 'Sub-category'
verbose_name_plural = 'Sub-categories'
def __str__(self):
return ("Men's " + self.name) if self.category.men else ("Women's " + self.name)
class Item(models.Model):
'''Each item represents a product'''
category = models.ForeignKey(Category, on_delete=models.CASCADE)
subcategory = models.ForeignKey(
SubCategory, on_delete=models.CASCADE, null=True, blank=True)
name = models.CharField(max_length=100)
description = models.TextField(blank=True)
price = models.IntegerField(default='0')
discount = models.IntegerField(null=True, blank=True)
uploaded_date = models.DateTimeField(
auto_now_add=True, null=True, blank=True)
class Meta:
ordering = ['-uploaded_date']
def __str__(self):
return self.name
def discounted_price(self):
'''to calculate the price after discount'''
return int(self.price * (100 - self.discount) * 0.01)
class ItemImage(models.Model):
item = models.ForeignKey(Item, on_delete=models.CASCADE)
image = models.ImageField(upload_to='itemimages', null=True, blank=True)
urls.py
app_name = 'boutique'
urlpatterns = [
# show index page
path('', views.IndexView.as_view(), name='index'),
# show categories of products for men or women
path('<slug:gender>/', views.ItemListView.as_view(), name='show-all'),
# show a specific category for men or women
path('<slug:gender>/cat_<int:category_pk>/', views.ItemListView.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.ItemListView.as_view(), name='subcategory'),
# show a specific item
path('item_<int:item_pk>/', views.ItemDetailView.as_view(), name='item'),
]
views.py
class IndexView(ListView):
'''landing page'''
model = Category
template_name = 'boutique/index.html'
context_object_name = 'categories'
class ItemListView(ListView):
'''display a list of items'''
# model = Category ??? what's the point of declaring model when get_context_data() ???
template_name = 'boutique/items.html'
context_object_name = 'categories'
paginate_by = 12
def get_object(self):
obj = get_object_or_404(Category, pk=self.kwargs.get('category_pk'))
return obj
def get_context_data(self, **kwargs):
context = super().get_context_data(**kwargs)
context['categories'] = Category.objects.all() # for rendering nav bar data
return context
class ItemDetailView(DetailView):
'''display an individual item'''
# model = Item
template_name = 'boutique/item.html'
context_object_name = 'item'
def get_object(self):
return get_object_or_404(Item, pk=self.kwargs['item_pk'])
def get_context_data(self, **kwargs):
context = super().get_context_data(**kwargs)
context['categories'] = Category.objects.all()
return context
items.html
<a href="{% url 'boutique:show-all' 'women' %}> link to categories of product for women </a>
<a href="{% url 'boutique:category' 'women' category.pk %}> link to a cat for women </a>
<a href="{% url 'boutique:subcategory' 'women' category.pk subcategory.pk %}> link to a subcat under a specific cat for women </a>
基本上,如您所见,我希望 ItemListView 根据传递给 CBV 的值呈现多个 url 路径...我可以在 FBV 中做到(传递多个值),但是,完全被 CBV 的机制弄糊涂了……
所以如果有人可以写一个示例ItemListView 并根据锚 url 模板标签(如果我的不正确),那就太棒了!谢谢!!!
编辑 1
class ItemListView(ListView):
'''display a list of items'''
model = Item
template_name = 'boutique/items.html'
# paginate_by = 12
def get_queryset(self):
# get original queryset: Item.objects.all()
qs = super().get_queryset()
# filter items: men/women
if self.kwargs['gender'] == 'women':
qs = qs.filter(category__women=True)
elif self.kwargs['gender'] == 'men':
qs = qs.filter(category__men=True)
if self.kwargs.get('category_pk'):
qs = qs.filter(category=self.kwargs.get('category_pk'))
if self.kwargs.get('subcategory_pk'):
qs = qs.filter(subcategory=self.kwargs.get('subcategory_pk'))
# print(qs)
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('gender') == 'women':
context['category_shown'] = Category.objects.filter(women=True)
if self.kwargs.get('gender') == 'men':
context['category_shown'] = Category.objects.filter(men=True)
if self.kwargs.get('category_pk'):
context['category_shown']=get_object_or_404(Category, pk=self.kwargs.get('category_pk'))
if self.kwargs.get('subcategory_pk'):
context['subcategory_shown']=get_object_or_404(SubCategory, pk=self.kwargs.get('subcategory_pk'))
# print(context)
return context
在 FiddleStix 的回答之后(我还没有去过 bluegrounds 线程),我尝试并设法使除了 ItemDetailView 之外的所有东西都能正常工作。
网址工作正常,get_queryset 函数的过滤工作正常,但是,
问题 1:我想知道这可能不够 DRY?!尽管如此,它仍然有效。那谢谢啦!!但是可以烘干吗??
问题 2:ItemDetailView 运行时,url 似乎是正确的,但是页面重定向到一个页面,该页面呈现所有类别的所有项目...
class ItemDetailView(DetailView):
'''display an individual item'''
# model = Item
template_name = 'boutique/item.html'
def get_object(self):
print(get_object_or_404(Item, pk=self.kwargs.get('item_pk')))
return get_object_or_404(Item, pk=self.kwargs.get('item_pk'))
def get_context_data(self, **kwargs):
context = super().get_context_data(**kwargs)
# add categories for navbar link texts
# context['categories'] = Category.objects.all()
print(context)
return context
对象和上下文都没有被打印出来......而且它也不会提示任何错误。我一定在某个地方犯了一个愚蠢的错误!
问题 2 的答案:
view.py
class ItemDetailView(DetailView):
'''display an individual item'''
model = Item
template_name = 'boutique/item.html'
如果不需要自定义,DetailView类应该很简单,问题出在urlpatterns:
url.py
urlpatterns = [
path('item_<int:pk>', view.ItemDetailView.as_view(), name='item'),
]
- 始终使用
<pk>作为在DetailView 中传递的值,因为它是默认值。我使用item_<int:item_pk>作为路径url。这就是为什么我不得不使用get_object()来手动获取项目对象(以覆盖默认的get_object())。正如@bluegrounds 的回答所暗示的那样,基于类的视图运行良好的原因是它们可以节省时间,因为它们拥有的默认功能。 - 如果您希望使用
item_<int:item_pk>,CBV 也提供了灵活性:只需在 View 类中覆盖pk_url_kwargs = 'item_pk'- 请随时查看 my other question: DetailView -get_objectfunction confusion 以获取说明。 @neverwalkaloner 的回答非常直接。
【问题讨论】:
-
你的问题不是很清楚。我假设您的 urls.py 正在运行,您可以转到 /women/1/ 或 /women/1/3 以获取要显示的页面。但是,我认为您想访问 CBV 中的“女性”、“1”和“3”,以便可以将它们传递回模板以呈现更多 URL?如果是这样,您可以在您的
get_context_data()中执行gender = self.kwargs['gender']。 category_pk 和 subcategory_pk 也是如此。 -
@FiddleStix 抱歉,我应该说得更清楚一点:
ImproperlyConfigured at /women/ ItemListView is missing a QuerySet. Define ItemListView.model, ItemListView.queryset, or override ItemListView.get_queryset().除了索引之外,所有网址都不起作用... -
确切地说,我不知道 url.py 中的
是如何传递到 ItemListView 中的,更不用说显示子类别列表视图时的 3 个 了。它不像FBV,一切都是在定义函数时以参数的形式传入的。但是,CBV 如何从 url 中捕获 3 个值让我感到困惑......
标签: python django django-class-based-views