Переделал модель Parameter и связь с ObjItem
This commit is contained in:
@@ -1,30 +1,24 @@
|
||||
# Standard library imports
|
||||
from collections import defaultdict
|
||||
from datetime import datetime
|
||||
from io import BytesIO
|
||||
|
||||
# Django imports
|
||||
from django.contrib import messages
|
||||
from django.contrib.admin.views.decorators import staff_member_required
|
||||
from django.contrib.auth import logout
|
||||
from django.contrib.auth.decorators import login_required
|
||||
from django.contrib.auth.mixins import LoginRequiredMixin, UserPassesTestMixin
|
||||
from django.contrib.gis.geos import Point
|
||||
from django.contrib.auth.mixins import LoginRequiredMixin
|
||||
from django.core.paginator import Paginator
|
||||
from django.db import models
|
||||
from django.db.models import OuterRef, Prefetch, Subquery
|
||||
from django.forms import inlineformset_factory, modelformset_factory
|
||||
from django.db.models import F
|
||||
from django.http import HttpResponse, JsonResponse
|
||||
from django.shortcuts import redirect, render
|
||||
from django.urls import reverse_lazy
|
||||
from django.utils.decorators import method_decorator
|
||||
from django.views import View
|
||||
from django.views.decorators.http import require_GET
|
||||
from django.views.generic import (
|
||||
CreateView,
|
||||
DeleteView,
|
||||
FormView,
|
||||
TemplateView,
|
||||
UpdateView,
|
||||
)
|
||||
|
||||
@@ -45,18 +39,17 @@ from .forms import (
|
||||
VchLinkForm,
|
||||
)
|
||||
from .mixins import CoordinateProcessingMixin, FormMessageMixin, RoleRequiredMixin
|
||||
from .models import Geo, Modulation, ObjItem, Parameter, Polarization, Satellite
|
||||
from .models import Geo, Modulation, ObjItem, Polarization, Satellite
|
||||
from .utils import (
|
||||
add_satellite_list,
|
||||
compare_and_link_vch_load,
|
||||
fill_data_from_df,
|
||||
get_first_param_subquery,
|
||||
get_points_from_csv,
|
||||
get_vch_load_from_html,
|
||||
kub_report,
|
||||
parse_pagination_params,
|
||||
)
|
||||
from mapsapp.utils import parse_transponders_from_json, parse_transponders_from_xml
|
||||
from mapsapp.utils import parse_transponders_from_xml
|
||||
|
||||
|
||||
class AddSatellitesView(LoginRequiredMixin, View):
|
||||
@@ -150,9 +143,12 @@ class LoadExcelDataView(LoginRequiredMixin, FormMessageMixin, FormView):
|
||||
class GetLocationsView(LoginRequiredMixin, View):
|
||||
def get(self, request, sat_id):
|
||||
locations = (
|
||||
ObjItem.objects.filter(parameters_obj__id_satellite=sat_id)
|
||||
.select_related("geo_obj")
|
||||
.prefetch_related("parameters_obj__polarization")
|
||||
ObjItem.objects.filter(parameter_obj__id_satellite=sat_id)
|
||||
.select_related(
|
||||
"geo_obj",
|
||||
"parameter_obj",
|
||||
"parameter_obj__polarization",
|
||||
)
|
||||
)
|
||||
|
||||
if not locations.exists():
|
||||
@@ -163,11 +159,10 @@ class GetLocationsView(LoginRequiredMixin, View):
|
||||
if not hasattr(loc, "geo_obj") or not loc.geo_obj or not loc.geo_obj.coords:
|
||||
continue
|
||||
|
||||
params = list(loc.parameters_obj.all())
|
||||
if not params:
|
||||
param = getattr(loc, 'parameter_obj', None)
|
||||
if not param:
|
||||
continue
|
||||
|
||||
param = params[0]
|
||||
features.append(
|
||||
{
|
||||
"type": "Feature",
|
||||
@@ -220,11 +215,12 @@ class ShowMapView(RoleRequiredMixin, View):
|
||||
points = []
|
||||
if ids:
|
||||
id_list = [int(x) for x in ids.split(",") if x.isdigit()]
|
||||
locations = ObjItem.objects.filter(id__in=id_list).prefetch_related(
|
||||
"parameters_obj__id_satellite",
|
||||
"parameters_obj__polarization",
|
||||
"parameters_obj__modulation",
|
||||
"parameters_obj__standard",
|
||||
locations = ObjItem.objects.filter(id__in=id_list).select_related(
|
||||
"parameter_obj",
|
||||
"parameter_obj__id_satellite",
|
||||
"parameter_obj__polarization",
|
||||
"parameter_obj__modulation",
|
||||
"parameter_obj__standard",
|
||||
"geo_obj",
|
||||
)
|
||||
for obj in locations:
|
||||
@@ -234,7 +230,9 @@ class ShowMapView(RoleRequiredMixin, View):
|
||||
or not obj.geo_obj.coords
|
||||
):
|
||||
continue
|
||||
param = obj.parameters_obj.get()
|
||||
param = getattr(obj, 'parameter_obj', None)
|
||||
if not param:
|
||||
continue
|
||||
points.append(
|
||||
{
|
||||
"name": f"{obj.name}",
|
||||
@@ -265,11 +263,12 @@ class ShowSelectedObjectsMapView(LoginRequiredMixin, View):
|
||||
points = []
|
||||
if ids:
|
||||
id_list = [int(x) for x in ids.split(",") if x.isdigit()]
|
||||
locations = ObjItem.objects.filter(id__in=id_list).prefetch_related(
|
||||
"parameters_obj__id_satellite",
|
||||
"parameters_obj__polarization",
|
||||
"parameters_obj__modulation",
|
||||
"parameters_obj__standard",
|
||||
locations = ObjItem.objects.filter(id__in=id_list).select_related(
|
||||
"parameter_obj",
|
||||
"parameter_obj__id_satellite",
|
||||
"parameter_obj__polarization",
|
||||
"parameter_obj__modulation",
|
||||
"parameter_obj__standard",
|
||||
"geo_obj",
|
||||
)
|
||||
for obj in locations:
|
||||
@@ -279,7 +278,9 @@ class ShowSelectedObjectsMapView(LoginRequiredMixin, View):
|
||||
or not obj.geo_obj.coords
|
||||
):
|
||||
continue
|
||||
param = obj.parameters_obj.get()
|
||||
param = getattr(obj, 'parameter_obj', None)
|
||||
if not param:
|
||||
continue
|
||||
points.append(
|
||||
{
|
||||
"name": f"{obj.name}",
|
||||
@@ -429,7 +430,7 @@ class DeleteSelectedObjectsView(RoleRequiredMixin, View):
|
||||
class ObjItemListView(LoginRequiredMixin, View):
|
||||
def get(self, request):
|
||||
satellites = (
|
||||
Satellite.objects.filter(parameters__objitems__isnull=False)
|
||||
Satellite.objects.filter(parameters__objitem__isnull=False)
|
||||
.distinct()
|
||||
.only("id", "name")
|
||||
.order_by("name")
|
||||
@@ -472,32 +473,31 @@ class ObjItemListView(LoginRequiredMixin, View):
|
||||
"geo_obj",
|
||||
"updated_by__user",
|
||||
"created_by__user",
|
||||
"parameter_obj",
|
||||
"parameter_obj__id_satellite",
|
||||
"parameter_obj__polarization",
|
||||
"parameter_obj__modulation",
|
||||
"parameter_obj__standard",
|
||||
)
|
||||
.prefetch_related(
|
||||
"parameters_obj__id_satellite",
|
||||
"parameters_obj__polarization",
|
||||
"parameters_obj__modulation",
|
||||
"parameters_obj__standard",
|
||||
)
|
||||
.filter(parameters_obj__id_satellite_id__in=selected_satellites)
|
||||
.filter(parameter_obj__id_satellite_id__in=selected_satellites)
|
||||
)
|
||||
else:
|
||||
objects = ObjItem.objects.select_related(
|
||||
"geo_obj",
|
||||
"updated_by__user",
|
||||
"created_by__user",
|
||||
).prefetch_related(
|
||||
"parameters_obj__id_satellite",
|
||||
"parameters_obj__polarization",
|
||||
"parameters_obj__modulation",
|
||||
"parameters_obj__standard",
|
||||
"parameter_obj",
|
||||
"parameter_obj__id_satellite",
|
||||
"parameter_obj__polarization",
|
||||
"parameter_obj__modulation",
|
||||
"parameter_obj__standard",
|
||||
)
|
||||
|
||||
if freq_min is not None and freq_min.strip() != "":
|
||||
try:
|
||||
freq_min_val = float(freq_min)
|
||||
objects = objects.filter(
|
||||
parameters_obj__frequency__gte=freq_min_val
|
||||
parameter_obj__frequency__gte=freq_min_val
|
||||
)
|
||||
except ValueError:
|
||||
pass
|
||||
@@ -505,7 +505,7 @@ class ObjItemListView(LoginRequiredMixin, View):
|
||||
try:
|
||||
freq_max_val = float(freq_max)
|
||||
objects = objects.filter(
|
||||
parameters_obj__frequency__lte=freq_max_val
|
||||
parameter_obj__frequency__lte=freq_max_val
|
||||
)
|
||||
except ValueError:
|
||||
pass
|
||||
@@ -514,7 +514,7 @@ class ObjItemListView(LoginRequiredMixin, View):
|
||||
try:
|
||||
range_min_val = float(range_min)
|
||||
objects = objects.filter(
|
||||
parameters_obj__freq_range__gte=range_min_val
|
||||
parameter_obj__freq_range__gte=range_min_val
|
||||
)
|
||||
except ValueError:
|
||||
pass
|
||||
@@ -522,7 +522,7 @@ class ObjItemListView(LoginRequiredMixin, View):
|
||||
try:
|
||||
range_max_val = float(range_max)
|
||||
objects = objects.filter(
|
||||
parameters_obj__freq_range__lte=range_max_val
|
||||
parameter_obj__freq_range__lte=range_max_val
|
||||
)
|
||||
except ValueError:
|
||||
pass
|
||||
@@ -530,13 +530,13 @@ class ObjItemListView(LoginRequiredMixin, View):
|
||||
if snr_min is not None and snr_min.strip() != "":
|
||||
try:
|
||||
snr_min_val = float(snr_min)
|
||||
objects = objects.filter(parameters_obj__snr__gte=snr_min_val)
|
||||
objects = objects.filter(parameter_obj__snr__gte=snr_min_val)
|
||||
except ValueError:
|
||||
pass
|
||||
if snr_max is not None and snr_max.strip() != "":
|
||||
try:
|
||||
snr_max_val = float(snr_max)
|
||||
objects = objects.filter(parameters_obj__snr__lte=snr_max_val)
|
||||
objects = objects.filter(parameter_obj__snr__lte=snr_max_val)
|
||||
except ValueError:
|
||||
pass
|
||||
|
||||
@@ -544,7 +544,7 @@ class ObjItemListView(LoginRequiredMixin, View):
|
||||
try:
|
||||
bod_min_val = float(bod_min)
|
||||
objects = objects.filter(
|
||||
parameters_obj__bod_velocity__gte=bod_min_val
|
||||
parameter_obj__bod_velocity__gte=bod_min_val
|
||||
)
|
||||
except ValueError:
|
||||
pass
|
||||
@@ -552,19 +552,19 @@ class ObjItemListView(LoginRequiredMixin, View):
|
||||
try:
|
||||
bod_max_val = float(bod_max)
|
||||
objects = objects.filter(
|
||||
parameters_obj__bod_velocity__lte=bod_max_val
|
||||
parameter_obj__bod_velocity__lte=bod_max_val
|
||||
)
|
||||
except ValueError:
|
||||
pass
|
||||
|
||||
if selected_modulations:
|
||||
objects = objects.filter(
|
||||
parameters_obj__modulation__id__in=selected_modulations
|
||||
parameter_obj__modulation__id__in=selected_modulations
|
||||
)
|
||||
|
||||
if selected_polarizations:
|
||||
objects = objects.filter(
|
||||
parameters_obj__polarization__id__in=selected_polarizations
|
||||
parameter_obj__polarization__id__in=selected_polarizations
|
||||
)
|
||||
|
||||
if has_kupsat == "1":
|
||||
@@ -609,22 +609,14 @@ class ObjItemListView(LoginRequiredMixin, View):
|
||||
else:
|
||||
selected_sat_id = None
|
||||
|
||||
first_param_freq_subq = get_first_param_subquery("frequency")
|
||||
first_param_range_subq = get_first_param_subquery("freq_range")
|
||||
first_param_snr_subq = get_first_param_subquery("snr")
|
||||
first_param_bod_subq = get_first_param_subquery("bod_velocity")
|
||||
first_param_sat_name_subq = get_first_param_subquery("id_satellite__name")
|
||||
first_param_pol_name_subq = get_first_param_subquery("polarization__name")
|
||||
first_param_mod_name_subq = get_first_param_subquery("modulation__name")
|
||||
|
||||
objects = objects.annotate(
|
||||
first_param_freq=Subquery(first_param_freq_subq),
|
||||
first_param_range=Subquery(first_param_range_subq),
|
||||
first_param_snr=Subquery(first_param_snr_subq),
|
||||
first_param_bod=Subquery(first_param_bod_subq),
|
||||
first_param_sat_name=Subquery(first_param_sat_name_subq),
|
||||
first_param_pol_name=Subquery(first_param_pol_name_subq),
|
||||
first_param_mod_name=Subquery(first_param_mod_name_subq),
|
||||
first_param_freq=F("parameter_obj__frequency"),
|
||||
first_param_range=F("parameter_obj__freq_range"),
|
||||
first_param_snr=F("parameter_obj__snr"),
|
||||
first_param_bod=F("parameter_obj__bod_velocity"),
|
||||
first_param_sat_name=F("parameter_obj__id_satellite__name"),
|
||||
first_param_pol_name=F("parameter_obj__polarization__name"),
|
||||
first_param_mod_name=F("parameter_obj__modulation__name"),
|
||||
)
|
||||
|
||||
valid_sort_fields = {
|
||||
@@ -664,11 +656,7 @@ class ObjItemListView(LoginRequiredMixin, View):
|
||||
|
||||
processed_objects = []
|
||||
for obj in page_obj:
|
||||
param = None
|
||||
if hasattr(obj, "parameters_obj") and obj.parameters_obj.all():
|
||||
param_list = list(obj.parameters_obj.all())
|
||||
if param_list:
|
||||
param = param_list[0]
|
||||
param = getattr(obj, 'parameter_obj', None)
|
||||
|
||||
geo_coords = "-"
|
||||
geo_timestamp = "-"
|
||||
@@ -874,40 +862,33 @@ class ObjItemFormView(
|
||||
# Сохраняем параметры возврата для кнопки "Назад"
|
||||
context["return_params"] = self.request.GET.get('return_params', '')
|
||||
|
||||
ParameterFormSet = modelformset_factory(
|
||||
Parameter,
|
||||
form=ParameterForm,
|
||||
extra=self.get_parameter_formset_extra(),
|
||||
can_delete=True,
|
||||
)
|
||||
|
||||
if self.object:
|
||||
parameter_queryset = self.object.parameters_obj.all()
|
||||
context["parameter_forms"] = ParameterFormSet(
|
||||
queryset=parameter_queryset, prefix="parameters"
|
||||
# Работаем с одной формой параметра вместо formset
|
||||
if self.object and hasattr(self.object, "parameter_obj") and self.object.parameter_obj:
|
||||
context["parameter_form"] = ParameterForm(
|
||||
instance=self.object.parameter_obj, prefix="parameter"
|
||||
)
|
||||
|
||||
if hasattr(self.object, "geo_obj"):
|
||||
context["geo_form"] = GeoForm(
|
||||
instance=self.object.geo_obj, prefix="geo"
|
||||
)
|
||||
else:
|
||||
context["geo_form"] = GeoForm(prefix="geo")
|
||||
else:
|
||||
context["parameter_forms"] = ParameterFormSet(
|
||||
queryset=Parameter.objects.none(), prefix="parameters"
|
||||
context["parameter_form"] = ParameterForm(prefix="parameter")
|
||||
|
||||
if self.object and hasattr(self.object, "geo_obj") and self.object.geo_obj:
|
||||
context["geo_form"] = GeoForm(
|
||||
instance=self.object.geo_obj, prefix="geo"
|
||||
)
|
||||
else:
|
||||
context["geo_form"] = GeoForm(prefix="geo")
|
||||
|
||||
return context
|
||||
|
||||
def get_parameter_formset_extra(self):
|
||||
"""Возвращает количество дополнительных форм для параметров."""
|
||||
return 0 if self.object else 1
|
||||
|
||||
def form_valid(self, form):
|
||||
context = self.get_context_data()
|
||||
parameter_forms = context["parameter_forms"]
|
||||
# Получаем форму параметра
|
||||
if self.object and hasattr(self.object, "parameter_obj") and self.object.parameter_obj:
|
||||
parameter_form = ParameterForm(
|
||||
self.request.POST,
|
||||
instance=self.object.parameter_obj,
|
||||
prefix="parameter"
|
||||
)
|
||||
else:
|
||||
parameter_form = ParameterForm(self.request.POST, prefix="parameter")
|
||||
|
||||
if self.object and hasattr(self.object, "geo_obj") and self.object.geo_obj:
|
||||
geo_form = GeoForm(self.request.POST, instance=self.object.geo_obj, prefix="geo")
|
||||
@@ -919,17 +900,26 @@ class ObjItemFormView(
|
||||
self.set_user_fields()
|
||||
self.object.save()
|
||||
|
||||
# Сохраняем связанные параметры
|
||||
if parameter_forms.is_valid():
|
||||
self.save_parameters(parameter_forms)
|
||||
# Сохраняем связанный параметр
|
||||
if parameter_form.is_valid():
|
||||
self.save_parameter(parameter_form)
|
||||
else:
|
||||
context = self.get_context_data()
|
||||
context.update({
|
||||
'form': form,
|
||||
'parameter_form': parameter_form,
|
||||
'geo_form': geo_form,
|
||||
})
|
||||
return self.render_to_response(context)
|
||||
|
||||
# Сохраняем геоданные
|
||||
if geo_form.is_valid():
|
||||
self.save_geo_data(geo_form)
|
||||
else:
|
||||
context = self.get_context_data()
|
||||
context.update({
|
||||
'form': form,
|
||||
'parameter_forms': parameter_forms,
|
||||
'parameter_form': parameter_form,
|
||||
'geo_form': geo_form,
|
||||
})
|
||||
return self.render_to_response(context)
|
||||
@@ -940,51 +930,12 @@ class ObjItemFormView(
|
||||
"""Устанавливает поля пользователя для объекта."""
|
||||
raise NotImplementedError("Subclasses must implement set_user_fields()")
|
||||
|
||||
def save_parameters(self, parameter_forms):
|
||||
"""Сохраняет параметры объекта с проверкой дубликатов."""
|
||||
instances = parameter_forms.save(commit=False)
|
||||
|
||||
# Обрабатываем удаленные параметры
|
||||
for deleted_obj in parameter_forms.deleted_objects:
|
||||
# Отвязываем параметр от объекта
|
||||
deleted_obj.objitems.remove(self.object)
|
||||
# Если параметр больше не связан ни с одним объектом, удаляем его
|
||||
if not deleted_obj.objitems.exists():
|
||||
deleted_obj.delete()
|
||||
|
||||
for instance in instances:
|
||||
# Проверяем, существует ли уже такая ВЧ загрузка
|
||||
existing_param = Parameter.objects.filter(
|
||||
id_satellite=instance.id_satellite,
|
||||
polarization=instance.polarization,
|
||||
frequency=instance.frequency,
|
||||
freq_range=instance.freq_range,
|
||||
bod_velocity=instance.bod_velocity,
|
||||
modulation=instance.modulation,
|
||||
snr=instance.snr,
|
||||
standard=instance.standard,
|
||||
).exclude(pk=instance.pk if instance.pk else None).first()
|
||||
|
||||
if existing_param:
|
||||
# Если найден дубликат, удаляем старую запись из объекта
|
||||
if instance.pk:
|
||||
# Отвязываем старый параметр от объекта
|
||||
instance.objitems.remove(self.object)
|
||||
# Если старый параметр больше не связан ни с одним объектом, удаляем его
|
||||
if not instance.objitems.exists():
|
||||
instance.delete()
|
||||
# Используем существующий параметр
|
||||
self.link_parameter_to_object(existing_param)
|
||||
else:
|
||||
# Сохраняем новый параметр
|
||||
instance.save()
|
||||
self.link_parameter_to_object(instance)
|
||||
|
||||
def link_parameter_to_object(self, parameter):
|
||||
"""Связывает параметр с объектом."""
|
||||
raise NotImplementedError(
|
||||
"Subclasses must implement link_parameter_to_object()"
|
||||
)
|
||||
def save_parameter(self, parameter_form):
|
||||
"""Сохраняет параметр объекта через OneToOne связь."""
|
||||
if parameter_form.is_valid():
|
||||
instance = parameter_form.save(commit=False)
|
||||
instance.objitem = self.object
|
||||
instance.save()
|
||||
|
||||
def save_geo_data(self, geo_form):
|
||||
"""Сохраняет геоданные объекта."""
|
||||
@@ -1019,11 +970,6 @@ class ObjItemUpdateView(ObjItemFormView):
|
||||
def set_user_fields(self):
|
||||
self.object.updated_by = self.request.user.customuser
|
||||
|
||||
def link_parameter_to_object(self, parameter):
|
||||
# Добавляем объект к параметру, если его там еще нет
|
||||
if self.object not in parameter.objitems.all():
|
||||
parameter.objitems.add(self.object)
|
||||
|
||||
|
||||
class ObjItemCreateView(ObjItemFormView, CreateView):
|
||||
"""Представление для создания ObjItem."""
|
||||
@@ -1034,9 +980,6 @@ class ObjItemCreateView(ObjItemFormView, CreateView):
|
||||
self.object.created_by = self.request.user.customuser
|
||||
self.object.updated_by = self.request.user.customuser
|
||||
|
||||
def link_parameter_to_object(self, parameter):
|
||||
parameter.objitems.add(self.object)
|
||||
|
||||
|
||||
class ObjItemDeleteView(RoleRequiredMixin, FormMessageMixin, DeleteView):
|
||||
model = ObjItem
|
||||
@@ -1066,11 +1009,11 @@ class ObjItemDetailView(LoginRequiredMixin, View):
|
||||
'geo_obj',
|
||||
'updated_by__user',
|
||||
'created_by__user',
|
||||
).prefetch_related(
|
||||
'parameters_obj__id_satellite',
|
||||
'parameters_obj__polarization',
|
||||
'parameters_obj__modulation',
|
||||
'parameters_obj__standard',
|
||||
'parameter_obj',
|
||||
'parameter_obj__id_satellite',
|
||||
'parameter_obj__polarization',
|
||||
'parameter_obj__modulation',
|
||||
'parameter_obj__standard',
|
||||
).first()
|
||||
|
||||
if not obj:
|
||||
|
||||
Reference in New Issue
Block a user