feat: index and product detail view
This commit is contained in:
@@ -218,3 +218,5 @@ SIMPLE_JWT = {
|
|||||||
}
|
}
|
||||||
|
|
||||||
TAILWIND_APP_NAME = "theme"
|
TAILWIND_APP_NAME = "theme"
|
||||||
|
|
||||||
|
ITEMS_PER_PAGE = 10
|
||||||
|
|||||||
@@ -9,7 +9,7 @@ from rest_framework.viewsets import ModelViewSet
|
|||||||
from rest_framework.exceptions import ValidationError
|
from rest_framework.exceptions import ValidationError
|
||||||
from rest_framework.permissions import IsAdminUser
|
from rest_framework.permissions import IsAdminUser
|
||||||
|
|
||||||
from shop.api.v1.filters import ProductFilter
|
from shop.filters import ProductFilter
|
||||||
from shop.api.v1.serializers import (
|
from shop.api.v1.serializers import (
|
||||||
ProductSerializer,
|
ProductSerializer,
|
||||||
ProductPriceSerializer,
|
ProductPriceSerializer,
|
||||||
|
|||||||
@@ -0,0 +1,27 @@
|
|||||||
|
from django.conf import settings
|
||||||
|
from django.core.paginator import Paginator
|
||||||
|
|
||||||
|
|
||||||
|
class FilteredQuerysetMixin:
|
||||||
|
queryset = None
|
||||||
|
filter_class = None
|
||||||
|
|
||||||
|
def get_filter_class(self):
|
||||||
|
return self.filter_class
|
||||||
|
|
||||||
|
def get_queryset(self):
|
||||||
|
filter_cls = self.get_filter_class()
|
||||||
|
filter_obj = filter_cls(self.request.GET)
|
||||||
|
filter_obj.is_valid()
|
||||||
|
qs = filter_obj.filter_queryset(self.queryset)
|
||||||
|
return qs
|
||||||
|
|
||||||
|
|
||||||
|
class PaginatedQuerysetMixin:
|
||||||
|
objects_per_page = settings.ITEMS_PER_PAGE or 20
|
||||||
|
|
||||||
|
def get_paginated_queryset(self, qs):
|
||||||
|
paginator = Paginator(qs, self.objects_per_page)
|
||||||
|
page = self.request.GET.get("page", 1)
|
||||||
|
qs = paginator.get_page(page)
|
||||||
|
return qs
|
||||||
@@ -0,0 +1,90 @@
|
|||||||
|
<div class="mb-4">
|
||||||
|
<div
|
||||||
|
class="rounded-lg border border-gray-200 bg-white p-6 shadow-sm dark:border-gray-700 dark:bg-gray-800">
|
||||||
|
<div class="h-56 w-full">
|
||||||
|
<a href="#">
|
||||||
|
<img class="mx-auto h-full dark:hidden" src="{{ product.images.all|first }}"
|
||||||
|
alt="{{ product.name }}"/>
|
||||||
|
<img class="mx-auto hidden h-full dark:block" src="{{ product.images.all|first }}"
|
||||||
|
alt="{{ product.name }}"/>
|
||||||
|
</a>
|
||||||
|
</div>
|
||||||
|
<div class="pt-6">
|
||||||
|
|
||||||
|
<a href="{% url 'shop:product_detail' pk=product.pk %}"
|
||||||
|
class="text-lg font-semibold leading-tight text-gray-900 hover:underline dark:text-white">{{ product.name }}</a>
|
||||||
|
|
||||||
|
<div class="mt-2 flex items-center gap-2">
|
||||||
|
<div class="flex items-center">
|
||||||
|
<svg class="h-4 w-4 text-yellow-400" aria-hidden="true" xmlns="http://www.w3.org/2000/svg"
|
||||||
|
fill="currentColor" viewBox="0 0 24 24">
|
||||||
|
<path
|
||||||
|
d="M13.8 4.2a2 2 0 0 0-3.6 0L8.4 8.4l-4.6.3a2 2 0 0 0-1.1 3.5l3.5 3-1 4.4c-.5 1.7 1.4 3 2.9 2.1l3.9-2.3 3.9 2.3c1.5 1 3.4-.4 3-2.1l-1-4.4 3.4-3a2 2 0 0 0-1.1-3.5l-4.6-.3-1.8-4.2Z"/>
|
||||||
|
</svg>
|
||||||
|
|
||||||
|
<svg class="h-4 w-4 text-yellow-400" aria-hidden="true" xmlns="http://www.w3.org/2000/svg"
|
||||||
|
fill="currentColor" viewBox="0 0 24 24">
|
||||||
|
<path
|
||||||
|
d="M13.8 4.2a2 2 0 0 0-3.6 0L8.4 8.4l-4.6.3a2 2 0 0 0-1.1 3.5l3.5 3-1 4.4c-.5 1.7 1.4 3 2.9 2.1l3.9-2.3 3.9 2.3c1.5 1 3.4-.4 3-2.1l-1-4.4 3.4-3a2 2 0 0 0-1.1-3.5l-4.6-.3-1.8-4.2Z"/>
|
||||||
|
</svg>
|
||||||
|
|
||||||
|
<svg class="h-4 w-4 text-yellow-400" aria-hidden="true" xmlns="http://www.w3.org/2000/svg"
|
||||||
|
fill="currentColor" viewBox="0 0 24 24">
|
||||||
|
<path
|
||||||
|
d="M13.8 4.2a2 2 0 0 0-3.6 0L8.4 8.4l-4.6.3a2 2 0 0 0-1.1 3.5l3.5 3-1 4.4c-.5 1.7 1.4 3 2.9 2.1l3.9-2.3 3.9 2.3c1.5 1 3.4-.4 3-2.1l-1-4.4 3.4-3a2 2 0 0 0-1.1-3.5l-4.6-.3-1.8-4.2Z"/>
|
||||||
|
</svg>
|
||||||
|
|
||||||
|
<svg class="h-4 w-4 text-yellow-400" aria-hidden="true" xmlns="http://www.w3.org/2000/svg"
|
||||||
|
fill="currentColor" viewBox="0 0 24 24">
|
||||||
|
<path
|
||||||
|
d="M13.8 4.2a2 2 0 0 0-3.6 0L8.4 8.4l-4.6.3a2 2 0 0 0-1.1 3.5l3.5 3-1 4.4c-.5 1.7 1.4 3 2.9 2.1l3.9-2.3 3.9 2.3c1.5 1 3.4-.4 3-2.1l-1-4.4 3.4-3a2 2 0 0 0-1.1-3.5l-4.6-.3-1.8-4.2Z"/>
|
||||||
|
</svg>
|
||||||
|
|
||||||
|
<svg class="h-4 w-4 text-yellow-400" aria-hidden="true" xmlns="http://www.w3.org/2000/svg"
|
||||||
|
fill="currentColor" viewBox="0 0 24 24">
|
||||||
|
<path
|
||||||
|
d="M13.8 4.2a2 2 0 0 0-3.6 0L8.4 8.4l-4.6.3a2 2 0 0 0-1.1 3.5l3.5 3-1 4.4c-.5 1.7 1.4 3 2.9 2.1l3.9-2.3 3.9 2.3c1.5 1 3.4-.4 3-2.1l-1-4.4 3.4-3a2 2 0 0 0-1.1-3.5l-4.6-.3-1.8-4.2Z"/>
|
||||||
|
</svg>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<p class="text-sm font-medium text-gray-900 dark:text-white">5.0</p>
|
||||||
|
<p class="text-sm font-medium text-gray-500 dark:text-gray-400">(455)</p>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<ul class="mt-2 flex items-center gap-4">
|
||||||
|
<li class="flex items-center gap-2">
|
||||||
|
<svg class="h-4 w-4 text-gray-500 dark:text-gray-400" aria-hidden="true"
|
||||||
|
xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 24 24">
|
||||||
|
<path stroke="currentColor" stroke-linecap="round" stroke-linejoin="round" stroke-width="2"
|
||||||
|
d="M13 7h6l2 4m-8-4v8m0-8V6a1 1 0 0 0-1-1H4a1 1 0 0 0-1 1v9h2m8 0H9m4 0h2m4 0h2v-4m0 0h-5m3.5 5.5a2.5 2.5 0 1 1-5 0 2.5 2.5 0 0 1 5 0Zm-10 0a2.5 2.5 0 1 1-5 0 2.5 2.5 0 0 1 5 0Z"/>
|
||||||
|
</svg>
|
||||||
|
<p class="text-sm font-medium text-gray-500 dark:text-gray-400">Fast Delivery</p>
|
||||||
|
</li>
|
||||||
|
|
||||||
|
<li class="flex items-center gap-2">
|
||||||
|
<svg class="h-4 w-4 text-gray-500 dark:text-gray-400" aria-hidden="true"
|
||||||
|
xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 24 24">
|
||||||
|
<path stroke="currentColor" stroke-linecap="round" stroke-width="2"
|
||||||
|
d="M8 7V6c0-.6.4-1 1-1h11c.6 0 1 .4 1 1v7c0 .6-.4 1-1 1h-1M3 18v-7c0-.6.4-1 1-1h11c.6 0 1 .4 1 1v7c0 .6-.4 1-1 1H4a1 1 0 0 1-1-1Zm8-3.5a1.5 1.5 0 1 1-3 0 1.5 1.5 0 0 1 3 0Z"/>
|
||||||
|
</svg>
|
||||||
|
<p class="text-sm font-medium text-gray-500 dark:text-gray-400">Best Price</p>
|
||||||
|
</li>
|
||||||
|
</ul>
|
||||||
|
|
||||||
|
<div class="mt-4 flex items-center justify-between gap-4">
|
||||||
|
<p
|
||||||
|
class="text-2xl font-extrabold leading-tight text-gray-900 dark:text-white">{{ product.price.price_with_tax }}€</p>
|
||||||
|
|
||||||
|
<button type="button"
|
||||||
|
class="inline-flex items-center rounded-lg bg-primary-700 px-5 py-2.5 text-sm font-medium text-white hover:bg-primary-800 focus:outline-none focus:ring-4 focus:ring-primary-300 dark:bg-primary-600 dark:hover:bg-primary-700 dark:focus:ring-primary-800">
|
||||||
|
<svg class="-ms-2 me-2 h-5 w-5" aria-hidden="true" xmlns="http://www.w3.org/2000/svg" width="24"
|
||||||
|
height="24" fill="none" viewBox="0 0 24 24">
|
||||||
|
<path stroke="currentColor" stroke-linecap="round" stroke-linejoin="round" stroke-width="2"
|
||||||
|
d="M4 4h1.5L8 16m0 0h8m-8 0a2 2 0 1 0 0 4 2 2 0 0 0 0-4Zm8 0a2 2 0 1 0 0 4 2 2 0 0 0 0-4Zm.75-3H7.5M11 7H6.312M17 4v6m-3-3h6"/>
|
||||||
|
</svg>
|
||||||
|
Add to cart
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
@@ -0,0 +1,9 @@
|
|||||||
|
{% extends 'theme/base.html' %}
|
||||||
|
|
||||||
|
{% load static %}
|
||||||
|
|
||||||
|
{% block main %}
|
||||||
|
<main class="bg-gray-50 dark:bg-gray-900" hx-get="{% url 'shop:list_products' %}" hx-trigger="load">
|
||||||
|
|
||||||
|
</main>
|
||||||
|
{% endblock %}
|
||||||
@@ -0,0 +1,21 @@
|
|||||||
|
{% extends 'theme/base.html' %}
|
||||||
|
|
||||||
|
{% load static %}
|
||||||
|
|
||||||
|
{% block main %}
|
||||||
|
<section class="py-8 antialiased md:py-12">
|
||||||
|
<section class="bg-gray-50 py-8 antialiased dark:bg-gray-900 md:py-12">
|
||||||
|
<div class="mb-4 grid gap-4 sm:grid-cols-2 md:mb-8 lg:grid-cols-3 xl:grid-cols-4">
|
||||||
|
{% for product in products %}
|
||||||
|
{% include 'components/product_card.html' %}
|
||||||
|
{% endfor %}
|
||||||
|
<div class="w-full text-center">
|
||||||
|
<button type="button"
|
||||||
|
class="rounded-lg border border-gray-200 bg-white px-5 py-2.5 text-sm font-medium text-gray-900 hover:bg-gray-100 hover:text-primary-700 focus:z-10 focus:outline-none focus:ring-4 focus:ring-gray-100 dark:border-gray-600 dark:bg-gray-800 dark:text-gray-400 dark:hover:bg-gray-700 dark:hover:text-white dark:focus:ring-gray-700">
|
||||||
|
Show more
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</section>
|
||||||
|
</section>
|
||||||
|
{% endblock %}
|
||||||
@@ -0,0 +1,174 @@
|
|||||||
|
{% extends 'theme/base.html' %}
|
||||||
|
|
||||||
|
{% load static %}
|
||||||
|
|
||||||
|
{% block main %}
|
||||||
|
|
||||||
|
<section class="min-w-full py-8 bg-white md:py-16 dark:bg-gray-900 antialiased">
|
||||||
|
<div class="max-w-screen-xl px-4 mx-auto 2xl:px-0">
|
||||||
|
<div class="lg:grid lg:grid-cols-2 lg:gap-8 xl:gap-16">
|
||||||
|
<div class="shrink-0 max-w-md lg:max-w-lg mx-auto">
|
||||||
|
<img class="w-full dark:hidden" src="{{ product.images.all|first }}" alt="" />
|
||||||
|
<img class="w-full hidden dark:block" src="{{ product.images.all|first }}" alt="" />
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="mt-6 sm:mt-8 lg:mt-0">
|
||||||
|
<h1
|
||||||
|
class="text-xl font-semibold text-gray-900 sm:text-2xl dark:text-white"
|
||||||
|
>
|
||||||
|
{{ product.name }}
|
||||||
|
</h1>
|
||||||
|
<div class="mt-4 sm:items-center sm:gap-4 sm:flex">
|
||||||
|
<p
|
||||||
|
class="text-2xl font-extrabold text-gray-900 sm:text-3xl dark:text-white"
|
||||||
|
>
|
||||||
|
{{ product.price.price_with_tax }} €
|
||||||
|
</p>
|
||||||
|
|
||||||
|
<div class="flex items-center gap-2 mt-2 sm:mt-0">
|
||||||
|
<div class="flex items-center gap-1">
|
||||||
|
<svg
|
||||||
|
class="w-4 h-4 text-yellow-300"
|
||||||
|
aria-hidden="true"
|
||||||
|
xmlns="http://www.w3.org/2000/svg"
|
||||||
|
width="24"
|
||||||
|
height="24"
|
||||||
|
fill="currentColor"
|
||||||
|
viewBox="0 0 24 24"
|
||||||
|
>
|
||||||
|
<path
|
||||||
|
d="M13.849 4.22c-.684-1.626-3.014-1.626-3.698 0L8.397 8.387l-4.552.361c-1.775.14-2.495 2.331-1.142 3.477l3.468 2.937-1.06 4.392c-.413 1.713 1.472 3.067 2.992 2.149L12 19.35l3.897 2.354c1.52.918 3.405-.436 2.992-2.15l-1.06-4.39 3.468-2.938c1.353-1.146.633-3.336-1.142-3.477l-4.552-.36-1.754-4.17Z"
|
||||||
|
/>
|
||||||
|
</svg>
|
||||||
|
<svg
|
||||||
|
class="w-4 h-4 text-yellow-300"
|
||||||
|
aria-hidden="true"
|
||||||
|
xmlns="http://www.w3.org/2000/svg"
|
||||||
|
width="24"
|
||||||
|
height="24"
|
||||||
|
fill="currentColor"
|
||||||
|
viewBox="0 0 24 24"
|
||||||
|
>
|
||||||
|
<path
|
||||||
|
d="M13.849 4.22c-.684-1.626-3.014-1.626-3.698 0L8.397 8.387l-4.552.361c-1.775.14-2.495 2.331-1.142 3.477l3.468 2.937-1.06 4.392c-.413 1.713 1.472 3.067 2.992 2.149L12 19.35l3.897 2.354c1.52.918 3.405-.436 2.992-2.15l-1.06-4.39 3.468-2.938c1.353-1.146.633-3.336-1.142-3.477l-4.552-.36-1.754-4.17Z"
|
||||||
|
/>
|
||||||
|
</svg>
|
||||||
|
<svg
|
||||||
|
class="w-4 h-4 text-yellow-300"
|
||||||
|
aria-hidden="true"
|
||||||
|
xmlns="http://www.w3.org/2000/svg"
|
||||||
|
width="24"
|
||||||
|
height="24"
|
||||||
|
fill="currentColor"
|
||||||
|
viewBox="0 0 24 24"
|
||||||
|
>
|
||||||
|
<path
|
||||||
|
d="M13.849 4.22c-.684-1.626-3.014-1.626-3.698 0L8.397 8.387l-4.552.361c-1.775.14-2.495 2.331-1.142 3.477l3.468 2.937-1.06 4.392c-.413 1.713 1.472 3.067 2.992 2.149L12 19.35l3.897 2.354c1.52.918 3.405-.436 2.992-2.15l-1.06-4.39 3.468-2.938c1.353-1.146.633-3.336-1.142-3.477l-4.552-.36-1.754-4.17Z"
|
||||||
|
/>
|
||||||
|
</svg>
|
||||||
|
<svg
|
||||||
|
class="w-4 h-4 text-yellow-300"
|
||||||
|
aria-hidden="true"
|
||||||
|
xmlns="http://www.w3.org/2000/svg"
|
||||||
|
width="24"
|
||||||
|
height="24"
|
||||||
|
fill="currentColor"
|
||||||
|
viewBox="0 0 24 24"
|
||||||
|
>
|
||||||
|
<path
|
||||||
|
d="M13.849 4.22c-.684-1.626-3.014-1.626-3.698 0L8.397 8.387l-4.552.361c-1.775.14-2.495 2.331-1.142 3.477l3.468 2.937-1.06 4.392c-.413 1.713 1.472 3.067 2.992 2.149L12 19.35l3.897 2.354c1.52.918 3.405-.436 2.992-2.15l-1.06-4.39 3.468-2.938c1.353-1.146.633-3.336-1.142-3.477l-4.552-.36-1.754-4.17Z"
|
||||||
|
/>
|
||||||
|
</svg>
|
||||||
|
<svg
|
||||||
|
class="w-4 h-4 text-yellow-300"
|
||||||
|
aria-hidden="true"
|
||||||
|
xmlns="http://www.w3.org/2000/svg"
|
||||||
|
width="24"
|
||||||
|
height="24"
|
||||||
|
fill="currentColor"
|
||||||
|
viewBox="0 0 24 24"
|
||||||
|
>
|
||||||
|
<path
|
||||||
|
d="M13.849 4.22c-.684-1.626-3.014-1.626-3.698 0L8.397 8.387l-4.552.361c-1.775.14-2.495 2.331-1.142 3.477l3.468 2.937-1.06 4.392c-.413 1.713 1.472 3.067 2.992 2.149L12 19.35l3.897 2.354c1.52.918 3.405-.436 2.992-2.15l-1.06-4.39 3.468-2.938c1.353-1.146.633-3.336-1.142-3.477l-4.552-.36-1.754-4.17Z"
|
||||||
|
/>
|
||||||
|
</svg>
|
||||||
|
</div>
|
||||||
|
<p
|
||||||
|
class="text-sm font-medium leading-none text-gray-500 dark:text-gray-400"
|
||||||
|
>
|
||||||
|
(5.0)
|
||||||
|
</p>
|
||||||
|
<a
|
||||||
|
href="#"
|
||||||
|
class="text-sm font-medium leading-none text-gray-900 underline hover:no-underline dark:text-white"
|
||||||
|
>
|
||||||
|
345 Reviews
|
||||||
|
</a>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="mt-6 sm:gap-4 sm:items-center sm:flex sm:mt-8">
|
||||||
|
<a
|
||||||
|
href="#"
|
||||||
|
title=""
|
||||||
|
class="flex items-center justify-center py-2.5 px-5 text-sm font-medium text-gray-900 focus:outline-none bg-white rounded-lg border border-gray-200 hover:bg-gray-100 hover:text-primary-700 focus:z-10 focus:ring-4 focus:ring-gray-100 dark:focus:ring-gray-700 dark:bg-gray-800 dark:text-gray-400 dark:border-gray-600 dark:hover:text-white dark:hover:bg-gray-700"
|
||||||
|
role="button"
|
||||||
|
>
|
||||||
|
<svg
|
||||||
|
class="w-5 h-5 -ms-2 me-2"
|
||||||
|
aria-hidden="true"
|
||||||
|
xmlns="http://www.w3.org/2000/svg"
|
||||||
|
width="24"
|
||||||
|
height="24"
|
||||||
|
fill="none"
|
||||||
|
viewBox="0 0 24 24"
|
||||||
|
>
|
||||||
|
<path
|
||||||
|
stroke="currentColor"
|
||||||
|
stroke-linecap="round"
|
||||||
|
stroke-linejoin="round"
|
||||||
|
stroke-width="2"
|
||||||
|
d="M12.01 6.001C6.5 1 1 8 5.782 13.001L12.011 20l6.23-7C23 8 17.5 1 12.01 6.002Z"
|
||||||
|
/>
|
||||||
|
</svg>
|
||||||
|
Add to favorites
|
||||||
|
</a>
|
||||||
|
|
||||||
|
<a
|
||||||
|
href="#"
|
||||||
|
title=""
|
||||||
|
class="text-white mt-4 sm:mt-0 bg-primary-700 hover:bg-primary-800 focus:ring-4 focus:ring-primary-300 font-medium rounded-lg text-sm px-5 py-2.5 dark:bg-primary-600 dark:hover:bg-primary-700 focus:outline-none dark:focus:ring-primary-800 flex items-center justify-center"
|
||||||
|
role="button"
|
||||||
|
>
|
||||||
|
<svg
|
||||||
|
class="w-5 h-5 -ms-2 me-2"
|
||||||
|
aria-hidden="true"
|
||||||
|
xmlns="http://www.w3.org/2000/svg"
|
||||||
|
width="24"
|
||||||
|
height="24"
|
||||||
|
fill="none"
|
||||||
|
viewBox="0 0 24 24"
|
||||||
|
>
|
||||||
|
<path
|
||||||
|
stroke="currentColor"
|
||||||
|
stroke-linecap="round"
|
||||||
|
stroke-linejoin="round"
|
||||||
|
stroke-width="2"
|
||||||
|
d="M4 4h1.5L8 16m0 0h8m-8 0a2 2 0 1 0 0 4 2 2 0 0 0 0-4Zm8 0a2 2 0 1 0 0 4 2 2 0 0 0 0-4Zm.75-3H7.5M11 7H6.312M17 4v6m-3-3h6"
|
||||||
|
/>
|
||||||
|
</svg>
|
||||||
|
|
||||||
|
Add to cart
|
||||||
|
</a>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<hr class="my-6 md:my-8 border-gray-200 dark:border-gray-800" />
|
||||||
|
|
||||||
|
<p class="mb-6 text-gray-500 dark:text-gray-400">
|
||||||
|
{{ product.description }}
|
||||||
|
</p>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</section>
|
||||||
|
{% endblock %}
|
||||||
+3
-1
@@ -1,9 +1,11 @@
|
|||||||
from django.urls import path
|
from django.urls import path
|
||||||
from shop.views import index
|
from shop.views import index, list_products, product_detail
|
||||||
|
|
||||||
app_name = "shop"
|
app_name = "shop"
|
||||||
|
|
||||||
|
|
||||||
urlpatterns = [
|
urlpatterns = [
|
||||||
path("", index, name="index"),
|
path("", index, name="index"),
|
||||||
|
path("products/<int:pk>/", product_detail, name="product_detail"),
|
||||||
|
path("shop/components/products/", list_products, name="list_products"),
|
||||||
]
|
]
|
||||||
|
|||||||
+40
-5
@@ -1,7 +1,42 @@
|
|||||||
from django.http.response import HttpResponse
|
from django.views.generic import TemplateView
|
||||||
from django.shortcuts import render
|
from django.shortcuts import get_object_or_404
|
||||||
|
from django.core.paginator import Paginator
|
||||||
|
|
||||||
|
from shop.filters import ProductFilter
|
||||||
|
from shop.models import Product
|
||||||
|
|
||||||
|
from shop.mixins import FilteredQuerysetMixin, PaginatedQuerysetMixin
|
||||||
|
|
||||||
|
|
||||||
# Create your views here.
|
class IndexView(TemplateView):
|
||||||
def index(request, *args, **kwargs):
|
template_name = "shop/index.html"
|
||||||
return HttpResponse("Hello there")
|
|
||||||
|
|
||||||
|
class ListProducts(TemplateView, FilteredQuerysetMixin, PaginatedQuerysetMixin):
|
||||||
|
template_name = "shop/list_products.html"
|
||||||
|
queryset = Product.objects.all()
|
||||||
|
filter_class = ProductFilter
|
||||||
|
|
||||||
|
def get_context_data(self, **kwargs):
|
||||||
|
qs = self.get_queryset()
|
||||||
|
products = self.get_paginated_queryset(qs)
|
||||||
|
|
||||||
|
return {
|
||||||
|
"products": products,
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
class ProductDetail(TemplateView):
|
||||||
|
template_name = "shop/product_detail.html"
|
||||||
|
|
||||||
|
def get_context_data(self, pk, **kwargs):
|
||||||
|
product = get_object_or_404(Product, pk=pk)
|
||||||
|
|
||||||
|
return {
|
||||||
|
"product": product,
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
index = IndexView.as_view()
|
||||||
|
list_products = ListProducts.as_view()
|
||||||
|
product_detail = ProductDetail.as_view()
|
||||||
|
|||||||
@@ -13,8 +13,10 @@
|
|||||||
{% endblock %}
|
{% endblock %}
|
||||||
</head>
|
</head>
|
||||||
<body>
|
<body>
|
||||||
|
<main>
|
||||||
{% block main %}
|
{% block main %}
|
||||||
|
|
||||||
{% endblock %}
|
{% endblock %}
|
||||||
|
</main>
|
||||||
</body>
|
</body>
|
||||||
</html>
|
</html>
|
||||||
|
|||||||
Reference in New Issue
Block a user