Добавление портфолио в тестовом виде
This commit is contained in:
parent
f200ec0bef
commit
9982c3b529
@ -37,6 +37,14 @@ class AccessTokenAdmin(admin.ModelAdmin):
|
|||||||
class OrderAdmin(admin.ModelAdmin):
|
class OrderAdmin(admin.ModelAdmin):
|
||||||
list_display = ['owner', 'phone', 'name', 'create_time', 'moderated', 'published']
|
list_display = ['owner', 'phone', 'name', 'create_time', 'moderated', 'published']
|
||||||
readonly_fields = ['create_time']
|
readonly_fields = ['create_time']
|
||||||
|
|
||||||
|
|
||||||
|
@admin.register(Portfolio)
|
||||||
|
class PortfolioAdmin(admin.ModelAdmin):
|
||||||
|
list_display = ['id', 'account', 'title', 'actual_date', 'actual_price']
|
||||||
|
readonly_fields = ['actual_date', 'actual_price']
|
||||||
|
|
||||||
|
|
||||||
#
|
#
|
||||||
#
|
#
|
||||||
# @admin.register(OrderImage)
|
# @admin.register(OrderImage)
|
||||||
|
@ -74,16 +74,14 @@ def __make_error(ex: Exception):
|
|||||||
def make_error_object(ex: Exception | list):
|
def make_error_object(ex: Exception | list):
|
||||||
try:
|
try:
|
||||||
data = {
|
data = {
|
||||||
"status": "error"
|
"status": "error",
|
||||||
}
|
"error": {
|
||||||
if type(ex) == list:
|
|
||||||
data["error"] = {
|
|
||||||
"code": API_ERROR_MULTIPLY_ERRORS[0],
|
"code": API_ERROR_MULTIPLY_ERRORS[0],
|
||||||
"message": API_ERROR_MULTIPLY_ERRORS[1],
|
"message": API_ERROR_MULTIPLY_ERRORS[1],
|
||||||
}
|
} if type(ex) == list else __make_error(ex)
|
||||||
|
}
|
||||||
|
if type(ex) == list:
|
||||||
data["related"] = [__make_error(e) for e in ex]
|
data["related"] = [__make_error(e) for e in ex]
|
||||||
else:
|
|
||||||
data["error"] = __make_error(ex)
|
|
||||||
|
|
||||||
return data
|
return data
|
||||||
except BaseException as err:
|
except BaseException as err:
|
||||||
|
@ -1,4 +1,8 @@
|
|||||||
|
import random
|
||||||
import time
|
import time
|
||||||
|
from datetime import date as dt
|
||||||
|
import traceback
|
||||||
|
|
||||||
from .api_media_utils import *
|
from .api_media_utils import *
|
||||||
from .api_utils import *
|
from .api_utils import *
|
||||||
from .models import *
|
from .models import *
|
||||||
@ -39,7 +43,7 @@ class ApiAccount:
|
|||||||
return True
|
return True
|
||||||
|
|
||||||
@staticmethod
|
@staticmethod
|
||||||
def __make_user_json(user: Account, self_using=False):
|
def make_user_json(user: Account, self_using=False):
|
||||||
obj = {
|
obj = {
|
||||||
"id": user.id,
|
"id": user.id,
|
||||||
"name": user.name,
|
"name": user.name,
|
||||||
@ -165,7 +169,7 @@ class ApiAccount:
|
|||||||
if user is None:
|
if user is None:
|
||||||
return make_error_object(Exception(API_ERROR_NOT_FOUND, {"user": user_id}))
|
return make_error_object(Exception(API_ERROR_NOT_FOUND, {"user": user_id}))
|
||||||
|
|
||||||
return api_make_response(ApiAccount.__make_user_json(user))
|
return api_make_response(ApiAccount.make_user_json(user))
|
||||||
|
|
||||||
@staticmethod
|
@staticmethod
|
||||||
@api_method("account.edit",
|
@api_method("account.edit",
|
||||||
@ -281,7 +285,7 @@ class ApiAccount:
|
|||||||
|
|
||||||
await sync_to_async(user.executoraccount.save)()
|
await sync_to_async(user.executoraccount.save)()
|
||||||
|
|
||||||
return api_make_response(ApiAccount.__make_user_json(user))
|
return api_make_response(ApiAccount.make_user_json(user))
|
||||||
|
|
||||||
@staticmethod
|
@staticmethod
|
||||||
@api_method("account.changePhone",
|
@api_method("account.changePhone",
|
||||||
@ -801,6 +805,78 @@ class ApiMedia:
|
|||||||
return api_make_response(res)
|
return api_make_response(res)
|
||||||
|
|
||||||
|
|
||||||
|
class ApiPortfolio:
|
||||||
|
# portfel.get(user_id=None, portfel_id=None, count=20, offset=0, order_by={date,-date, .... })
|
||||||
|
# portfel.create(поля портфолио)
|
||||||
|
# portfel.delete(portfel_id)
|
||||||
|
@staticmethod
|
||||||
|
@api_method("portfolio.create",
|
||||||
|
doc="Создания объекта портфолио",
|
||||||
|
params=[
|
||||||
|
ApiParamAccessToken(),
|
||||||
|
ApiParamStr(name="title", min_length=5, max_length=200,
|
||||||
|
description="Название выполненного заказа, 5-200 символов"),
|
||||||
|
ApiParamInt(name="date", required=False,
|
||||||
|
description="Дата в формате UNIX time, потом будет нормальный объект даты,"
|
||||||
|
"если не передать то будет вставлена текущая дата"),
|
||||||
|
ApiParamFloat(name="price", description="Цена заказа, актуальная на момент выполнения")
|
||||||
|
], returns="<code>id</code> созданного объекта")
|
||||||
|
async def create(access_token, title, date, price):
|
||||||
|
# проверка на роль, нужна сразу
|
||||||
|
if access_token.user.role != Account.ROLE_EXECUTOR:
|
||||||
|
raise Exception(API_ERROR_NOT_ALLOWED, "you must have executor role")
|
||||||
|
|
||||||
|
try:
|
||||||
|
p = await Portfolio.objects.acreate(account=access_token.user, title=title, actual_price=price,
|
||||||
|
actual_date=(dt.fromtimestamp(date) if date is not None else None))
|
||||||
|
return api_make_response({"portfolio_id": p.id})
|
||||||
|
except Exception:
|
||||||
|
traceback.print_exc()
|
||||||
|
raise Exception(API_ERROR_INTERNAL_ERROR)
|
||||||
|
|
||||||
|
@staticmethod
|
||||||
|
@api_method("portfolio.get",
|
||||||
|
doc="Получение портфолио или ленты портфолио",
|
||||||
|
params=[
|
||||||
|
ApiParamAccessToken(),
|
||||||
|
ApiParamInt(name="owner_id", required=False, value_min=0, default=None,
|
||||||
|
description="ID пользователя, портфолио которого нужно вернуть."),
|
||||||
|
ApiParamInt(name="portfolio_id", required=False, value_min=0, default=None,
|
||||||
|
description="ID портфолио, которое нужно вернуть."),
|
||||||
|
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="Количество объектов")
|
||||||
|
], returns="")
|
||||||
|
async def get(access_token, owner_id, portfolio_id, count, offset):
|
||||||
|
res = Portfolio.objects.order_by('actual_date')
|
||||||
|
res = res.select_related('account', 'account__accountavatar', 'account__accountavatar__photo',
|
||||||
|
'account__accountavatar__profile_background',
|
||||||
|
'account__executoraccount')
|
||||||
|
|
||||||
|
if owner_id is not None:
|
||||||
|
res = res.filter(account_id=owner_id)
|
||||||
|
|
||||||
|
if portfolio_id is not None:
|
||||||
|
res = res.filter(id=portfolio_id)
|
||||||
|
|
||||||
|
res = res[offset:offset + count]
|
||||||
|
|
||||||
|
# выполняем fetch
|
||||||
|
objects = [{
|
||||||
|
"owner": ApiAccount.make_user_json(item.account),
|
||||||
|
"id": item.id,
|
||||||
|
"title": item.title,
|
||||||
|
"actual_date": int(time.mktime(item.actual_date.timetuple())),
|
||||||
|
"actual_price": item.actual_price,
|
||||||
|
"photos": [random.randint(1, 10) for _ in range(random.randint(1, 5))]
|
||||||
|
} async for item in res]
|
||||||
|
|
||||||
|
return api_make_response(objects)
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
class DatabaseApi:
|
class DatabaseApi:
|
||||||
# TODO переместить сюда форму заказа, список городов
|
# TODO переместить сюда форму заказа, список городов
|
||||||
# def get_order_form
|
# def get_order_form
|
||||||
|
@ -300,9 +300,9 @@ class Order(models.Model):
|
|||||||
default=CHOICE_UNDEFINED, verbose_name="Тип помещения")
|
default=CHOICE_UNDEFINED, verbose_name="Тип помещения")
|
||||||
|
|
||||||
number_of_rooms = models.SmallIntegerField(verbose_name='Количество комнат, -1 = студия', validators=[
|
number_of_rooms = models.SmallIntegerField(verbose_name='Количество комнат, -1 = студия', validators=[
|
||||||
MinValueValidator(-1),
|
MinValueValidator(-1),
|
||||||
MaxValueValidator(100)
|
MaxValueValidator(100)
|
||||||
])
|
])
|
||||||
|
|
||||||
is_balcony = models.BooleanField(default=False, verbose_name="Балкон")
|
is_balcony = models.BooleanField(default=False, verbose_name="Балкон")
|
||||||
is_loggia = models.BooleanField(default=False, verbose_name="Лоджия")
|
is_loggia = models.BooleanField(default=False, verbose_name="Лоджия")
|
||||||
@ -402,6 +402,33 @@ class Order(models.Model):
|
|||||||
else:
|
else:
|
||||||
return q[0]
|
return q[0]
|
||||||
|
|
||||||
|
|
||||||
|
class Portfolio(models.Model):
|
||||||
|
account = models.ForeignKey(Account, on_delete=models.CASCADE, verbose_name="Аккаунт")
|
||||||
|
|
||||||
|
title = models.CharField(max_length=200, verbose_name="Название")
|
||||||
|
actual_date = models.DateField(verbose_name="Дата выполнения", default=datetime.now)
|
||||||
|
actual_price = models.DecimalField(max_digits=12, decimal_places=2, blank=False, verbose_name="Цена")
|
||||||
|
|
||||||
|
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="Аватар")
|
||||||
|
is_preview = models.BooleanField(verbose_name="Это главная фотография")
|
||||||
|
|
||||||
|
class Meta:
|
||||||
|
constraints = [
|
||||||
|
models.UniqueConstraint(
|
||||||
|
fields=['portfolio', 'photo'], name='unique_portfoliophoto_photo'
|
||||||
|
)
|
||||||
|
]
|
||||||
|
|
||||||
|
# TODO добавить проверку того, чтобы нельзя было приложить медиа другого юзера
|
||||||
|
|
||||||
|
|
||||||
# def _upload_image_filename(instance, filename):
|
# def _upload_image_filename(instance, filename):
|
||||||
# name, ext = os.path.splitext(filename)
|
# name, ext = os.path.splitext(filename)
|
||||||
# fn = sha256((str(datetime.now()) + name).encode('utf-8')).hexdigest() + ext
|
# fn = sha256((str(datetime.now()) + name).encode('utf-8')).hexdigest() + ext
|
||||||
|
Reference in New Issue
Block a user