Добавление тегов в портфолио, добавление вадилатора почты, добавление параметров телеофн и почта в создание заказа

This commit is contained in:
VladislavOstapov 2023-04-26 00:30:29 +03:00
parent 451a8b9bc3
commit 82a957f659
5 changed files with 102 additions and 27 deletions

View File

@ -52,6 +52,12 @@ class PortfolioAdmin(admin.ModelAdmin):
readonly_fields = ['actual_date', 'actual_price']
@admin.register(PortfolioPhoto)
class PortfolioAdmin(admin.ModelAdmin):
# list_display = ['id', 'account', 'title', 'actual_date', 'actual_price']
# readonly_fields = ['actual_date', 'actual_price']
pass
#
#
# @admin.register(OrderImage)

View File

@ -1,9 +1,6 @@
import random
import time
from datetime import date as dt
import traceback
from django.http import HttpResponseNotFound
from .api_media_utils import *
from .api_utils import *
@ -194,9 +191,11 @@ class ApiAccount:
ApiParamEnum(name="city", description="Город, в котором находится ползователь: {choices}",
required=False, choices=CITIES_CHOICES),
ApiParamInt(name="photo", required=False, default=None,
description="ID медиа, которое будет использоваться в качестве фото профиля"),
description="ID медиа, которое будет использоваться в качестве фото профиля, "
"-1 для сброса"),
ApiParamInt(name="profile_background", required=False, default=None,
description="ID медиа, которое будет использоваться в качестве банера профиля")
description="ID медиа, которое будет использоваться в качестве банера профиля, "
"-1 для сброса")
],
returns="Вернет основную информацию о пользователе, иначе ошибки")
async def edit(access_token, name, surname, about, executor_type, executor_inn, city, photo, profile_background):
@ -220,9 +219,12 @@ class ApiAccount:
need_save = True
if photo is not None:
p = await Media.objects.filter(pk=photo).select_related('owner').afirst()
if p is None:
raise Exception(API_ERROR_NOT_FOUND, 'field "photo" not found')
if photo != -1:
p = await Media.objects.filter(pk=photo).select_related('owner').afirst()
if p is None:
raise Exception(API_ERROR_NOT_FOUND, 'field "photo" not found')
else:
p = None
if p.owner.id == user.id and p.extension in Media.PHOTO_EXTENSIONS:
if not hasattr(user, 'accountavatar'):
@ -233,9 +235,12 @@ class ApiAccount:
raise Exception(API_ERROR_NOT_FOUND, 'field "photo" not correct')
if profile_background is not None:
p = await Media.objects.filter(pk=profile_background).select_related('owner').afirst()
if p is None:
raise Exception(API_ERROR_NOT_FOUND, 'field "profile_background" not found')
if profile_background != -1:
p = await Media.objects.filter(pk=profile_background).select_related('owner').afirst()
if p is None:
raise Exception(API_ERROR_NOT_FOUND, 'field "profile_background" not found')
else:
p = None
if p.owner.id == user.id and p.extension in Media.PHOTO_EXTENSIONS:
if not hasattr(user, 'accountavatar'):
@ -528,9 +533,8 @@ class ApiOrder:
required=False, default=""),
ApiParamStr(name='video_link', max_length=160, description="Ссылка на видео",
required=False, default=""),
# email = models.EmailField(null=True, blank=True, verbose_name="Email")
# phone = models.CharField(null=True, blank=True, max_length=16, verbose_name="Телефон")
ApiParamPhone(max_length=60, required=False, default=None),
ApiParamEmail(required=False, default=None)
],
returns="ID созданного заказа, иначе одну из ошибок")
async def create(**kwargs):
@ -541,7 +545,7 @@ class ApiOrder:
order = await Order.objects.acreate(owner=access_token.user, **kwargs)
return api_make_response({"order_id": order.id})
except ValidationError as ve:
return _make_model_validation_errors(ve, API_ERROR_USER_MODIFY)
return _make_model_validation_errors(ve, API_ERROR_OBJECT_VALIDATION)
@staticmethod
@api_method("order.setPublished",
@ -689,15 +693,15 @@ class ApiMedia:
@api_method("media.get",
doc="Получение медиа",
params=[
ApiRequestParam(),
ApiParamAccessToken(),
ApiParamInt(name="media_id", description="ID медиа",
value_min=0, value_max=1000000000),
], returns="<code>медиа</code>, в противном случае ошибку")
async def get(request, access_token, media_id):
async def get(access_token, media_id):
m = await Media.objects.filter(Q(owner=access_token.user) |
Q(owner__accountavatar__photo=media_id) |
Q(owner__accountavatar__profile_background_id=media_id)).filter(pk=media_id).afirst()
Q(accountavatar__photo=media_id) |
Q(accountavatar__profile_background=media_id) |
Q(portfoliophoto=media_id)).filter(pk=media_id).afirst()
if m is not None:
try:
@ -753,8 +757,9 @@ class ApiPortfolio:
ApiParamFloat(name="price", description="Цена заказа, актуальная на момент выполнения"),
ApiParamFloat(name='square', value_max=99999.99, value_min=1.0,
description='Площадь в м²'),
ApiParamTags(name='tags', tags=Portfolio.TAGS_NAMES, required=False, default=[])
], returns="<code>id</code> созданного объекта")
async def create(access_token, title, date, price, square):
async def create(access_token, title, date, price, square, tags):
# проверка на роль, нужна сразу
if access_token.user.role != Account.ROLE_EXECUTOR:
raise Exception(API_ERROR_NOT_ALLOWED, "you must have executor role")
@ -762,7 +767,7 @@ class ApiPortfolio:
try:
p = await Portfolio.objects.acreate(account=access_token.user, actual_price=price, square=square,
actual_date=(dt.fromtimestamp(date) if date is not None else None),
title=title,)
title=title, attributes={"tags": tags})
return api_make_response({"portfolio_id": p.id})
except Exception:
traceback.print_exc()
@ -780,9 +785,10 @@ class ApiPortfolio:
ApiParamInt(name="count", required=False, value_min=1, value_max=100, default=20,
description="Количество объектов, по умолчанию 20"),
ApiParamInt(name="offset", required=False, value_min=0, default=0,
description="Количество объектов")
description="Количество объектов"),
ApiParamTags(name='tags', tags=Portfolio.TAGS_NAMES, required=False, default=None)
], returns="")
async def get(access_token, owner_id, portfolio_id, count, offset):
async def get(access_token, owner_id, portfolio_id, tags, count, offset):
res = Portfolio.objects.order_by('actual_date')
res = res.select_related('account', 'account__accountavatar', 'account__accountavatar__photo',
'account__accountavatar__profile_background',
@ -794,6 +800,9 @@ class ApiPortfolio:
if portfolio_id is not None:
res = res.filter(id=portfolio_id)
if tags is not None:
res = res.filter(attributes__tags__contains=tags)
res = res[offset:offset + count]
# выполняем fetch
@ -805,7 +814,8 @@ class ApiPortfolio:
"publish_date": int(time.mktime(item.publish_date.timetuple())),
"actual_price": float(item.actual_price),
"square": float(item.square),
"photos": [random.randint(4, 28) for _ in range(random.randint(1, 5))]
"photos": [random.randint(4, 28) for _ in range(random.randint(1, 5))],
"attributes": item.attributes
} async for item in res]
return api_make_response(objects)

View File

@ -288,3 +288,45 @@ class ApiParamVerifyCode(ApiParamInt):
description="Код верификации (требуется если клиенту будет отправлена "
"одна из ошибок <i>верификации</i>)", **kwargs):
super().__init__(name=name, required=False, value_min=0, value_max=9999, description=description, **kwargs)
class ApiParamEmail(ApiParamStr):
def __init__(self, name="email",
description="Почта", **kwargs):
super().__init__(name=name, description=description,
regex="^[\\w\\-.]+@([\\w\\-]+\\.)+[\\w\\-]{2,4}$", **kwargs)
class ApiParamTags(ApiParamStr):
def __init__(self, tags: list[list[str, str] | tuple[str, str]], name="tags", default=None,
description="Один или несколько тегов из списка: {tags}"
" Теги перечисляются через запятую, без кавычек", **kwargs):
super().__init__(name=name, description=description,
regex="^[\\w\\_]+(,[\\w\\_]+)*$", default=None, **kwargs)
self.tags = tags
self.__tags_names = [i[0] for i in tags]
self.__default_tags = default
def validate(self, value):
items = super(ApiParamTags, self).validate(value)
if items is not None:
items = value.split(',')
# проверка того, что параметры входят в список
for i in items:
if i not in self.__tags_names:
_make_invalid_argument_value_error(self.name, value, "unexpected",
f"expected items in {self.__tags_names}")
else:
items = self.__default_tags
return items
def get_doc(self):
doc = super().get_doc()
ch = "<ul>"
for c in self.tags:
if c[0] != '':
ch += f"<li>{c[0]} - {c[1]}</li>"
ch += "</ul>"
return doc.replace('{tags}', ch)

View File

@ -407,6 +407,10 @@ class Order(models.Model):
return q[0]
def _portfolio_default_attrs():
return {"tags": []}
class Portfolio(models.Model):
account = models.ForeignKey(Account, on_delete=models.CASCADE, verbose_name="Аккаунт")
@ -416,13 +420,26 @@ class Portfolio(models.Model):
actual_price = models.DecimalField(max_digits=12, decimal_places=2, blank=False, verbose_name="Цена")
square = models.DecimalField(max_digits=7, decimal_places=2, blank=False, verbose_name="Площадь в м²")
TAGS_NAMES = [
("housings", "Квартиры"),
("private_houses", "Частные дома"),
("country_houses", "Дачные дома"),
("penthouses", "Пентхаусы"),
("apartments", "Апартаменты"),
("rooms", "Комнаты"),
("kitchens", "Кухни"),
("bathrooms", "Ванные комнаты"),
("child_rooms", "Детские комнаты")
]
attributes = models.JSONField(verbose_name="Атрибуты", default=_portfolio_default_attrs)
def __str__(self):
return f"{self.id}: \"{self.title}\""
class PortfolioPhoto(models.Model):
portfolio = models.ForeignKey(Portfolio, on_delete=models.CASCADE, verbose_name="Портфолио")
photo = models.ForeignKey(Media, on_delete=models.SET_NULL, null=True, verbose_name="Аватар")
photo = models.ForeignKey(Media, on_delete=models.CASCADE, verbose_name="Фотография")
is_preview = models.BooleanField(verbose_name="Это главная фотография")
class Meta:

View File

@ -1,5 +1,5 @@
psycopg2
psycopg2-binary==2.9.6
django==4.1.7
requests==2.28.2
python-dotenv
boto3
boto3==1.26.120