diff --git a/api/admin.py b/api/admin.py
index a406125..3f3e6a7 100755
--- a/api/admin.py
+++ b/api/admin.py
@@ -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)
diff --git a/api/api_methods.py b/api/api_methods.py
index 347dd2b..3d15928 100755
--- a/api/api_methods.py
+++ b/api/api_methods.py
@@ -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="медиа
, в противном случае ошибку")
- 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="id
созданного объекта")
- 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)
diff --git a/api/api_params.py b/api/api_params.py
index 3b0f2d2..65bf855 100755
--- a/api/api_params.py
+++ b/api/api_params.py
@@ -288,3 +288,45 @@ class ApiParamVerifyCode(ApiParamInt):
description="Код верификации (требуется если клиенту будет отправлена "
"одна из ошибок верификации)", **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 = "