Init commit

This commit is contained in:
apheyhys 2025-04-23 22:23:51 +03:00
commit 3a1dbab9e2
23 changed files with 2314 additions and 0 deletions

179
.gitignore vendored Normal file
View file

@ -0,0 +1,179 @@
__pycache__/
*.py[cod]
*$py.class
# C extensions
*.so
# Distribution / packaging
.Python
build/
develop-eggs/
dist/
downloads/
eggs/
.eggs/
lib/
lib64/
parts/
sdist/
var/
wheels/
share/python-wheels/
*.egg-info/
.installed.cfg
*.egg
MANIFEST
# PyInstaller
# Usually these files are written by a python script from a template
# before PyInstaller builds the exe, so as to inject date/other infos into it.
*.manifest
*.spec
# Installer logs
pip-log.txt
pip-delete-this-directory.txt
# Unit test / coverage reports
htmlcov/
.tox/
.nox/
.coverage
.coverage.*
.cache
nosetests.xml
coverage.xml
*.cover
*.py,cover
.hypothesis/
.pytest_cache/
cover/
# Translations
*.mo
*.pot
# Django stuff:
*.log
local_settings.py
db.sqlite3
db.sqlite3-journal
# Flask stuff:
instance/
.webassets-cache
# Scrapy stuff:
.scrapy
# Sphinx documentation
docs/_build/
# PyBuilder
.pybuilder/
target/
# Jupyter Notebook
.ipynb_checkpoints
# IPython
profile_default/
ipython_config.py
# pyenv
# For a library or package, you might want to ignore these files since the code is
# intended to run in multiple environments; otherwise, check them in:
# .python-version
# pipenv
# According to pypa/pipenv#598, it is recommended to include Pipfile.lock in version control.
# However, in case of collaboration, if having platform-specific dependencies or dependencies
# having no cross-platform support, pipenv may install dependencies that don't work, or not
# install all needed dependencies.
#Pipfile.lock
# UV
# Similar to Pipfile.lock, it is generally recommended to include uv.lock in version control.
# This is especially recommended for binary packages to ensure reproducibility, and is more
# commonly ignored for libraries.
#uv.lock
# poetry
# Similar to Pipfile.lock, it is generally recommended to include poetry.lock in version control.
# This is especially recommended for binary packages to ensure reproducibility, and is more
# commonly ignored for libraries.
# https://python-poetry.org/docs/basic-usage/#commit-your-poetrylock-file-to-version-control
#poetry.lock
# pdm
# Similar to Pipfile.lock, it is generally recommended to include pdm.lock in version control.
#pdm.lock
# pdm stores project-wide configurations in .pdm.toml, but it is recommended to not include it
# in version control.
# https://pdm.fming.dev/latest/usage/project/#working-with-version-control
.pdm.toml
.pdm-python
.pdm-build/
# PEP 582; used by e.g. github.com/David-OConnor/pyflow and github.com/pdm-project/pdm
__pypackages__/
# Celery stuff
celerybeat-schedule
celerybeat.pid
# SageMath parsed files
*.sage.py
# Environments
.env
.venv
env/
venv/
ENV/
env.bak/
venv.bak/
# Spyder project settings
.spyderproject
.spyproject
# Rope project settings
.ropeproject
# mkdocs documentation
/site
# mypy
.mypy_cache/
.dmypy.json
dmypy.json
# Pyre type checker
.pyre/
# pytype static type analyzer
.pytype/
# Cython debug symbols
cython_debug/
# PyCharm
# JetBrains specific template is maintained in a separate JetBrains.gitignore that can
# be found at https://github.com/github/gitignore/blob/main/Global/JetBrains.gitignore
# and can be added to the global gitignore or merged into this file. For a more nuclear
# option (not recommended) you can uncomment the following to ignore the entire idea folder.
#.idea/
# Ruff stuff:
.ruff_cache/
# PyPI configuration file
.pypirc
media/
static/
*/migrations/*.py
!*/migrations/__init__.py

0
headphones/__init__.py Normal file
View file

555
headphones/admin.py Normal file
View file

@ -0,0 +1,555 @@
from django.contrib import admin
from django import forms
from django.urls import reverse
from django.utils.safestring import mark_safe
from django.utils.html import format_html
from .models import *
class HeadphonesAdminForm(forms.ModelForm):
class Meta:
model = Headphones
fields = '__all__'
def __init__(self, *args, **kwargs):
super().__init__(*args, **kwargs)
if self.instance and self.instance.pk:
# Скрываем/показываем поля в зависимости от типа подключения
if self.instance.connection_type == 'wired':
self.fields.pop('wireless_details', None)
self.fields.pop('hybrid_details', None)
elif self.instance.connection_type == 'wireless':
self.fields.pop('wired_details', None)
self.fields.pop('hybrid_details', None)
elif self.instance.connection_type == 'hybrid':
self.fields.pop('wired_details', None)
self.fields.pop('wireless_details', None)
class HeadphonesConnectorInline(admin.TabularInline):
model = HeadphonesConnector
extra = 1
fields = ('connector', 'is_primary', 'notes')
fk_name = 'wired_headphones'
@admin.register(WiredHeadphones)
class WiredHeadphonesAdmin(admin.ModelAdmin):
inlines = [HeadphonesConnectorInline]
list_display = ('headphones', 'cable_connection_type', 'cable_length')
list_filter = ('headphones__brand', 'cable_connection_type')
search_fields = ('headphones__model',)
def get_queryset(self, request):
return super().get_queryset(request).select_related('headphones', 'cable_connection_type')
class WiredHeadphonesInline(admin.StackedInline):
model = WiredHeadphones
extra = 0
fieldsets = [
(None, {
'fields': [
'cable_connection_type',
'cable_length',
'custom_connector_name'
]
})
]
class WirelessHeadphonesInline(admin.StackedInline):
model = WirelessHeadphones
extra = 0
fieldsets = [
(None, {
'fields': [
('wireless_technology', 'bluetooth_version'),
('battery_life', 'charging_time'),
'charging_interface',
('has_charging_case', 'charging_case_battery_life'),
'supported_codecs'
]
})
]
class HybridHeadphonesInline(admin.StackedInline):
model = HybridHeadphones
extra = 0
fieldsets = [
('Беспроводная часть', {
'fields': [
('wireless_technology', 'bluetooth_version'),
('battery_life', 'charging_time'),
'charging_interface',
('has_charging_case', 'charging_case_battery_life'),
'supported_codecs'
]
}),
('Проводная часть', {
'fields': [
('cable_connection_type', 'connector_to_device'),
'cable_length'
]
}),
('Режимы работы', {
'fields': [
'auto_switch_mode',
'simultaneous_connection'
]
})
]
class HeadphonesReviewInline(admin.StackedInline):
model = HeadphonesReview
extra = 1
fields = ['resource', 'author', 'language', 'title', 'url', 'date']
ordering = ['-date']
autocomplete_fields = ['resource', 'author', 'language']
class HeadphonesImagesInline(admin.TabularInline):
model = HeadphonesImages
extra = 1
fields = ('photo', 'thumbnail_preview', 'admin_image_info', 'caption', 'is_main')
readonly_fields = ('thumbnail_preview', 'admin_image_info')
def thumbnail_preview(self, obj):
if obj.photo:
return format_html('<img src="{}" style="max-height: 100px;"/>', obj.photo.url)
return "-"
def admin_image_info(self, obj):
return obj.image_info
admin_image_info.short_description = "Информация о изображении"
admin_image_info.allow_tags = True
thumbnail_preview.short_description = "Превью"
@admin.register(CaseMaterial)
class CaseMaterialAdmin(admin.ModelAdmin):
list_display = ('name', 'slug')
search_fields = ('name',)
prepopulated_fields = {'slug': ('name',)}
list_per_page = 20
class HeadphonesDriverInline(admin.TabularInline):
model = HeadphonesDriver
extra = 1
fields = ('driver', 'count')
autocomplete_fields = ['driver']
@admin.register(Headphones)
class HeadphonesAdmin(admin.ModelAdmin):
form = HeadphonesAdminForm
list_display = (
'model', 'brand', 'connection_type', 'get_reviews_count', 'published')
list_filter = ('brand', 'connection_type', 'published', 'case_type')
search_fields = ('model', 'brand__name', 'description')
prepopulated_fields = {'slug': ('brand', 'model')}
filter_horizontal = ('tags', 'colors')
list_editable = ('published',)
actions = ['make_published', 'make_unpublished']
readonly_fields = (
'get_reviews_count_display',
'date_added',
'driver_configuration_display'
)
autocomplete_fields = ['case_material', 'noise_cancellation']
fieldsets = [
('Основная информация', {
'fields': [
('brand', 'model'),
'slug',
'description',
('connection_type', 'case_type', 'case_material'),
('headphone_purpose', 'official_price'),
'tags', # Добавляем теги в основную секцию
]
}),
('Технические характеристики', {
'fields': [
('impedance', 'sensitivity'),
'frequency_range',
'frequency_response_chart',
('microphone', 'noise_cancellation'),
'ip_rating',
]
}),
('Внешний вид', {
'fields': [
'colors',
('weight', 'release_year')
]
}),
('Метаданные', {
'fields': [
('date_added', 'published')
],
'classes': ('collapse',)
}),
]
inlines = [HeadphonesDriverInline, HeadphonesImagesInline]
# Методы для отображения
def get_reviews_count(self, obj):
return obj.reviews.count()
get_reviews_count.short_description = 'Кол-во обзоров'
def get_reviews_count_display(self, obj):
count = self.get_reviews_count(obj)
url = reverse('admin:headphones_headphonesreview_changelist') + f'?headphones__id__exact={obj.id}'
return format_html('<a href="{}">{} обзоров</a>', url, count)
get_reviews_count_display.short_description = 'Обзоры'
def driver_configuration_display(self, obj):
drivers = obj.headphonesdriver_set.select_related('driver__driver_type').all()
return format_html("<br>".join(
f"{d.driver.driver_type.name} {d.driver.size}mm × {d.count}"
for d in drivers
)) if drivers.exists() else "Не указано"
driver_configuration_display.short_description = 'Конфигурация драйверов'
def get_inline_instances(self, request, obj=None):
inlines = [
HeadphonesDriverInline(self.model, self.admin_site),
HeadphonesImagesInline(self.model, self.admin_site)
]
if obj:
if obj.connection_type == 'wired':
inlines.append(WiredHeadphonesInline(self.model, self.admin_site))
elif obj.connection_type == 'wireless':
inlines.append(WirelessHeadphonesInline(self.model, self.admin_site))
elif obj.connection_type == 'hybrid':
inlines.append(HybridHeadphonesInline(self.model, self.admin_site))
inlines.append(HeadphonesReviewInline(self.model, self.admin_site))
return inlines
# Регистрация остальных моделей
@admin.register(Brand)
class BrandAdmin(admin.ModelAdmin):
list_display = ('name', 'country', 'year_founded', 'parent_company')
list_filter = ('country',)
search_fields = ('name',)
prepopulated_fields = {'slug': ('name',)}
@admin.register(Driver)
class DriverAdmin(admin.ModelAdmin):
list_display = ('driver_type', 'size', 'frequency_range')
list_filter = ('driver_type',)
search_fields = ('driver_type__name', 'size', 'frequency_range') # Добавлено
autocomplete_fields = ['driver_type'] # Если нужно автозаполнение для типа
def get_queryset(self, request):
return super().get_queryset(request).select_related('driver_type')
# ======================
# Регистрация справочников
# ======================
@admin.register(BrandCountry)
class BrandCountryAdmin(admin.ModelAdmin):
list_display = ('name', 'slug')
search_fields = ('name',)
prepopulated_fields = {'slug': ('name',)}
@admin.register(CaseColors)
class CaseColorsAdmin(admin.ModelAdmin):
list_display = ('name', 'hex_code')
search_fields = ('name',)
@admin.register(CaseType)
class CaseTypeAdmin(admin.ModelAdmin):
list_display = ('name', 'slug')
search_fields = ('name',)
prepopulated_fields = {'slug': ('name',)}
@admin.register(HeadphonePurpose)
class HeadphonePurposeAdmin(admin.ModelAdmin):
list_display = ('name', 'slug')
search_fields = ('name',)
prepopulated_fields = {'slug': ('name',)}
@admin.register(TagCategory)
class TagCategoryAdmin(admin.ModelAdmin):
list_display = ('name', 'slug')
search_fields = ('name',)
prepopulated_fields = {'slug': ('name',)}
@admin.register(Tags)
class TagsAdmin(admin.ModelAdmin):
list_display = ('name', 'category', 'slug')
list_filter = ('category',)
search_fields = ('name', 'category__name')
prepopulated_fields = {'slug': ('name',)}
# ======================
# Регистрация аудио компонентов
# ======================
@admin.register(DriverType)
class DriverTypeAdmin(admin.ModelAdmin):
list_display = ('name', 'slug')
search_fields = ('name',)
prepopulated_fields = {'slug': ('name',)}
@admin.register(NoiseCancellationType)
class NoiseCancellationTypeAdmin(admin.ModelAdmin):
list_display = ('name', 'slug')
search_fields = ('name',)
prepopulated_fields = {'slug': ('name',)}
@admin.register(CodecType)
class CodecTypeAdmin(admin.ModelAdmin):
list_display = ('name',)
search_fields = ('name',)
@admin.register(AudioCodec)
class AudioCodecAdmin(admin.ModelAdmin):
list_display = ('name', 'codec_type', 'max_bitrate', 'latency')
list_filter = ('codec_type',)
search_fields = ('name', 'description')
filter_horizontal = ()
# ======================
# Регистрация разъемов и подключений
# ======================
@admin.register(CableConnectionType)
class CableConnectionTypeAdmin(admin.ModelAdmin):
list_display = ('name', 'is_detachable')
search_fields = ('name', 'description')
readonly_fields = ('image_preview',)
def image_preview(self, obj):
if obj.image:
return mark_safe(f'<img src="{obj.image.url}" width="150" />')
return "Нет изображения"
image_preview.short_description = 'Превью'
@admin.register(DeviceConnectorType)
class DeviceConnectorTypeAdmin(admin.ModelAdmin):
list_display = ('name',)
search_fields = ('name', 'description')
@admin.register(WirelessTechnology)
class WirelessTechnologyAdmin(admin.ModelAdmin):
list_display = ('name',)
search_fields = ('name', 'description')
@admin.register(ChargingInterface)
class ChargingInterfaceAdmin(admin.ModelAdmin):
list_display = ('name',)
search_fields = ('name', 'description')
# ======================
# Регистрация отзывов и медиа
# ======================
@admin.register(HeadphonesReviewType)
class HeadphonesReviewTypeAdmin(admin.ModelAdmin):
list_display = ('name', 'slug')
search_fields = ('name',)
prepopulated_fields = {'slug': ('name',)}
@admin.register(HeadphonesReviewResource)
class HeadphonesReviewResourceAdmin(admin.ModelAdmin):
list_display = ('name', 'review_type', 'website_link', 'slug')
list_filter = ('type',)
search_fields = ('name', 'type__name')
def website_link(self, obj):
return format_html('<a href="{}" target="_blank">{}</a>', obj.url, obj.url)
website_link.short_description = "Ссылка на ресурс"
def review_type(self, obj):
return obj.type.name if obj.type else '-'
review_type.short_description = 'Тип обзора'
review_type.admin_order_field = 'type__name'
@admin.register(HeadphonesConnector)
class HeadphonesConnectorAdmin(admin.ModelAdmin):
list_display = ('headphones', 'connector_display', 'is_primary', 'notes')
list_filter = ('is_primary', 'connector')
def connector_display(self, obj):
return str(obj.connector)
connector_display.short_description = 'Разъем'
connector_display.admin_order_field = 'connector__name'
@admin.register(HeadphonesReviewLanguage)
class HeadphonesReviewLanguageAdmin(admin.ModelAdmin):
list_display = ('language',)
search_fields = ('language',)
@admin.register(HeadphonesReviewAuthor)
class HeadphonesReviewAuthorAdmin(admin.ModelAdmin):
list_display = ('name', 'nickname', 'reviews_count')
search_fields = ('name', 'nickname')
filter_horizontal = ('resources',)
def reviews_count(self, obj):
return obj.headphonesreview_set.count()
reviews_count.short_description = 'Кол-во обзоров'
# Добавьте инлайн для отображения обзоров автора
class ReviewsInline(admin.TabularInline):
model = HeadphonesReview
extra = 0
fields = ('title', 'headphones', 'date')
readonly_fields = ('title', 'headphones', 'date')
inlines = [ReviewsInline]
class HeadphonesReviewAuthorInline(admin.StackedInline):
model = HeadphonesReviewAuthor
extra = 1
fields = ('name', 'nickname', 'resources')
filter_horizontal = ('resources',)
@admin.register(HeadphonesReview)
class HeadphonesReviewAdmin(admin.ModelAdmin):
list_display = (
'title',
'headphones_link',
'resource_link',
'date',
# 'author_display',
'review_link'
)
list_filter = (
'resource__type',
'headphones__brand',
'date'
)
search_fields = (
'title',
'headphones__model',
# 'author',
'resource__name'
)
raw_id_fields = ('headphones', 'resource')
date_hierarchy = 'date'
list_select_related = ('headphones', 'resource')
def headphones_link(self, obj):
if obj.headphones:
url = reverse('admin:headphones_headphones_change', args=[obj.headphones.id])
return format_html('<a href="{}">{}</a>', url, obj.headphones.model)
return "-"
headphones_link.short_description = "Наушники"
headphones_link.admin_order_field = 'headphones__model'
def resource_link(self, obj):
url = reverse('admin:headphones_headphonesreviewresource_change', args=[obj.resource.id])
return format_html('<a href="{}">{}</a>', url, obj.resource.name)
resource_link.short_description = "Ресурс"
resource_link.admin_order_field = 'resource__name'
def review_link(self, obj):
return format_html('<a href="{}" target="_blank">Открыть</a>', obj.url)
review_link.short_description = "Ссылка"
def author_display(self, obj):
if obj.author:
url = reverse('admin:headphones_headphonesreviewauthor_change', args=[obj.author.id])
return format_html('<a href="{}">{}</a>', url, obj.author.name)
return "-"
author_display.short_description = "Автор"
@admin.register(HeadphonesImages)
class HeadphonesImagesAdmin(admin.ModelAdmin):
list_display = ('product', 'caption')
list_filter = ('product__brand',)
search_fields = ('product__model', 'caption')
readonly_fields = ('image_preview',)
def image_preview(self, obj):
if obj.photo:
return mark_safe(f'<img src="{obj.photo.url}" width="150" />')
return "Нет изображения"
image_preview.short_description = 'Превью'
# ======================
# Регистрация вспомогательных моделей
# ======================
@admin.register(HeadphonesDriver)
class HeadphonesDriverAdmin(admin.ModelAdmin):
list_display = ('headphones', 'driver_type', 'size', 'count')
list_filter = ('driver__driver_type',)
def driver_type(self, obj):
return obj.driver.driver_type.name
driver_type.short_description = 'Тип драйвера'
def size(self, obj):
return f"{obj.driver.size}mm" if obj.driver.size else '-'
size.short_description = 'Размер'
@admin.register(Comment)
class CommentAdmin(admin.ModelAdmin):
list_display = ('user', 'post', 'date', 'short_body')
list_filter = ('date', 'user')
# search_fields = ('body', 'post__model', 'user__username')
readonly_fields = ('date',)
def short_body(self, obj):
return obj.body[:50] + '...' if len(obj.body) > 50 else obj.body
short_body.short_description = 'Текст'

6
headphones/apps.py Normal file
View file

@ -0,0 +1,6 @@
from django.apps import AppConfig
class HeadphonesConfig(AppConfig):
default_auto_field = 'django.db.models.BigAutoField'
name = 'headphones'

View file

1050
headphones/models.py Normal file

File diff suppressed because it is too large Load diff

3
headphones/tests.py Normal file
View file

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

3
headphones/views.py Normal file
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 headphones_backend 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/5.2/howto/deployment/asgi/
"""
import os
from django.core.asgi import get_asgi_application
os.environ.setdefault('DJANGO_SETTINGS_MODULE', 'headphones_backend.settings')
application = get_asgi_application()

View file

@ -0,0 +1,144 @@
"""
Django settings for headphones_backend project.
Generated by 'django-admin startproject' using Django 5.2.
For more information on this file, see
https://docs.djangoproject.com/en/5.2/topics/settings/
For the full list of settings and their values, see
https://docs.djangoproject.com/en/5.2/ref/settings/
"""
from pathlib import Path
import os
# Build paths inside the project like this: BASE_DIR / 'subdir'.
BASE_DIR = Path(__file__).resolve().parent.parent
# Quick-start development settings - unsuitable for production
# See https://docs.djangoproject.com/en/5.2/howto/deployment/checklist/
# SECURITY WARNING: keep the secret key used in production secret!
SECRET_KEY = 'django-insecure-8ig5m_im8c%+f_$8px^yas!qh=#j@e)ul(y3cg%6&jzb#cn0ab'
# SECURITY WARNING: don't run with debug turned on in production!
DEBUG = True
ALLOWED_HOSTS = []
# Application definition
INSTALLED_APPS = [
'django.contrib.admin',
'django.contrib.auth',
'django.contrib.contenttypes',
'django.contrib.sessions',
'django.contrib.messages',
'django.contrib.staticfiles',
'rest_framework',
'imagekit',
'headphones',
'users.apps.UsersConfig',
# 'headphones_main.apps.HeadphonesMainConfig',
]
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 = 'headphones_backend.urls'
TEMPLATES = [
{
'BACKEND': 'django.template.backends.django.DjangoTemplates',
'DIRS': [BASE_DIR / 'templates']
,
'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 = 'headphones_backend.wsgi.application'
# Database
# https://docs.djangoproject.com/en/5.2/ref/settings/#databases
DATABASES = {
"default": {
"ENGINE": "django.db.backends.postgresql",
"OPTIONS": {
"service": "headphones",
"passfile": ".headphones_db",
},
}
}
# Password validation
# https://docs.djangoproject.com/en/5.2/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',
},
]
REST_FRAMEWORK = {
'DEFAULT_PERMISSION_CLASSES': [
'rest_framework.permissions.AllowAny',
],
'DEFAULT_PAGINATION_CLASS': 'rest_framework.pagination.PageNumberPagination',
'PAGE_SIZE': 10
}
AUTH_USER_MODEL = 'users.User'
# Internationalization
# https://docs.djangoproject.com/en/5.2/topics/i18n/
LANGUAGE_CODE = 'ru-ru'
TIME_ZONE = 'UTC'
USE_I18N = True
USE_TZ = True
# Static files (CSS, JavaScript, Images)
# https://docs.djangoproject.com/en/5.2/howto/static-files/
STATIC_URL = '/static/'
STATIC_ROOT = os.path.join(BASE_DIR, 'static')
MEDIA_URL = '/media/'
MEDIA_ROOT = os.path.join(BASE_DIR, 'media')
# Default primary key field type
# https://docs.djangoproject.com/en/5.2/ref/settings/#default-auto-field
DEFAULT_AUTO_FIELD = 'django.db.models.BigAutoField'

View file

@ -0,0 +1,26 @@
"""
URL configuration for headphones_backend project.
The `urlpatterns` list routes URLs to views. For more information please see:
https://docs.djangoproject.com/en/5.2/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 django.conf import settings
from django.conf.urls.static import static
urlpatterns = [
path('admin/', admin.site.urls),
]
if settings.DEBUG:
urlpatterns += static(settings.MEDIA_URL, document_root=settings.MEDIA_ROOT)

View file

@ -0,0 +1,16 @@
"""
WSGI config for headphones_backend 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/5.2/howto/deployment/wsgi/
"""
import os
from django.core.wsgi import get_wsgi_application
os.environ.setdefault('DJANGO_SETTINGS_MODULE', 'headphones_backend.settings')
application = get_wsgi_application()

22
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', 'headphones_backend.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()

249
poetry.lock generated Normal file
View file

@ -0,0 +1,249 @@
# This file is automatically @generated by Poetry 2.1.2 and should not be changed by hand.
[[package]]
name = "asgiref"
version = "3.8.1"
description = "ASGI specs, helper code, and adapters"
optional = false
python-versions = ">=3.8"
groups = ["main"]
files = [
{file = "asgiref-3.8.1-py3-none-any.whl", hash = "sha256:3e1e3ecc849832fe52ccf2cb6686b7a55f82bb1d6aee72a58826471390335e47"},
{file = "asgiref-3.8.1.tar.gz", hash = "sha256:c343bd80a0bec947a9860adb4c432ffa7db769836c64238fc34bdc3fec84d590"},
]
[package.extras]
tests = ["mypy (>=0.800)", "pytest", "pytest-asyncio"]
[[package]]
name = "django"
version = "5.2"
description = "A high-level Python web framework that encourages rapid development and clean, pragmatic design."
optional = false
python-versions = ">=3.10"
groups = ["main"]
files = [
{file = "Django-5.2-py3-none-any.whl", hash = "sha256:91ceed4e3a6db5aedced65e3c8f963118ea9ba753fc620831c77074e620e7d83"},
{file = "Django-5.2.tar.gz", hash = "sha256:1a47f7a7a3d43ce64570d350e008d2949abe8c7e21737b351b6a1611277c6d89"},
]
[package.dependencies]
asgiref = ">=3.8.1"
sqlparse = ">=0.3.1"
tzdata = {version = "*", markers = "sys_platform == \"win32\""}
[package.extras]
argon2 = ["argon2-cffi (>=19.1.0)"]
bcrypt = ["bcrypt"]
[[package]]
name = "django-appconf"
version = "1.1.0"
description = "A helper class for handling configuration defaults of packaged apps gracefully."
optional = false
python-versions = ">=3.9"
groups = ["main"]
files = [
{file = "django-appconf-1.1.0.tar.gz", hash = "sha256:9fcead372f82a0f21ee189434e7ae9c007cbb29af1118c18251720f3d06243e4"},
{file = "django_appconf-1.1.0-py3-none-any.whl", hash = "sha256:7abd5a163ff57557f216e84d3ce9dac36c37ffce1ab9a044d3d53b7c943dd10f"},
]
[package.dependencies]
django = "*"
[[package]]
name = "django-imagekit"
version = "5.0.0"
description = "Automated image processing for Django models."
optional = false
python-versions = "*"
groups = ["main"]
files = [
{file = "django-imagekit-5.0.0.tar.gz", hash = "sha256:aae9f74a8e9b6ceb5d15f7d8e266302901e76d9f532c78bd5135cb0fa206a6b0"},
{file = "django_imagekit-5.0.0-py3-none-any.whl", hash = "sha256:a8e77ed6549751026a51f961bb2cd5fda739be691496da8eecbe68ffb966c261"},
]
[package.dependencies]
django-appconf = "*"
pilkit = "*"
[package.extras]
async = ["django-celery (>=3.0)"]
async-dramatiq = ["django-dramatiq (>=0.4.0)"]
async-rq = ["django-rq (>=0.6.0)"]
[[package]]
name = "djangorestframework"
version = "3.16.0"
description = "Web APIs for Django, made easy."
optional = false
python-versions = ">=3.9"
groups = ["main"]
files = [
{file = "djangorestframework-3.16.0-py3-none-any.whl", hash = "sha256:bea7e9f6b96a8584c5224bfb2e4348dfb3f8b5e34edbecb98da258e892089361"},
{file = "djangorestframework-3.16.0.tar.gz", hash = "sha256:f022ff46613584de994c0c6a4aebbace5fd700555fbe9d33b865ebf173eba6c9"},
]
[package.dependencies]
django = ">=4.2"
[[package]]
name = "pilkit"
version = "3.0"
description = "A collection of utilities and processors for the Python Imaging Library."
optional = false
python-versions = "*"
groups = ["main"]
files = [
{file = "pilkit-3.0-py3-none-any.whl", hash = "sha256:fe1707b0411a1d0cbf9ad3986779fa5a346cec4582a188740924aa39f504d117"},
{file = "pilkit-3.0.tar.gz", hash = "sha256:f6719e8cc0482e5447f5cb94f18b949d8e604ea9673a9b019c74d41b779e4eab"},
]
[package.dependencies]
Pillow = ">=7.0"
[[package]]
name = "pillow"
version = "11.1.0"
description = "Python Imaging Library (Fork)"
optional = false
python-versions = ">=3.9"
groups = ["main"]
files = [
{file = "pillow-11.1.0-cp310-cp310-macosx_10_10_x86_64.whl", hash = "sha256:e1abe69aca89514737465752b4bcaf8016de61b3be1397a8fc260ba33321b3a8"},
{file = "pillow-11.1.0-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:c640e5a06869c75994624551f45e5506e4256562ead981cce820d5ab39ae2192"},
{file = "pillow-11.1.0-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:a07dba04c5e22824816b2615ad7a7484432d7f540e6fa86af60d2de57b0fcee2"},
{file = "pillow-11.1.0-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:e267b0ed063341f3e60acd25c05200df4193e15a4a5807075cd71225a2386e26"},
{file = "pillow-11.1.0-cp310-cp310-manylinux_2_28_aarch64.whl", hash = "sha256:bd165131fd51697e22421d0e467997ad31621b74bfc0b75956608cb2906dda07"},
{file = "pillow-11.1.0-cp310-cp310-manylinux_2_28_x86_64.whl", hash = "sha256:abc56501c3fd148d60659aae0af6ddc149660469082859fa7b066a298bde9482"},
{file = "pillow-11.1.0-cp310-cp310-musllinux_1_2_aarch64.whl", hash = "sha256:54ce1c9a16a9561b6d6d8cb30089ab1e5eb66918cb47d457bd996ef34182922e"},
{file = "pillow-11.1.0-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:73ddde795ee9b06257dac5ad42fcb07f3b9b813f8c1f7f870f402f4dc54b5269"},
{file = "pillow-11.1.0-cp310-cp310-win32.whl", hash = "sha256:3a5fe20a7b66e8135d7fd617b13272626a28278d0e578c98720d9ba4b2439d49"},
{file = "pillow-11.1.0-cp310-cp310-win_amd64.whl", hash = "sha256:b6123aa4a59d75f06e9dd3dac5bf8bc9aa383121bb3dd9a7a612e05eabc9961a"},
{file = "pillow-11.1.0-cp310-cp310-win_arm64.whl", hash = "sha256:a76da0a31da6fcae4210aa94fd779c65c75786bc9af06289cd1c184451ef7a65"},
{file = "pillow-11.1.0-cp311-cp311-macosx_10_10_x86_64.whl", hash = "sha256:e06695e0326d05b06833b40b7ef477e475d0b1ba3a6d27da1bb48c23209bf457"},
{file = "pillow-11.1.0-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:96f82000e12f23e4f29346e42702b6ed9a2f2fea34a740dd5ffffcc8c539eb35"},
{file = "pillow-11.1.0-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:a3cd561ded2cf2bbae44d4605837221b987c216cff94f49dfeed63488bb228d2"},
{file = "pillow-11.1.0-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:f189805c8be5ca5add39e6f899e6ce2ed824e65fb45f3c28cb2841911da19070"},
{file = "pillow-11.1.0-cp311-cp311-manylinux_2_28_aarch64.whl", hash = "sha256:dd0052e9db3474df30433f83a71b9b23bd9e4ef1de13d92df21a52c0303b8ab6"},
{file = "pillow-11.1.0-cp311-cp311-manylinux_2_28_x86_64.whl", hash = "sha256:837060a8599b8f5d402e97197d4924f05a2e0d68756998345c829c33186217b1"},
{file = "pillow-11.1.0-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:aa8dd43daa836b9a8128dbe7d923423e5ad86f50a7a14dc688194b7be5c0dea2"},
{file = "pillow-11.1.0-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:0a2f91f8a8b367e7a57c6e91cd25af510168091fb89ec5146003e424e1558a96"},
{file = "pillow-11.1.0-cp311-cp311-win32.whl", hash = "sha256:c12fc111ef090845de2bb15009372175d76ac99969bdf31e2ce9b42e4b8cd88f"},
{file = "pillow-11.1.0-cp311-cp311-win_amd64.whl", hash = "sha256:fbd43429d0d7ed6533b25fc993861b8fd512c42d04514a0dd6337fb3ccf22761"},
{file = "pillow-11.1.0-cp311-cp311-win_arm64.whl", hash = "sha256:f7955ecf5609dee9442cbface754f2c6e541d9e6eda87fad7f7a989b0bdb9d71"},
{file = "pillow-11.1.0-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:2062ffb1d36544d42fcaa277b069c88b01bb7298f4efa06731a7fd6cc290b81a"},
{file = "pillow-11.1.0-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:a85b653980faad27e88b141348707ceeef8a1186f75ecc600c395dcac19f385b"},
{file = "pillow-11.1.0-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:9409c080586d1f683df3f184f20e36fb647f2e0bc3988094d4fd8c9f4eb1b3b3"},
{file = "pillow-11.1.0-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:7fdadc077553621911f27ce206ffcbec7d3f8d7b50e0da39f10997e8e2bb7f6a"},
{file = "pillow-11.1.0-cp312-cp312-manylinux_2_28_aarch64.whl", hash = "sha256:93a18841d09bcdd774dcdc308e4537e1f867b3dec059c131fde0327899734aa1"},
{file = "pillow-11.1.0-cp312-cp312-manylinux_2_28_x86_64.whl", hash = "sha256:9aa9aeddeed452b2f616ff5507459e7bab436916ccb10961c4a382cd3e03f47f"},
{file = "pillow-11.1.0-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:3cdcdb0b896e981678eee140d882b70092dac83ac1cdf6b3a60e2216a73f2b91"},
{file = "pillow-11.1.0-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:36ba10b9cb413e7c7dfa3e189aba252deee0602c86c309799da5a74009ac7a1c"},
{file = "pillow-11.1.0-cp312-cp312-win32.whl", hash = "sha256:cfd5cd998c2e36a862d0e27b2df63237e67273f2fc78f47445b14e73a810e7e6"},
{file = "pillow-11.1.0-cp312-cp312-win_amd64.whl", hash = "sha256:a697cd8ba0383bba3d2d3ada02b34ed268cb548b369943cd349007730c92bddf"},
{file = "pillow-11.1.0-cp312-cp312-win_arm64.whl", hash = "sha256:4dd43a78897793f60766563969442020e90eb7847463eca901e41ba186a7d4a5"},
{file = "pillow-11.1.0-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:ae98e14432d458fc3de11a77ccb3ae65ddce70f730e7c76140653048c71bfcbc"},
{file = "pillow-11.1.0-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:cc1331b6d5a6e144aeb5e626f4375f5b7ae9934ba620c0ac6b3e43d5e683a0f0"},
{file = "pillow-11.1.0-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:758e9d4ef15d3560214cddbc97b8ef3ef86ce04d62ddac17ad39ba87e89bd3b1"},
{file = "pillow-11.1.0-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:b523466b1a31d0dcef7c5be1f20b942919b62fd6e9a9be199d035509cbefc0ec"},
{file = "pillow-11.1.0-cp313-cp313-manylinux_2_28_aarch64.whl", hash = "sha256:9044b5e4f7083f209c4e35aa5dd54b1dd5b112b108648f5c902ad586d4f945c5"},
{file = "pillow-11.1.0-cp313-cp313-manylinux_2_28_x86_64.whl", hash = "sha256:3764d53e09cdedd91bee65c2527815d315c6b90d7b8b79759cc48d7bf5d4f114"},
{file = "pillow-11.1.0-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:31eba6bbdd27dde97b0174ddf0297d7a9c3a507a8a1480e1e60ef914fe23d352"},
{file = "pillow-11.1.0-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:b5d658fbd9f0d6eea113aea286b21d3cd4d3fd978157cbf2447a6035916506d3"},
{file = "pillow-11.1.0-cp313-cp313-win32.whl", hash = "sha256:f86d3a7a9af5d826744fabf4afd15b9dfef44fe69a98541f666f66fbb8d3fef9"},
{file = "pillow-11.1.0-cp313-cp313-win_amd64.whl", hash = "sha256:593c5fd6be85da83656b93ffcccc2312d2d149d251e98588b14fbc288fd8909c"},
{file = "pillow-11.1.0-cp313-cp313-win_arm64.whl", hash = "sha256:11633d58b6ee5733bde153a8dafd25e505ea3d32e261accd388827ee987baf65"},
{file = "pillow-11.1.0-cp313-cp313t-macosx_10_13_x86_64.whl", hash = "sha256:70ca5ef3b3b1c4a0812b5c63c57c23b63e53bc38e758b37a951e5bc466449861"},
{file = "pillow-11.1.0-cp313-cp313t-macosx_11_0_arm64.whl", hash = "sha256:8000376f139d4d38d6851eb149b321a52bb8893a88dae8ee7d95840431977081"},
{file = "pillow-11.1.0-cp313-cp313t-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:9ee85f0696a17dd28fbcfceb59f9510aa71934b483d1f5601d1030c3c8304f3c"},
{file = "pillow-11.1.0-cp313-cp313t-manylinux_2_28_x86_64.whl", hash = "sha256:dd0e081319328928531df7a0e63621caf67652c8464303fd102141b785ef9547"},
{file = "pillow-11.1.0-cp313-cp313t-musllinux_1_2_x86_64.whl", hash = "sha256:e63e4e5081de46517099dc30abe418122f54531a6ae2ebc8680bcd7096860eab"},
{file = "pillow-11.1.0-cp313-cp313t-win32.whl", hash = "sha256:dda60aa465b861324e65a78c9f5cf0f4bc713e4309f83bc387be158b077963d9"},
{file = "pillow-11.1.0-cp313-cp313t-win_amd64.whl", hash = "sha256:ad5db5781c774ab9a9b2c4302bbf0c1014960a0a7be63278d13ae6fdf88126fe"},
{file = "pillow-11.1.0-cp313-cp313t-win_arm64.whl", hash = "sha256:67cd427c68926108778a9005f2a04adbd5e67c442ed21d95389fe1d595458756"},
{file = "pillow-11.1.0-cp39-cp39-macosx_10_10_x86_64.whl", hash = "sha256:bf902d7413c82a1bfa08b06a070876132a5ae6b2388e2712aab3a7cbc02205c6"},
{file = "pillow-11.1.0-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:c1eec9d950b6fe688edee07138993e54ee4ae634c51443cfb7c1e7613322718e"},
{file = "pillow-11.1.0-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:8e275ee4cb11c262bd108ab2081f750db2a1c0b8c12c1897f27b160c8bd57bbc"},
{file = "pillow-11.1.0-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:4db853948ce4e718f2fc775b75c37ba2efb6aaea41a1a5fc57f0af59eee774b2"},
{file = "pillow-11.1.0-cp39-cp39-manylinux_2_28_aarch64.whl", hash = "sha256:ab8a209b8485d3db694fa97a896d96dd6533d63c22829043fd9de627060beade"},
{file = "pillow-11.1.0-cp39-cp39-manylinux_2_28_x86_64.whl", hash = "sha256:54251ef02a2309b5eec99d151ebf5c9904b77976c8abdcbce7891ed22df53884"},
{file = "pillow-11.1.0-cp39-cp39-musllinux_1_2_aarch64.whl", hash = "sha256:5bb94705aea800051a743aa4874bb1397d4695fb0583ba5e425ee0328757f196"},
{file = "pillow-11.1.0-cp39-cp39-musllinux_1_2_x86_64.whl", hash = "sha256:89dbdb3e6e9594d512780a5a1c42801879628b38e3efc7038094430844e271d8"},
{file = "pillow-11.1.0-cp39-cp39-win32.whl", hash = "sha256:e5449ca63da169a2e6068dd0e2fcc8d91f9558aba89ff6d02121ca8ab11e79e5"},
{file = "pillow-11.1.0-cp39-cp39-win_amd64.whl", hash = "sha256:3362c6ca227e65c54bf71a5f88b3d4565ff1bcbc63ae72c34b07bbb1cc59a43f"},
{file = "pillow-11.1.0-cp39-cp39-win_arm64.whl", hash = "sha256:b20be51b37a75cc54c2c55def3fa2c65bb94ba859dde241cd0a4fd302de5ae0a"},
{file = "pillow-11.1.0-pp310-pypy310_pp73-macosx_10_15_x86_64.whl", hash = "sha256:8c730dc3a83e5ac137fbc92dfcfe1511ce3b2b5d7578315b63dbbb76f7f51d90"},
{file = "pillow-11.1.0-pp310-pypy310_pp73-macosx_11_0_arm64.whl", hash = "sha256:7d33d2fae0e8b170b6a6c57400e077412240f6f5bb2a342cf1ee512a787942bb"},
{file = "pillow-11.1.0-pp310-pypy310_pp73-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:a8d65b38173085f24bc07f8b6c505cbb7418009fa1a1fcb111b1f4961814a442"},
{file = "pillow-11.1.0-pp310-pypy310_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:015c6e863faa4779251436db398ae75051469f7c903b043a48f078e437656f83"},
{file = "pillow-11.1.0-pp310-pypy310_pp73-manylinux_2_28_aarch64.whl", hash = "sha256:d44ff19eea13ae4acdaaab0179fa68c0c6f2f45d66a4d8ec1eda7d6cecbcc15f"},
{file = "pillow-11.1.0-pp310-pypy310_pp73-manylinux_2_28_x86_64.whl", hash = "sha256:d3d8da4a631471dfaf94c10c85f5277b1f8e42ac42bade1ac67da4b4a7359b73"},
{file = "pillow-11.1.0-pp310-pypy310_pp73-win_amd64.whl", hash = "sha256:4637b88343166249fe8aa94e7c4a62a180c4b3898283bb5d3d2fd5fe10d8e4e0"},
{file = "pillow-11.1.0.tar.gz", hash = "sha256:368da70808b36d73b4b390a8ffac11069f8a5c85f29eff1f1b01bcf3ef5b2a20"},
]
[package.extras]
docs = ["furo", "olefile", "sphinx (>=8.1)", "sphinx-copybutton", "sphinx-inline-tabs", "sphinxext-opengraph"]
fpx = ["olefile"]
mic = ["olefile"]
tests = ["check-manifest", "coverage (>=7.4.2)", "defusedxml", "markdown2", "olefile", "packaging", "pyroma", "pytest", "pytest-cov", "pytest-timeout", "trove-classifiers (>=2024.10.12)"]
typing = ["typing-extensions ; python_version < \"3.10\""]
xmp = ["defusedxml"]
[[package]]
name = "psycopg"
version = "3.2.6"
description = "PostgreSQL database adapter for Python"
optional = false
python-versions = ">=3.8"
groups = ["main"]
files = [
{file = "psycopg-3.2.6-py3-none-any.whl", hash = "sha256:f3ff5488525890abb0566c429146add66b329e20d6d4835662b920cbbf90ac58"},
{file = "psycopg-3.2.6.tar.gz", hash = "sha256:16fa094efa2698f260f2af74f3710f781e4a6f226efe9d1fd0c37f384639ed8a"},
]
[package.dependencies]
tzdata = {version = "*", markers = "sys_platform == \"win32\""}
[package.extras]
binary = ["psycopg-binary (==3.2.6) ; implementation_name != \"pypy\""]
c = ["psycopg-c (==3.2.6) ; implementation_name != \"pypy\""]
dev = ["ast-comments (>=1.1.2)", "black (>=24.1.0)", "codespell (>=2.2)", "dnspython (>=2.1)", "flake8 (>=4.0)", "isort-psycopg", "isort[colors] (>=6.0)", "mypy (>=1.14)", "pre-commit (>=4.0.1)", "types-setuptools (>=57.4)", "wheel (>=0.37)"]
docs = ["Sphinx (>=5.0)", "furo (==2022.6.21)", "sphinx-autobuild (>=2021.3.14)", "sphinx-autodoc-typehints (>=1.12)"]
pool = ["psycopg-pool"]
test = ["anyio (>=4.0)", "mypy (>=1.14)", "pproxy (>=2.7)", "pytest (>=6.2.5)", "pytest-cov (>=3.0)", "pytest-randomly (>=3.5)"]
[[package]]
name = "sqlparse"
version = "0.5.3"
description = "A non-validating SQL parser."
optional = false
python-versions = ">=3.8"
groups = ["main"]
files = [
{file = "sqlparse-0.5.3-py3-none-any.whl", hash = "sha256:cf2196ed3418f3ba5de6af7e82c694a9fbdbfecccdfc72e281548517081f16ca"},
{file = "sqlparse-0.5.3.tar.gz", hash = "sha256:09f67787f56a0b16ecdbde1bfc7f5d9c3371ca683cfeaa8e6ff60b4807ec9272"},
]
[package.extras]
dev = ["build", "hatch"]
doc = ["sphinx"]
[[package]]
name = "tzdata"
version = "2025.2"
description = "Provider of IANA time zone data"
optional = false
python-versions = ">=2"
groups = ["main"]
markers = "sys_platform == \"win32\""
files = [
{file = "tzdata-2025.2-py2.py3-none-any.whl", hash = "sha256:1a403fada01ff9221ca8044d701868fa132215d84beb92242d9acd2147f667a8"},
{file = "tzdata-2025.2.tar.gz", hash = "sha256:b60a638fcc0daffadf82fe0f57e53d06bdec2f36c4df66280ae79bce6bd6f2b9"},
]
[metadata]
lock-version = "2.1"
python-versions = ">=3.13"
content-hash = "04a0cf20da59a089ce0256eef5326fcf4b928120ee38f3cbdef93ff118269b0d"

21
pyproject.toml Normal file
View file

@ -0,0 +1,21 @@
[project]
name = "headphones-backend"
version = "0.1.0"
description = ""
authors = [
{name = "apheyhys",email = "apheyhys@gmail.com"}
]
readme = "README.md"
requires-python = ">=3.13"
dependencies = [
"django (>=5.2,<6.0)",
"psycopg (>=3.2.6,<4.0.0)",
"pillow (>=11.1.0,<12.0.0)",
"django-imagekit (>=5.0.0,<6.0.0)",
"djangorestframework (>=3.16.0,<4.0.0)"
]
[build-system]
requires = ["poetry-core>=2.0.0,<3.0.0"]
build-backend = "poetry.core.masonry.api"

0
users/__init__.py Normal file
View file

3
users/admin.py Normal file
View file

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

6
users/apps.py Normal file
View file

@ -0,0 +1,6 @@
from django.apps import AppConfig
class UsersConfig(AppConfig):
default_auto_field = 'django.db.models.BigAutoField'
name = 'users'

View file

9
users/models.py Normal file
View file

@ -0,0 +1,9 @@
from django.contrib.auth.models import AbstractUser
from django.db import models
class User(AbstractUser):
photo = models.CharField(max_length=350, blank=True)
def __str__(self):
return self.username

3
users/tests.py Normal file
View file

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

3
users/views.py Normal file
View file

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