Skip to content

Fix/shoppingcart product photo full paths #247

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 14 commits into from
Feb 1, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
453 changes: 425 additions & 28 deletions README.md

Large diffs are not rendered by default.

6 changes: 3 additions & 3 deletions backend/api/orders_views.py
Original file line number Diff line number Diff line change
Expand Up @@ -41,7 +41,7 @@
StripePaySuccessPageSerializer,
StripeSessionCreateSerializer,
)
from .products_serializers import CouponSerializer
from .products_serializers import CouponApplySerializer
from .products_views import STATUS_200_RESPONSE_ON_DELETE_IN_DOCS
from core.loggers import logger
from core.utils import generate_order_number
Expand Down Expand Up @@ -114,7 +114,7 @@
decorator=swagger_auto_schema(
operation_summary="Apply promocode",
responses={
201: CouponSerializer,
201: CouponApplySerializer,
400: ErrorResponse406Serializer,
403: ErrorResponse403Serializer,
},
Expand All @@ -137,7 +137,7 @@ def get_serializer_class(self):
if self.request.method in permissions.SAFE_METHODS:
return ShoppingCartListSerializer
if self.action == "coupon_apply":
return CouponSerializer
return CouponApplySerializer
return ShoppingCartSerializer

# TODO: test this endpoint
Expand Down
38 changes: 36 additions & 2 deletions backend/api/products_serializers.py
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@
from .users_serializers import UserLightSerializer
from orders.shopping_carts import ShopCart
from products.models import (
INCORRECT_COUPON_APPLY_ERROR,
MAX_PROMOTIONS_NUMBER,
Category,
Component,
Expand All @@ -17,6 +18,9 @@
Tag,
)

COUPON_PROMOTION_TYPE_API_ERROR_MESSAGE = (
f"Указан неверный тип промоакции, нужно выбрать {Promotion.COUPON}."
)
RATING_DECIMAL_PLACES = 1


Expand Down Expand Up @@ -123,7 +127,7 @@ class Meta:
fields = ("promotion_name", "promotion_slug", "discount")


class PromotionSerializer(ProducerLightSerializer):
class PromotionSerializer(PromotionLightSerializer):
"""Serializer for promotions representation."""

class Meta(PromotionLightSerializer.Meta):
Expand Down Expand Up @@ -287,7 +291,7 @@ class ProductUpdateSerializer(ProductCreateSerializer):
many=True, queryset=Promotion.objects.all()
)

class Meta(ProductSerializer.Meta):
class Meta(ProductCreateSerializer.Meta):
fields = (
"id",
"name",
Expand Down Expand Up @@ -317,6 +321,9 @@ def validate_promotions(self, value):
raise serializers.ValidationError(
ProductPromotion.MAX_PROMOTIONS_ERROR_MESSAGE
)
for promotion in value:
if promotion.promotion_type == Promotion.COUPON:
raise serializers.ValidationError(INCORRECT_COUPON_APPLY_ERROR)
return value


Expand Down Expand Up @@ -507,6 +514,33 @@ def setup_eager_loading(cls, queryset, user):


class CouponSerializer(serializers.ModelSerializer):
"""Serializer for coupons representation."""

class Meta:
model = Coupon
fields = (
"id",
"code",
"name",
"slug",
"promotion_type",
"discount",
"is_active",
"is_constant",
"start_time",
"end_time",
"conditions",
"image",
)

def validate_promotion_type(self, value):
"""Checks that promotion_type is correct."""
if value != Promotion.COUPON:
raise serializers.ValidationError(COUPON_PROMOTION_TYPE_API_ERROR_MESSAGE)
return value


class CouponApplySerializer(serializers.ModelSerializer):
"""Serializer to apply coupon promoaction to the order."""

name = serializers.ReadOnlyField()
Expand Down
67 changes: 67 additions & 0 deletions backend/api/products_views.py
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,7 @@
CategoryCreateSerializer,
CategorySerializer,
ComponentSerializer,
CouponSerializer,
FavoriteProductCreateSerializer,
FavoriteProductDeleteSerializer,
FavoriteProductSerializer,
Expand All @@ -40,6 +41,7 @@
from products.models import (
Category,
Component,
Coupon,
FavoriteProduct,
Producer,
Product,
Expand Down Expand Up @@ -511,6 +513,71 @@ class PromotionViewSet(DestroyWithPayloadMixin, viewsets.ModelViewSet):
permission_classes = [IsAdminOrReadOnly]


@method_decorator(
name="list",
decorator=swagger_auto_schema(
operation_summary="List all coupons",
operation_description="Returns a list of all the coupons",
responses={200: CouponSerializer},
),
)
@method_decorator(
name="retrieve",
decorator=swagger_auto_schema(
operation_summary="Get coupon by id",
operation_description="Retrieves a coupon by its id",
responses={200: CouponSerializer, 404: ErrorResponse404Serializer},
),
)
@method_decorator(
name="create",
decorator=swagger_auto_schema(
operation_summary="Create coupon",
operation_description="Creates a coupon (admin only)",
responses={
201: CouponSerializer,
400: ValidationErrorResponseSerializer,
401: ErrorResponse401Serializer,
403: ErrorResponse403Serializer,
},
),
)
@method_decorator(
name="partial_update",
decorator=swagger_auto_schema(
operation_summary="Edit coupon",
operation_description="Edits a coupon by its id (admin only)",
responses={
200: CouponSerializer,
400: ValidationErrorResponseSerializer,
401: ErrorResponse401Serializer,
403: ErrorResponse403Serializer,
404: ErrorResponse404Serializer,
},
),
)
@method_decorator(
name="destroy",
decorator=swagger_auto_schema(
operation_summary="Delete coupon",
operation_description="Deletes a coupon by its id (admin only)",
responses={
200: STATUS_200_RESPONSE_ON_DELETE_IN_DOCS,
401: ErrorResponse401Serializer,
403: ErrorResponse403Serializer,
404: ErrorResponse404Serializer,
},
),
)
class CouponViewSet(DestroyWithPayloadMixin, viewsets.ModelViewSet):
"""Viewset for coupons."""

http_method_names = ["get", "post", "patch", "delete"]
queryset = Coupon.objects.all()
serializer_class = CouponSerializer
permission_classes = [IsAdminOrReadOnly]


@method_decorator(
name="list",
decorator=swagger_auto_schema(
Expand Down
1 change: 1 addition & 0 deletions backend/api/recipes_serializers.py
Original file line number Diff line number Diff line change
Expand Up @@ -105,6 +105,7 @@ class Meta:
"id",
"author",
"name",
"pub_date",
"servings_quantity",
"short_description",
"text",
Expand Down
2 changes: 2 additions & 0 deletions backend/api/urls.py
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@
from .products_views import (
CategoryViewSet,
ComponentViewSet,
CouponViewSet,
FavoriteProductViewSet,
ProducerViewSet,
ProductViewSet,
Expand All @@ -33,6 +34,7 @@
router.register("tags", TagViewSet)
router.register("producers", ProducerViewSet)
router.register("promotions", PromotionViewSet)
router.register("coupons", CouponViewSet)
router.register("products", ProductViewSet)
router.register(
r"products/(?P<product_id>\d+)/reviews", ReviewViewSet, basename="reviews"
Expand Down
21 changes: 21 additions & 0 deletions backend/core/management/commands/export_data.py
Original file line number Diff line number Diff line change
Expand Up @@ -153,6 +153,19 @@ def export_promotions():
writer.writerow(row)


def export_coupons():
data = apps.get_model("products", "Coupon")
field_names = [f.name for f in data._meta.fields]
with open(
os.path.join(DATA_DIR, "coupons.csv"), "w", newline="", encoding="utf-8"
) as csvfile:
writer = csv.writer(csvfile)
writer.writerow(field_names)
for obj in data.objects.all():
row = [getattr(obj, field) for field in field_names]
writer.writerow(row)


def export_products_promotions():
data = apps.get_model("products", "ProductPromotion")
field_names = ["id", "product_id", "promotion_id"]
Expand Down Expand Up @@ -285,6 +298,8 @@ def export_orders():
"add_address",
"total_price",
"user_data",
"coupon_applied_id",
"coupon_discount",
]
with open(
os.path.join(DATA_DIR, "orders.csv"),
Expand Down Expand Up @@ -357,6 +372,8 @@ def export_recipes():
"image",
"text",
"cooking_time",
"short_description",
"servings_quantity",
]
with open(
os.path.join(DATA_DIR, "recipes.csv"), "w", newline="", encoding="utf-8"
Expand Down Expand Up @@ -426,6 +443,10 @@ def handle(self, *args, **options):
self.stdout.write(
self.style.SUCCESS("Экспорт данных модели Promotion прошёл успешно.")
)
export_coupons()
self.stdout.write(
self.style.SUCCESS("Экспорт данных модели Coupon прошёл успешно.")
)
export_products_promotions()
self.stdout.write(
self.style.SUCCESS("Экспорт данных модели ProductPromoton прошёл успешно.")
Expand Down
33 changes: 33 additions & 0 deletions backend/core/management/commands/load_data.py
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@
from products.models import (
Category,
Component,
Coupon,
FavoriteProduct,
Producer,
Product,
Expand Down Expand Up @@ -134,6 +135,29 @@ def read_promotions():
promotion.save()


def read_coupons():
with open(os.path.join(DATA_DIR, "coupons.csv"), "r", encoding="utf-8") as f:
reader = DictReader(f)
for row in reader:
coupon = Coupon(
id=row["id"],
promotion_type=row["promotion_type"],
name=row["name"],
slug=row["slug"],
code=row["code"],
discount=row["discount"],
conditions=row["conditions"],
is_active=row["is_active"],
is_constant=row["is_constant"],
image=row["image"],
)
if row.get("start_time"):
coupon.start_time = row["start_time"]
if row.get("end_time"):
coupon.start_time = row["end_time"]
coupon.save()


def read_products():
with open(os.path.join(DATA_DIR, "products.csv"), "r", encoding="utf-8") as f:
reader = DictReader(f)
Expand Down Expand Up @@ -238,6 +262,10 @@ def read_orders():
add_address=row["add_address"],
total_price=row["total_price"],
user_data=row["user_data"],
coupon_applied_id=row["coupon_applied_id"],
coupon_discount=None
if row["coupon_discount"] == ""
else row["coupon_discount"],
)
order.save()

Expand Down Expand Up @@ -322,6 +350,8 @@ def read_recipes():
image=row["image"],
text=row["text"],
cooking_time=row["cooking_time"],
short_description=row["short_description"],
servings_quantity=row["servings_quantity"],
)
recipe.save()

Expand Down Expand Up @@ -359,6 +389,8 @@ def handle(self, *args, **options):
self.stdout.write("Данные из файла components.csv загружены")
read_promotions()
self.stdout.write("Данные из файла promotions.csv загружены")
read_coupons()
self.stdout.write("Данные из файла coupons.csv загружены")
read_products()
self.stdout.write("Данные из файла products.csv загружены")
read_products_components()
Expand Down Expand Up @@ -396,6 +428,7 @@ def handle(self, *args, **options):
Producer,
Product,
Promotion,
Coupon,
Subcategory,
Tag,
Review,
Expand Down
7 changes: 6 additions & 1 deletion backend/core/utils.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,11 +2,16 @@

from django.utils import timezone

from orders.models import Order

LENGTH = 6


def generate_order_number():
"""Generates random ID."""
time = timezone.localdate()
random_int = random.randrange(100000, 1000000)
return str(time) + "-" + str(random_int)
number = str(time) + "-" + str(random_int)
if Order.objects.filter(order_number=number):
number += str(random.randrange(100000, 1000000))
return number
17 changes: 17 additions & 0 deletions backend/orders/migrations/0009_alter_order_order_number.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
# Generated by Django 4.2.7 on 2024-02-01 04:20

from django.db import migrations, models


class Migration(migrations.Migration):
dependencies = [
("orders", "0008_order_coupon_applied_order_coupon_discount_and_more"),
]

operations = [
migrations.AlterField(
model_name="order",
name="order_number",
field=models.CharField(max_length=50, unique=True, verbose_name="Number"),
),
]
2 changes: 1 addition & 1 deletion backend/orders/models.py
Original file line number Diff line number Diff line change
Expand Up @@ -94,7 +94,7 @@ class Order(models.Model):
related_name="orders",
verbose_name="Покупатель",
)
order_number = models.CharField("Number", max_length=50, default="1")
order_number = models.CharField("Number", max_length=50, unique=True)
ordering_date = models.DateTimeField(auto_now_add=True, verbose_name="DateTime")
products = models.ManyToManyField(
Product,
Expand Down
Loading