Added multiple file upload for chart files
This commit is contained in:
parent
c9243c7b06
commit
d04a549469
2 changed files with 92 additions and 11 deletions
|
|
@ -176,6 +176,23 @@ class CaseColorsInline(admin.TabularInline):
|
||||||
return formset
|
return formset
|
||||||
|
|
||||||
|
|
||||||
|
class FrequencyResponseInline(admin.StackedInline):
|
||||||
|
model = FrequencyResponse
|
||||||
|
extra = 1
|
||||||
|
fields = ('file', 'author', 'source_link', 'is_primary', 'notes', 'file_preview')
|
||||||
|
readonly_fields = ('file_preview',)
|
||||||
|
|
||||||
|
def file_preview(self, obj):
|
||||||
|
if obj.file:
|
||||||
|
return format_html(
|
||||||
|
'<a href="{}" target="_blank">{}</a>',
|
||||||
|
obj.file.url,
|
||||||
|
os.path.basename(obj.file.name)
|
||||||
|
)
|
||||||
|
return "-"
|
||||||
|
file_preview.short_description = "Файл"
|
||||||
|
|
||||||
|
|
||||||
@admin.register(Headphones)
|
@admin.register(Headphones)
|
||||||
class HeadphonesAdmin(admin.ModelAdmin):
|
class HeadphonesAdmin(admin.ModelAdmin):
|
||||||
form = HeadphonesAdminForm
|
form = HeadphonesAdminForm
|
||||||
|
|
@ -209,7 +226,7 @@ class HeadphonesAdmin(admin.ModelAdmin):
|
||||||
'fields': [
|
'fields': [
|
||||||
('impedance', 'sensitivity'),
|
('impedance', 'sensitivity'),
|
||||||
'frequency_range',
|
'frequency_range',
|
||||||
('frequency_response_chart', 'frequency_response_chart_author', 'frequency_response_chart_link'),
|
# ('frequency_response_chart', 'frequency_response_chart_author', 'frequency_response_chart_link'),
|
||||||
('microphone', 'noise_cancellation'),
|
('microphone', 'noise_cancellation'),
|
||||||
'ip_rating',
|
'ip_rating',
|
||||||
]
|
]
|
||||||
|
|
@ -228,7 +245,7 @@ class HeadphonesAdmin(admin.ModelAdmin):
|
||||||
}),
|
}),
|
||||||
]
|
]
|
||||||
|
|
||||||
inlines = [HeadphonesDriverInline, HeadphonesImagesInline, CaseColorsInline]
|
inlines = [HeadphonesDriverInline, HeadphonesImagesInline, CaseColorsInline, FrequencyResponseInline]
|
||||||
|
|
||||||
# Методы для отображения
|
# Методы для отображения
|
||||||
def get_reviews_count(self, obj):
|
def get_reviews_count(self, obj):
|
||||||
|
|
@ -421,7 +438,6 @@ class AudioCodecAdmin(admin.ModelAdmin):
|
||||||
# ======================
|
# ======================
|
||||||
# Регистрация разъемов и подключений
|
# Регистрация разъемов и подключений
|
||||||
# ======================
|
# ======================
|
||||||
|
|
||||||
@admin.register(CableConnectionType)
|
@admin.register(CableConnectionType)
|
||||||
class CableConnectionTypeAdmin(admin.ModelAdmin):
|
class CableConnectionTypeAdmin(admin.ModelAdmin):
|
||||||
list_display = ('name', 'is_detachable')
|
list_display = ('name', 'is_detachable')
|
||||||
|
|
@ -464,6 +480,7 @@ class ChargingInterfaceAdmin(admin.ModelAdmin):
|
||||||
# Регистрация отзывов и медиа
|
# Регистрация отзывов и медиа
|
||||||
# ======================
|
# ======================
|
||||||
|
|
||||||
|
|
||||||
@admin.register(HeadphonesReviewType)
|
@admin.register(HeadphonesReviewType)
|
||||||
class HeadphonesReviewTypeAdmin(admin.ModelAdmin):
|
class HeadphonesReviewTypeAdmin(admin.ModelAdmin):
|
||||||
list_display = ('name', 'slug')
|
list_display = ('name', 'slug')
|
||||||
|
|
|
||||||
|
|
@ -24,7 +24,10 @@ def upload_to_case_image(instance, filename):
|
||||||
return f"headphones/{instance.headphones.id}/case_images/{instance.slug}.{ext}"
|
return f"headphones/{instance.headphones.id}/case_images/{instance.slug}.{ext}"
|
||||||
|
|
||||||
def upload_to_frequency_response(instance, filename):
|
def upload_to_frequency_response(instance, filename):
|
||||||
return f"headphones/{instance.headphones.id}/frequency_response/{filename}"
|
"""Путь для сохранения: headphones/<headphones_id>/frequency_response/<filename>"""
|
||||||
|
ext = filename.split('.')[-1]
|
||||||
|
unique_filename = f"{uuid.uuid4().hex}.{ext}"
|
||||||
|
return f"headphones/{instance.headphones.id}/frequency_response/{unique_filename}"
|
||||||
|
|
||||||
# ======================
|
# ======================
|
||||||
# Базовые справочники
|
# Базовые справочники
|
||||||
|
|
@ -639,6 +642,67 @@ class HeadphonesCaseIP(models.Model):
|
||||||
return self.code
|
return self.code
|
||||||
|
|
||||||
|
|
||||||
|
class FrequencyResponse(models.Model):
|
||||||
|
headphones = models.ForeignKey(
|
||||||
|
'Headphones',
|
||||||
|
on_delete=models.CASCADE,
|
||||||
|
related_name='frequency_responses',
|
||||||
|
verbose_name="Наушники"
|
||||||
|
)
|
||||||
|
file = models.FileField(
|
||||||
|
upload_to=upload_to_frequency_response,
|
||||||
|
verbose_name="Файл АЧХ"
|
||||||
|
)
|
||||||
|
author = models.CharField(
|
||||||
|
max_length=100,
|
||||||
|
verbose_name="Автор измерения",
|
||||||
|
blank=True,
|
||||||
|
null=True
|
||||||
|
)
|
||||||
|
source_link = models.URLField(
|
||||||
|
verbose_name="Ссылка на источник",
|
||||||
|
blank=True,
|
||||||
|
null=True
|
||||||
|
)
|
||||||
|
notes = models.TextField(
|
||||||
|
verbose_name="Примечания",
|
||||||
|
blank=True,
|
||||||
|
null=True
|
||||||
|
)
|
||||||
|
is_primary = models.BooleanField(
|
||||||
|
default=False,
|
||||||
|
verbose_name="Основная АЧХ"
|
||||||
|
)
|
||||||
|
|
||||||
|
class Meta:
|
||||||
|
verbose_name = "АЧХ наушников"
|
||||||
|
verbose_name_plural = "АЧХ наушников"
|
||||||
|
ordering = ['-is_primary', 'headphones__model']
|
||||||
|
|
||||||
|
def __str__(self):
|
||||||
|
return f"АЧХ {self.headphones.model} ({self.author or 'без автора'})"
|
||||||
|
|
||||||
|
def save(self, *args, **kwargs):
|
||||||
|
if self.is_primary:
|
||||||
|
# Снимаем флаг is_primary у других АЧХ этих наушников
|
||||||
|
FrequencyResponse.objects.filter(
|
||||||
|
headphones=self.headphones
|
||||||
|
).exclude(
|
||||||
|
pk=self.pk
|
||||||
|
).update(is_primary=False)
|
||||||
|
super().save(*args, **kwargs)
|
||||||
|
|
||||||
|
def delete(self, *args, **kwargs):
|
||||||
|
"""Удаление файла при удалении записи"""
|
||||||
|
if self.file:
|
||||||
|
try:
|
||||||
|
if default_storage.exists(self.file.name):
|
||||||
|
default_storage.delete(self.file.name)
|
||||||
|
except Exception as e:
|
||||||
|
print(f"Ошибка при удалении файла АЧХ: {e}")
|
||||||
|
super().delete(*args, **kwargs)
|
||||||
|
|
||||||
|
|
||||||
# ======================
|
# ======================
|
||||||
# Основная модель наушников
|
# Основная модель наушников
|
||||||
# ======================
|
# ======================
|
||||||
|
|
@ -711,13 +775,13 @@ class Headphones(models.Model):
|
||||||
)
|
)
|
||||||
|
|
||||||
# Технические характеристики
|
# Технические характеристики
|
||||||
frequency_response_chart = models.FileField(
|
# frequency_response_chart = models.FileField(
|
||||||
verbose_name="АЧХ (CSV)",
|
# verbose_name="АЧХ (CSV)",
|
||||||
upload_to = upload_to_frequency_response,
|
# upload_to = upload_to_frequency_response,
|
||||||
null=True,
|
# null=True,
|
||||||
blank=True,
|
# blank=True,
|
||||||
help_text="Данные в формате [{'frequency': 20, 'amplitude': -2.5}, ...]"
|
# help_text="Данные в формате [{'frequency': 20, 'amplitude': -2.5}, ...]"
|
||||||
)
|
# )
|
||||||
|
|
||||||
frequency_response_chart_author = models.CharField(
|
frequency_response_chart_author = models.CharField(
|
||||||
verbose_name="Автор АЧХ",
|
verbose_name="Автор АЧХ",
|
||||||
|
|
|
||||||
Loading…
Add table
Reference in a new issue