schon/vibes_auth/models.py
Egor fureunoir Gorbunov bb5911abe6 Features: 1) Introduced search fields with transliteration support across filters and Elasticsearch queries; 2) Simplified name filters in product, category, and brand filters by replacing custom logic with standard icontains; 3) Added process_system_query function with enhanced query capabilities.
Fixes: 1) Corrected inconsistent `QuerySet` type hints in vendors module; 2) Resolved string concatenation issue in `get_uuid_as_path` by prepending the path with "users/".

Extra: Updated Elasticsearch weighting factors and SMART_FIELDS configuration for better search relevance; removed unused methods and redundant comments in filters and documents; cleaned up migrations and adjusted query building logic.
2025-09-27 15:56:52 +03:00

216 lines
8.4 KiB
Python

from uuid import uuid4
from django.contrib.auth.models import AbstractUser
from django.contrib.auth.models import Group as BaseGroup
from django.core.cache import cache
from django.db.models import (
BooleanField,
CharField,
EmailField,
ImageField,
JSONField,
QuerySet,
UUIDField,
)
from django.utils.translation import gettext_lazy as _
from rest_framework_simplejwt.token_blacklist.models import (
BlacklistedToken as BaseBlacklistedToken,
)
from rest_framework_simplejwt.token_blacklist.models import (
OutstandingToken as BaseOutstandingToken,
)
from core.abstract import NiceModel
from core.models import Order, Wishlist
from evibes.settings import LANGUAGE_CODE, LANGUAGES
from payments.models import Balance
from vibes_auth.managers import UserManager
from vibes_auth.validators import validate_phone_number
class User(AbstractUser, NiceModel): # type: ignore [django-manager-missing]
"""
Represents a User entity with customized fields and methods for extended functionality.
This class extends the AbstractUser model and integrates additional features like
custom email login, validation methods, subscription status, verification, and
attributes storage. It also provides utilities for managing recently viewed items and
token-based activation for verifying accounts. The User model is designed to handle
specific use cases for enhanced user management.
Attributes:
email: EmailField to store the user's email address.
phone_number: CharField for the user's phone number, allowing for optional storage and validation.
username: Has been set to None as email is the primary unique identifier.
first_name: Optional CharField for the user's first name.
last_name: Optional CharField for the user's last name.
avatar: ImageField for storing the path to the user's profile picture.
is_verified: BooleanField indicating whether the user's email has been verified.
is_active: BooleanField to toggle user activity without deleting the account.
is_subscribed: BooleanField indicating the user's newsletter subscription status.
activation_token: UUIDField for assigning a unique activation token to the user.
language: CharField storing the user's preferred language setting.
attributes: JSONField for custom storage of user-specific additional attributes.
USERNAME_FIELD: Specifies the unique identifier for the user (email in this case).
REQUIRED_FIELDS: A list of fields required when creating a user via createsuperuser, left empty here.
objects: Custom manager for the User model providing additional methods for user creation.
payments_balance: Reference to the user's payment balance (related to the external model).
user_related_wishlist: Reference to the user's wishlist (related to the external model).
orders: QuerySet representing the user's associated orders.
Methods:
add_to_recently_viewed(product_uuid):
Adds a product's UUID to the user's recently viewed items cache. Keeps a maximum
of 48 items and maintains their order of viewing.
recently_viewed: (read-only property)
Retrieves a list of UUIDs representing the products recently viewed by the user
from the cache.
check_token(token):
Validates the input token against the user's activation token.
__str__():
Returns the string representation of the user, which is the email address.
Meta:
swappable: Configures the model to be replaceable with another user model.
verbose_name: Sets the human-readable name for singular instances of the model.
verbose_name_plural: Sets the human-readable name for multiple instances of the model.
"""
def get_uuid_as_path(self, *args):
return "users/" + str(self.uuid) + "/" + args[0]
email = EmailField(_("email"), unique=True, help_text=_("user email address"))
phone_number = CharField(
_("phone_number"),
max_length=20,
unique=True,
blank=True,
null=True,
help_text=_("user phone number"),
validators=[
validate_phone_number,
],
)
username: None = None # type: ignore [assignment]
first_name = CharField(_("first_name"), max_length=150, blank=True, null=True) # type: ignore [assignment]
last_name = CharField(_("last_name"), max_length=150, blank=True, null=True) # type: ignore [assignment]
avatar = ImageField(
null=True,
verbose_name=_("avatar"),
upload_to=get_uuid_as_path,
blank=True,
help_text=_("user profile image"),
)
is_verified = BooleanField(
default=False,
verbose_name=_("is verified"),
help_text=_("user verification status"),
)
is_active = BooleanField(
_("is_active"),
default=False,
help_text=_("unselect this instead of deleting accounts"),
)
is_subscribed = BooleanField(
verbose_name=_("is_subscribed"), help_text=_("user's newsletter subscription status"), default=False
)
activation_token = UUIDField(default=uuid4, verbose_name=_("activation token"))
language = CharField(choices=LANGUAGES, default=LANGUAGE_CODE, null=False, blank=False, max_length=7)
attributes = JSONField(verbose_name=_("attributes"), default=dict, blank=True, null=True)
USERNAME_FIELD = "email"
REQUIRED_FIELDS = []
# noinspection PyClassVar
objects = UserManager() # type: ignore [misc, assignment]
payments_balance: "Balance"
user_related_wishlist: "Wishlist"
orders: QuerySet["Order"]
def add_to_recently_viewed(self, product_uuid):
recently_viewed = self.recently_viewed
if product_uuid not in recently_viewed:
if not len(recently_viewed) >= 48:
recently_viewed.append(product_uuid)
cache.set(f"user_{self.uuid}_rv", recently_viewed)
else:
recently_viewed.pop(0)
recently_viewed.append(product_uuid)
cache.set(f"user_{self.uuid}_rv", recently_viewed)
@property
def recently_viewed(self):
return cache.get(f"user_{self.uuid}_rv", [])
def check_token(self, token):
return str(token) == str(self.activation_token)
def __str__(self):
return self.email
class Meta:
swappable = "AUTH_USER_MODEL"
verbose_name = _("user")
verbose_name_plural = _("users")
class Group(BaseGroup):
"""
Proxy model representing a Group entity.
This class acts as a proxy for the `BaseGroup` model, providing additional
configuration for display and usage purposes. It does not introduce new fields
or behavior but alters representation and metadata for the Group model.
Attributes:
Meta.proxy (bool): Indicates this is a proxy model.
Meta.verbose_name (str): Human-readable singular name for the Group model.
Meta.verbose_name_plural (str): Human-readable plural name for the Group
model.
"""
class Meta:
proxy = True
verbose_name = _("group")
verbose_name_plural = _("groups")
class OutstandingToken(BaseOutstandingToken):
"""
Represents a proxy model for outstanding tokens.
This class is a proxy for the `BaseOutstandingToken` model, used to manage
and customize behaviors or configurations related to outstanding tokens.
It does not add additional fields or logic to the base model but allows for
overloading or extending its default functionality as required.
"""
class Meta:
proxy = True
verbose_name = _("outstanding token")
verbose_name_plural = _("outstanding tokens")
class BlacklistedToken(BaseBlacklistedToken):
"""
Represents a blacklisted token model in the system.
This class acts as a Django model proxy and serves the purpose of managing
blacklisted tokens. It inherits from a base class provided for this purpose,
allowing for the extension of functionality or customization of behavior
without altering the original base class's structure. It also defines the
Meta options for the model, affecting its database and administrative
representation.
"""
class Meta:
proxy = True
verbose_name = _("blacklisted token")
verbose_name_plural = _("blacklisted tokens")