feat: added promocode getting by id, added likes add/delete, code refactoring

This commit is contained in:
ITQ
2025-01-24 22:51:44 +03:00
parent fe2bc038a9
commit afc6a5cf09
5 changed files with 127 additions and 16 deletions
+8 -5
View File
@@ -2,6 +2,7 @@ import datetime
from collections import Counter from collections import Counter
from http import HTTPStatus as status from http import HTTPStatus as status
from django.db import transaction
from django.db.models import Count, Q, Value from django.db.models import Count, Q, Value
from django.db.models.functions import Coalesce from django.db.models.functions import Coalesce
from django.http import HttpRequest, HttpResponse from django.http import HttpRequest, HttpResponse
@@ -119,10 +120,11 @@ def create_promocode(
validate_unique=False, validate_unique=False,
) )
target_obj.save() with transaction.atomic():
target_obj.save()
promocode_obj.target = target_obj promocode_obj.target = target_obj
promocode_obj.save() promocode_obj.save()
return status.CREATED, schemas.CreatePromocodeOut(id=promocode_obj.id) return status.CREATED, schemas.CreatePromocodeOut(id=promocode_obj.id)
@@ -271,9 +273,10 @@ def patch_promocode(
setattr(promocode.target, field, value) setattr(promocode.target, field, value)
if "country" in target_data: if "country" in target_data:
promocode.target.country_raw = target_data["country"] promocode.target.country_raw = target_data["country"]
promocode.target.save()
promocode.save() with transaction.atomic():
promocode.target.save()
promocode.save()
return status.OK, utils.map_promocode_to_schema(promocode) return status.OK, utils.map_promocode_to_schema(promocode)
+8
View File
@@ -88,3 +88,11 @@ class PromocodeViewOut(Schema):
is_liked_by_user: bool is_liked_by_user: bool
like_count: int like_count: int
comment_count: int comment_count: int
class PromocodeLikeOut(Schema):
status: str = "ok"
class PromocodeRemoveLikeOut(Schema):
status: str = "ok"
+106 -9
View File
@@ -1,15 +1,17 @@
import contextlib
from http import HTTPStatus as status from http import HTTPStatus as status
from django.db.models import Count, Exists, OuterRef, Q from django.db.models import Count, Exists, OuterRef, Q
from django.http import HttpRequest, HttpResponse from django.http import HttpRequest, HttpResponse
from ninja import Query, Router from ninja import Query, Router
from ninja.errors import AuthenticationError from ninja.errors import AuthenticationError, HttpError
from api.v1 import schemas as global_schemas from api.v1 import schemas as global_schemas
from api.v1.auth import UserAuth from api.v1.auth import UserAuth
from api.v1.user import schemas, utils from api.v1.user import schemas, utils
from apps.promo.models import Promocode, PromocodeActivation, PromocodeLike from apps.promo.models import Promocode, PromocodeActivation, PromocodeLike
from apps.user.models import User from apps.user.models import User
from config.errors import UniqueConstraintError
router = Router(tags=["user"]) router = Router(tags=["user"])
@@ -81,10 +83,10 @@ def signin(
}, },
exclude_none=True, exclude_none=True,
) )
def get_profile(request: HttpRequest) -> schemas.ViewUserOut: def get_profile(request: HttpRequest) -> tuple[int, schemas.ViewUserOut]:
user = request.auth user = request.auth
return utils.map_user_to_schema(user) return status.OK, utils.map_user_to_schema(user)
@router.patch( @router.patch(
@@ -98,7 +100,7 @@ def get_profile(request: HttpRequest) -> schemas.ViewUserOut:
) )
def patch_profile( def patch_profile(
request: HttpRequest, patched_fields: schemas.PatchUserIn request: HttpRequest, patched_fields: schemas.PatchUserIn
) -> schemas.ViewUserOut: ) -> tuple[int, schemas.ViewUserOut]:
user = request.auth user = request.auth
patch_data = patched_fields.dict(exclude_unset=True) patch_data = patched_fields.dict(exclude_unset=True)
@@ -107,7 +109,7 @@ def patch_profile(
user.save() user.save()
return utils.map_user_to_schema(user) return status.OK, utils.map_user_to_schema(user)
@router.get( @router.get(
@@ -123,10 +125,10 @@ def feed(
request: HttpRequest, request: HttpRequest,
filters: Query[schemas.PromocodeFeedFilters], filters: Query[schemas.PromocodeFeedFilters],
response: HttpResponse, response: HttpResponse,
) -> list[schemas.PromocodeViewOut]: ) -> tuple[status.OK, list[schemas.PromocodeViewOut]]:
user: User = request.auth user: User = request.auth
promocodes = Promocode.objects promocodes = Promocode.objects.select_related("target")
promocodes = promocodes.filter( promocodes = promocodes.filter(
Q( Q(
@@ -138,7 +140,7 @@ def feed(
) )
) )
promocodes = promocodes.annotate( promocodes = promocodes.prefetch_related("likes", "comments").annotate(
like_count=Count("likes"), like_count=Count("likes"),
comment_count=Count("comments"), comment_count=Count("comments"),
is_liked_by_user=Exists( is_liked_by_user=Exists(
@@ -166,6 +168,101 @@ def feed(
promocodes = promocodes[filters.offset : filters.offset + filters.limit] promocodes = promocodes[filters.offset : filters.offset + filters.limit]
return [ return status.OK, [
utils.map_promocode_to_schema(promocode) for promocode in promocodes utils.map_promocode_to_schema(promocode) for promocode in promocodes
] ]
@router.get(
"/promo/{promocode_id}",
auth=UserAuth(),
response={
status.OK: schemas.PromocodeViewOut,
status.BAD_REQUEST: global_schemas.ValidationError,
},
exclude_none=True,
)
def get_promocode(
request: HttpRequest, promocode_id: str
) -> tuple[status.OK, schemas.PromocodeViewOut]:
user: User = request.auth
promocodes = Promocode.objects.filter(id=promocode_id)
if not promocodes.exists():
raise HttpError(status.NOT_FOUND, status.NOT_FOUND.phrase)
promocodes = (
promocodes.select_related("business")
.prefetch_related("likes", "comments")
.annotate(
like_count=Count("likes"),
comment_count=Count("comments"),
is_liked_by_user=Exists(
PromocodeLike.objects.filter(
promocode=OuterRef("pk"), user=user
)
),
is_activated_by_user=Exists(
PromocodeActivation.objects.filter(
promocode=OuterRef("pk"), user=user
)
),
)
)
promocode = promocodes.first()
return status.OK, utils.map_promocode_to_schema(promocode)
@router.post(
"/promo/{promocode_id}/like",
auth=UserAuth(),
response={
status.OK: schemas.PromocodeLikeOut,
status.BAD_REQUEST: global_schemas.ValidationError,
},
exclude_none=True,
)
def add_like(
request: HttpRequest, promocode_id: str
) -> tuple[status.OK, schemas.PromocodeViewOut]:
user: User = request.auth
promocodes = Promocode.objects.filter(id=promocode_id)
if not promocodes.exists():
raise HttpError(status.NOT_FOUND, status.NOT_FOUND.phrase)
with contextlib.suppress(UniqueConstraintError):
PromocodeLike.objects.create(promocode=promocodes.first(), user=user)
return status.OK, schemas.PromocodeLikeOut()
@router.delete(
"/promo/{promocode_id}/like",
auth=UserAuth(),
response={
status.OK: schemas.PromocodeRemoveLikeOut,
status.BAD_REQUEST: global_schemas.ValidationError,
},
exclude_none=True,
)
def delete_like(
request: HttpRequest, promocode_id: str
) -> tuple[status.OK, schemas.PromocodeViewOut]:
user: User = request.auth
promocodes = Promocode.objects.filter(id=promocode_id)
if not promocodes.exists():
raise HttpError(status.NOT_FOUND, status.NOT_FOUND.phrase)
with contextlib.suppress(PromocodeLike.DoesNotExist):
PromocodeLike.objects.get(
promocode=promocodes.first(), user=user
).delete()
return status.OK, schemas.PromocodeLikeOut()
@@ -1,4 +1,4 @@
# Generated by Django 5.1.5 on 2025-01-23 17:54 # Generated by Django 5.1.5 on 2025-01-24 18:26
import apps.promo.validators import apps.promo.validators
import django.core.validators import django.core.validators
@@ -86,7 +86,7 @@ class Migration(migrations.Migration):
('user', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, related_name='liked_promocodes', to='user.user')), ('user', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, related_name='liked_promocodes', to='user.user')),
], ],
options={ options={
'abstract': False, 'unique_together': {('promocode', 'user')},
}, },
), ),
] ]
+3
View File
@@ -209,3 +209,6 @@ class PromocodeLike(BaseModel):
user = models.ForeignKey( user = models.ForeignKey(
User, on_delete=models.CASCADE, related_name="liked_promocodes" User, on_delete=models.CASCADE, related_name="liked_promocodes"
) )
class Meta:
unique_together = ("promocode", "user")