add: модели (draft 09)

This commit is contained in:
2026-06-02 16:06:29 +03:00
parent 61be28786b
commit 76e0350688

View File

@@ -31,7 +31,7 @@ class TbImage(models.Model):
help_text='Файл изображения, загруженный через django_filer.', help_text='Файл изображения, загруженный через django_filer.',
) )
l_source = models.CharField( l_img_source = models.CharField(
max_length=10, max_length=10,
choices=ImageSource.choices, choices=ImageSource.choices,
default=ImageSource.MANUAL_UPLOAD, default=ImageSource.MANUAL_UPLOAD,
@@ -40,7 +40,7 @@ class TbImage(models.Model):
' Discogs), предоставлен продавцом и т.д.', ' Discogs), предоставлен продавцом и т.д.',
) )
l_reality = models.CharField( l_img_reality = models.CharField(
max_length=10, max_length=10,
choices=ImageReality.choices, choices=ImageReality.choices,
default=ImageReality.ABSTRACT, default=ImageReality.ABSTRACT,
@@ -48,7 +48,7 @@ class TbImage(models.Model):
help_text='Реальная фотография товара или картинка из внешнего источника?', help_text='Реальная фотография товара или картинка из внешнего источника?',
) )
s_source_url = models.URLField( s_img_src_url = models.URLField(
blank=True, blank=True,
null=True, null=True,
verbose_name='URL источника', verbose_name='URL источника',
@@ -56,7 +56,7 @@ class TbImage(models.Model):
) )
# Порядок вывода # Порядок вывода
i_sort_order = models.IntegerField( i_img_sort = models.IntegerField(
default=0, default=0,
db_index=True, db_index=True,
verbose_name='Cортировка', verbose_name='Cортировка',
@@ -65,7 +65,7 @@ class TbImage(models.Model):
) )
# Доверие данным (для парсеров и API) # Доверие данным (для парсеров и API)
f_confidence_score = models.FloatField( f_img_confidence_score = models.FloatField(
null=True, null=True,
blank=True, blank=True,
default=None, default=None,
@@ -73,7 +73,7 @@ class TbImage(models.Model):
help_text='0.0 - 1.0, насколько уверены, что это правильное изображение', help_text='0.0 - 1.0, насколько уверены, что это правильное изображение',
) )
# Авторские права # Авторские права
s_copyright = models.CharField( s_img_copyright = models.CharField(
max_length=255, max_length=255,
blank=True, blank=True,
default='', default='',
@@ -82,11 +82,11 @@ class TbImage(models.Model):
) )
# Timestamps # Timestamps
t_created = models.DateTimeField( t_img_created = models.DateTimeField(
auto_now_add=True, auto_now_add=True,
verbose_name='Дата добавления', verbose_name='Дата добавления',
) )
t_updated = models.DateTimeField( t_img_updated = models.DateTimeField(
auto_now=True, auto_now=True,
verbose_name='Дата обновления', verbose_name='Дата обновления',
) )
@@ -94,7 +94,7 @@ class TbImage(models.Model):
class Meta: class Meta:
verbose_name = 'Изображение' verbose_name = 'Изображение'
verbose_name_plural = 'Изображения' verbose_name_plural = 'Изображения'
ordering = ('i_sort_order', 't_created') ordering = ('i_img_sort', 't_img_created')
# ============================================================================ # ============================================================================
@@ -114,7 +114,8 @@ class TbArtist(models.Model):
null=True, null=True,
default='', default='',
verbose_name='Исполнитель (типографированно в HTML)', verbose_name='Исполнитель (типографированно в HTML)',
help_text='С сохранением типографирования и спецсимволов.', help_text='С сохранением типографирования и спецсимволов (всякие "метки" и иконки, типа "Иноагент",'
' тоже можно заверстать сюда.',
) )
k_artist_to_image = models.OneToOneField( k_artist_to_image = models.OneToOneField(
TbImage, TbImage,
@@ -126,6 +127,13 @@ class TbArtist(models.Model):
help_text='Изображение исполнителя, например, логотип группы или фотография. Если указано, будет' help_text='Изображение исполнителя, например, логотип группы или фотография. Если указано, будет'
' отображаться рядом с именем исполнителя.', ' отображаться рядом с именем исполнителя.',
) )
s_artist_article_html = models.TextField(
blank=True,
null=True,
default='',
verbose_name='Статья',
help_text='Статья об исполнителе (типографированно в HTML)',
)
s_slug = models.SlugField( s_slug = models.SlugField(
max_length=64, max_length=64,
verbose_name='Слаг', verbose_name='Слаг',
@@ -137,11 +145,11 @@ class TbArtist(models.Model):
verbose_name='Варианты написания в источниках', verbose_name='Варианты написания в источниках',
help_text='Список вариантов: ["The Beatles", "Beatles", "Beatles, The"]', help_text='Список вариантов: ["The Beatles", "Beatles", "Beatles, The"]',
) )
t_created = models.DateTimeField( t_artist_created = models.DateTimeField(
auto_now_add=True, auto_now_add=True,
verbose_name="Дата создания", verbose_name="Дата создания",
) )
t_updated = models.DateTimeField( t_artist_updated = models.DateTimeField(
auto_now=True, auto_now=True,
verbose_name="Дата обновления", verbose_name="Дата обновления",
) )
@@ -171,7 +179,6 @@ class TbItem(models.Model):
verbose_name='Исполнители', verbose_name='Исполнители',
help_text="Один или несколько для коллабораций", help_text="Один или несколько для коллабораций",
) )
s_item_title = models.CharField( s_item_title = models.CharField(
max_length=255, blank=False, null=False, default='', max_length=255, blank=False, null=False, default='',
# db_index=True, # db_index=True,
@@ -179,7 +186,6 @@ class TbItem(models.Model):
help_text='Название товара (релиза), как указано на обложке. Например: <tt>Abbey Road</tt>' help_text='Название товара (релиза), как указано на обложке. Например: <tt>Abbey Road</tt>'
' или <tt>TDK, CDing I, 90</tt>.', ' или <tt>TDK, CDing I, 90</tt>.',
) )
s_item_title_html = models.TextField( s_item_title_html = models.TextField(
blank=True, blank=True,
null=True, null=True,
@@ -187,7 +193,6 @@ class TbItem(models.Model):
verbose_name='Название (типографированно)', verbose_name='Название (типографированно)',
help_text='С HTML-тегами для типографирования', help_text='С HTML-тегами для типографирования',
) )
s_item_to_image = models.OneToOneField( s_item_to_image = models.OneToOneField(
TbImage, TbImage,
on_delete=models.SET_NULL, on_delete=models.SET_NULL,
@@ -198,6 +203,15 @@ class TbItem(models.Model):
help_text='Изображение товара из каталога, например, обложка альбома. Если указано, будет отображаться' help_text='Изображение товара из каталога, например, обложка альбома. Если указано, будет отображаться'
' рядом с названием товара.', ' рядом с названием товара.',
) )
s_item_article_html = models.TextField(
blank=True,
null=True,
default='',
verbose_name='Статья',
help_text='Статья о релизе, например, описание из Википедии или Discogs (а также список композиций,'
' исполнители и т.п.). Для товара это может быть техническое описание и характеристики.'
' Текст сверстан в HTML с сохранением типографирования и спецсимволов.',
)
s_item_slug = models.SlugField( s_item_slug = models.SlugField(
blank=True, blank=True,
null=True, null=True,
@@ -205,7 +219,6 @@ class TbItem(models.Model):
verbose_name='Слаг', verbose_name='Слаг',
help_text='Слаг для товара, формируемый на основе названия товара (релиза)', help_text='Слаг для товара, формируемый на основе названия товара (релиза)',
) )
# Год выпуска (важно для коллекционеров) # Год выпуска (важно для коллекционеров)
s_item_date = models.CharField( s_item_date = models.CharField(
max_length=10, max_length=10,
@@ -218,14 +231,12 @@ class TbItem(models.Model):
' релиза, чем t_release_date, так как может содержать неполную дату и/или текстовую информацию.' ' релиза, чем t_release_date, так как может содержать неполную дату и/или текстовую информацию.'
' Срабатывает только если t_release_date не указано.' ' Срабатывает только если t_release_date не указано.'
) )
t_item_date = models.DateField( t_item_date = models.DateField(
blank=True, blank=True,
null=True, null=True,
verbose_name='Дата релиза', verbose_name='Дата релиза',
help_text='Полная дата если известна, например: 1969-09-26. Если точно известен.', help_text='Полная дата если известна, например: 1969-09-26. Если точно известен.',
) )
i_discogs_master_id = models.IntegerField( i_discogs_master_id = models.IntegerField(
blank=True, blank=True,
null=True, null=True,
@@ -233,14 +244,12 @@ class TbItem(models.Model):
verbose_name='ID на мастер-релиз Discogs', verbose_name='ID на мастер-релиз Discogs',
help_text='Уникальный идентификатор мастер-релиза на Discogs, если он там есть. Например: <tt>306323</tt>', help_text='Уникальный идентификатор мастер-релиза на Discogs, если он там есть. Например: <tt>306323</tt>',
) )
# Метаданные релиза (страна, жанр, количество треков и т.д.)
j_item_metadata = models.JSONField( j_item_metadata = models.JSONField(
default=dict, blank=True, null=True, default=dict, blank=True, null=True,
verbose_name='Дополнительные данные', verbose_name='Дополнительные данные',
help_text='Дополнительные данные о релизе в виде JSON-словаря. Сюда же включены варианты написания релиза в источниках', help_text='Дополнительные данные и метаданные релиза (страна, жанр, количество треков и т.д.) или товавра'
' в виде JSON-словаря. Сюда же включены варианты написания релиза в источниках',
) )
t_item_created = models.DateTimeField( t_item_created = models.DateTimeField(
auto_now_add=True, auto_now_add=True,
verbose_name="Дата создания", verbose_name="Дата создания",
@@ -262,7 +271,6 @@ class TbItem(models.Model):
# ============================================================================ # ============================================================================
# ЛЕЙБЛЫ (производители релизов) # ЛЕЙБЛЫ (производители релизов)
# ============================================================================ # ============================================================================
class TbLabel(models.Model): class TbLabel(models.Model):
""" """
Лейбл или издатель релиза. Лейбл или издатель релиза.
@@ -270,7 +278,6 @@ class TbLabel(models.Model):
для кассет под запись это: TDK, AXIA, Maxell, JVC ... для кассет под запись это: TDK, AXIA, Maxell, JVC ...
для hi-fi это: Sony, Pioneer, Technics, Marantz ... для hi-fi это: Sony, Pioneer, Technics, Marantz ...
""" """
s_label = models.CharField( s_label = models.CharField(
max_length=128, max_length=128,
blank=False, blank=False,
@@ -279,7 +286,13 @@ class TbLabel(models.Model):
verbose_name='Название лейбла', verbose_name='Название лейбла',
help_text='Например: "Sony Records" или "Мелодия"', help_text='Например: "Sony Records" или "Мелодия"',
) )
s_label_article_html = models.TextField(
blank=True,
null=True,
default='',
verbose_name='Статья',
help_text='Статья о лейбле (типографированно в HTML)',
)
s_label_slug = models.SlugField( s_label_slug = models.SlugField(
max_length=64, max_length=64,
blank=False, blank=False,
@@ -287,20 +300,18 @@ class TbLabel(models.Model):
default='', default='',
verbose_name='Слаг', verbose_name='Слаг',
) )
j_label_metadata = models.JSONField( j_label_metadata = models.JSONField(
default=dict, default=dict,
blank=True, blank=True,
null=True, null=True,
verbose_name='Дополнительные данные', verbose_name='Метаданные',
help_text='JSON: страна лейбла, официальный сайт и т.д.', help_text='JSON: страна лейбла, официальный сайт и т.д.',
) )
t_label_created = models.DateTimeField(
t_created = models.DateTimeField(
auto_now_add=True, auto_now_add=True,
verbose_name="Дата создания", verbose_name="Дата создания",
) )
t_updated = models.DateTimeField( t_tabel_updated = models.DateTimeField(
auto_now=True, auto_now=True,
verbose_name="Дата обновления", verbose_name="Дата обновления",
) )
@@ -320,44 +331,45 @@ class TbLabel(models.Model):
class TbSeller(models.Model): class TbSeller(models.Model):
"""Продавец или магазин, который продаёт товары.""" """Продавец или магазин, который продаёт товары."""
class SellerType(models.TextChoices): class SellerType(models.TextChoices):
SELLER = 'seller', 'Продавец' SELLER = 'seller', 'Продавец'
LABEL = 'label', 'Лейбл (издатель)' LABEL = 'label', 'Лейбл (издатель)'
DIY = 'diy', 'Самиздат группы' DIY = 'diy', 'Самиздат группы'
CROWD = 'crowdfunding', 'Краудфандинг' CROWD = 'crowdfunding', 'Краудфандинг'
OTHER = '++', 'Другое' OTHER = '++', 'Другое'
s_seller = models.CharField( s_seller = models.CharField(
max_length=32, blank=False, null=False, default='', max_length=32, blank=False, null=False, default='',
verbose_name='Название продавца', verbose_name='Название продавца',
help_text='Название продавца или магазина, например: <tt>Клюква Рекодс</tt>', help_text='Название продавца или магазина, например: <tt>Клюква Рекодс</tt>',
) )
s_seller_article_html = models.TextField(
blank=True,
null=True,
default='',
verbose_name='Статья',
help_text='Статья о продавце (типографированно в HTML)',
)
s_seller_slug = models.SlugField( s_seller_slug = models.SlugField(
max_length=32, blank=False, null=False, default='', max_length=32, blank=False, null=False, default='',
verbose_name='Слаг', verbose_name='Слаг',
help_text='Слаг для продавца, формируемый на основе названия продавца', help_text='Слаг для продавца, формируемый на основе названия продавца',
) )
l_seller_type = models.CharField( l_seller_type = models.CharField(
max_length=9, max_length=9,
default=SellerType.SELLER, default=SellerType.SELLER,
choices=SellerType.choices, choices=SellerType.choices,
verbose_name='Тип продавца', verbose_name='Тип продавца',
) )
j_seller_metadata = models.JSONField( j_seller_metadata = models.JSONField(
default=dict, blank=True, null=True, default=dict, blank=True, null=True,
verbose_name='Дополнительные данные', verbose_name='Дополнительные данные',
help_text='Дополнительные данные о продавце в виде JSON-словаря. Телефон, email, адрес, ссылка на сайт и т.д.', help_text='Дополнительные данные о продавце в виде JSON-словаря. Телефон, email, адрес, ссылка на сайт и т.д.',
) )
t_seller_created = models.DateTimeField(
t_created = models.DateTimeField(
auto_now_add=True, auto_now_add=True,
verbose_name="Дата создания", verbose_name="Дата создания",
) )
t_updated = models.DateTimeField( t_seller_updated = models.DateTimeField(
auto_now=True, auto_now=True,
verbose_name="Дата обновления", verbose_name="Дата обновления",
) )
@@ -373,7 +385,6 @@ class TbSeller(models.Model):
# ============================================================================ # ============================================================================
# ПРЕДЛОЖЕНИЯ / ОФФЕРЫ # ПРЕДЛОЖЕНИЯ / ОФФЕРЫ
# ============================================================================ # ============================================================================
class TbOffer(models.Model): class TbOffer(models.Model):
""" """
Конкретное предложение от продавца. Конкретное предложение от продавца.
@@ -436,13 +447,13 @@ class TbOffer(models.Model):
# Изображения товара (одна картинка может быть у многих офферов, а офер иметь много картинок) # Изображения товара (одна картинка может быть у многих офферов, а офер иметь много картинок)
# M2M связь для удобства в админке (filter_horizontal) # M2M связь для удобства в админке (filter_horizontal)
# Порядок картинок определяется полем i_sort_order в TbImage # Порядок картинок определяется полем i_img_sort в TbImage
k_offer_to_image = models.ManyToManyField( k_offer_to_image = models.ManyToManyField(
TbImage, TbImage,
blank=True, blank=True,
related_name='image_to_offer', related_name='image_to_offer',
verbose_name='Изображения', verbose_name='Изображения',
help_text='Картинки этого товара. Порядок определяется полем i_sort_order в ка<D0BA><D0B0>тинке.', help_text='Картинки этого товара. Порядок определяется полем i_img_sort в ка<D0BA><D0B0>тинке.',
) )
# Характеристики # Характеристики
@@ -461,6 +472,14 @@ class TbOffer(models.Model):
help_text='Основной формат носителя (пластинка, CD, кассета и т.п.)' help_text='Основной формат носителя (пластинка, CD, кассета и т.п.)'
) )
d_offer_date_release = models.DateField(
blank=True,
null=True,
default=None,
verbose_name='Дата релиза',
help_text='Дата релиза, если она известна (например, дата выпуска переиздания)',
)
i_offer_discogs_id = models.IntegerField( i_offer_discogs_id = models.IntegerField(
blank=True,default=0, blank=True,default=0,
verbose_name='ID на релиз Discogs', verbose_name='ID на релиз Discogs',
@@ -502,20 +521,20 @@ class TbOffer(models.Model):
) )
i_offer_discount_to_daily_sale = models.IntegerField( i_offer_discount_to_daily_sale = models.IntegerField(
blank=True, default=0, blank=True,
default=0,
db_index=True,
verbose_name='Скидка', verbose_name='Скидка',
help_text='Процент скидки, для ежедневной распродажи', help_text='Процент возможной скидки, если участвует в "ежедневной распродаже" или акции. Если указано'
' <tt>0</tt> то данное предложение не может участвовать в распродажах, спецпредложениях и акциях',
) )
s_offer_comment_html = models.TextField(
# Комментарии
s_offer_comment = models.TextField(
blank=True, blank=True,
default='', default='',
verbose_name='Доп.инфо', verbose_name='Доп.инфо',
help_text='Дополнительная информация или комментарий к предложению от продавца, например:' help_text='Дополнительная информация или комментарий к предложению от продавца с сохранением'
' <tt>"Пластинка запаяна в целлофан, угол обложки замят."</tt>.', ' типографирования и спецсимволов.',
) )
j_offer_metadata = models.JSONField( j_offer_metadata = models.JSONField(
# Метаданные оффера (сырые данные из источника, координаты в Excel и т.д.) # Метаданные оффера (сырые данные из источника, координаты в Excel и т.д.)
default=dict, null=True, default=dict, null=True,
@@ -579,7 +598,7 @@ class TbSource(models.Model):
verbose_name='Продавец', verbose_name='Продавец',
) )
l_currency = models.CharField( l_source_currency = models.CharField(
max_length=3, max_length=3,
choices=Currency.choices, choices=Currency.choices,
default=Currency.RUB, default=Currency.RUB,
@@ -655,13 +674,11 @@ class TbSource(models.Model):
# ============================================================================ # ============================================================================
# ИСТОРИЯ ИЗМЕНЕНИЙ ОФФЕРОВ # ИСТОРИЯ ИЗМЕНЕНИЙ ОФФЕРОВ
# ============================================================================ # ============================================================================
class TbOfferHistory(models.Model): class TbOfferHistory(models.Model):
""" """
История изменений оффера (снапшот цены, количества, наличия). История изменений оффера (снапшот цены, количества, наличия).
Создаётся при каждом импорте, если что-то изменилось. Создаётся при каждом импорте, если что-то изменилось.
""" """
k_history_to_offer = models.ForeignKey( k_history_to_offer = models.ForeignKey(
TbOffer, TbOffer,
on_delete=models.CASCADE, on_delete=models.CASCADE,