You've already forked Promocode-API
mirror of
https://github.com/devitq/Promocode-API.git
synced 2026-05-22 20:57:11 +00:00
feat: added promocode getting by id, added likes add/delete, code refactoring
This commit is contained in:
@@ -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)
|
||||||
|
|
||||||
|
|||||||
@@ -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"
|
||||||
|
|||||||
@@ -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')},
|
||||||
},
|
},
|
||||||
),
|
),
|
||||||
]
|
]
|
||||||
|
|||||||
@@ -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")
|
||||||
|
|||||||
Reference in New Issue
Block a user