db va type togirlandi
This commit is contained in:
7
.claude/settings.json
Normal file
7
.claude/settings.json
Normal file
@@ -0,0 +1,7 @@
|
||||
{
|
||||
"permissions": {
|
||||
"additionalDirectories": [
|
||||
"/Users/uzmacbook/felix/karomat-backend/core/apps/api/filters"
|
||||
]
|
||||
}
|
||||
}
|
||||
1
.claude/worktrees/cool-bohr
Submodule
1
.claude/worktrees/cool-bohr
Submodule
Submodule .claude/worktrees/cool-bohr added at cf23a61b98
2714
backups/backup_20260331_204542.sql
Normal file
2714
backups/backup_20260331_204542.sql
Normal file
File diff suppressed because it is too large
Load Diff
@@ -21,6 +21,11 @@ PAGES = [
|
||||
"icon": "store",
|
||||
"link": reverse_lazy("admin:api_filialmodel_changelist"),
|
||||
},
|
||||
{
|
||||
"title": _("Turlar"),
|
||||
"icon": "label",
|
||||
"link": reverse_lazy("admin:api_typemodel_changelist"),
|
||||
},
|
||||
{
|
||||
"title": _("Kategoriyalar"),
|
||||
"icon": "category",
|
||||
|
||||
@@ -1,2 +1,3 @@
|
||||
from .category import * # noqa
|
||||
from .products import * # noqa
|
||||
from .type import * # noqa
|
||||
|
||||
8
core/apps/api/admin/type.py
Normal file
8
core/apps/api/admin/type.py
Normal file
@@ -0,0 +1,8 @@
|
||||
from core.apps.api.models import TypeModel
|
||||
from unfold.admin import ModelAdmin
|
||||
from django.contrib import admin
|
||||
|
||||
|
||||
@admin.register(TypeModel)
|
||||
class TypeAdmin(ModelAdmin):
|
||||
list_display = ("id", "__str__",)
|
||||
@@ -1,2 +1,3 @@
|
||||
from .category import * # noqa
|
||||
from .products import * # noqa
|
||||
from .type import * # noqa
|
||||
|
||||
@@ -4,6 +4,8 @@ from core.apps.api.models import CategoryModel, SubcategoryModel
|
||||
|
||||
|
||||
class CategoryFilter(filters.FilterSet):
|
||||
type = filters.NumberFilter(field_name="type_id")
|
||||
|
||||
class Meta:
|
||||
model = CategoryModel
|
||||
fields = [
|
||||
|
||||
@@ -6,7 +6,7 @@ from core.apps.api.models import ProductsModel, SubProductModel
|
||||
class ProductsFilter(filters.FilterSet):
|
||||
category = filters.NumberFilter(field_name="subcategory__category_id")
|
||||
filial = filters.NumberFilter(field_name="subcategory__category__filial_id")
|
||||
category_type = filters.CharFilter(field_name="subcategory__category__type")
|
||||
category_type = filters.NumberFilter(field_name="subcategory__category__type_id")
|
||||
|
||||
class Meta:
|
||||
model = ProductsModel
|
||||
@@ -20,7 +20,7 @@ class ProductsFilter(filters.FilterSet):
|
||||
|
||||
|
||||
class SubProductFilter(filters.FilterSet):
|
||||
category_type = filters.CharFilter(field_name="product__subcategory__category__type")
|
||||
category_type = filters.NumberFilter(field_name="product__subcategory__category__type_id")
|
||||
|
||||
class Meta:
|
||||
model = SubProductModel
|
||||
|
||||
8
core/apps/api/filters/type.py
Normal file
8
core/apps/api/filters/type.py
Normal file
@@ -0,0 +1,8 @@
|
||||
from core.apps.api.models import TypeModel
|
||||
from django_filters import rest_framework as filters
|
||||
|
||||
|
||||
class TypeFilter(filters.FilterSet):
|
||||
class Meta:
|
||||
model = TypeModel
|
||||
fields = ["name"]
|
||||
@@ -1,2 +1,3 @@
|
||||
from .category import * # noqa
|
||||
from .products import * # noqa
|
||||
from .type import * # noqa
|
||||
|
||||
9
core/apps/api/forms/type.py
Normal file
9
core/apps/api/forms/type.py
Normal file
@@ -0,0 +1,9 @@
|
||||
from core.apps.api.models import TypeModel
|
||||
from django import forms
|
||||
|
||||
|
||||
class TypeForm(forms.ModelForm):
|
||||
|
||||
class Meta:
|
||||
model = TypeModel
|
||||
fields = "__all__"
|
||||
43
core/apps/api/migrations/0009_typemodel_category_type_fk.py
Normal file
43
core/apps/api/migrations/0009_typemodel_category_type_fk.py
Normal file
@@ -0,0 +1,43 @@
|
||||
from django.db import migrations, models
|
||||
import django.db.models.deletion
|
||||
|
||||
|
||||
class Migration(migrations.Migration):
|
||||
"""
|
||||
1-qadam: TypeModel jadvalini yaratish va categoryga type_fk ustunini qo'shish.
|
||||
Eski 'type' (CharField) ustuni hali o'chmaydi — ma'lumot saqlanadi.
|
||||
"""
|
||||
|
||||
dependencies = [
|
||||
('api', '0008_subproductmodel_description'),
|
||||
]
|
||||
|
||||
operations = [
|
||||
migrations.CreateModel(
|
||||
name='TypeModel',
|
||||
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)),
|
||||
('name', models.CharField(max_length=255, verbose_name='name')),
|
||||
],
|
||||
options={
|
||||
'verbose_name': 'TypeModel',
|
||||
'verbose_name_plural': 'TypeModels',
|
||||
'db_table': 'type',
|
||||
},
|
||||
),
|
||||
# Yangi FK ustun qo'shamiz (type_fk_id), eski type ustuni saqlanib qoladi
|
||||
migrations.AddField(
|
||||
model_name='categorymodel',
|
||||
name='type_fk',
|
||||
field=models.ForeignKey(
|
||||
blank=True,
|
||||
null=True,
|
||||
on_delete=django.db.models.deletion.SET_NULL,
|
||||
related_name='categories',
|
||||
to='api.typemodel',
|
||||
verbose_name='type',
|
||||
),
|
||||
),
|
||||
]
|
||||
48
core/apps/api/migrations/0010_migrate_type_data.py
Normal file
48
core/apps/api/migrations/0010_migrate_type_data.py
Normal file
@@ -0,0 +1,48 @@
|
||||
from django.db import migrations
|
||||
|
||||
|
||||
def forwards(apps, schema_editor):
|
||||
"""
|
||||
Eski type (CharField: RESTAURANT/BAR) qiymatlaridan TypeModel yozuvlari yaratadi,
|
||||
keyin har bir categoryni to'g'ri TypeModelga ulaydi.
|
||||
"""
|
||||
TypeModel = apps.get_model('api', 'TypeModel')
|
||||
CategoryModel = apps.get_model('api', 'CategoryModel')
|
||||
|
||||
# DB dagi barcha unique type qiymatlarini topib TypeModel yozuvlari yaratamiz
|
||||
existing_types = (
|
||||
CategoryModel.objects.exclude(type__isnull=True)
|
||||
.exclude(type='')
|
||||
.values_list('type', flat=True)
|
||||
.distinct()
|
||||
)
|
||||
|
||||
type_map = {}
|
||||
for type_value in existing_types:
|
||||
obj, _ = TypeModel.objects.get_or_create(name=type_value)
|
||||
type_map[type_value] = obj
|
||||
|
||||
# Har bir categoryni mos TypeModelga ulaymiz
|
||||
for category in CategoryModel.objects.exclude(type__isnull=True).exclude(type=''):
|
||||
category.type_fk = type_map.get(category.type)
|
||||
category.save(update_fields=['type_fk'])
|
||||
|
||||
|
||||
def backwards(apps, schema_editor):
|
||||
CategoryModel = apps.get_model('api', 'CategoryModel')
|
||||
CategoryModel.objects.update(type_fk=None)
|
||||
|
||||
|
||||
class Migration(migrations.Migration):
|
||||
"""
|
||||
2-qadam: Mavjud type (RESTAURANT/BAR) qiymatlaridan TypeModel yozuvlari yaratib,
|
||||
categorylarni ulaydi.
|
||||
"""
|
||||
|
||||
dependencies = [
|
||||
('api', '0009_typemodel_category_type_fk'),
|
||||
]
|
||||
|
||||
operations = [
|
||||
migrations.RunPython(forwards, backwards),
|
||||
]
|
||||
26
core/apps/api/migrations/0011_category_type_cleanup.py
Normal file
26
core/apps/api/migrations/0011_category_type_cleanup.py
Normal file
@@ -0,0 +1,26 @@
|
||||
from django.db import migrations, models
|
||||
import django.db.models.deletion
|
||||
|
||||
|
||||
class Migration(migrations.Migration):
|
||||
"""
|
||||
3-qadam: Eski type (CharField) ustunini o'chirib, type_fk ni type deb nomini o'zgartirish.
|
||||
"""
|
||||
|
||||
dependencies = [
|
||||
('api', '0010_migrate_type_data'),
|
||||
]
|
||||
|
||||
operations = [
|
||||
# Eski CharField type ustunini o'chiramiz
|
||||
migrations.RemoveField(
|
||||
model_name='categorymodel',
|
||||
name='type',
|
||||
),
|
||||
# type_fk ni type deb nomini o'zgartirамiz
|
||||
migrations.RenameField(
|
||||
model_name='categorymodel',
|
||||
old_name='type_fk',
|
||||
new_name='type',
|
||||
),
|
||||
]
|
||||
@@ -1,2 +1,3 @@
|
||||
from .category import * # noqa
|
||||
from .products import * # noqa
|
||||
from .type import * # noqa
|
||||
|
||||
@@ -22,10 +22,6 @@ class FilialModel(AbstractBaseModel):
|
||||
|
||||
|
||||
class CategoryModel(AbstractBaseModel):
|
||||
class CategoryType(models.TextChoices):
|
||||
RESTAURANT = "RESTAURANT", _("Restaurant")
|
||||
BAR = "BAR", _("Bar")
|
||||
|
||||
filial = models.ForeignKey(
|
||||
FilialModel,
|
||||
verbose_name=_("filial"),
|
||||
@@ -35,11 +31,13 @@ class CategoryModel(AbstractBaseModel):
|
||||
blank=True,
|
||||
)
|
||||
name = models.CharField(verbose_name=_("name"), max_length=255)
|
||||
type = models.CharField(
|
||||
type = models.ForeignKey(
|
||||
"api.TypeModel",
|
||||
verbose_name=_("type"),
|
||||
max_length=20,
|
||||
choices=CategoryType.choices,
|
||||
default=CategoryType.RESTAURANT,
|
||||
related_name="categories",
|
||||
on_delete=models.SET_NULL,
|
||||
null=True,
|
||||
blank=True,
|
||||
)
|
||||
image = models.ImageField(verbose_name=_("image"), upload_to="categories/", null=True, blank=True)
|
||||
active_image = models.ImageField(
|
||||
@@ -49,7 +47,8 @@ class CategoryModel(AbstractBaseModel):
|
||||
|
||||
def __str__(self):
|
||||
filial_name = self.filial.name if self.filial else "Filial yo'q"
|
||||
return f"{self.name} ({self.type}) - {filial_name}"
|
||||
type_name = self.type.name if self.type else "Type yo'q"
|
||||
return f"{self.name} ({type_name}) - {filial_name}"
|
||||
|
||||
@classmethod
|
||||
def _baker(cls):
|
||||
|
||||
20
core/apps/api/models/type.py
Normal file
20
core/apps/api/models/type.py
Normal file
@@ -0,0 +1,20 @@
|
||||
from django.db import models
|
||||
from django_core.models import AbstractBaseModel
|
||||
from django.utils.translation import gettext_lazy as _
|
||||
from model_bakery import baker
|
||||
|
||||
|
||||
class TypeModel(AbstractBaseModel):
|
||||
name = models.CharField(verbose_name=_("name"), max_length=255)
|
||||
|
||||
def __str__(self):
|
||||
return self.name
|
||||
|
||||
@classmethod
|
||||
def _baker(cls):
|
||||
return baker.make(cls)
|
||||
|
||||
class Meta:
|
||||
db_table = "type"
|
||||
verbose_name = _("TypeModel")
|
||||
verbose_name_plural = _("TypeModels")
|
||||
@@ -1,2 +1,3 @@
|
||||
from .category import * # noqa
|
||||
from .products import * # noqa
|
||||
from .type import * # noqa
|
||||
|
||||
7
core/apps/api/permissions/type.py
Normal file
7
core/apps/api/permissions/type.py
Normal file
@@ -0,0 +1,7 @@
|
||||
from rest_framework import permissions
|
||||
|
||||
|
||||
class TypePermission(permissions.BasePermission):
|
||||
|
||||
def has_permission(self, request, view):
|
||||
return True
|
||||
@@ -1,2 +1,3 @@
|
||||
from .category import * # noqa
|
||||
from .products import * # noqa
|
||||
from .type import * # noqa
|
||||
|
||||
@@ -2,10 +2,19 @@ from rest_framework import serializers
|
||||
|
||||
from core.apps.api.models import CategoryModel
|
||||
from core.apps.api.serializers.category.subcategory import BaseSubcategorySerializer
|
||||
from core.apps.api.serializers.type.type import BaseTypeSerializer
|
||||
|
||||
|
||||
class BaseCategorySerializer(serializers.ModelSerializer):
|
||||
subcategories = BaseSubcategorySerializer(many=True, read_only=True)
|
||||
type = BaseTypeSerializer(read_only=True)
|
||||
type_id = serializers.PrimaryKeyRelatedField(
|
||||
source="type",
|
||||
queryset=CategoryModel._meta.get_field("type").related_model.objects.all(),
|
||||
write_only=True,
|
||||
required=False,
|
||||
allow_null=True,
|
||||
)
|
||||
|
||||
class Meta:
|
||||
model = CategoryModel
|
||||
@@ -13,6 +22,7 @@ class BaseCategorySerializer(serializers.ModelSerializer):
|
||||
"id",
|
||||
"name",
|
||||
"type",
|
||||
"type_id",
|
||||
"order",
|
||||
"image",
|
||||
"active_image",
|
||||
@@ -42,6 +52,7 @@ class CreateCategorySerializer(BaseCategorySerializer):
|
||||
fields = [
|
||||
"id",
|
||||
"name",
|
||||
"type_id",
|
||||
"order",
|
||||
"image",
|
||||
"active_image",
|
||||
|
||||
@@ -4,7 +4,7 @@ from core.apps.api.models import ProductsModel, SubProductModel
|
||||
|
||||
|
||||
class SubProductSerializer(serializers.ModelSerializer):
|
||||
type = serializers.CharField(source="product.subcategory.category.type", read_only=True)
|
||||
type = serializers.CharField(source="product.subcategory.category.type.name", read_only=True)
|
||||
|
||||
class Meta:
|
||||
model = SubProductModel
|
||||
@@ -20,7 +20,7 @@ class SubProductSerializer(serializers.ModelSerializer):
|
||||
|
||||
class BaseProductsSerializer(serializers.ModelSerializer):
|
||||
subproducts = SubProductSerializer(many=True, read_only=True)
|
||||
type = serializers.CharField(source="subcategory.category.type", read_only=True)
|
||||
type = serializers.CharField(source="subcategory.category.type.name", read_only=True)
|
||||
|
||||
class Meta:
|
||||
model = ProductsModel
|
||||
|
||||
1
core/apps/api/serializers/type/__init__.py
Normal file
1
core/apps/api/serializers/type/__init__.py
Normal file
@@ -0,0 +1 @@
|
||||
from .type import * # noqa
|
||||
31
core/apps/api/serializers/type/type .py
Normal file
31
core/apps/api/serializers/type/type .py
Normal file
@@ -0,0 +1,31 @@
|
||||
from core.apps.api.models import Type Model
|
||||
from rest_framework import serializers
|
||||
|
||||
|
||||
class BaseType Serializer(serializers.ModelSerializer):
|
||||
class Meta:
|
||||
model = Type Model
|
||||
fields = [
|
||||
"id",
|
||||
|
||||
"name",
|
||||
|
||||
]
|
||||
|
||||
|
||||
class ListType Serializer(BaseType Serializer):
|
||||
class Meta(BaseType Serializer.Meta): ...
|
||||
|
||||
|
||||
class RetrieveType Serializer(BaseType Serializer):
|
||||
class Meta(BaseType Serializer.Meta): ...
|
||||
|
||||
|
||||
class CreateType Serializer(BaseType Serializer):
|
||||
class Meta(BaseType Serializer.Meta):
|
||||
fields = [
|
||||
"id",
|
||||
|
||||
"name",
|
||||
|
||||
]
|
||||
21
core/apps/api/serializers/type/type.py
Normal file
21
core/apps/api/serializers/type/type.py
Normal file
@@ -0,0 +1,21 @@
|
||||
from core.apps.api.models import TypeModel
|
||||
from rest_framework import serializers
|
||||
|
||||
|
||||
class BaseTypeSerializer(serializers.ModelSerializer):
|
||||
class Meta:
|
||||
model = TypeModel
|
||||
fields = ["id", "name"]
|
||||
|
||||
|
||||
class ListTypeSerializer(BaseTypeSerializer):
|
||||
class Meta(BaseTypeSerializer.Meta): ...
|
||||
|
||||
|
||||
class RetrieveTypeSerializer(BaseTypeSerializer):
|
||||
class Meta(BaseTypeSerializer.Meta): ...
|
||||
|
||||
|
||||
class CreateTypeSerializer(BaseTypeSerializer):
|
||||
class Meta(BaseTypeSerializer.Meta):
|
||||
fields = ["id", "name"]
|
||||
@@ -1,2 +1,3 @@
|
||||
from .category import * # noqa
|
||||
from .products import * # noqa
|
||||
from .type import * # noqa
|
||||
|
||||
7
core/apps/api/signals/type.py
Normal file
7
core/apps/api/signals/type.py
Normal file
@@ -0,0 +1,7 @@
|
||||
from core.apps.api.models import TypeModel
|
||||
from django.db.models.signals import post_save
|
||||
from django.dispatch import receiver
|
||||
|
||||
|
||||
@receiver(post_save, sender=TypeModel)
|
||||
def type_signal(sender, instance, created, **kwargs): ...
|
||||
@@ -1,2 +1,3 @@
|
||||
from .category import * # noqa
|
||||
from .products import * # noqa
|
||||
from .type import * # noqa
|
||||
|
||||
1
core/apps/api/tests/type/__init__.py
Normal file
1
core/apps/api/tests/type/__init__.py
Normal file
@@ -0,0 +1 @@
|
||||
from .test_type import * # noqa
|
||||
56
core/apps/api/tests/type/test_type .py
Normal file
56
core/apps/api/tests/type/test_type .py
Normal file
@@ -0,0 +1,56 @@
|
||||
import pytest
|
||||
from django.urls import reverse
|
||||
from rest_framework.test import APIClient
|
||||
from core.apps.api.models import TypeModel
|
||||
|
||||
|
||||
@pytest.fixture
|
||||
def instance(db):
|
||||
return TypeModel._baker()
|
||||
|
||||
|
||||
@pytest.fixture
|
||||
def api_client(instance):
|
||||
client = APIClient()
|
||||
return client, instance
|
||||
|
||||
|
||||
@pytest.fixture
|
||||
def data(api_client):
|
||||
client, instance = api_client
|
||||
return (
|
||||
{
|
||||
"list": reverse("type-list"),
|
||||
"retrieve": reverse("type-detail", kwargs={"pk": instance.pk}),
|
||||
"retrieve-not-found": reverse("type-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
|
||||
@@ -1,2 +1,3 @@
|
||||
from .category import * # noqa
|
||||
from .products import * # noqa
|
||||
from .type import * # noqa
|
||||
|
||||
7
core/apps/api/translation/type.py
Normal file
7
core/apps/api/translation/type.py
Normal file
@@ -0,0 +1,7 @@
|
||||
from core.apps.api.models import TypeModel
|
||||
from modeltranslation.translator import TranslationOptions, register
|
||||
|
||||
|
||||
@register(TypeModel)
|
||||
class TypeTranslation(TranslationOptions):
|
||||
fields = []
|
||||
@@ -1,12 +1,18 @@
|
||||
from django.urls import include, path
|
||||
from django.urls import path, include
|
||||
from rest_framework.routers import DefaultRouter
|
||||
|
||||
from .views import CategoryView, FilialView, ProductsView, SubProductView, SubcategoryView
|
||||
from core.apps.api.views.category import FilialView, CategoryView, SubcategoryView, SubProductView
|
||||
from core.apps.api.views.products import ProductsView
|
||||
from core.apps.api.views.type import TypeView
|
||||
|
||||
router = DefaultRouter()
|
||||
router.register("filial", FilialView, basename="filial")
|
||||
router.register("subcategory", SubcategoryView, basename="subcategory")
|
||||
router.register("category", CategoryView, basename="category")
|
||||
router.register("products", ProductsView, basename="products")
|
||||
router.register("subproducts", SubProductView, basename="subproducts")
|
||||
urlpatterns = [path("", include(router.urls))]
|
||||
router.register("filials", FilialView, basename="filial")
|
||||
router.register("categories", CategoryView, basename="category")
|
||||
router.register("subcategories", SubcategoryView, basename="subcategory")
|
||||
router.register("products", ProductsView, basename="product")
|
||||
router.register("subproducts", SubProductView, basename="subproduct")
|
||||
router.register("types", TypeView, basename="type")
|
||||
|
||||
urlpatterns = [
|
||||
path("", include(router.urls)),
|
||||
]
|
||||
|
||||
@@ -1,2 +1,3 @@
|
||||
from .category import * # noqa
|
||||
from .products import * # noqa
|
||||
from .type import * # noqa
|
||||
|
||||
6
core/apps/api/validators/type.py
Normal file
6
core/apps/api/validators/type.py
Normal file
@@ -0,0 +1,6 @@
|
||||
class TypeValidator:
|
||||
def __init__(self):
|
||||
...
|
||||
|
||||
def __call__(self):
|
||||
return True
|
||||
@@ -1,2 +1,3 @@
|
||||
from .category import * # noqa
|
||||
from .products import * # noqa
|
||||
from .type import * # noqa
|
||||
|
||||
20
core/apps/api/views/type.py
Normal file
20
core/apps/api/views/type.py
Normal file
@@ -0,0 +1,20 @@
|
||||
from core.apps.api.models import TypeModel
|
||||
from core.apps.api.serializers.type import RetrieveTypeSerializer, ListTypeSerializer, CreateTypeSerializer
|
||||
from rest_framework.viewsets import ReadOnlyModelViewSet
|
||||
from rest_framework.permissions import AllowAny
|
||||
from drf_spectacular.utils import extend_schema
|
||||
from django_core.mixins import BaseViewSetMixin
|
||||
|
||||
|
||||
@extend_schema(tags=["type"])
|
||||
class TypeView(BaseViewSetMixin, ReadOnlyModelViewSet):
|
||||
queryset = TypeModel.objects.all()
|
||||
serializer_class = ListTypeSerializer
|
||||
permission_classes = [AllowAny]
|
||||
|
||||
action_permission_classes = {}
|
||||
action_serializer_class = {
|
||||
"list": ListTypeSerializer,
|
||||
"retrieve": RetrieveTypeSerializer,
|
||||
"create": CreateTypeSerializer,
|
||||
}
|
||||
2637
karomat_backup_20260331_153544.sql
Normal file
2637
karomat_backup_20260331_153544.sql
Normal file
File diff suppressed because it is too large
Load Diff
Reference in New Issue
Block a user