prepared some basic model modification for configurator implementation
rodzic
e99a07f45d
commit
51c4ef9623
|
@ -0,0 +1,16 @@
|
|||
# Generated by Django 4.1.9 on 2023-06-25 08:05
|
||||
|
||||
from django.db import migrations
|
||||
|
||||
|
||||
class Migration(migrations.Migration):
|
||||
dependencies = [
|
||||
("store", "0006_remove_orderdocument_sent"),
|
||||
]
|
||||
|
||||
operations = [
|
||||
migrations.RenameModel(
|
||||
old_name="ProductImage",
|
||||
new_name="ProductTemplateImage",
|
||||
),
|
||||
]
|
|
@ -0,0 +1,47 @@
|
|||
# Generated by Django 4.1.9 on 2023-06-25 19:24
|
||||
|
||||
from django.db import migrations, models
|
||||
import django.db.models.deletion
|
||||
import modelcluster.fields
|
||||
|
||||
|
||||
class Migration(migrations.Migration):
|
||||
dependencies = [
|
||||
("store", "0007_rename_productimage_producttemplateimage"),
|
||||
]
|
||||
|
||||
operations = [
|
||||
migrations.RemoveField(
|
||||
model_name="product",
|
||||
name="info",
|
||||
),
|
||||
migrations.AddField(
|
||||
model_name="producttemplateimage",
|
||||
name="is_main",
|
||||
field=models.BooleanField(default=False),
|
||||
),
|
||||
migrations.AlterField(
|
||||
model_name="producttemplateimage",
|
||||
name="template",
|
||||
field=modelcluster.fields.ParentalKey(
|
||||
on_delete=django.db.models.deletion.CASCADE, related_name="template_images", to="store.producttemplate"
|
||||
),
|
||||
),
|
||||
migrations.CreateModel(
|
||||
name="ProductImage",
|
||||
fields=[
|
||||
("id", models.BigAutoField(auto_created=True, primary_key=True, serialize=False, verbose_name="ID")),
|
||||
("image", models.ImageField(upload_to="")),
|
||||
("is_main", models.BooleanField(default=False)),
|
||||
(
|
||||
"product",
|
||||
modelcluster.fields.ParentalKey(
|
||||
on_delete=django.db.models.deletion.CASCADE, related_name="product_images", to="store.product"
|
||||
),
|
||||
),
|
||||
],
|
||||
options={
|
||||
"abstract": False,
|
||||
},
|
||||
),
|
||||
]
|
|
@ -0,0 +1,32 @@
|
|||
# Generated by Django 4.1.9 on 2023-06-30 16:11
|
||||
|
||||
from django.db import migrations, models
|
||||
import django.db.models.deletion
|
||||
import modelcluster.fields
|
||||
|
||||
|
||||
class Migration(migrations.Migration):
|
||||
dependencies = [
|
||||
("store", "0008_remove_product_info_producttemplateimage_is_main_and_more"),
|
||||
]
|
||||
|
||||
operations = [
|
||||
migrations.CreateModel(
|
||||
name="ProductCategoryParamValue",
|
||||
fields=[
|
||||
("id", models.BigAutoField(auto_created=True, primary_key=True, serialize=False, verbose_name="ID")),
|
||||
("value", models.CharField(max_length=255)),
|
||||
(
|
||||
"param",
|
||||
modelcluster.fields.ParentalKey(
|
||||
on_delete=django.db.models.deletion.CASCADE,
|
||||
related_name="param_values",
|
||||
to="store.productcategoryparam",
|
||||
),
|
||||
),
|
||||
],
|
||||
options={
|
||||
"abstract": False,
|
||||
},
|
||||
),
|
||||
]
|
|
@ -0,0 +1,29 @@
|
|||
# Generated by Django 4.1.9 on 2023-06-30 16:11
|
||||
|
||||
from django.db import migrations
|
||||
|
||||
|
||||
def copy_old_data(apps, schema_editor):
|
||||
TemplateParamValue = apps.get_model("store", "TemplateParamValue")
|
||||
ProductCategoryParamValue = apps.get_model("store", "ProductCategoryParamValue")
|
||||
|
||||
for param_value in TemplateParamValue.objects.all():
|
||||
ProductCategoryParamValue.objects.create(
|
||||
param=param_value.param,
|
||||
value=param_value.value
|
||||
)
|
||||
|
||||
|
||||
def remove_new_data(apps, schema_editor):
|
||||
ProductCategoryParamValue = apps.get_model("store", "ProductCategoryParamValue")
|
||||
ProductCategoryParamValue.objects.all().delete()
|
||||
|
||||
|
||||
class Migration(migrations.Migration):
|
||||
dependencies = [
|
||||
("store", "0009_productcategoryparam_order_productcategoryparamvalue"),
|
||||
]
|
||||
|
||||
operations = [
|
||||
migrations.RunPython(copy_old_data, remove_new_data),
|
||||
]
|
|
@ -0,0 +1,42 @@
|
|||
# Generated by Django 4.1.9 on 2023-07-02 09:34
|
||||
|
||||
from django.db import migrations, models
|
||||
import django.db.models.deletion
|
||||
import modelcluster.fields
|
||||
|
||||
|
||||
class Migration(migrations.Migration):
|
||||
dependencies = [
|
||||
("store", "0010_auto_20230630_1611"),
|
||||
]
|
||||
|
||||
operations = [
|
||||
migrations.CreateModel(
|
||||
name="ProductParam",
|
||||
fields=[
|
||||
("id", models.BigAutoField(auto_created=True, primary_key=True, serialize=False, verbose_name="ID")),
|
||||
(
|
||||
"param_value",
|
||||
models.ForeignKey(
|
||||
on_delete=django.db.models.deletion.CASCADE, to="store.productcategoryparamvalue"
|
||||
),
|
||||
),
|
||||
(
|
||||
"product",
|
||||
modelcluster.fields.ParentalKey(
|
||||
on_delete=django.db.models.deletion.CASCADE, related_name="product_params", to="store.product"
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
migrations.DeleteModel(
|
||||
name="TemplateParamValue",
|
||||
),
|
||||
migrations.AddField(
|
||||
model_name="product",
|
||||
name="params",
|
||||
field=models.ManyToManyField(
|
||||
blank=True, through="store.ProductParam", to="store.productcategoryparamvalue"
|
||||
),
|
||||
),
|
||||
]
|
|
@ -1,5 +1,6 @@
|
|||
import pdfkit
|
||||
import datetime
|
||||
import builtins
|
||||
|
||||
from decimal import Decimal
|
||||
from typing import Any
|
||||
|
@ -14,6 +15,7 @@ from django.template import (
|
|||
Template,
|
||||
Context
|
||||
)
|
||||
from django.core.exceptions import ValidationError
|
||||
|
||||
from modelcluster.models import ClusterableModel
|
||||
from modelcluster.fields import ParentalKey
|
||||
|
@ -27,14 +29,19 @@ from taggit.managers import TaggableManager
|
|||
from phonenumber_field.modelfields import PhoneNumberField
|
||||
from num2words import num2words
|
||||
|
||||
from store.utils import (
|
||||
notify_user_about_order,
|
||||
notify_manufacturer_about_order
|
||||
)
|
||||
from mailings.models import (
|
||||
OutgoingEmail,
|
||||
Attachment
|
||||
)
|
||||
from store.validators import ProductParamDuplicateValidator
|
||||
|
||||
|
||||
class BaseImageModel(models.Model):
|
||||
image = models.ImageField()
|
||||
is_main = models.BooleanField(default=False)
|
||||
|
||||
class Meta:
|
||||
abstract = True
|
||||
|
||||
|
||||
class PersonalData(models.Model):
|
||||
|
@ -92,6 +99,28 @@ class ProductCategoryParam(ClusterableModel):
|
|||
|
||||
def __str__(self):
|
||||
return self.key
|
||||
|
||||
panels = [
|
||||
FieldPanel("category"),
|
||||
FieldPanel("key"),
|
||||
FieldPanel("param_type"),
|
||||
InlinePanel("param_values")
|
||||
]
|
||||
|
||||
|
||||
class ProductCategoryParamValue(ClusterableModel):
|
||||
param = ParentalKey(ProductCategoryParam, on_delete=models.CASCADE, related_name="param_values")
|
||||
value = models.CharField(max_length=255)
|
||||
|
||||
def get_value(self):
|
||||
try:
|
||||
func = getattr(builtins, self.param.param_type)
|
||||
return func(self.value)
|
||||
except ValueError:
|
||||
return
|
||||
|
||||
def __str__(self):
|
||||
return f"{self.param.key}: {self.value}"
|
||||
|
||||
|
||||
class ProductTemplate(ClusterableModel):
|
||||
|
@ -106,45 +135,58 @@ class ProductTemplate(ClusterableModel):
|
|||
def __str__(self):
|
||||
return self.title
|
||||
|
||||
@property
|
||||
def main_image(self):
|
||||
try:
|
||||
return self.template_images.get(is_main=True)
|
||||
except ProductImage.DoesNotExist:
|
||||
return self.template_images.first()
|
||||
|
||||
panels = [
|
||||
FieldPanel("category"),
|
||||
FieldPanel("author"),
|
||||
FieldPanel('title'),
|
||||
FieldPanel('code'),
|
||||
FieldPanel('description'),
|
||||
InlinePanel("images"),
|
||||
InlinePanel("template_images", label="Template Images"),
|
||||
FieldPanel("tags"),
|
||||
]
|
||||
|
||||
|
||||
class ProductImage(models.Model):
|
||||
class ProductTemplateImage(BaseImageModel):
|
||||
template = ParentalKey(
|
||||
ProductTemplate, on_delete=models.CASCADE, related_name="images"
|
||||
ProductTemplate, on_delete=models.CASCADE, related_name="template_images"
|
||||
)
|
||||
image = models.ImageField()
|
||||
is_main = models.BooleanField(default=False)
|
||||
|
||||
|
||||
class Product(ClusterableModel):
|
||||
name = models.CharField(max_length=255, blank=True)
|
||||
info = models.TextField(blank=True)
|
||||
template = models.ForeignKey(ProductTemplate, on_delete=models.CASCADE, related_name="products")
|
||||
params = models.ManyToManyField(
|
||||
ProductCategoryParamValue, blank=True, through="ProductParam",
|
||||
validators=(ProductParamDuplicateValidator(),)
|
||||
)
|
||||
price = models.FloatField()
|
||||
available = models.BooleanField(default=True)
|
||||
|
||||
|
||||
panels = [
|
||||
FieldPanel("template"),
|
||||
FieldPanel("price"),
|
||||
InlinePanel("param_values"),
|
||||
FieldPanel("params"),
|
||||
FieldPanel("available"),
|
||||
FieldPanel("name"),
|
||||
FieldPanel("info")
|
||||
InlinePanel("product_images", label="Variant Images"),
|
||||
]
|
||||
|
||||
@property
|
||||
def main_image(self):
|
||||
images = self.template.images.all()
|
||||
if images:
|
||||
return images.first().image
|
||||
try:
|
||||
return self.product_images.get(is_main=True)
|
||||
except ProductImage.DoesNotExist:
|
||||
return self.product_images.first()
|
||||
|
||||
|
||||
@property
|
||||
def tags(self):
|
||||
|
@ -163,10 +205,30 @@ class Product(ClusterableModel):
|
|||
return self.name or self.template.title
|
||||
|
||||
|
||||
class TemplateParamValue(models.Model):
|
||||
param = models.ForeignKey(ProductCategoryParam, on_delete=models.CASCADE)
|
||||
product = ParentalKey(Product, on_delete=models.CASCADE, related_name="param_values")
|
||||
value = models.CharField(max_length=255)
|
||||
class ProductImage(BaseImageModel):
|
||||
product = ParentalKey(
|
||||
"Product", on_delete=models.CASCADE, related_name="product_images"
|
||||
)
|
||||
|
||||
|
||||
class ProductParam(models.Model):
|
||||
product = ParentalKey(Product, on_delete=models.CASCADE, related_name="product_params")
|
||||
param_value = models.ForeignKey(ProductCategoryParamValue, on_delete=models.CASCADE)
|
||||
|
||||
def save(self, *args, **kwargs):
|
||||
self.full_clean()
|
||||
return super().save(*args, **kwargs)
|
||||
|
||||
def clean(self) -> None:
|
||||
print("SDADASDASDASD")
|
||||
# get all of this product params with same category
|
||||
product_params_count = self.product.product_params.filter(
|
||||
param_value__param=self.param_value.param
|
||||
).count()
|
||||
if product_params_count > 1:
|
||||
raise ValidationError("Product can't have two values for one param")
|
||||
|
||||
return super().clean()
|
||||
|
||||
|
||||
class ProductListPage(Page):
|
||||
|
@ -177,9 +239,9 @@ class ProductListPage(Page):
|
|||
|
||||
def _get_items(self):
|
||||
if self.tags.all():
|
||||
return Product.objects.filter(available=True, template__tags__in=self.tags.all())
|
||||
return Product.objects.filter(available=True)
|
||||
|
||||
return ProductTemplate.objects.filter(tags__in=self.tags.all())
|
||||
return ProductTemplate.objects.all()
|
||||
|
||||
def get_context(self, request):
|
||||
context = super().get_context(request)
|
||||
items = self._get_items()
|
||||
|
|
|
@ -0,0 +1,51 @@
|
|||
{% extends 'base.html' %}
|
||||
|
||||
{% block content %}
|
||||
|
||||
<div class="card">
|
||||
|
||||
<div class="card-header text-center">
|
||||
<h2>{{template.title}}</h2>
|
||||
</div>
|
||||
|
||||
|
||||
|
||||
|
||||
<div class="card-body">
|
||||
<div class="container">
|
||||
<div class="row">
|
||||
<div class="col-6">
|
||||
<img src="{{template.main_image.image.url}}" class="img-fluid img-thumbnail" alt="Responsive image">
|
||||
</div>
|
||||
<div class="col-6">
|
||||
<p>{{template.description}}</p>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
</div>
|
||||
|
||||
<h4 class="mt-3">Dostępne konfiguracje:</h4>
|
||||
|
||||
<div class="container mt-3">
|
||||
<div class="row">
|
||||
{% for product in template.products.all %}
|
||||
<div class="col-4">
|
||||
<div class="card">
|
||||
<div class="card-header text-center">
|
||||
{{product.name}}
|
||||
</div>
|
||||
<div class="card-body">
|
||||
<img src="{{product.main_image.image.url}}" class="img-fluid img-thumbnail" alt="Responsive image">
|
||||
</div>
|
||||
<div class="card-footer text-center">
|
||||
<input type="button" class="btn btn-primary btn-block" value="Wybierz">
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
{% if forloop.counter|divisibleby:3 %}</div><div class="row">{% endif %}
|
||||
{% endfor %}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{% endblock %}
|
|
@ -3,21 +3,12 @@
|
|||
<div class="card h-100" >
|
||||
<div class="card-header text-truncate">{{item.title}}</div>
|
||||
<div class="card-body p-0">
|
||||
<img src="{{item.main_image.url}}" class="img-fluid rounded mx-auto d-block" style="width: 10rem; height: 15rem;" alt="{{item.title}}">
|
||||
<img src="{{item.main_image.image.url}}"
|
||||
class="img-fluid img-thumbnail rounded mx-auto d-block mt-2 mb-2" style="width: 13rem; height: 15rem;" alt="{{item.title}}">
|
||||
|
||||
<div class="card-footer row d-flex mt-3 m-0">
|
||||
<div class="col">
|
||||
<input type="number" id="quantity{{item.id}}" name="quantity" min="1" value="1" class="form-control form-control-sm">
|
||||
</div>
|
||||
<div class="col text-end">
|
||||
<button class="btn btn-outline-success add-to-cart-button"
|
||||
data-product-id="{{item.id}}"
|
||||
data-csrf-token="{{csrf_token}}"
|
||||
data-add-to-cart-url={% url "cart-action-add-product" %}
|
||||
data-bs-toggle="modal" data-bs-target="#addToCartModal"
|
||||
>
|
||||
<img src="{% static 'images/icons/cart.svg' %}" style="width: 1rem; height: 1rem;" alt="Koszyk"/>
|
||||
</button>
|
||||
<div class="card-footer row d-flex m-0">
|
||||
<div class="col text-center">
|
||||
<a href="{% url 'product-configure' item.id %}" class="btn btn-primary">Konfiguruj</a>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
|
|
@ -40,6 +40,14 @@ class ProductCategoryParamFactory(DjangoModelFactory):
|
|||
param_type = 'str'
|
||||
|
||||
|
||||
class ProductCategoryParamValueFactory(DjangoModelFactory):
|
||||
class Meta:
|
||||
model = 'store.ProductCategoryParamValue'
|
||||
|
||||
param = SubFactory(ProductCategoryParamFactory)
|
||||
value = Faker('name')
|
||||
|
||||
|
||||
class ProductTemplateFactory(DjangoModelFactory):
|
||||
class Meta:
|
||||
model = 'store.ProductTemplate'
|
||||
|
@ -56,12 +64,19 @@ class ProductFactory(DjangoModelFactory):
|
|||
model = 'store.Product'
|
||||
|
||||
name = Faker('name')
|
||||
info = Faker('text')
|
||||
price = Faker('pydecimal', left_digits=5, right_digits=2, positive=True)
|
||||
available = Faker('boolean')
|
||||
template = SubFactory(ProductTemplateFactory)
|
||||
|
||||
|
||||
class ProductParamFactory(DjangoModelFactory):
|
||||
class Meta:
|
||||
model = 'store.ProductParam'
|
||||
|
||||
product = SubFactory(ProductFactory)
|
||||
param = SubFactory(ProductCategoryParamFactory)
|
||||
|
||||
|
||||
class PaymentMethodFactory(DjangoModelFactory):
|
||||
class Meta:
|
||||
model = 'store.PaymentMethod'
|
||||
|
|
|
@ -3,15 +3,67 @@ from unittest.mock import patch
|
|||
from django.test import TestCase
|
||||
from django.urls import reverse
|
||||
from django.core import mail
|
||||
from django.core.exceptions import ValidationError
|
||||
|
||||
from store.tests import factories
|
||||
from store import models as store_models
|
||||
from mailings.tests.factories import MailTemplateFactory
|
||||
|
||||
|
||||
# TODO - this is fine for now, but we'll want to use factoryboy for this:
|
||||
# https://factoryboy.readthedocs.io/en/stable/
|
||||
# TODO - test have to rewritten - I'll do it tommorow
|
||||
class ProductCategoryParamValueTestCase(TestCase):
|
||||
def setUp(self):
|
||||
super().setUp()
|
||||
self.category = factories.ProductCategoryFactory()
|
||||
|
||||
|
||||
def test_get_value_success(self):
|
||||
param = factories.ProductCategoryParamFactory(
|
||||
category=self.category,
|
||||
param_type="int",
|
||||
key="test_param"
|
||||
)
|
||||
param_value = factories.ProductCategoryParamValueFactory(param=param, value="23")
|
||||
proper_value = param_value.get_value()
|
||||
self.assertEqual(proper_value, 23)
|
||||
|
||||
def test_get_value_failure_wrong_value(self):
|
||||
param = factories.ProductCategoryParamFactory(
|
||||
category=self.category,
|
||||
param_type="int",
|
||||
key="test_param"
|
||||
)
|
||||
param_value = factories.ProductCategoryParamValueFactory(param=param, value="wrong_value")
|
||||
proper_value = param_value.get_value()
|
||||
self.assertEqual(proper_value, None)
|
||||
|
||||
|
||||
class ProductTestCase(TestCase):
|
||||
|
||||
def test_category_params_one_value_success(self):
|
||||
product = factories.ProductFactory()
|
||||
param = factories.ProductCategoryParamFactory(
|
||||
category=product.template.category,
|
||||
param_type="int",
|
||||
key="test_param"
|
||||
)
|
||||
param_value = factories.ProductCategoryParamValueFactory(param=param, value="23")
|
||||
product.params.add(param_value)
|
||||
product.save()
|
||||
self.assertEqual(product.params.count(), 1)
|
||||
self.assertEqual(product.params.first().get_value(), 23)
|
||||
|
||||
def test_category_params_multiple_values_failure(self):
|
||||
product = factories.ProductFactory()
|
||||
param = factories.ProductCategoryParamFactory(
|
||||
category=product.template.category,
|
||||
param_type="int",
|
||||
key="test_param"
|
||||
)
|
||||
param_value = factories.ProductCategoryParamValueFactory(param=param, value="23")
|
||||
sec_param_value = factories.ProductCategoryParamValueFactory(param=param, value="24")
|
||||
with self.assertRaises(ValidationError):
|
||||
product.params.add(param_value)
|
||||
product.params.add(sec_param_value)
|
||||
|
||||
|
||||
class OrderProductTestCase(TestCase):
|
||||
|
|
|
@ -0,0 +1,42 @@
|
|||
from django.test import TestCase
|
||||
from django.core.exceptions import ValidationError
|
||||
|
||||
from store.tests import factories
|
||||
from store.validators import ProductParamDuplicateValidator
|
||||
from store.models import (
|
||||
ProductParam,
|
||||
Product
|
||||
)
|
||||
|
||||
|
||||
class CategoryParamValidationTestCase(TestCase):
|
||||
|
||||
def setUp(self):
|
||||
super().setUp()
|
||||
self.product = factories.ProductFactory()
|
||||
|
||||
def test_set_single_param_success(self):
|
||||
param = factories.ProductCategoryParamFactory(
|
||||
category=self.product.template.category,
|
||||
param_type="int",
|
||||
key="test_param"
|
||||
)
|
||||
param_value = factories.ProductCategoryParamValueFactory(param=param, value="23")
|
||||
param = ProductParam(param_value=param_value)
|
||||
self.product.params.add(param_value)
|
||||
self.assertEqual(self.product.params.count(), 1)
|
||||
self.assertEqual(self.product.params.first().get_value(), 23)
|
||||
|
||||
def test_set_multuiple_same_type_param_failure(self):
|
||||
param = factories.ProductCategoryParamFactory(
|
||||
category=self.product.template.category,
|
||||
param_type="int",
|
||||
key="test_param"
|
||||
)
|
||||
param_value = factories.ProductCategoryParamValueFactory(param=param, value="23")
|
||||
sec_param_value = factories.ProductCategoryParamValueFactory(param=param, value="24")
|
||||
param = ProductParam(param_value=param_value)
|
||||
self.product.params.add(param_value)
|
||||
with self.assertRaises(ValidationError):
|
||||
self.product.params.add(sec_param_value)
|
||||
|
|
@ -9,6 +9,7 @@ router.register("cart-action", store_views.CartActionView, "cart-action")
|
|||
|
||||
|
||||
urlpatterns = [
|
||||
path('product-configure/<int:pk>/', store_views.ConfigureProductView.as_view(), name='product-configure'),
|
||||
path('cart/', store_views.CartView.as_view(), name='cart'),
|
||||
path("order/", store_views.OrderView.as_view(), name="order"),
|
||||
path("order/confirm/", store_views.OrderConfirmView.as_view(), name="order-confirm")
|
||||
|
|
|
@ -0,0 +1,20 @@
|
|||
from typing import Any
|
||||
from django.core.exceptions import ValidationError
|
||||
from django.core.validators import BaseValidator
|
||||
|
||||
|
||||
class ProductParamDuplicateValidator(BaseValidator):
|
||||
|
||||
message = "Product param with this key already exists."
|
||||
code = "duplicate"
|
||||
|
||||
def __init__(self, *args, **kwargs):
|
||||
super().__init__(limit_value=1, *args, **kwargs)
|
||||
|
||||
def compare(self, param: Any, limit: Any) -> bool:
|
||||
raise ValidationError("Not implemented")
|
||||
print(param, limit)
|
||||
count = param.product.params.filter(
|
||||
param__param_value__param__key=param.param_value.param.key
|
||||
).count()
|
||||
return count >= limit
|
|
@ -20,7 +20,8 @@ from store.serializers import (
|
|||
from store.forms import CustomerDataForm
|
||||
from store.models import (
|
||||
Order,
|
||||
Product
|
||||
Product,
|
||||
ProductTemplate
|
||||
)
|
||||
|
||||
|
||||
|
@ -84,7 +85,20 @@ class CartActionView(ViewSet):
|
|||
items = cart.get_items()
|
||||
serializer = CartSerializer(instance=items, many=True)
|
||||
return Response(serializer.data, status=201)
|
||||
|
||||
|
||||
|
||||
class ConfigureProductView(View):
|
||||
template_name = "store/configure_product.html"
|
||||
|
||||
def get_context_data(self, pk: int, **kwargs: Any) -> Dict[str, Any]:
|
||||
context = {}
|
||||
context["template"] = ProductTemplate.objects.get(pk=pk)
|
||||
return context
|
||||
|
||||
def get(self, request, pk: int, *args, **kwargs):
|
||||
context = self.get_context_data(pk)
|
||||
return render(request, self.template_name, context)
|
||||
|
||||
|
||||
class OrderView(View):
|
||||
template_name = "store/order.html"
|
||||
|
|
Ładowanie…
Reference in New Issue