feat: added tags

This commit is contained in:
Pablo Moreno
2024-05-15 22:21:20 +02:00
parent 2043440499
commit 84bd7b1951
9 changed files with 219 additions and 2 deletions
+3
View File
@@ -3,3 +3,6 @@ from django.db import models
class FileUpload(models.Model): class FileUpload(models.Model):
file = models.FileField(upload_to="uploads", blank=False, null=False) file = models.FileField(upload_to="uploads", blank=False, null=False)
def __str__(self):
return self.file.url
+28 -1
View File
@@ -1,5 +1,5 @@
from django.contrib import admin from django.contrib import admin
from shop.models import Product, ProductPrice, OrderLine, Order, Tax, Customer from shop.models import Product, ProductPrice, OrderLine, Order, Tax, Customer, Provider, Tag, ProductBatch
from unfold.admin import ModelAdmin from unfold.admin import ModelAdmin
@@ -58,3 +58,30 @@ class OrderLineAdmin(ModelAdmin):
@admin.register(Order) @admin.register(Order)
class OrderAdmin(ModelAdmin): class OrderAdmin(ModelAdmin):
list_display = ("id",) list_display = ("id",)
@admin.register(Provider)
class ProviderAdmin(ModelAdmin):
list_display = (
"vat_id",
"name",
)
@admin.register(Tag)
class TagAdmin(ModelAdmin):
list_display = (
"id",
"name",
)
@admin.register(ProductBatch)
class ProductBatchAdmin(ModelAdmin):
list_display = (
"id",
"product",
"quantity",
"provider",
)
+2
View File
@@ -8,6 +8,7 @@ from shop.api.v1.viewsets import (
OrderLineViewSet, OrderLineViewSet,
ProviderViewSet, ProviderViewSet,
ProductBatchViewSet, ProductBatchViewSet,
TagViewSet,
) )
@@ -17,6 +18,7 @@ app_name = "shop"
router = DefaultRouter() router = DefaultRouter()
router.register("taxes", TaxViewSet) router.register("taxes", TaxViewSet)
router.register("tags", TagViewSet)
router.register("customers", CustomerViewSet) router.register("customers", CustomerViewSet)
router.register("providers", ProviderViewSet) router.register("providers", ProviderViewSet)
router.register("products", ProductViewSet) router.register("products", ProductViewSet)
+31
View File
@@ -11,6 +11,7 @@ from shop.models import (
Order, Order,
ProductBatch, ProductBatch,
Provider, Provider,
Tag,
) )
@@ -24,6 +25,15 @@ class TaxSerializer(serializers.ModelSerializer):
) )
class TagSerializer(serializers.ModelSerializer):
class Meta:
model = Tag
fields = (
"id",
"name",
)
class ProductPriceSerializer(serializers.ModelSerializer): class ProductPriceSerializer(serializers.ModelSerializer):
tax = TaxSerializer() tax = TaxSerializer()
@@ -60,6 +70,23 @@ class ProductSerializer(serializers.ModelSerializer):
"is_digital_asset", "is_digital_asset",
"images", "images",
"price", "price",
"tags",
)
class ListProductSerializer(serializers.ModelSerializer):
price = ProductPriceSerializer()
tags = TagSerializer(many=True)
class Meta:
model = Product
fields = (
"name",
"description",
"stock",
"is_digital_asset",
"price",
"tags",
) )
@@ -98,6 +125,8 @@ class CreateProductSerializer(serializers.ModelSerializer):
"url", "url",
"price", "price",
"tax", "tax",
"tags",
"images",
) )
@@ -197,3 +226,5 @@ class OrderLineSerializer(serializers.ModelSerializer):
"taxes", "taxes",
"tax_value", "tax_value",
) )
+10
View File
@@ -20,6 +20,8 @@ from shop.api.v1.serializers import (
ProductBatchSerializer, ProductBatchSerializer,
CreateProductSerializer, CreateProductSerializer,
CreateProductPriceSerializer, CreateProductPriceSerializer,
TagSerializer,
ListProductSerializer,
) )
from shop.models import ( from shop.models import (
Product, Product,
@@ -30,6 +32,7 @@ from shop.models import (
OrderLine, OrderLine,
Provider, Provider,
ProductBatch, ProductBatch,
Tag,
) )
from shop.api.v1.permissions import ProductPermissions, ProductPricePermissions from shop.api.v1.permissions import ProductPermissions, ProductPricePermissions
@@ -46,6 +49,8 @@ class ProductViewSet(ModelViewSet):
def get_serializer_class(self): def get_serializer_class(self):
if self.action == "create": if self.action == "create":
return CreateProductSerializer return CreateProductSerializer
elif self.action == "list":
return ListProductSerializer
return self.serializer_class return self.serializer_class
def perform_destroy(self, instance): def perform_destroy(self, instance):
@@ -145,3 +150,8 @@ class OrderLineViewSet(ModelViewSet):
.select_related("order") .select_related("order")
.filter(order__uuid=self.kwargs.get("order_id")) .filter(order__uuid=self.kwargs.get("order_id"))
) )
class TagViewSet(ModelViewSet):
serializer_class = TagSerializer
queryset = Tag.objects.all()
@@ -0,0 +1,41 @@
# Generated by Django 5.0.6 on 2024-05-14 16:04
from django.db import migrations, models
class Migration(migrations.Migration):
dependencies = [
("files", "0001_initial"),
("shop", "0004_productbatch_provider"),
]
operations = [
migrations.CreateModel(
name="Tag",
fields=[
(
"id",
models.BigAutoField(
auto_created=True,
primary_key=True,
serialize=False,
verbose_name="ID",
),
),
("name", models.CharField(max_length=16)),
],
),
migrations.AlterField(
model_name="product",
name="images",
field=models.ManyToManyField(
to="files.fileupload", verbose_name="Imágenes"
),
),
migrations.AddField(
model_name="product",
name="tags",
field=models.ManyToManyField(to="shop.tag", verbose_name="Etiquetas"),
),
]
+16
View File
@@ -6,6 +6,17 @@ from django.utils import timezone
from django.utils.text import gettext_lazy as _ from django.utils.text import gettext_lazy as _
class Tag(models.Model):
name = models.CharField(max_length=16)
def __str__(self):
return self.name
class Meta:
verbose_name = _("Etiqueta")
verbose_name_plural = _("Etiquetas")
class Product(models.Model): class Product(models.Model):
name = models.CharField( name = models.CharField(
max_length=96, max_length=96,
@@ -30,6 +41,7 @@ class Product(models.Model):
images = models.ManyToManyField( images = models.ManyToManyField(
"files.FileUpload", blank=True, verbose_name=_("Imágenes") "files.FileUpload", blank=True, verbose_name=_("Imágenes")
) )
tags = models.ManyToManyField("shop.Tag", blank=True, verbose_name=_("Etiquetas"))
def __str__(self): def __str__(self):
return self.name return self.name
@@ -103,6 +115,10 @@ class ProductBatch(models.Model):
def __str__(self): def __str__(self):
return f"{self.code} - {self.product.name} - {self.quantity}" return f"{self.code} - {self.product.name} - {self.quantity}"
class Meta:
verbose_name = _("Remesa de producto")
verbose_name_plural = _("Remesas de productos")
class Tax(models.Model): class Tax(models.Model):
code = models.CharField( code = models.CharField(
+38 -1
View File
@@ -5,7 +5,7 @@ from rest_framework.test import APITestCase
from django.utils import timezone from django.utils import timezone
from config.tests.mixins import TestUserAuthenticationMixin from config.tests.mixins import TestUserAuthenticationMixin
from shop.models import Product, ProductPrice, Tax, ProductBatch from shop.models import Product, ProductPrice, Tax, ProductBatch, Tag
class TestProductsAPI(APITestCase, TestUserAuthenticationMixin): class TestProductsAPI(APITestCase, TestUserAuthenticationMixin):
@@ -17,6 +17,7 @@ class TestProductsAPI(APITestCase, TestUserAuthenticationMixin):
code="IVA", code="IVA",
value=21, value=21,
) )
self.tag = Tag.objects.create(name="Alimentos")
def create_products(self): def create_products(self):
self.product = Product.objects.create( self.product = Product.objects.create(
@@ -58,6 +59,7 @@ class TestProductsAPI(APITestCase, TestUserAuthenticationMixin):
"price": "1.20", "price": "1.20",
"stock": "100", "stock": "100",
"tax": self.tax.pk, "tax": self.tax.pk,
"tags": [self.tag.pk],
}, },
) )
assert response.status_code == status.HTTP_201_CREATED assert response.status_code == status.HTTP_201_CREATED
@@ -78,6 +80,9 @@ class TestProductsAPI(APITestCase, TestUserAuthenticationMixin):
"price": "1.20", "price": "1.20",
"stock": "100", "stock": "100",
"tax": self.tax.pk, "tax": self.tax.pk,
"tags": [
self.tag.pk,
],
}, },
) )
assert response.status_code == status.HTTP_201_CREATED assert response.status_code == status.HTTP_201_CREATED
@@ -105,6 +110,9 @@ class TestProductsAPI(APITestCase, TestUserAuthenticationMixin):
"price": "1.20", "price": "1.20",
"stock": "100", "stock": "100",
"tax": self.tax.pk, "tax": self.tax.pk,
"tags": [
self.tag.pk,
],
}, },
) )
assert response.status_code == status.HTTP_201_CREATED assert response.status_code == status.HTTP_201_CREATED
@@ -127,3 +135,32 @@ class TestProductsAPI(APITestCase, TestUserAuthenticationMixin):
response = self.client.delete(f"/api/v1/shop/products/{self.product.pk}/") response = self.client.delete(f"/api/v1/shop/products/{self.product.pk}/")
assert response.status_code == status.HTTP_400_BAD_REQUEST assert response.status_code == status.HTTP_400_BAD_REQUEST
def test_create_update_product_tags(self):
self.login()
tag = Tag.objects.create(name="Alimentación")
response = self.client.post(
"/api/v1/shop/products/",
{
"name": "Papafritas",
"description": "Las mejores papafritas",
"is_digital_asset": False,
"url": "",
"price": "1.20",
"stock": "100",
"tax": self.tax.pk,
"tags": [
self.tag.pk,
],
},
)
assert response.status_code == status.HTTP_201_CREATED
pk = response.data.get("id")
response = self.client.get(f"/api/v1/shop/products/{pk}/")
assert response.status_code == status.HTTP_200_OK
response = self.client.patch(f"/api/v1/shop/products/{pk}/", {"tags": [tag.pk]})
assert response.status_code == status.HTTP_200_OK
response = self.client.get(f"/api/v1/shop/products/{pk}/")
assert response.data.get("tags", [])[0] == tag.pk
+50
View File
@@ -0,0 +1,50 @@
from rest_framework import status
from rest_framework.test import APITestCase
from config.tests.mixins import TestUserAuthenticationMixin
from shop.models import Tag
class TestTagsAPI(APITestCase, TestUserAuthenticationMixin):
model_name = "tag"
def setUp(self):
self.create_user()
def create_tags(self):
Tag.objects.create(name="Deportes")
def test_fetch_taxes(self):
self.create_tags()
self.login()
response = self.client.get("/api/v1/shop/tags/")
assert response.status_code == status.HTTP_200_OK
assert len(response.data.get("results")) == 1
def test_create_retrieve_tax(self):
self.login()
response = self.client.post("/api/v1/shop/tags/", {"name": "Videojuegos"})
assert response.status_code == status.HTTP_201_CREATED
pk = response.data.get("id")
response = self.client.get(f"/api/v1/shop/tags/{pk}/")
assert response.status_code == status.HTTP_200_OK
def test_create_update_tax(self):
self.login()
response = self.client.post("/api/v1/shop/tags/", {"name": "Videojuegos"})
assert response.status_code == status.HTTP_201_CREATED
pk = response.data.get("id")
response = self.client.get(f"/api/v1/shop/tags/{pk}/")
assert response.status_code == status.HTTP_200_OK
response = self.client.patch(
f"/api/v1/shop/tags/{pk}/", {"name": "Juegos de mesa"}
)
assert response.status_code == status.HTTP_200_OK
response = self.client.get(f"/api/v1/shop/tags/{pk}/")
assert response.status_code == status.HTTP_200_OK
assert response.data.get("name") == "Juegos de mesa"