Compare commits
No commits in common. "2a83135234377a54e6f210b56530d15fd1d91dc1" and "3a1dbab9e237927811c93c2f3e4be1a8891edf4c" have entirely different histories.
2a83135234
...
3a1dbab9e2
2 changed files with 30 additions and 143 deletions
|
|
@ -146,34 +146,6 @@ class HeadphonesDriverInline(admin.TabularInline):
|
||||||
autocomplete_fields = ['driver']
|
autocomplete_fields = ['driver']
|
||||||
|
|
||||||
|
|
||||||
# class HeadphonesColorInline(admin.TabularInline):
|
|
||||||
# model = Headphones.colors.through # Используем автоматическую промежуточную модель
|
|
||||||
# extra = 1
|
|
||||||
# # autocomplete_fields = ['casecolors'] # Убедитесь, что это правильное имя related_name
|
|
||||||
# verbose_name = "Доступный цвет"
|
|
||||||
# verbose_name_plural = "Доступные цвета"
|
|
||||||
|
|
||||||
|
|
||||||
class CaseColorsInline(admin.TabularInline):
|
|
||||||
model = CaseColors
|
|
||||||
extra = 1
|
|
||||||
fields = ('name_ru', 'name_en', 'image', 'image_preview', 'slug')
|
|
||||||
readonly_fields = ('image_preview', 'slug')
|
|
||||||
|
|
||||||
def image_preview(self, obj):
|
|
||||||
if obj.image:
|
|
||||||
return format_html('<img src="{}" width="50" height="50" />', obj.image.url)
|
|
||||||
return "Нет изображения"
|
|
||||||
image_preview.short_description = "Превью"
|
|
||||||
|
|
||||||
def get_formset(self, request, obj=None, **kwargs):
|
|
||||||
formset = super().get_formset(request, obj, **kwargs)
|
|
||||||
# Устанавливаем начальное значение для slug, если объект новый
|
|
||||||
if obj and not obj.pk:
|
|
||||||
formset.form.base_fields['slug'].initial = f"{obj.model}_"
|
|
||||||
return formset
|
|
||||||
|
|
||||||
|
|
||||||
@admin.register(Headphones)
|
@admin.register(Headphones)
|
||||||
class HeadphonesAdmin(admin.ModelAdmin):
|
class HeadphonesAdmin(admin.ModelAdmin):
|
||||||
form = HeadphonesAdminForm
|
form = HeadphonesAdminForm
|
||||||
|
|
@ -182,7 +154,7 @@ class HeadphonesAdmin(admin.ModelAdmin):
|
||||||
list_filter = ('brand', 'connection_type', 'published', 'case_type')
|
list_filter = ('brand', 'connection_type', 'published', 'case_type')
|
||||||
search_fields = ('model', 'brand__name', 'description')
|
search_fields = ('model', 'brand__name', 'description')
|
||||||
prepopulated_fields = {'slug': ('brand', 'model')}
|
prepopulated_fields = {'slug': ('brand', 'model')}
|
||||||
filter_horizontal = ('tags',)
|
filter_horizontal = ('tags', 'colors')
|
||||||
list_editable = ('published',)
|
list_editable = ('published',)
|
||||||
actions = ['make_published', 'make_unpublished']
|
actions = ['make_published', 'make_unpublished']
|
||||||
readonly_fields = (
|
readonly_fields = (
|
||||||
|
|
@ -214,7 +186,7 @@ class HeadphonesAdmin(admin.ModelAdmin):
|
||||||
}),
|
}),
|
||||||
('Внешний вид', {
|
('Внешний вид', {
|
||||||
'fields': [
|
'fields': [
|
||||||
# 'colors',
|
'colors',
|
||||||
('weight', 'release_year')
|
('weight', 'release_year')
|
||||||
]
|
]
|
||||||
}),
|
}),
|
||||||
|
|
@ -226,7 +198,7 @@ class HeadphonesAdmin(admin.ModelAdmin):
|
||||||
}),
|
}),
|
||||||
]
|
]
|
||||||
|
|
||||||
inlines = [HeadphonesDriverInline, HeadphonesImagesInline, CaseColorsInline]
|
inlines = [HeadphonesDriverInline, HeadphonesImagesInline]
|
||||||
|
|
||||||
# Методы для отображения
|
# Методы для отображения
|
||||||
def get_reviews_count(self, obj):
|
def get_reviews_count(self, obj):
|
||||||
|
|
@ -251,11 +223,10 @@ class HeadphonesAdmin(admin.ModelAdmin):
|
||||||
driver_configuration_display.short_description = 'Конфигурация драйверов'
|
driver_configuration_display.short_description = 'Конфигурация драйверов'
|
||||||
|
|
||||||
def get_inline_instances(self, request, obj=None):
|
def get_inline_instances(self, request, obj=None):
|
||||||
# inlines = [
|
inlines = [
|
||||||
# HeadphonesDriverInline(self.model, self.admin_site),
|
HeadphonesDriverInline(self.model, self.admin_site),
|
||||||
# HeadphonesImagesInline(self.model, self.admin_site)
|
HeadphonesImagesInline(self.model, self.admin_site)
|
||||||
# ]
|
]
|
||||||
inlines = super().get_inline_instances(request, obj)
|
|
||||||
|
|
||||||
if obj:
|
if obj:
|
||||||
if obj.connection_type == 'wired':
|
if obj.connection_type == 'wired':
|
||||||
|
|
@ -278,27 +249,12 @@ class BrandAdmin(admin.ModelAdmin):
|
||||||
prepopulated_fields = {'slug': ('name',)}
|
prepopulated_fields = {'slug': ('name',)}
|
||||||
|
|
||||||
|
|
||||||
class DriverInline(admin.TabularInline):
|
|
||||||
model = Driver
|
|
||||||
extra = 1
|
|
||||||
fields = ('driver_type', 'driver_model', 'size', 'frequency_range')
|
|
||||||
autocomplete_fields = ['driver_type', 'driver_model']
|
|
||||||
|
|
||||||
|
|
||||||
@admin.register(DriverModel)
|
|
||||||
class DriverModelAdmin(admin.ModelAdmin):
|
|
||||||
list_display = ('name', 'slug')
|
|
||||||
search_fields = ('name',)
|
|
||||||
prepopulated_fields = {'slug': ('name',)}
|
|
||||||
inlines = [DriverInline]
|
|
||||||
|
|
||||||
|
|
||||||
@admin.register(Driver)
|
@admin.register(Driver)
|
||||||
class DriverAdmin(admin.ModelAdmin):
|
class DriverAdmin(admin.ModelAdmin):
|
||||||
list_display = ('driver_type', 'driver_model', 'size', 'frequency_range')
|
list_display = ('driver_type', 'size', 'frequency_range')
|
||||||
list_filter = ('driver_type',)
|
list_filter = ('driver_type',)
|
||||||
search_fields = ('driver_type__name', 'driver_model__name', 'size', 'frequency_range') # Добавлено
|
search_fields = ('driver_type__name', 'size', 'frequency_range') # Добавлено
|
||||||
autocomplete_fields = ['driver_type', 'driver_model']
|
autocomplete_fields = ['driver_type'] # Если нужно автозаполнение для типа
|
||||||
|
|
||||||
def get_queryset(self, request):
|
def get_queryset(self, request):
|
||||||
return super().get_queryset(request).select_related('driver_type')
|
return super().get_queryset(request).select_related('driver_type')
|
||||||
|
|
@ -317,28 +273,8 @@ class BrandCountryAdmin(admin.ModelAdmin):
|
||||||
|
|
||||||
@admin.register(CaseColors)
|
@admin.register(CaseColors)
|
||||||
class CaseColorsAdmin(admin.ModelAdmin):
|
class CaseColorsAdmin(admin.ModelAdmin):
|
||||||
list_display = ('name_ru', 'name_en', 'headphones', 'image_preview', 'slug')
|
list_display = ('name', 'hex_code')
|
||||||
list_filter = ('headphones__brand',)
|
search_fields = ('name',)
|
||||||
search_fields = ('name_ru', 'name_en', 'headphones__model')
|
|
||||||
readonly_fields = ('image_preview', 'slug')
|
|
||||||
list_select_related = ('headphones', 'headphones__brand')
|
|
||||||
|
|
||||||
fieldsets = [
|
|
||||||
('Основная информация', {
|
|
||||||
'fields': ['headphones', 'name_ru', 'name_en', 'slug']
|
|
||||||
}),
|
|
||||||
('Изображение', {
|
|
||||||
'fields': ['image', 'image_preview']
|
|
||||||
})
|
|
||||||
]
|
|
||||||
|
|
||||||
def get_queryset(self, request):
|
|
||||||
return super().get_queryset(request).select_related('headphones__brand')
|
|
||||||
|
|
||||||
def save_model(self, request, obj, form, change):
|
|
||||||
if not obj.slug:
|
|
||||||
obj.slug = slugify(f"{obj.headphones.model}_{obj.name_en}")
|
|
||||||
super().save_model(request, obj, form, change)
|
|
||||||
|
|
||||||
|
|
||||||
@admin.register(CaseType)
|
@admin.register(CaseType)
|
||||||
|
|
@ -513,6 +449,9 @@ class HeadphonesReviewAuthorInline(admin.StackedInline):
|
||||||
filter_horizontal = ('resources',)
|
filter_horizontal = ('resources',)
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
@admin.register(HeadphonesReview)
|
@admin.register(HeadphonesReview)
|
||||||
class HeadphonesReviewAdmin(admin.ModelAdmin):
|
class HeadphonesReviewAdmin(admin.ModelAdmin):
|
||||||
list_display = (
|
list_display = (
|
||||||
|
|
|
||||||
|
|
@ -18,11 +18,6 @@ from PIL import Image
|
||||||
def upload_to(instance, filename):
|
def upload_to(instance, filename):
|
||||||
return f"headphones/{instance.product.id}/{filename}"
|
return f"headphones/{instance.product.id}/{filename}"
|
||||||
|
|
||||||
def upload_to_case_image(instance, filename):
|
|
||||||
"""Путь для сохранения: headphones/<product_id>/case_images/<slug>.<ext>"""
|
|
||||||
ext = filename.split('.')[-1]
|
|
||||||
return f"headphones/{instance.headphones.id}/case_images/{instance.slug}.{ext}"
|
|
||||||
|
|
||||||
# ======================
|
# ======================
|
||||||
# Базовые справочники
|
# Базовые справочники
|
||||||
# ======================
|
# ======================
|
||||||
|
|
@ -38,45 +33,16 @@ class BrandCountry(models.Model):
|
||||||
|
|
||||||
|
|
||||||
class CaseColors(models.Model):
|
class CaseColors(models.Model):
|
||||||
headphones = models.ForeignKey(
|
name = models.CharField(max_length=50, verbose_name="Название цвета")
|
||||||
'Headphones',
|
hex_code = models.CharField(max_length=50, verbose_name="HEX-код цвета")
|
||||||
on_delete=models.CASCADE,
|
|
||||||
related_name='case_colors',
|
|
||||||
verbose_name='Наушники'
|
|
||||||
)
|
|
||||||
name_ru = models.CharField('Название цвета (рус)', max_length=100)
|
|
||||||
name_en = models.CharField('Название цвета (англ)', max_length=100)
|
|
||||||
slug = models.SlugField(max_length=150, unique=True, blank=True, verbose_name="URL-идентификатор")
|
|
||||||
image = models.ImageField('Изображение', upload_to=upload_to_case_image)
|
|
||||||
|
|
||||||
def save(self, *args, **kwargs):
|
|
||||||
if not self.slug:
|
|
||||||
# Генерируем slug в формате "модельнаушников_цветангл"
|
|
||||||
base_slug = slugify(f"{self.headphones.model}_{self.name_en}")
|
|
||||||
self.slug = base_slug
|
|
||||||
|
|
||||||
# Проверяем уникальность slug
|
|
||||||
counter = 1
|
|
||||||
while CaseColors.objects.filter(slug=self.slug).exists():
|
|
||||||
self.slug = f"{base_slug}-{counter}"
|
|
||||||
counter += 1
|
|
||||||
super().save(*args, **kwargs)
|
|
||||||
|
|
||||||
def image_preview(self):
|
|
||||||
if self.image:
|
|
||||||
return format_html('<img src="{}" width="50" height="50" />', self.image.url)
|
|
||||||
return "Нет изображения"
|
|
||||||
|
|
||||||
image_preview.short_description = 'Превью'
|
|
||||||
image_preview.allow_tags = True
|
|
||||||
|
|
||||||
def __str__(self):
|
|
||||||
return f"{self.name_ru} ({self.headphones.model})"
|
|
||||||
|
|
||||||
class Meta:
|
class Meta:
|
||||||
verbose_name = 'Цвет корпуса'
|
verbose_name = "Цвет корпуса"
|
||||||
verbose_name_plural = 'Цвета корпусов'
|
verbose_name_plural = "Цвета корпусов"
|
||||||
ordering = ['headphones__model', 'name_ru']
|
|
||||||
|
def __str__(self):
|
||||||
|
return f"{self.name} {self.hex_code}"
|
||||||
|
|
||||||
|
|
||||||
# ======================
|
# ======================
|
||||||
# Основные модели брендов и категорий
|
# Основные модели брендов и категорий
|
||||||
|
|
@ -246,15 +212,7 @@ class Driver(models.Model):
|
||||||
verbose_name_plural = "Драйверы"
|
verbose_name_plural = "Драйверы"
|
||||||
|
|
||||||
def __str__(self):
|
def __str__(self):
|
||||||
parts = [self.driver_type.name]
|
return f"{self.driver_type.name} {self.size}mm"
|
||||||
|
|
||||||
if self.driver_model:
|
|
||||||
parts.append(self.driver_model.name)
|
|
||||||
|
|
||||||
if self.size:
|
|
||||||
parts.append(f"{self.size}mm")
|
|
||||||
|
|
||||||
return " ".join(parts)
|
|
||||||
|
|
||||||
|
|
||||||
class NoiseCancellationType(models.Model):
|
class NoiseCancellationType(models.Model):
|
||||||
|
|
@ -743,13 +701,12 @@ class Headphones(models.Model):
|
||||||
blank=True
|
blank=True
|
||||||
)
|
)
|
||||||
|
|
||||||
# # Внешний вид и физические параметры
|
# Внешний вид и физические параметры
|
||||||
# colors = models.ManyToManyField(
|
colors = models.ManyToManyField(
|
||||||
# CaseColors,
|
CaseColors,
|
||||||
# related_name='headphones',
|
related_name='headphones',
|
||||||
# blank=True,
|
verbose_name="Цвета"
|
||||||
# verbose_name="Доступные цвета"
|
)
|
||||||
# )
|
|
||||||
weight = models.PositiveSmallIntegerField(
|
weight = models.PositiveSmallIntegerField(
|
||||||
verbose_name="Вес (г)",
|
verbose_name="Вес (г)",
|
||||||
null=True,
|
null=True,
|
||||||
|
|
@ -785,15 +742,6 @@ class Headphones(models.Model):
|
||||||
self.slug = slugify(f"{self.brand.name} {self.model}")
|
self.slug = slugify(f"{self.brand.name} {self.model}")
|
||||||
super().save(*args, **kwargs)
|
super().save(*args, **kwargs)
|
||||||
|
|
||||||
# Обновляем slug для связанных цветов, если изменилась модель
|
|
||||||
if self.pk and hasattr(self, 'case_colors'):
|
|
||||||
for color in self.case_colors.all():
|
|
||||||
new_slug = slugify(f"{self.model}_{color.name_en}")
|
|
||||||
if color.slug != new_slug:
|
|
||||||
color.slug = new_slug
|
|
||||||
color.save()
|
|
||||||
|
|
||||||
|
|
||||||
class HeadphonesConnector(models.Model):
|
class HeadphonesConnector(models.Model):
|
||||||
headphones = models.ForeignKey(
|
headphones = models.ForeignKey(
|
||||||
'Headphones',
|
'Headphones',
|
||||||
|
|
|
||||||
Loading…
Add table
Reference in a new issue