Ad like uchun apilar chiqarildi
This commit is contained in:
@@ -1,4 +1,4 @@
|
|||||||
#type: ignore
|
# type: ignore
|
||||||
import os
|
import os
|
||||||
import pathlib
|
import pathlib
|
||||||
from typing import List, Union
|
from typing import List, Union
|
||||||
@@ -37,19 +37,19 @@ PASSWORD_HASHERS = [
|
|||||||
]
|
]
|
||||||
|
|
||||||
INSTALLED_APPS = [
|
INSTALLED_APPS = [
|
||||||
"modeltranslation",
|
"modeltranslation",
|
||||||
"unfold",
|
"unfold",
|
||||||
"unfold.contrib.filters",
|
"unfold.contrib.filters",
|
||||||
"unfold.contrib.forms",
|
"unfold.contrib.forms",
|
||||||
"unfold.contrib.guardian",
|
"unfold.contrib.guardian",
|
||||||
"unfold.contrib.simple_history",
|
"unfold.contrib.simple_history",
|
||||||
"django.contrib.admin",
|
"django.contrib.admin",
|
||||||
"django.contrib.auth",
|
"django.contrib.auth",
|
||||||
"django.contrib.contenttypes",
|
"django.contrib.contenttypes",
|
||||||
"django.contrib.sessions",
|
"django.contrib.sessions",
|
||||||
"django.contrib.messages",
|
"django.contrib.messages",
|
||||||
"django.contrib.staticfiles",
|
"django.contrib.staticfiles",
|
||||||
] + APPS
|
] + APPS
|
||||||
|
|
||||||
MODULES = [app for app in MODULES if isinstance(app, str)]
|
MODULES = [app for app in MODULES if isinstance(app, str)]
|
||||||
|
|
||||||
@@ -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
|
||||||
|
|
||||||
|
|
||||||
@admin.register(SearchHistory)
|
@admin.register(SearchHistory)
|
||||||
@@ -10,3 +10,11 @@ class SearchHistoryAdmin(ModelAdmin):
|
|||||||
"id",
|
"id",
|
||||||
"__str__",
|
"__str__",
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
|
@admin.register(UserLike)
|
||||||
|
class UserLikeAdmin(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',
|
||||||
|
),
|
||||||
|
]
|
||||||
@@ -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.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")
|
||||||
|
|||||||
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,3 +1,4 @@
|
|||||||
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
|
||||||
|
|||||||
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,4 @@
|
|||||||
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
|
||||||
|
|||||||
1
core/apps/api/tests/user/__init__.py
Normal file
1
core/apps/api/tests/user/__init__.py
Normal file
@@ -0,0 +1 @@
|
|||||||
|
from .test_user_like import * # noqa
|
||||||
62
core/apps/api/tests/user/test_user_like.py
Normal file
62
core/apps/api/tests/user/test_user_like.py
Normal file
@@ -0,0 +1,62 @@
|
|||||||
|
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()
|
||||||
|
print(data_resp)
|
||||||
|
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
|
||||||
@@ -1,9 +1,11 @@
|
|||||||
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
|
||||||
|
|
||||||
router = DefaultRouter()
|
router = DefaultRouter()
|
||||||
|
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,4 @@
|
|||||||
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
|
||||||
|
|||||||
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