9 Commits

Author SHA1 Message Date
be4839ecc2 add: модели (draft 01) 2026-05-28 11:44:20 +03:00
b2b38783cb minor: .env 2026-05-28 11:43:47 +03:00
f0502e605f mod: использование .env 2026-05-26 11:01:01 +03:00
75d359aeb3 add: django-environ 2026-05-25 10:37:22 +03:00
5b6c8a8ded add: Django 6.x инициализация 2026-05-24 21:08:38 +03:00
5cb05a353f add: poetry 2026-05-24 21:07:43 +03:00
6bf79c460b minor: - 2026-04-15 11:52:33 +03:00
447d2ab9dd add: пример переменных окружения 2026-04-15 11:52:04 +03:00
63c52eaeaa fix: прячем порт контейнера из интернет 2026-04-15 11:50:29 +03:00
19 changed files with 792 additions and 4 deletions

35
.env.example Normal file
View File

@@ -0,0 +1,35 @@
# Локальная конфигурация для dev-окружения lpon-site.
DJANGO_DEBUG=True
DJANGO_SECRET_KEY=insecure-dev-key-change-me-in-prod
DJANGO_ALLOWED_HOSTS=localhost,127.0.0.1,testserver,lpon.ru
DJANGO_ADMINS=YouName:you@email.com
# Доверенные источники для CSRF (важно для Docker/Nginx)
# Укажите здесь URL, по которому вы заходите на сайт (с протоколом и портом gunicorn)
DJANGO_CSRF_TRUSTED_ORIGINS=http://127.0.0.1:8000,http://localhost:8000,http://testserver,https://lpon.ru
# URL для доступа к админке Django (можно сменить для безопасности, чтобы боты не могли её найти)
DJANGO_ADMIN_URL=admin/
# База данных (SQLite)
DJANGO_SQLITE_NAME=lpon-db.sqlite3
# Почта SMTP
DJANGO_EMAIL_HOST=smtp.mail.ru
DJANGO_EMAIL_PORT=2525
DJANGO_EMAIL_HOST_USER=you@email.com
DJANGO_EMAIL_HOST_PASSWORD=CHANGE_ME
DJANGO_EMAIL_FROM=you@email.com
# ==============================================
# Системные пути на хосте (ТОЛЬКО ДЛЯ ПРОДАКШЕНА)
# Используется скриптом для генерации корректного Nginx конфига и alias к media-файлам.
# На локальной машине разработчика (Dev) эта переменная игнорируется.
# В ПРОДАКШЕНЕ: Укажите полный путь к папке проекта на сервере
HOST_PROJECT_PATH=/home/user/docker-app/your-site
# ==============================================
# Настройки достпа к пакетам в репозитории, чтобы watchtower мог проверять их свежесть и скачивать
REPO_USER=xxxxx
REPO_PASS=xxxxx

5
.gitignore vendored
View File

@@ -363,7 +363,8 @@ db.sqlite3-wal
public/media/* public/media/*
!public/media/README.md !public/media/README.md
staticfiles/ staticfiles/
public/static/static_collected/ public/static_collected/
.github/
# Data Backup # Data Backup
database/data.json database/

View File

@@ -13,7 +13,7 @@ server {
# Все домены, которые будет обслуживать этот сервер. # Все домены, которые будет обслуживать этот сервер.
# Certbot выпустит сертификаты для всех перечисленных доменов. # Certbot выпустит сертификаты для всех перечисленных доменов.
server_name lpon.ru t.lpon.ru; server_name lpon.ru;
# --- ЛОГИ --- # --- ЛОГИ ---
access_log /var/log/nginx/lpon.access.log; access_log /var/log/nginx/lpon.access.log;

View File

@@ -14,7 +14,7 @@ services:
restart: always restart: always
ports: ports:
# Пробрасываем свободный порт 8020 на хосте на стандартный порт 80 внутри контейнера # Пробрасываем свободный порт 8020 на хосте на стандартный порт 80 внутри контейнера
- "8020:80" - "127.0.0.1:8020:80"
# Настройка ротации логов # Настройка ротации логов
logging: logging:
driver: "json-file" driver: "json-file"

View File

View File

@@ -0,0 +1,3 @@
from django.contrib import admin
# Register your models here.

View File

@@ -0,0 +1,5 @@
from django.apps import AppConfig
class FrontendConfig(AppConfig):
name = 'frontend'

View File

@@ -0,0 +1,380 @@
from django.db import models
# Create your models here.
class TbArtist(models.Model):
s_artist = models.CharField(
max_length=255,
db_index=True,
verbose_name='Исполнитель',
help_text="Исполнитель или группа.",
)
s_artist_html = models.TextField(
blank=True, null=True, default='',
verbose_name='Исполнитель (типографированно в HTML)',
help_text='Исполнитель или группа, указанные на обложке релиза, с сохранением типографирования и спецсимволов в виде HTML-тегов.',
)
s_slug = models.SlugField(
max_length=64,
)
j_artist_in_source = models.JSONField(
default=list, blank=True, null=True,
verbose_name='Исполнитель в источнике',
help_text='Список вариантов написания исполнителя, встречающихся в источниках (в разных релизах/каталогах'
' или у разных поставщиков). Например: <tt>["The Beatles", "Beatles", "Beatles, The"]</tt>.',
)
t_created = models.DateTimeField(
auto_now_add=True,
verbose_name="Дата Создания записи в БД",
)
t_updated = models.DateTimeField(
auto_now=True,
verbose_name="Дата последнего обновления записи в БД",
)
def __unicode__(self):
return f"product {self.id:0>4}: {self.s_artist}"
def __str__(self):
return self.__unicode__()
class Meta:
verbose_name = 'Исполнитель'
verbose_name_plural = 'Исполнители'
ordering = ('s_artist',)
class TbProduct(models.Model):
# Абстрактный релиз / сущность / товар, который может быть представлен в виде одного или нескольких предложений
# от разных продавцов.
k_artists = models.ManyToManyField(
TbArtist,
blank=True,
null=True,
default=None,
related_name='+', # ← '+' отключает обратную связь
verbose_name='Исполнители',
help_text="Выберите исполнителей или группы для этого релиза (можно не указывать)",
)
s_title = models.CharField(
max_length=255, blank=False, null=False, default='',
# db_index=True,
verbose_name='Название товара (релиза)',
help_text='Название товара (релиза), как указано на обложке. Например: <tt>Abbey Road (remastered)</tt>'
' или <tt>TDK, CDing I, 90</tt>.',
)
s_title_html = models.TextField(
blank=True, null=True, default='',
verbose_name='Название товара (типографированно в HTML)',
help_text='Название товара (релиза), как указано на обложке, с сохранением типографирования и спецсимволов'
' в виде HTML-тегов.',
)
s_title_slug = models.SlugField(
blank=True, null=True, default='',
verbose_name='Слаг',
help_text='Слаг для товара, формируемый на основе названия товара (релиза)',
)
t_title_date = models.DateField(
blank=True,
null=True,
verbose_name='Дата релиза',
help_text='Дата релиза, если известна. Если известен только год, можно указать 1 января этого года.'
' Например: <tt>1969-09-26</tt> или не указывать вовсе.',
)
i_discogs_master_id = models.IntegerField(
blank=True, null=True, default=None,
verbose_name='ID на мастер-релиз Discogs',
help_text='Уникальный идентификатор мастер-релиза на Discogs, если он там есть. Например: <tt>306323</tt>',
)
j_product_metadata = models.JSONField(
default=dict, blank=True, null=True,
verbose_name='Дополнительные данные',
help_text='Дополнительные данные о релизе в виде JSON-словаря,',
)
t_created = models.DateTimeField(
auto_now_add=True,
verbose_name="Дата Создания записи в БД",
)
t_updated = models.DateTimeField(
auto_now=True,
verbose_name="Дата последнего обновления записи в БД",
)
def __unicode__(self):
return f"product {self.id:0>4}: {self.s_title}"
def __str__(self):
return self.__unicode__()
class Meta:
verbose_name = 'Релиз (товар)'
verbose_name_plural = 'Релизы (товары)'
ordering = ('s_title',)
class TbSeller(models.Model):
# Продавец товара
class SellerType(models.TextChoices):
SELLER = 'seller', 'Продавец'
LABEL = 'label', 'Лейбл (издатель)'
DIY = 'diy', 'Самиздат группы'
CROWD = 'crowdfunding', 'Краудфандинг'
OTHER = '++', 'Другое'
s_seller = models.CharField(
max_length=32, blank=False, null=False, default='',
verbose_name='Название продавца',
help_text='Название продавца или магазина, например: <tt>Клюква Рекодс</tt>',
)
s_seller_slug = models.SlugField(
max_length=32, blank=False, null=False, default='',
verbose_name='Слаг',
help_text='Слаг для продавца, формируемый на основе названия продавца',
)
l_seller_type = models.CharField(
max_length=9, default=SellerType.SELLER, choices=SellerType.choices,
verbose_name='Тип продавца',
)
j_seller_metadata = models.JSONField(
default=dict, blank=True, null=True,
verbose_name='Дополнительные данные',
help_text='Дополнительные данные о продавце в виде JSON-словаря',
)
def __unicode__(self):
return f"vendor {self.id:0>3}: {self.s_vendor}"
def __str__(self):
return self.__unicode__()
class Meta:
verbose_name = 'Продавец'
verbose_name_plural = 'Продавцы'
ordering = ('s_seller',)
class TbLabel(models.Model):
# Лейблы: производители (Улитка Рекородс, Мелодия, Sony... а так же производители TDK, AXIA, Maxwell и т.д.)
s_label = models.CharField(
max_length=32, blank=False, null=False, default='',
verbose_name='Название лейбла',
help_text='Название лейбла, например: <tt>Мелодия</tt> или <tt>TDK</tt>',
)
s_label_slug = models.SlugField(
max_length=32, blank=False, null=False, default='',
verbose_name='Слаг',
help_text='Слаг для лейбла, формируемый на основе названия лейбла',
)
j_label_metadata = models.JSONField(
default=dict, blank=True, null=True,
verbose_name='Дополнительные данные',
help_text='Дополнительные данные о лейбле в виде JSON-словаря',
)
#
class TbOffer(models.Model):
# Конкретное предложение продавца
class Format(models.TextChoices):
LP = 'lp', 'vinyl'
CD = 'cd', 'CD'
BD = 'bd', 'Blu-ray'
CS = 'cs', 'Cassette'
MD = 'md', 'Minidisc'
BX = 'bx', 'Box Set'
HI = 'hi', 'Hi-Fi'
AC = 'ac', 'Accessory'
OTHER = '++', 'Other'
class Condition(models.TextChoices):
S = 's', 'Still Sealed (новое, запечатано)'
M = 'm', 'Mint'
NM = 'nm', 'Near Mint'
VG = 'vg', 'Very Good'
G = 'g', 'Good'
F = 'f', 'Fair'
P = 'p', 'Poor'
OTHER = '++', 'Other'
class Currency(models.TextChoices):
RUB = 'rub', 'RUB: российский рубль'
TRY = 'try', 'TRY: турецкая лира'
AMD = 'amd', 'AMD: армянский драм'
USD = 'usd', 'USD: американский доллар'
EUR = 'eur', 'EUR: евро'
JPY = 'jpy', 'JPY: японская иена'
GBP = 'gbp', 'GBP: британский фунт'
CNY = 'cny', 'CNY: китайский юань'
BYN = 'byn', 'BYN: белорусский рубль'
TON = 'ton', 'TON: криптовалюта TON'
OTHER = '++', 'Other'
s_catalog_num = models.TextField(
blank=True, default='',
verbose_name='Каталожный номер или barcode',
help_text='Каталожный номер релиза, если он есть. Например: <tt>SD 16023</tt> для <i>ABBA — Super Trouper'
' — 1980 — Atlantic (USA)</i>',
)
k_product = models.ForeignKey(
TbProduct,
blank=True, default=None,
on_delete=models.SET_NULL, related_name='+',
verbose_name='Релиз (товар)',
)
k_label = models.ForeignKey(
TbLabel,
null=True, default=None,
on_delete=models.SET_NULL, related_name='+',
verbose_name='Лейбл',
help_text='Лейбл, на котором был выпущен релиз, если он известен. Например: <tt>Atlantic</tt> или <tt>Мелодия</tt>',
)
k_seller = models.ForeignKey(
TbSeller,
null=True, default=None,
on_delete=models.SET_NULL,
related_name='+',
verbose_name='Продавец',
)
l_primary_media = models.CharField(
max_length=2,
choices=Format.choices,
default=Format.LP,
verbose_name='Формат',
help_text='Основной формат носителя (пластинка, CD, кассета и т.п.)'
)
i_discogs_id = models.IntegerField(
blank=True,default=0,
verbose_name='ID на релиз Discogs',
help_text='Уникальный идентификатор релиза на Discogs, если он там есть. Например: <tt>306323</tt>',
)
l_condition_media = models.CharField(
max_length=2,
choices=Condition.choices,
default=Condition.S,
verbose_name="Состояние носителя",
help_text='Состояние носителя (пластинки, CD и т.п.) по шкале от "Still Sealed" (запечатано) до "Poor" (плохое).',
)
l_condition_sleeve = models.CharField(
max_length=2,
choices=Condition.choices,
default=Condition.S,
verbose_name="Состояние обложки",
help_text='Состояние обложки по шкале от "Still Sealed" (запечатано) до "Poor" (плохое).',
)
f_price = models.DecimalField(
max_digits=10,
decimal_places=2,
blank=True, default=0.00,
verbose_name='Цена',
)
l_currency = models.CharField(
max_length=3,
choices=Currency.choices,
default=Currency.RUB,
verbose_name="Валюта",
)
i_quantity = models.IntegerField(
blank=True, default=0,
verbose_name='Количество',
)
b_is_available = models.BooleanField(
default=True,
verbose_name='В наличии',
)
i_discount_to_daily_sale = models.IntegerField(
blank=True, default=0,
verbose_name='Скидка',
help_text='Процент скидки, для ежедневной распродажи',
)
s_offer_comment = models.TextField(
blank=True, default='',
verbose_name='Доп.инфо',
help_text='Дополнительная информация или комментарий к предложению от продавца, например:'
' <tt>"Пластинка запаяна в целлофан, угол обложки замят."</tt>.',
)
j_offer_metadata = models.JSONField(
default=dict, null=True,
verbose_name='Дополнительные данные',
help_text='Дополнительные данные о предложении в виде JSON-словаря',
)
t_created = models.DateTimeField(
auto_now_add=True,
verbose_name="Дата Создания записи в БД",
)
t_updated = models.DateTimeField(
auto_now=True,
verbose_name="Дата последнего обновления записи в БД",
)
#
# │ 1
# │
# │ N
# ┌───────▼────────────┐
# │ ProductImage │
# ├────────────────────┤
# │ id │
# │ product_id │ FK
# │ image_type │ ← cover/back/obi/matrix/etc
# │ source │ ← discogs/manual/vendor
# │ url │
# │ local_path │
# │ is_primary │
# │ sort_order │
# │ metadata_json │
# └────────────────────┘
#
#
# ┌────────────────────┐
# │ ProductEnrichment │
# ├────────────────────┤
# │ id │
# │ product_id │ FK
# │ source │ ← discogs/api/scraper
# │ confidence_score │
# │ data_json │
# │ fetched_at │
# └────────────────────┘
#
#
# ┌────────────────────┐
# │ PriceHistory │
# ├────────────────────┤
# │ id │
# │ offer_id │ FK
# │ old_price │
# │ new_price │
# │ quantity_snapshot │
# │ source │
# │ changed_at │
# └────────────────────┘
#
#
# ┌────────────────────┐
# │ Source │ ← Импорт / источник
# ├────────────────────┤
# │ id │
# │ vendor_id │ FK
# │ type │ ← excel/csv/api/html
# │ source_url │
# │ file_name │
# │ file_hash │
# │ imported_at │
# │ parser_version │
# │ meta_json │
# └─────────┬──────────┘
# │ 1
# │
# │ N
# ┌─────────▼──────────┐
# │ SourceItem │ ← Строка из Excel/CSV
# ├────────────────────┤
# │ id │
# │ source_id │ FK
# │ row_index │
# │ raw_row_json │
# │ parsed_data_json │
# │ matching_status │
# │ confidence_score │
# │ matched_product_id │ FK nullable
# │ created_offer_id │ FK nullable
# │ created_at │
# └────────────────────┘

View File

@@ -0,0 +1,3 @@
from django.test import TestCase
# Create your tests here.

View File

@@ -0,0 +1,3 @@
from django.shortcuts import render
# Create your views here.

View File

View File

@@ -0,0 +1,16 @@
"""
ASGI config for lpon_site project.
It exposes the ASGI callable as a module-level variable named ``application``.
For more information on this file, see
https://docs.djangoproject.com/en/6.0/howto/deployment/asgi/
"""
import os
from django.core.asgi import get_asgi_application
os.environ.setdefault('DJANGO_SETTINGS_MODULE', 'lpon_site.settings')
application = get_asgi_application()

View File

@@ -0,0 +1,149 @@
"""
Django settings for lpon_site project.
Generated by 'django-admin startproject' using Django 6.0.5.
For more information on this file, see
https://docs.djangoproject.com/en/6.0/topics/settings/
For the full list of settings and their values, see
https://docs.djangoproject.com/en/6.0/ref/settings/
"""
from pathlib import Path
import environ
import os
# Build paths inside the project like this: BASE_DIR / 'subdir'.
BASE_DIR = Path(__file__).resolve().parent.parent
# Читаем переменные окружения
env = environ.Env()
environ.Env.read_env(os.path.join(BASE_DIR.parent, '.env'))
def _normalize_admin_url(value: str) -> str:
"""Приводит URL админки к виду `segment/` без ведущего слэша."""
normalized = value.strip().lstrip('/')
if not normalized:
return 'admin/'
if not normalized.endswith('/'):
normalized += '/'
return normalized
# Quick-start development settings - unsuitable for production
# See https://docs.djangoproject.com/en/6.0/howto/deployment/checklist/
# SECURITY WARNING: keep the secret key used in production secret!
SECRET_KEY = env('DJANGO_SECRET_KEY', default='3xym$l+!)erah-k23lf0=t=c_4$e0nr*zls&l%pbz@k6v6qn89')
ADMIN_URL = _normalize_admin_url(env('DJANGO_ADMIN_URL', default='admin/'))
# SECURITY WARNING: don't run with debug turned on in production!
DEBUG = env.bool('DJANGO_DEBUG', default=False)
ALLOWED_HOSTS = env.list(
'DJANGO_ALLOWED_HOSTS',
default=['127.0.0.1', 'localhost', 'testserver', 'lpon.ru'],
)
CSRF_TRUSTED_ORIGINS = env.list('DJANGO_CSRF_TRUSTED_ORIGINS', default=['127.0.0.1', 'localhost', 'testserver'])
#########################################
# Настройки сообщений об ошибках когда все упало и т.п.
ADMINS = tuple(
tuple(item.split(':', 1))
for item in env.list('DJANGO_ADMINS', default=['S.Erjemin:erjemin@gmail.com'])
)
# Application definition
INSTALLED_APPS = [
'django.contrib.admin',
'django.contrib.auth',
'django.contrib.contenttypes',
'django.contrib.sessions',
'django.contrib.messages',
'django.contrib.staticfiles',
]
MIDDLEWARE = [
'django.middleware.security.SecurityMiddleware',
'django.contrib.sessions.middleware.SessionMiddleware',
'django.middleware.common.CommonMiddleware',
'django.middleware.csrf.CsrfViewMiddleware',
'django.contrib.auth.middleware.AuthenticationMiddleware',
'django.contrib.messages.middleware.MessageMiddleware',
'django.middleware.clickjacking.XFrameOptionsMiddleware',
]
ROOT_URLCONF = 'lpon_site.urls'
TEMPLATES = [
{
'BACKEND': 'django.template.backends.django.DjangoTemplates',
'DIRS': [],
'APP_DIRS': True,
'OPTIONS': {
'context_processors': [
'django.template.context_processors.request',
'django.contrib.auth.context_processors.auth',
'django.contrib.messages.context_processors.messages',
],
},
},
]
WSGI_APPLICATION = 'lpon_site.wsgi.application'
# Database
# https://docs.djangoproject.com/en/6.0/ref/settings/#databases
DATABASES = {
'default': {
'ENGINE': 'django.db.backends.sqlite3',
'NAME': BASE_DIR.parent.joinpath('database', env('DJANGO_SQLITE_NAME', default='lpon-db.sqlite3')),
'OPTIONS': {
'timeout': 25,
},
}
}
# Password validation
# https://docs.djangoproject.com/en/6.0/ref/settings/#auth-password-validators
AUTH_PASSWORD_VALIDATORS = [
{
'NAME': 'django.contrib.auth.password_validation.UserAttributeSimilarityValidator',
},
{
'NAME': 'django.contrib.auth.password_validation.MinimumLengthValidator',
},
{
'NAME': 'django.contrib.auth.password_validation.CommonPasswordValidator',
},
{
'NAME': 'django.contrib.auth.password_validation.NumericPasswordValidator',
},
]
# Internationalization
# https://docs.djangoproject.com/en/6.0/topics/i18n/
LANGUAGE_CODE = 'ru-RU' #
# TIME_ZONE = 'Etc/GMT+3' #
TIME_ZONE = 'Europe/Moscow' #
USE_I18N = True
USE_TZ = True
FIRST_DAY_OF_WEEK = 1 # неделя начинается с понедельника
DEFAULT_CHARSET = 'utf-8'
# Static files (CSS, JavaScript, Images)
# https://docs.djangoproject.com/en/6.0/howto/static-files/
STATIC_URL = '/static/'
MEDIA_URL = '/media/'
# Локальные каталоги проекта: медиа и статика лежат рядом в `public`.
PUBLIC_DIR = BASE_DIR.parent.joinpath('public') # Папка `public` находится в корне пректа
MEDIA_ROOT = PUBLIC_DIR.joinpath('media')
STATICFILES_DIRS = [PUBLIC_DIR.joinpath('static')]
STATIC_ROOT = PUBLIC_DIR.joinpath('staticfiles')

View File

@@ -0,0 +1,23 @@
"""
URL configuration for lpon_site project.
The `urlpatterns` list routes URLs to views. For more information please see:
https://docs.djangoproject.com/en/6.0/topics/http/urls/
Examples:
Function views
1. Add an import: from my_app import views
2. Add a URL to urlpatterns: path('', views.home, name='home')
Class-based views
1. Add an import: from other_app.views import Home
2. Add a URL to urlpatterns: path('', Home.as_view(), name='home')
Including another URLconf
1. Import the include() function: from django.urls import include, path
2. Add a URL to urlpatterns: path('blog/', include('blog.urls'))
"""
from django.contrib import admin
from django.urls import path
from lpon_site import settings
urlpatterns = [
path(settings.ADMIN_URL, admin.site.urls),
]

View File

@@ -0,0 +1,16 @@
"""
WSGI config for lpon_site project.
It exposes the WSGI callable as a module-level variable named ``application``.
For more information on this file, see
https://docs.djangoproject.com/en/6.0/howto/deployment/wsgi/
"""
import os
from django.core.wsgi import get_wsgi_application
os.environ.setdefault('DJANGO_SETTINGS_MODULE', 'lpon_site.settings')
application = get_wsgi_application()

22
lpon_site/manage.py Executable file
View File

@@ -0,0 +1,22 @@
#!/usr/bin/env python
"""Django's command-line utility for administrative tasks."""
import os
import sys
def main():
"""Run administrative tasks."""
os.environ.setdefault('DJANGO_SETTINGS_MODULE', 'lpon_site.settings')
try:
from django.core.management import execute_from_command_line
except ImportError as exc:
raise ImportError(
"Couldn't import Django. Are you sure it's installed and "
"available on your PYTHONPATH environment variable? Did you "
"forget to activate a virtual environment?"
) from exc
execute_from_command_line(sys.argv)
if __name__ == '__main__':
main()

110
poetry.lock generated Normal file
View File

@@ -0,0 +1,110 @@
# This file is automatically @generated by Poetry 1.8.0 and should not be changed by hand.
[[package]]
name = "asgiref"
version = "3.11.1"
description = "ASGI specs, helper code, and adapters"
optional = false
python-versions = ">=3.9"
files = [
{file = "asgiref-3.11.1-py3-none-any.whl", hash = "sha256:e8667a091e69529631969fd45dc268fa79b99c92c5fcdda727757e52146ec133"},
{file = "asgiref-3.11.1.tar.gz", hash = "sha256:5f184dc43b7e763efe848065441eac62229c9f7b0475f41f80e207a114eda4ce"},
]
[package.extras]
tests = ["mypy (>=1.14.0)", "pytest", "pytest-asyncio"]
[[package]]
name = "django"
version = "6.0.5"
description = "A high-level Python web framework that encourages rapid development and clean, pragmatic design."
optional = false
python-versions = ">=3.12"
files = [
{file = "django-6.0.5-py3-none-any.whl", hash = "sha256:9d58a7cb49244e74c8e161d5e403a46d6209f1009ba40f5a66d6aa0d0786a8f0"},
{file = "django-6.0.5.tar.gz", hash = "sha256:bc6d6872e98a2864c836e42edd644b362db311147dd5aa8d5b82ba7a032f5269"},
]
[package.dependencies]
asgiref = ">=3.9.1"
sqlparse = ">=0.5.0"
tzdata = {version = "*", markers = "sys_platform == \"win32\""}
[package.extras]
argon2 = ["argon2-cffi (>=23.1.0)"]
bcrypt = ["bcrypt (>=4.1.1)"]
[[package]]
name = "django-environ"
version = "0.13.0"
description = "A package that allows you to utilize 12factor inspired environment variables to configure your Django application."
optional = false
python-versions = "<4,>=3.9"
files = [
{file = "django_environ-0.13.0-py3-none-any.whl", hash = "sha256:37799d14cd78222c6fd8298e48bfe17965ff8e586091ad66a463e52e0e7b799e"},
{file = "django_environ-0.13.0.tar.gz", hash = "sha256:6c401e4c219442c2c4588c2116d5292b5484a6f69163ed09cd41f3943bfb645f"},
]
[package.extras]
develop = ["coverage[toml] (>=5.0a4)", "furo (>=2024.8.6)", "pytest (>=4.6.11)", "setuptools (>=71.0.0)", "sphinx (>=5.0)", "sphinx-copybutton", "sphinx-notfound-page"]
docs = ["furo (>=2024.8.6)", "sphinx (>=5.0)", "sphinx-copybutton", "sphinx-notfound-page"]
testing = ["coverage[toml] (>=5.0a4)", "pytest (>=4.6.11)", "setuptools (>=71.0.0)"]
[[package]]
name = "django-extensions"
version = "3.2.3"
description = "Extensions for Django"
optional = false
python-versions = ">=3.6"
files = [
{file = "django-extensions-3.2.3.tar.gz", hash = "sha256:44d27919d04e23b3f40231c4ab7af4e61ce832ef46d610cc650d53e68328410a"},
{file = "django_extensions-3.2.3-py3-none-any.whl", hash = "sha256:9600b7562f79a92cbf1fde6403c04fee314608fefbb595502e34383ae8203401"},
]
[package.dependencies]
Django = ">=3.2"
[[package]]
name = "python-dotenv"
version = "1.2.2"
description = "Read key-value pairs from a .env file and set them as environment variables"
optional = false
python-versions = ">=3.10"
files = [
{file = "python_dotenv-1.2.2-py3-none-any.whl", hash = "sha256:1d8214789a24de455a8b8bd8ae6fe3c6b69a5e3d64aa8a8e5d68e694bbcb285a"},
{file = "python_dotenv-1.2.2.tar.gz", hash = "sha256:2c371a91fbd7ba082c2c1dc1f8bf89ca22564a087c2c287cd9b662adde799cf3"},
]
[package.extras]
cli = ["click (>=5.0)"]
[[package]]
name = "sqlparse"
version = "0.5.5"
description = "A non-validating SQL parser."
optional = false
python-versions = ">=3.8"
files = [
{file = "sqlparse-0.5.5-py3-none-any.whl", hash = "sha256:12a08b3bf3eec877c519589833aed092e2444e68240a3577e8e26148acc7b1ba"},
{file = "sqlparse-0.5.5.tar.gz", hash = "sha256:e20d4a9b0b8585fdf63b10d30066c7c94c5d7a7ec47c889a2d83a3caa93ff28e"},
]
[package.extras]
dev = ["build"]
doc = ["sphinx"]
[[package]]
name = "tzdata"
version = "2026.2"
description = "Provider of IANA time zone data"
optional = false
python-versions = ">=2"
files = [
{file = "tzdata-2026.2-py2.py3-none-any.whl", hash = "sha256:bbe9af844f658da81a5f95019480da3a89415801f6cc966806612cc7169bffe7"},
{file = "tzdata-2026.2.tar.gz", hash = "sha256:9173fde7d80d9018e02a662e168e5a2d04f87c41ea174b139fbef642eda62d10"},
]
[metadata]
lock-version = "2.0"
python-versions = "^3.12"
content-hash = "7424447119e980f55d2bf47e9e97063dfbb9ae3230680cade5893701992c3e96"

22
pyproject.toml Normal file
View File

@@ -0,0 +1,22 @@
[tool.poetry]
name = "lpon-site"
version = "0.1.0"
description = "LPON - platform for selling vinyl records (and related products)"
authors = ["Sergei Erjemin <erjemin@gmail.com>"]
readme = "README.md"
packages = [
{ include = "lpon_site" }
]
[tool.poetry.dependencies]
python = "^3.12"
django = "^6.0"
python-dotenv = "^1.0.0"
django-environ = "^0.13.0"
[tool.poetry.group.dev.dependencies]
django-extensions = "^3.2"
[build-system]
requires = ["poetry-core"]
build-backend = "poetry.core.masonry.api"