Banner api lari #10
@@ -72,7 +72,6 @@ if env.bool("SILK_ENABLED", False):
|
|||||||
"silk.middleware.SilkyMiddleware",
|
"silk.middleware.SilkyMiddleware",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
|
||||||
ROOT_URLCONF = "config.urls"
|
ROOT_URLCONF = "config.urls"
|
||||||
|
|
||||||
TEMPLATES = [
|
TEMPLATES = [
|
||||||
@@ -119,7 +118,6 @@ DATE_FORMAT = "d.m.y"
|
|||||||
TIME_FORMAT = "H:i:s"
|
TIME_FORMAT = "H:i:s"
|
||||||
DATE_INPUT_FORMATS = ["%d.%m.%Y", "%Y.%d.%m", "%Y.%d.%m"]
|
DATE_INPUT_FORMATS = ["%d.%m.%Y", "%Y.%d.%m", "%Y.%d.%m"]
|
||||||
|
|
||||||
|
|
||||||
SEEDERS = ["core.apps.accounts.seeder.UserSeeder"]
|
SEEDERS = ["core.apps.accounts.seeder.UserSeeder"]
|
||||||
|
|
||||||
STATICFILES_DIRS = [
|
STATICFILES_DIRS = [
|
||||||
@@ -156,8 +154,6 @@ SILKY_PYTHON_PROFILER = True
|
|||||||
MODELTRANSLATION_LANGUAGES = ("uz", "ru", "en")
|
MODELTRANSLATION_LANGUAGES = ("uz", "ru", "en")
|
||||||
MODELTRANSLATION_DEFAULT_LANGUAGE = "uz"
|
MODELTRANSLATION_DEFAULT_LANGUAGE = "uz"
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
JST_LANGUAGES = [
|
JST_LANGUAGES = [
|
||||||
{
|
{
|
||||||
"code": "uz",
|
"code": "uz",
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
from django.contrib import admin
|
from django.contrib import admin
|
||||||
from unfold.admin import ModelAdmin
|
from unfold.admin import ModelAdmin
|
||||||
|
|
||||||
from core.apps.accounts.models import SearchHistory
|
from core.apps.accounts.models import SearchHistory, UserLike, UserNotification, Notification
|
||||||
|
|
||||||
|
|
||||||
@admin.register(SearchHistory)
|
@admin.register(SearchHistory)
|
||||||
@@ -10,3 +10,27 @@ class SearchHistoryAdmin(ModelAdmin):
|
|||||||
"id",
|
"id",
|
||||||
"__str__",
|
"__str__",
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
|
@admin.register(UserLike)
|
||||||
|
class UserLikeAdmin(ModelAdmin):
|
||||||
|
list_display = (
|
||||||
|
"id",
|
||||||
|
"__str__",
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
|
@admin.register(UserNotification)
|
||||||
|
class UserNotificationAdmin(ModelAdmin):
|
||||||
|
list_display = (
|
||||||
|
"id",
|
||||||
|
"__str__",
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
|
@admin.register(Notification)
|
||||||
|
class NotificationAdmin(ModelAdmin):
|
||||||
|
list_display = (
|
||||||
|
"id",
|
||||||
|
"__str__",
|
||||||
|
)
|
||||||
|
|||||||
31
core/apps/accounts/migrations/0005_userlike.py
Normal file
31
core/apps/accounts/migrations/0005_userlike.py
Normal file
@@ -0,0 +1,31 @@
|
|||||||
|
# Generated by Django 5.2.7 on 2025-11-26 10:04
|
||||||
|
|
||||||
|
import django.db.models.deletion
|
||||||
|
from django.conf import settings
|
||||||
|
from django.db import migrations, models
|
||||||
|
|
||||||
|
|
||||||
|
class Migration(migrations.Migration):
|
||||||
|
|
||||||
|
dependencies = [
|
||||||
|
('accounts', '0004_business_searchhistory'),
|
||||||
|
('api', '0013_alter_feedback_comment'),
|
||||||
|
]
|
||||||
|
|
||||||
|
operations = [
|
||||||
|
migrations.CreateModel(
|
||||||
|
name='UserLike',
|
||||||
|
fields=[
|
||||||
|
('id', models.BigAutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
|
||||||
|
('created_at', models.DateTimeField(auto_now_add=True)),
|
||||||
|
('updated_at', models.DateTimeField(auto_now=True)),
|
||||||
|
('ad', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, related_name='likes', to='api.admodel', verbose_name='Ad')),
|
||||||
|
('user', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, related_name='likes', to=settings.AUTH_USER_MODEL, verbose_name='User')),
|
||||||
|
],
|
||||||
|
options={
|
||||||
|
'verbose_name': 'User Like',
|
||||||
|
'verbose_name_plural': 'User Likes',
|
||||||
|
'db_table': 'user_like',
|
||||||
|
},
|
||||||
|
),
|
||||||
|
]
|
||||||
@@ -0,0 +1,28 @@
|
|||||||
|
# Generated by Django 5.2.7 on 2025-11-26 10:06
|
||||||
|
|
||||||
|
from django.db import migrations
|
||||||
|
|
||||||
|
|
||||||
|
class Migration(migrations.Migration):
|
||||||
|
|
||||||
|
dependencies = [
|
||||||
|
('accounts', '0005_userlike'),
|
||||||
|
]
|
||||||
|
|
||||||
|
operations = [
|
||||||
|
migrations.RenameField(
|
||||||
|
model_name='business',
|
||||||
|
old_name='address_name',
|
||||||
|
new_name='address',
|
||||||
|
),
|
||||||
|
migrations.RenameField(
|
||||||
|
model_name='business',
|
||||||
|
old_name='latitude',
|
||||||
|
new_name='lat',
|
||||||
|
),
|
||||||
|
migrations.RenameField(
|
||||||
|
model_name='business',
|
||||||
|
old_name='longitude',
|
||||||
|
new_name='long',
|
||||||
|
),
|
||||||
|
]
|
||||||
@@ -0,0 +1,49 @@
|
|||||||
|
# Generated by Django 5.2.7 on 2025-11-26 12:24
|
||||||
|
|
||||||
|
import django.db.models.deletion
|
||||||
|
from django.conf import settings
|
||||||
|
from django.db import migrations, models
|
||||||
|
|
||||||
|
|
||||||
|
class Migration(migrations.Migration):
|
||||||
|
|
||||||
|
dependencies = [
|
||||||
|
('accounts', '0006_rename_address_name_business_address_and_more'),
|
||||||
|
]
|
||||||
|
|
||||||
|
operations = [
|
||||||
|
migrations.CreateModel(
|
||||||
|
name='Notification',
|
||||||
|
fields=[
|
||||||
|
('id', models.BigAutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
|
||||||
|
('created_at', models.DateTimeField(auto_now_add=True)),
|
||||||
|
('updated_at', models.DateTimeField(auto_now=True)),
|
||||||
|
('title', models.CharField(max_length=255, verbose_name='Title')),
|
||||||
|
('description', models.TextField(verbose_name='Description')),
|
||||||
|
('notification_type', models.CharField(choices=[('System', 'System'), ('Another', 'Another')], max_length=255, verbose_name='Type')),
|
||||||
|
('long', models.FloatField(verbose_name='Long')),
|
||||||
|
('lat', models.FloatField(verbose_name='Lat')),
|
||||||
|
],
|
||||||
|
options={
|
||||||
|
'verbose_name': 'Notification',
|
||||||
|
'verbose_name_plural': 'Notifications',
|
||||||
|
'db_table': 'notification',
|
||||||
|
},
|
||||||
|
),
|
||||||
|
migrations.CreateModel(
|
||||||
|
name='UserNotification',
|
||||||
|
fields=[
|
||||||
|
('id', models.BigAutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
|
||||||
|
('created_at', models.DateTimeField(auto_now_add=True)),
|
||||||
|
('updated_at', models.DateTimeField(auto_now=True)),
|
||||||
|
('is_read', models.BooleanField(default=False, verbose_name='Read')),
|
||||||
|
('notification', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to='accounts.notification', verbose_name='Notification')),
|
||||||
|
('user', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to=settings.AUTH_USER_MODEL, verbose_name='User')),
|
||||||
|
],
|
||||||
|
options={
|
||||||
|
'verbose_name': 'User Notification',
|
||||||
|
'verbose_name_plural': 'User Notifications',
|
||||||
|
'db_table': 'user_notification',
|
||||||
|
},
|
||||||
|
),
|
||||||
|
]
|
||||||
@@ -5,3 +5,4 @@ from .address import * # noqa
|
|||||||
from .business import * # noqa
|
from .business import * # noqa
|
||||||
from .user_like import * # noqa
|
from .user_like import * # noqa
|
||||||
from .search_history import * # noqa
|
from .search_history import * # noqa
|
||||||
|
from .notification import * # noqa
|
||||||
|
|||||||
@@ -13,9 +13,9 @@ class Business(AbstractBaseModel):
|
|||||||
facebook = models.CharField(max_length=255, verbose_name=_('Facebook'))
|
facebook = models.CharField(max_length=255, verbose_name=_('Facebook'))
|
||||||
telegram = models.CharField(max_length=255, verbose_name=_('Telegram'))
|
telegram = models.CharField(max_length=255, verbose_name=_('Telegram'))
|
||||||
bio = models.TextField(verbose_name=_('Bio'))
|
bio = models.TextField(verbose_name=_('Bio'))
|
||||||
address_name = models.CharField(max_length=255, verbose_name=_('Address Name'))
|
address = models.CharField(max_length=255, verbose_name=_('Address Name'))
|
||||||
longitude = models.FloatField(verbose_name=_('Longitude'))
|
long = models.FloatField(verbose_name=_('Longitude'))
|
||||||
latitude = models.FloatField(verbose_name=_('Latitude'))
|
lat = models.FloatField(verbose_name=_('Latitude'))
|
||||||
|
|
||||||
def __str__(self):
|
def __str__(self):
|
||||||
return str(self.pk)
|
return str(self.pk)
|
||||||
|
|||||||
@@ -3,6 +3,7 @@ from django_core.models.base import AbstractBaseModel
|
|||||||
from django.utils.translation import gettext_lazy as _
|
from django.utils.translation import gettext_lazy as _
|
||||||
from core.apps.accounts.choices import NotificationType
|
from core.apps.accounts.choices import NotificationType
|
||||||
from django.contrib.auth import get_user_model
|
from django.contrib.auth import get_user_model
|
||||||
|
from model_bakery import baker
|
||||||
|
|
||||||
|
|
||||||
class Notification(AbstractBaseModel):
|
class Notification(AbstractBaseModel):
|
||||||
@@ -12,6 +13,10 @@ class Notification(AbstractBaseModel):
|
|||||||
long = models.FloatField(verbose_name=_("Long"))
|
long = models.FloatField(verbose_name=_("Long"))
|
||||||
lat = models.FloatField(verbose_name=_("Lat"))
|
lat = models.FloatField(verbose_name=_("Lat"))
|
||||||
|
|
||||||
|
@classmethod
|
||||||
|
def _baker(cls):
|
||||||
|
return baker.make(cls)
|
||||||
|
|
||||||
def __str__(self):
|
def __str__(self):
|
||||||
return str(self.pk)
|
return str(self.pk)
|
||||||
|
|
||||||
@@ -26,6 +31,10 @@ class UserNotification(AbstractBaseModel):
|
|||||||
notification = models.ForeignKey(Notification, verbose_name=_("Notification"), on_delete=models.CASCADE)
|
notification = models.ForeignKey(Notification, verbose_name=_("Notification"), on_delete=models.CASCADE)
|
||||||
is_read = models.BooleanField(verbose_name=_("Read"), default=False)
|
is_read = models.BooleanField(verbose_name=_("Read"), default=False)
|
||||||
|
|
||||||
|
@classmethod
|
||||||
|
def _baker(cls):
|
||||||
|
return baker.make(cls)
|
||||||
|
|
||||||
def __str__(self):
|
def __str__(self):
|
||||||
return str(self.pk)
|
return str(self.pk)
|
||||||
|
|
||||||
|
|||||||
@@ -3,6 +3,7 @@ from django.db import models
|
|||||||
|
|
||||||
from ..choices import RoleChoice, AccountType
|
from ..choices import RoleChoice, AccountType
|
||||||
from ..managers import UserManager
|
from ..managers import UserManager
|
||||||
|
from model_bakery import baker
|
||||||
|
|
||||||
|
|
||||||
class User(auth_models.AbstractUser):
|
class User(auth_models.AbstractUser):
|
||||||
@@ -22,5 +23,9 @@ class User(auth_models.AbstractUser):
|
|||||||
USERNAME_FIELD = "phone"
|
USERNAME_FIELD = "phone"
|
||||||
objects = UserManager()
|
objects = UserManager()
|
||||||
|
|
||||||
|
@classmethod
|
||||||
|
def _baker(cls):
|
||||||
|
return baker.make(cls)
|
||||||
|
|
||||||
def __str__(self):
|
def __str__(self):
|
||||||
return self.phone
|
return self.phone
|
||||||
|
|||||||
@@ -0,0 +1,23 @@
|
|||||||
|
from django.contrib.auth import get_user_model
|
||||||
|
from django.db import models
|
||||||
|
from django_core.models.base import AbstractBaseModel
|
||||||
|
from django.utils.translation import gettext_lazy as _
|
||||||
|
from core.apps.api.models import AdModel
|
||||||
|
from model_bakery import baker
|
||||||
|
|
||||||
|
|
||||||
|
class UserLike(AbstractBaseModel):
|
||||||
|
user = models.ForeignKey(get_user_model(), on_delete=models.CASCADE, verbose_name=_("User"), related_name="likes")
|
||||||
|
ad = models.ForeignKey(AdModel, on_delete=models.CASCADE, verbose_name=_("Ad"), related_name="likes")
|
||||||
|
|
||||||
|
@classmethod
|
||||||
|
def _baker(cls):
|
||||||
|
return baker.make(cls)
|
||||||
|
|
||||||
|
def __str__(self):
|
||||||
|
return str(self.pk)
|
||||||
|
|
||||||
|
class Meta:
|
||||||
|
db_table = "user_like"
|
||||||
|
verbose_name = _("User Like")
|
||||||
|
verbose_name_plural = _("User Likes")
|
||||||
|
|||||||
@@ -2,3 +2,4 @@ from .category import * # noqa
|
|||||||
from .ad import * # noqa
|
from .ad import * # noqa
|
||||||
from .ad_items import * # noqa
|
from .ad_items import * # noqa
|
||||||
from .feedback import * # noqa
|
from .feedback import * # noqa
|
||||||
|
from .banner import * # noqa
|
||||||
|
|||||||
1
core/apps/api/admin/banner/__init__.py
Normal file
1
core/apps/api/admin/banner/__init__.py
Normal file
@@ -0,0 +1 @@
|
|||||||
|
from .banner import * # noqa
|
||||||
12
core/apps/api/admin/banner/banner.py
Normal file
12
core/apps/api/admin/banner/banner.py
Normal file
@@ -0,0 +1,12 @@
|
|||||||
|
from django.contrib import admin
|
||||||
|
from unfold.admin import ModelAdmin
|
||||||
|
|
||||||
|
from core.apps.api.models import Banner
|
||||||
|
|
||||||
|
|
||||||
|
@admin.register(Banner)
|
||||||
|
class BannerAdmin(ModelAdmin):
|
||||||
|
list_display = (
|
||||||
|
"id",
|
||||||
|
"__str__",
|
||||||
|
)
|
||||||
18
core/apps/api/migrations/0013_alter_feedback_comment.py
Normal file
18
core/apps/api/migrations/0013_alter_feedback_comment.py
Normal file
@@ -0,0 +1,18 @@
|
|||||||
|
# Generated by Django 5.2.7 on 2025-11-26 10:04
|
||||||
|
|
||||||
|
from django.db import migrations, models
|
||||||
|
|
||||||
|
|
||||||
|
class Migration(migrations.Migration):
|
||||||
|
|
||||||
|
dependencies = [
|
||||||
|
('api', '0012_rename_command_feedback_comment'),
|
||||||
|
]
|
||||||
|
|
||||||
|
operations = [
|
||||||
|
migrations.AlterField(
|
||||||
|
model_name='feedback',
|
||||||
|
name='comment',
|
||||||
|
field=models.CharField(max_length=255, verbose_name='Comment'),
|
||||||
|
),
|
||||||
|
]
|
||||||
@@ -1,6 +1,7 @@
|
|||||||
from django.db import models
|
from django.db import models
|
||||||
from django.utils.translation import gettext_lazy as _
|
from django.utils.translation import gettext_lazy as _
|
||||||
from django_core.models.base import AbstractBaseModel
|
from django_core.models.base import AbstractBaseModel
|
||||||
|
from model_bakery import baker
|
||||||
|
|
||||||
|
|
||||||
class Banner(AbstractBaseModel):
|
class Banner(AbstractBaseModel):
|
||||||
@@ -12,6 +13,10 @@ class Banner(AbstractBaseModel):
|
|||||||
bg_color = models.CharField(verbose_name=_("BG Color"), max_length=255)
|
bg_color = models.CharField(verbose_name=_("BG Color"), max_length=255)
|
||||||
text_color = models.CharField(verbose_name=_("Text Color"), max_length=255)
|
text_color = models.CharField(verbose_name=_("Text Color"), max_length=255)
|
||||||
|
|
||||||
|
@classmethod
|
||||||
|
def _baker(cls):
|
||||||
|
return baker.make(cls)
|
||||||
|
|
||||||
def __str__(self):
|
def __str__(self):
|
||||||
return str(self.pk)
|
return str(self.pk)
|
||||||
|
|
||||||
|
|||||||
@@ -1,3 +1,6 @@
|
|||||||
from .category import * # noqa
|
from .category import * # noqa
|
||||||
from .search import * # noqa
|
from .search import * # noqa
|
||||||
from .ad import * # noqa
|
from .ad import * # noqa
|
||||||
|
from .user import * # noqa
|
||||||
|
from .notification import * # noqa
|
||||||
|
from .banner import * # noqa
|
||||||
|
|||||||
1
core/apps/api/serializers/banner/__init__.py
Normal file
1
core/apps/api/serializers/banner/__init__.py
Normal file
@@ -0,0 +1 @@
|
|||||||
|
from .banner import * # noqa
|
||||||
28
core/apps/api/serializers/banner/banner.py
Normal file
28
core/apps/api/serializers/banner/banner.py
Normal file
@@ -0,0 +1,28 @@
|
|||||||
|
from rest_framework import serializers
|
||||||
|
from core.apps.api.models import Banner
|
||||||
|
|
||||||
|
|
||||||
|
class BaseBannerSerializer(serializers.ModelSerializer):
|
||||||
|
class Meta:
|
||||||
|
model = Banner
|
||||||
|
fields = [
|
||||||
|
"title",
|
||||||
|
"description",
|
||||||
|
"mobile_image",
|
||||||
|
"desktop_image",
|
||||||
|
"link",
|
||||||
|
"bg_color",
|
||||||
|
"text_color",
|
||||||
|
]
|
||||||
|
|
||||||
|
|
||||||
|
class ListBannerSerializer(BaseBannerSerializer):
|
||||||
|
class Meta(BaseBannerSerializer.Meta): ...
|
||||||
|
|
||||||
|
|
||||||
|
class RetrieveBannerSerializer(BaseBannerSerializer):
|
||||||
|
class Meta(BaseBannerSerializer.Meta): ...
|
||||||
|
|
||||||
|
|
||||||
|
class CreateBannerSerializer(BaseBannerSerializer):
|
||||||
|
class Meta(BaseBannerSerializer.Meta): ...
|
||||||
1
core/apps/api/serializers/notification/__init__.py
Normal file
1
core/apps/api/serializers/notification/__init__.py
Normal file
@@ -0,0 +1 @@
|
|||||||
|
from .natification import * # noqa
|
||||||
49
core/apps/api/serializers/notification/natification.py
Normal file
49
core/apps/api/serializers/notification/natification.py
Normal file
@@ -0,0 +1,49 @@
|
|||||||
|
from rest_framework import serializers
|
||||||
|
from core.apps.accounts.models import UserNotification, Notification
|
||||||
|
|
||||||
|
|
||||||
|
class NotificationSerializer(serializers.ModelSerializer):
|
||||||
|
class Meta:
|
||||||
|
model = Notification
|
||||||
|
fields = [
|
||||||
|
"id",
|
||||||
|
"title",
|
||||||
|
"description",
|
||||||
|
"long",
|
||||||
|
"lat"
|
||||||
|
|
||||||
|
]
|
||||||
|
|
||||||
|
|
||||||
|
class BaseUserNotificationSerializer(serializers.ModelSerializer):
|
||||||
|
notification = NotificationSerializer(many=False, read_only=True)
|
||||||
|
|
||||||
|
class Meta:
|
||||||
|
model = UserNotification
|
||||||
|
fields = [
|
||||||
|
"id",
|
||||||
|
"is_read",
|
||||||
|
"notification",
|
||||||
|
"created_at",
|
||||||
|
]
|
||||||
|
|
||||||
|
|
||||||
|
class ListUserNotificationSerializer(BaseUserNotificationSerializer):
|
||||||
|
class Meta(BaseUserNotificationSerializer.Meta): ...
|
||||||
|
|
||||||
|
|
||||||
|
class RetrieveUserNotificationSerializer(BaseUserNotificationSerializer):
|
||||||
|
class Meta(BaseUserNotificationSerializer.Meta): ...
|
||||||
|
|
||||||
|
|
||||||
|
class CreateUserNotificationSerializer(BaseUserNotificationSerializer):
|
||||||
|
class Meta(BaseUserNotificationSerializer.Meta): ...
|
||||||
|
|
||||||
|
|
||||||
|
class UpdateUserNotificationSerializer(BaseUserNotificationSerializer):
|
||||||
|
class Meta(BaseUserNotificationSerializer.Meta):
|
||||||
|
fields = [
|
||||||
|
"is_read"
|
||||||
|
]
|
||||||
|
|
||||||
|
|
||||||
1
core/apps/api/serializers/user/__init__.py
Normal file
1
core/apps/api/serializers/user/__init__.py
Normal file
@@ -0,0 +1 @@
|
|||||||
|
from .ad_like import * # noqa
|
||||||
45
core/apps/api/serializers/user/ad_like.py
Normal file
45
core/apps/api/serializers/user/ad_like.py
Normal file
@@ -0,0 +1,45 @@
|
|||||||
|
from rest_framework import serializers
|
||||||
|
from core.apps.accounts.models import UserLike
|
||||||
|
from core.apps.api.models import AdModel
|
||||||
|
from core.apps.api.serializers.ad.home_api import ListHomeAdSerializer
|
||||||
|
from rest_framework.exceptions import ValidationError
|
||||||
|
from django.utils.translation import gettext_lazy as _
|
||||||
|
|
||||||
|
|
||||||
|
class BaseUserLikeSerializer(serializers.ModelSerializer):
|
||||||
|
ad = ListHomeAdSerializer(many=False, read_only=True)
|
||||||
|
|
||||||
|
class Meta:
|
||||||
|
model = UserLike
|
||||||
|
fields = [
|
||||||
|
"id",
|
||||||
|
"ad",
|
||||||
|
]
|
||||||
|
|
||||||
|
|
||||||
|
class ListUserLikeSerializer(BaseUserLikeSerializer):
|
||||||
|
class Meta(BaseUserLikeSerializer.Meta): ...
|
||||||
|
|
||||||
|
|
||||||
|
class RetrieveUserLikeSerializer(BaseUserLikeSerializer):
|
||||||
|
class Meta(BaseUserLikeSerializer.Meta): ...
|
||||||
|
|
||||||
|
|
||||||
|
class CreateUserLikeSerializer(BaseUserLikeSerializer):
|
||||||
|
ad = serializers.PrimaryKeyRelatedField(queryset=AdModel.objects.all())
|
||||||
|
|
||||||
|
class Meta(BaseUserLikeSerializer.Meta): ...
|
||||||
|
|
||||||
|
def validate(self, data):
|
||||||
|
user = self.context["request"].user
|
||||||
|
ad = data["ad"]
|
||||||
|
|
||||||
|
if UserLike.objects.filter(user=user, ad=ad).exists():
|
||||||
|
raise ValidationError({"detail": _("Siz bu e’longa allaqachon like bosgansiz.")})
|
||||||
|
|
||||||
|
return data
|
||||||
|
|
||||||
|
def create(self, validated_data):
|
||||||
|
validated_data['user'] = self.context['request'].user
|
||||||
|
like = UserLike.objects.create(**validated_data)
|
||||||
|
return like
|
||||||
@@ -1,3 +1,5 @@
|
|||||||
from .category import * # noqa
|
from .category import * # noqa
|
||||||
from .ad import * # noqa
|
from .ad import * # noqa
|
||||||
from .search import * # noqa
|
from .search import * # noqa
|
||||||
|
from .user import * # noqa
|
||||||
|
from .banner import * # noqa
|
||||||
|
|||||||
1
core/apps/api/tests/banner/__init__.py
Normal file
1
core/apps/api/tests/banner/__init__.py
Normal file
@@ -0,0 +1 @@
|
|||||||
|
from .test_banner import * # noqa
|
||||||
58
core/apps/api/tests/banner/test_banner.py
Normal file
58
core/apps/api/tests/banner/test_banner.py
Normal file
@@ -0,0 +1,58 @@
|
|||||||
|
import pytest
|
||||||
|
from django.urls import reverse
|
||||||
|
from rest_framework.test import APIClient
|
||||||
|
|
||||||
|
from core.apps.api.models import Banner
|
||||||
|
|
||||||
|
|
||||||
|
@pytest.fixture
|
||||||
|
def instance(db):
|
||||||
|
return Banner._baker()
|
||||||
|
|
||||||
|
|
||||||
|
@pytest.fixture
|
||||||
|
def api_client(instance):
|
||||||
|
client = APIClient()
|
||||||
|
## client.force_authenticate(user=instance.user)
|
||||||
|
return client, instance
|
||||||
|
|
||||||
|
|
||||||
|
@pytest.fixture
|
||||||
|
def data(api_client):
|
||||||
|
client, instance = api_client
|
||||||
|
return (
|
||||||
|
{
|
||||||
|
"list": reverse("banner-list"),
|
||||||
|
"retrieve": reverse("banner-detail", kwargs={"pk": instance.pk}),
|
||||||
|
"retrieve-not-found": reverse("banner-detail", kwargs={"pk": 1000}),
|
||||||
|
},
|
||||||
|
client,
|
||||||
|
instance,
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
|
@pytest.mark.django_db
|
||||||
|
def test_list(data):
|
||||||
|
urls, client, _ = data
|
||||||
|
response = client.get(urls["list"])
|
||||||
|
data_resp = response.json()
|
||||||
|
assert response.status_code == 200
|
||||||
|
assert data_resp["status"] is True
|
||||||
|
|
||||||
|
|
||||||
|
@pytest.mark.django_db
|
||||||
|
def test_retrieve(data):
|
||||||
|
urls, client, _ = data
|
||||||
|
response = client.get(urls["retrieve"])
|
||||||
|
data_resp = response.json()
|
||||||
|
assert response.status_code == 200
|
||||||
|
assert data_resp["status"] is True
|
||||||
|
|
||||||
|
|
||||||
|
@pytest.mark.django_db
|
||||||
|
def test_retrieve_not_found(data):
|
||||||
|
urls, client, _ = data
|
||||||
|
response = client.get(urls["retrieve-not-found"])
|
||||||
|
data_resp = response.json()
|
||||||
|
assert response.status_code == 404
|
||||||
|
assert data_resp["status"] is False
|
||||||
2
core/apps/api/tests/user/__init__.py
Normal file
2
core/apps/api/tests/user/__init__.py
Normal file
@@ -0,0 +1,2 @@
|
|||||||
|
from .test_user_like import * # noqa
|
||||||
|
from .test_user_notification import * # noqa
|
||||||
61
core/apps/api/tests/user/test_user_like.py
Normal file
61
core/apps/api/tests/user/test_user_like.py
Normal file
@@ -0,0 +1,61 @@
|
|||||||
|
import pytest
|
||||||
|
from django.urls import reverse
|
||||||
|
from rest_framework.test import APIClient
|
||||||
|
|
||||||
|
from core.apps.accounts.models import UserLike, AdModel
|
||||||
|
|
||||||
|
|
||||||
|
@pytest.fixture
|
||||||
|
def instance(db):
|
||||||
|
return UserLike._baker()
|
||||||
|
|
||||||
|
|
||||||
|
@pytest.fixture
|
||||||
|
def ad(db):
|
||||||
|
return AdModel._baker()
|
||||||
|
|
||||||
|
|
||||||
|
@pytest.fixture
|
||||||
|
def api_client(instance):
|
||||||
|
client = APIClient()
|
||||||
|
client.force_authenticate(user=instance.user)
|
||||||
|
return client, instance
|
||||||
|
|
||||||
|
|
||||||
|
@pytest.fixture
|
||||||
|
def data(api_client):
|
||||||
|
client, instance = api_client
|
||||||
|
return (
|
||||||
|
{
|
||||||
|
"list": reverse("user-like-list"),
|
||||||
|
"retrieve": reverse("user-like-detail", kwargs={"pk": instance.pk}),
|
||||||
|
"retrieve-not-found": reverse("user-like-detail", kwargs={"pk": 1000}),
|
||||||
|
},
|
||||||
|
client,
|
||||||
|
instance,
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
|
@pytest.mark.django_db
|
||||||
|
def test_list(data):
|
||||||
|
urls, client, _ = data
|
||||||
|
response = client.get(urls["list"])
|
||||||
|
data_resp = response.json()
|
||||||
|
assert response.status_code == 200
|
||||||
|
assert data_resp["status"] is True
|
||||||
|
|
||||||
|
|
||||||
|
@pytest.mark.django_db
|
||||||
|
def test_create(data, ad):
|
||||||
|
urls, client, instance = data
|
||||||
|
response = client.post(urls["list"], data={"ad": ad.pk})
|
||||||
|
data_resp = response.json()
|
||||||
|
assert response.status_code == 201
|
||||||
|
assert data_resp["status"] is True
|
||||||
|
|
||||||
|
|
||||||
|
@pytest.mark.django_db
|
||||||
|
def test_destroy(data):
|
||||||
|
urls, client, _ = data
|
||||||
|
response = client.delete(urls["retrieve"])
|
||||||
|
assert response.status_code == 204
|
||||||
78
core/apps/api/tests/user/test_user_notification.py
Normal file
78
core/apps/api/tests/user/test_user_notification.py
Normal file
@@ -0,0 +1,78 @@
|
|||||||
|
import pytest
|
||||||
|
from django.urls import reverse
|
||||||
|
from rest_framework.test import APIClient
|
||||||
|
|
||||||
|
from core.apps.accounts.models import UserNotification
|
||||||
|
|
||||||
|
|
||||||
|
@pytest.fixture
|
||||||
|
def instance(db):
|
||||||
|
return UserNotification._baker()
|
||||||
|
|
||||||
|
|
||||||
|
@pytest.fixture
|
||||||
|
def api_client(instance):
|
||||||
|
client = APIClient()
|
||||||
|
client.force_authenticate(user=instance.user)
|
||||||
|
return client, instance
|
||||||
|
|
||||||
|
|
||||||
|
@pytest.fixture
|
||||||
|
def data(api_client):
|
||||||
|
client, instance = api_client
|
||||||
|
return (
|
||||||
|
{
|
||||||
|
"list": reverse("notification-list"),
|
||||||
|
"retrieve": reverse("notification-detail", kwargs={"pk": instance.pk}),
|
||||||
|
"retrieve-not-found": reverse("notification-detail", kwargs={"pk": 1000}),
|
||||||
|
"notification-read": reverse("notification-notification-read", kwargs={"pk": instance.pk}),
|
||||||
|
"all-read": reverse("notification-all-read"),
|
||||||
|
},
|
||||||
|
client,
|
||||||
|
instance,
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
|
@pytest.mark.django_db
|
||||||
|
def test_list(data):
|
||||||
|
urls, client, _ = data
|
||||||
|
response = client.get(urls["list"])
|
||||||
|
data_resp = response.json()
|
||||||
|
assert response.status_code == 200
|
||||||
|
assert data_resp["status"] is True
|
||||||
|
|
||||||
|
|
||||||
|
@pytest.mark.django_db
|
||||||
|
def test_retrieve(data):
|
||||||
|
urls, client, _ = data
|
||||||
|
response = client.get(urls["retrieve"])
|
||||||
|
data_resp = response.json()
|
||||||
|
assert response.status_code == 200
|
||||||
|
assert data_resp["status"] is True
|
||||||
|
|
||||||
|
|
||||||
|
@pytest.mark.django_db
|
||||||
|
def test_retrieve_not_found(data):
|
||||||
|
urls, client, _ = data
|
||||||
|
response = client.get(urls["retrieve-not-found"])
|
||||||
|
data_resp = response.json()
|
||||||
|
assert response.status_code == 404
|
||||||
|
assert data_resp["status"] is False
|
||||||
|
|
||||||
|
|
||||||
|
@pytest.mark.django_db
|
||||||
|
def test_notification_reads(data):
|
||||||
|
urls, client, _ = data
|
||||||
|
response = client.post(urls["notification-read"])
|
||||||
|
data_resp = response.json()
|
||||||
|
assert response.status_code == 200
|
||||||
|
assert data_resp["status"] is True
|
||||||
|
|
||||||
|
|
||||||
|
@pytest.mark.django_db
|
||||||
|
def test_all_read(data):
|
||||||
|
urls, client, _ = data
|
||||||
|
response = client.post(urls["all-read"])
|
||||||
|
data_resp = response.json()
|
||||||
|
assert response.status_code == 200
|
||||||
|
assert data_resp["status"] is True
|
||||||
@@ -1,9 +1,13 @@
|
|||||||
from django.urls import include, path
|
from django.urls import include, path
|
||||||
from rest_framework.routers import DefaultRouter
|
from rest_framework.routers import DefaultRouter
|
||||||
|
|
||||||
from core.apps.api.views import CategoryHomeApiViewSet, CategoryViewSet, HomeAdApiView, SearchHistoryViewSet
|
from core.apps.api.views import CategoryHomeApiViewSet, CategoryViewSet, HomeAdApiView, SearchHistoryViewSet, \
|
||||||
|
UserLikeViewSet, NotificationViewSet, BannerViewSet
|
||||||
|
|
||||||
router = DefaultRouter()
|
router = DefaultRouter()
|
||||||
|
router.register("banner", BannerViewSet, basename="banner")
|
||||||
|
router.register("notification", NotificationViewSet, basename="notification")
|
||||||
|
router.register("user-like", UserLikeViewSet, basename="user-like")
|
||||||
router.register("category", CategoryViewSet, basename="category")
|
router.register("category", CategoryViewSet, basename="category")
|
||||||
router.register("category-home", CategoryHomeApiViewSet, basename="category-home")
|
router.register("category-home", CategoryHomeApiViewSet, basename="category-home")
|
||||||
router.register("search-history", SearchHistoryViewSet, basename="search-history")
|
router.register("search-history", SearchHistoryViewSet, basename="search-history")
|
||||||
|
|||||||
@@ -1,4 +1,6 @@
|
|||||||
from .category import * # noqa
|
from .category import * # noqa
|
||||||
from .search import * # noqa
|
from .search import * # noqa
|
||||||
from .ad import * # noqa
|
from .ad import * # noqa
|
||||||
|
from .user import * # noqa
|
||||||
|
from .notification import * # noqa
|
||||||
|
from .banner import * # noqa
|
||||||
|
|||||||
1
core/apps/api/views/banner/__init__.py
Normal file
1
core/apps/api/views/banner/__init__.py
Normal file
@@ -0,0 +1 @@
|
|||||||
|
from .banner import * # noqa
|
||||||
25
core/apps/api/views/banner/banner.py
Normal file
25
core/apps/api/views/banner/banner.py
Normal file
@@ -0,0 +1,25 @@
|
|||||||
|
from rest_framework.permissions import IsAuthenticated, AllowAny
|
||||||
|
from rest_framework.viewsets import ReadOnlyModelViewSet
|
||||||
|
from drf_spectacular.utils import extend_schema
|
||||||
|
from django_core.mixins import BaseViewSetMixin
|
||||||
|
|
||||||
|
from core.apps.api.models import Banner
|
||||||
|
from core.apps.api.serializers.banner import (
|
||||||
|
ListBannerSerializer,
|
||||||
|
RetrieveBannerSerializer,
|
||||||
|
CreateBannerSerializer,
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
|
@extend_schema(tags=['Banner'])
|
||||||
|
class BannerViewSet(BaseViewSetMixin, ReadOnlyModelViewSet):
|
||||||
|
queryset = Banner.objects.all()
|
||||||
|
serializer_class = ListBannerSerializer
|
||||||
|
permission_classes = [AllowAny]
|
||||||
|
|
||||||
|
action_permission_classes = {}
|
||||||
|
action_serializers = {
|
||||||
|
'list': ListBannerSerializer,
|
||||||
|
'retrieve': RetrieveBannerSerializer,
|
||||||
|
'create': CreateBannerSerializer,
|
||||||
|
}
|
||||||
1
core/apps/api/views/notification/__init__.py
Normal file
1
core/apps/api/views/notification/__init__.py
Normal file
@@ -0,0 +1 @@
|
|||||||
|
from .notification import * # noqa
|
||||||
54
core/apps/api/views/notification/notification.py
Normal file
54
core/apps/api/views/notification/notification.py
Normal file
@@ -0,0 +1,54 @@
|
|||||||
|
from xmlrpc.client import Fault
|
||||||
|
|
||||||
|
from rest_framework import status
|
||||||
|
from rest_framework.permissions import IsAuthenticated
|
||||||
|
from rest_framework.response import Response
|
||||||
|
from rest_framework.viewsets import ReadOnlyModelViewSet
|
||||||
|
from django_core.mixins.base import BaseViewSetMixin
|
||||||
|
from drf_spectacular.utils import extend_schema
|
||||||
|
from core.apps.accounts.models import UserNotification
|
||||||
|
from rest_framework.decorators import action
|
||||||
|
from django_filters.rest_framework import DjangoFilterBackend
|
||||||
|
from django.utils.translation import gettext_lazy as _
|
||||||
|
from core.apps.api.serializers.notification import (
|
||||||
|
ListUserNotificationSerializer,
|
||||||
|
CreateUserNotificationSerializer,
|
||||||
|
RetrieveUserNotificationSerializer,
|
||||||
|
UpdateUserNotificationSerializer
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
|
@extend_schema(tags=["Notification"])
|
||||||
|
class NotificationViewSet(BaseViewSetMixin, ReadOnlyModelViewSet):
|
||||||
|
permission_classes = [IsAuthenticated]
|
||||||
|
serializer_class = ListUserNotificationSerializer
|
||||||
|
filter_backends = [DjangoFilterBackend]
|
||||||
|
|
||||||
|
action_permission_classes = {}
|
||||||
|
action_serializer_class = {
|
||||||
|
"list": ListUserNotificationSerializer,
|
||||||
|
"retrieve": RetrieveUserNotificationSerializer,
|
||||||
|
"create": CreateUserNotificationSerializer,
|
||||||
|
}
|
||||||
|
|
||||||
|
def get_queryset(self):
|
||||||
|
qs = UserNotification.objects.filter(user=self.request.user).order_by("-created_at")
|
||||||
|
return qs
|
||||||
|
|
||||||
|
@action(detail=True, methods=["post"])
|
||||||
|
def notification_read(self, request, pk=None):
|
||||||
|
notification = self.get_object()
|
||||||
|
serializer = UpdateUserNotificationSerializer(
|
||||||
|
notification,
|
||||||
|
data={"is_read": True},
|
||||||
|
partial=True
|
||||||
|
)
|
||||||
|
serializer.is_valid(raise_exception=True)
|
||||||
|
serializer.save()
|
||||||
|
return Response(serializer.data)
|
||||||
|
|
||||||
|
@action(detail=False, methods=["post"])
|
||||||
|
def all_read(self, request):
|
||||||
|
user = request.user
|
||||||
|
UserNotification.objects.filter(user=user, is_read=False).update(is_read=True)
|
||||||
|
return Response({"detail": _("Barcha xabarlar o'qilgan qilindi!")}, status=status.HTTP_200_OK)
|
||||||
1
core/apps/api/views/user/__init__.py
Normal file
1
core/apps/api/views/user/__init__.py
Normal file
@@ -0,0 +1 @@
|
|||||||
|
from .ad_like import * # noqa
|
||||||
28
core/apps/api/views/user/ad_like.py
Normal file
28
core/apps/api/views/user/ad_like.py
Normal file
@@ -0,0 +1,28 @@
|
|||||||
|
from rest_framework import mixins
|
||||||
|
from rest_framework.viewsets import GenericViewSet
|
||||||
|
from django_core.mixins.base import BaseViewSetMixin
|
||||||
|
from drf_spectacular.utils import extend_schema
|
||||||
|
from rest_framework.permissions import IsAuthenticated
|
||||||
|
from core.apps.accounts.models import UserLike
|
||||||
|
from core.apps.api.serializers.user.ad_like import (
|
||||||
|
ListUserLikeSerializer,
|
||||||
|
CreateUserLikeSerializer,
|
||||||
|
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
|
@extend_schema(tags=['User Like'])
|
||||||
|
class UserLikeViewSet(BaseViewSetMixin, mixins.ListModelMixin, mixins.CreateModelMixin,
|
||||||
|
mixins.DestroyModelMixin, GenericViewSet):
|
||||||
|
serializer_class = ListUserLikeSerializer
|
||||||
|
permission_classes = [IsAuthenticated]
|
||||||
|
http_method_names = ['get', 'post', 'delete']
|
||||||
|
action_permission_classes = {}
|
||||||
|
action_serializer_class = {
|
||||||
|
'list': ListUserLikeSerializer,
|
||||||
|
'create': CreateUserLikeSerializer,
|
||||||
|
}
|
||||||
|
|
||||||
|
def get_queryset(self):
|
||||||
|
queryset = UserLike.objects.filter(user=self.request.user).order_by('-id')
|
||||||
|
return queryset
|
||||||
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
Reference in New Issue
Block a user