Вопрос или проблема
В моем проекте Django есть следующий код:
@login_required
def editar_acumulado(request, estudiante_id):
estudiante = Estudiante.objects.get(id=estudiante_id)
grupo_clase = estudiante.matriculas.first().grupoclase
asignatura = grupo_clase.asignatura # Получить предмет, зарегистрированный в классе студента
try:
acumulado = Acumulado.objects.get(estudiante=estudiante, asignatura=asignatura)
except Acumulado.DoesNotExist:
messages.warning(request, 'Вы должны зарегистрировать накопленные баллы в "0" для этого студента, прежде чем регистрировать его накопленные баллы.')
return redirect('RegistroCalificaciones', grupo_clase_id=grupo_clase.id)
if request.method == 'POST':
form = EditAcumuladoForm(request.POST, instance=acumulado)
if form.is_valid():
form.save()
messages.success(request, 'Накопленные баллы успешно обновлены')
return redirect('RegistroCalificaciones', grupo_clase_id=grupo_clase.id)
else:
form = EditAcumuladoForm(instance=acumulado)
return render(request, 'PanelDocentes/PortalNotas.html', {'form': form, 'estudiante': estudiante})
также оставлю здесь код своей формы:
class EditAcumuladoForm(forms.ModelForm):
class Meta:
model = Acumulado
fields = ('parcial', 'asignatura', 'semestre', 'AnioEscol', 'Nota1', 'Nota2', 'Nota3', 'Nota4', 'Nota5', 'Examen')
def __init__(self, *args, **kwargs):
estudiante = kwargs.pop('estudiante', None)
super().__init__(*args, **kwargs)
if estudiante:
matricula_grupo = estudiante.matriculas.first()
grupo_clase = matricula_grupo.grupoclase
self.fields['asignatura'].queryset = Asignatura.objects.filter(id=grupo_clase.asignatura.id)
self.fields['parcial'].widget.attrs['class'] = 'form-control'
self._set_attributes('Nota1', 'Nota2', 'Nota3', 'Nota4', 'Nota5', min_value=0, max_value=20)
self._set_attributes('Examen', min_value=0, max_value=40)
self.fields['semestre'] = forms.ModelChoiceField(queryset=Semestre.objects.all(), widget=forms.Select(attrs={'class': 'form-control'}))
self.fields['AnioEscol'] = forms.ModelChoiceField(queryset=AnioEscolar.objects.all(), widget=forms.Select(attrs={'class': 'form-control'}))
self.fields['suma_notas'] = forms.CharField(label="Итоговые накопленные баллы", widget=forms.TextInput(attrs={'readonly': 'readonly', 'class': 'form-control'}))
self.fields['suma_total'] = forms.CharField(label="Итоговая оценка", widget=forms.TextInput(attrs={'readonly': 'readonly', 'class': 'form-control'}))
instance = kwargs.get('instance')
if instance:
Nota1 = instance.Nota1 if instance.Nota1 is not None else 0
Nota2 = instance.Nota2 if instance.Nota2 is not None else 0
Nota3 = instance.Nota3 if instance.Nota3 is not None else 0
Nota4 = instance.Nota4 if instance.Nota4 is not None else 0
Nota5 = instance.Nota5 if instance.Nota5 is not None else 0
Examen = instance.Examen if instance.Examen is not None else 0
suma_notas = Nota1 + Nota2 + Nota3 + Nota4 + Nota5
suma_total = suma_notas + Examen
self.fields['suma_notas'] = forms.CharField(label="Итоговые накопленные баллы", initial=suma_notas, widget=forms.TextInput(attrs={'readonly': 'readonly', 'class':'form-control'}))
self.fields['suma_total'] = forms.CharField(label="Итоговая оценка", initial=suma_total, widget=forms.TextInput(attrs={'readonly': 'readonly', 'class':'form-control'}))
if suma_total == 60:
for field_name in ['Nota1', 'Nota2', 'Nota3', 'Nota4', 'Nota5']:
field = self.fields[field_name]
if field.initial is None:
field.widget.attrs['disabled'] = True
field.widget.attrs['class'] += ' disabled'
def _set_attributes(self, *fields, min_value=None, max_value=None):
for field in fields:
self.fields[field].widget.attrs['class'] = 'form-control'
if min_value is not None:
self.fields[field].widget.attrs['min'] = min_value
if max_value is not None:
self.fields[field].widget.attrs['max'] = max_value
models.py:
from django.db import models
from django.contrib.auth.models import AbstractUser
from datetime import date
#=======================ТАБЛИЦА СТАТУСА ПОЛЬЗОВАТЕЛЯ=======================
class Estado(models.Model):
estado = [
('Activo','Activo'),
('Inactivo', 'Inactivo'),
]
Estados = models.CharField(max_length=10, choices=estado, blank=True, null=True)
#Показать свойства в админ-панели Django.
def __str__(self):
return f"{self.Estados}"
class Meta:
verbose_name_plural = "Estados"
#=======================ТАБЛИЦА РАЗДЕЛ=======================
class Seccion(models.Model):
secciones = [
('A', 'A'),
('B', 'B'),
('C', 'C'),
('D', 'D'),
]
SeccionNivel = models.CharField(max_length=1, choices=secciones, blank=True, null=True)
#Показать свойства в админ-панели Django.
def __str__(self):
return f"{self.SeccionNivel}"
class Meta:
verbose_name_plural = "Secciones"
#=======================ТАБЛИЦА КЛАССОВ=======================
class Grado(models.Model):
CodigoGrado = models.CharField(max_length=4)
grados = [
('1ro', '1ro'), ('2do', '2do'), ('3ro', '3ro'),
('4to', '4to'), ('5to', '5to'), ('6to', '6to'),
('7mo', '7mo'), ('8vo', '8vo'), ('9no', '9no'),
('10mo','10mo'), ('11mo','11mo')
]
GradoNivel = models.CharField(max_length=4, choices=grados, blank=True, null=True)
seccion = models.ForeignKey(Seccion, on_delete=models.PROTECT, blank=True,null=True)
#Показать свойства в админ-панели Django.
def __str__(self):
return f"{self.CodigoGrado}, {self.GradoNivel}"
class Meta:
verbose_name_plural = "Grados"
#=======================ТАБЛИЦА ПРЕДМЕТОВ===================
class Asignatura(models.Model):
CodigoAsigantura = models.CharField(max_length=6, default="codigo")
nombreAsignatura = [
('Lengua y Literatura', 'Lengua y Literatura'),
('Lengua Extranjera','Lengua Extranjera'),
('Creciendo en Valores','Creciendo en Valores'),
('Conociendo mi Mundo','Conociendo mi Mundo'),
('Derecho y Dignidad de la Mujer','Derecho y Дignidad de la Mujer'),
('Educación Física','Educación Física'),
('Aprender Emprender y Prosperar','Aprender Emprender y Prosperar'),
('Estudios Sociales','Estudios Sociales'),
('Talleres de Arte y Cultura','Talleres de Arte y Cultura'),
('Ciencias Naturales','Ciencias Naturales'),
('Biología','Biología'),
('Matemática','Matemática'),
('Física','Física'),
('Química','Química'),
('Computación','Computación')
]
asignatura = models.CharField(max_length=100, choices=nombreAsignatura, blank=True, null=True)
#Показать свойства в админ-панели Django.
def __str__(self):
return f"{self.CodigoAsigantura}, {self.asignatura}"
class Meta:
verbose_name_plural = "Asignaturas"
#=======================ТАБЛИЦА СТУДЕНТОВ=======================
class Estudiante(models.Model):
Nombres = models.CharField(max_length=50)
Apellidos = models.CharField(max_length=50)
CodigoEstudiantil = models.CharField(max_length=25)
estado = models.ForeignKey(Estado, on_delete=models.PROTECT)
FechaRegistro = models.DateTimeField(auto_now_add=True)
#Показать свойства в админ-панели Django.
def __str__(self):
return f"{self.Nombres} {self.Apellidos}"
class Meta:
verbose_name_plural = "Estudiantes"
#=======================ТАБЛИЦА УЧЕБНОГО ГОДА===================
class AnioEscolar(models.Model):
anio = models.CharField(max_length=5, default=date.today().year)
#Показать свойства в админ-панели Django.
def __str__(self):
return f"{self.anio}"
class Meta:
verbose_name_plural = "Año Escolar"
#=======================ТАБЛИЦА ПАРЦИАЛЬНЫХ ОЦЕНОК=======================
class Parciales(models.Model):
parciales = [
('I Parcial','I Parcial'),
('II Parcial','II Parcial'),
('III Parcial','III Parcial'),
('IV Parcial','IV Parcial')
]
parcial = models.CharField(max_length=20, choices=parciales, blank=True, null=True)
#Показать свойства в админ-панели Django.
def __str__(self):
return f"{self.parcial}"
class Meta:
verbose_name_plural = "Parciales"
class Semestre(models.Model):
CodigoSemestre = models.CharField(max_length=3, default="semestre")
sem = [
('I Semestre','I Semestre'),
('II Semestre','II Semestre')
]
semestreNivel = models.CharField(max_length=15, choices=sem, blank=True, null=True)
parcial = models.ForeignKey(Parciales, on_delete=models.PROTECT, blank=True,null=True)
#Показать свойства в админ-панели Django.
def __str__(self):
return f"{self.CodigoSemestre}, {self.semestreNivel}"
class Meta:
verbose_name_plural = "Semestres"
#=======================ТАБЛИЦА НАКОПЛЕННЫХ ОЦЕНОК=======================
class Acumulado(models.Model):
Nota1 = models.IntegerField(blank=True, null=True)
Nota2 = models.IntegerField(blank=True, null=True)
Nota3 = models.IntegerField(blank=True, null=True)
Nota4 = models.IntegerField(blank=True, null=True)
Nota5 = models.IntegerField(blank=True, null=True)
Examen = models.IntegerField(blank=True, null=True)
actividad = models.TextField()
asignatura = models.ForeignKey(Asignatura, on_delete=models.PROTECT)
estudiante = models.ForeignKey(Estudiante, on_delete=models.PROTECT)
parcial = models.ForeignKey(Parciales, on_delete=models.PROTECT)
semestre = models.ForeignKey(Semestre, on_delete=models.PROTECT)
AnioEscol = models.ForeignKey(AnioEscolar, on_delete=models.PROTECT, default="")
#Показать свойства в админ-панели Django.
def __str__(self):
return f"{self.estudiante}, {self.Nota1}, {self.Nota2}, {self.Nota3}, {self.Nota4}, {self.Nota5}, {self.Examen}, {self.asignatura.CodigoAsigantura}, {self.semestre.CodigoSemestre}, {self.parcial.parcial}"
class Meta:
verbose_name_plural = "Acumulados"
#=======================ТАБЛИЦА ОЦЕНОК=======================
class Nota(models.Model):
NotaCualitativa = models.CharField(max_length=2)
NotaCuantitativa = models.DecimalField(max_digits=5, decimal_places=2)
Fecha = models.DateTimeField(auto_now_add=True)
asignatura = models.ForeignKey(Asignatura, on_delete=models.PROTECT)
estudiante = models.ForeignKey(Estudiante, on_delete=models.PROTECT)
acumulado = models.ForeignKey(Acumulado, on_delete=models.PROTECT)
parcial = models.ForeignKey(Parciales, on_delete=models.PROTECT)
semestre = models.ForeignKey(Semestre, on_delete=models.PROTECT)
class Meta:
verbose_name_plural = "Notas"
#=======================ТАБЛИЦА ПОЛЬЗОВАТЕЛЕЙ=======================
class User(AbstractUser):
CodigoUser = models.CharField(max_length=15, null=False)
rol = [
('Administrador','Administrador'),
('Asistente','Asistente'),
('Docente','Docente')
]
Rol = models.CharField(max_length=255, choices=rol, blank=True, null=True)
#Показать свойства в админ-панели Django.
def __str__(self):
return f"{self.first_name} {self.last_name}, {self.CodigoUser}"
class Meta:
verbose_name_plural = "Usuarios"
#=======================ТАБЛИЦА ГРУППЫ КЛАССОВ=======================
class GrupoClase(models.Model):
CodigoGrupo = models.CharField(max_length=20,blank=True, null=True)
NombreGrupo = models.CharField(max_length=50)
FechaInicio = models.DateField(null=False)
FechaFin = models.DateField(blank=True, null=True)
grado = models.ForeignKey(Grado, on_delete=models.PROTECT)
seccion = models.ForeignKey(Seccion, on_delete=models.PROTECT)
docente = models.ForeignKey(User, on_delete=models.PROTECT)
asignatura = models.ForeignKey(Asignatura, on_delete=models.PROTECT)
anio = models.ForeignKey(AnioEscolar, on_delete=models.PROTECT)
estado = models.ForeignKey(Estado, on_delete=models.PROTECT)
semestre = models.ForeignKey(Semestre, on_delete=models.PROTECT)
#Показать свойства в админ-панели Django.
def __str__(self):
return f"{self.NombreGrupo}, {self.docente}, {self.asignatura}"
class Meta:
verbose_name_plural = "Grupos De Clases"
def generate_codigo_grupo(self):
return f"{self.grado.CodigoGrado}{self.seccion}-{self.asignatura.CodigoAsigantura}-{self.semestre.CodigoSemestre}{self.anio.anio}"
def save(self, *args, **kwargs):
# Всегда генерировать новый код при сохранении
self.CodigoGrupo = self.generate_codigo_grupo()
super().save(*args, **kwargs)
#=======================ТАБЛИЦА ЗАЧЕТ В ГРУППАХ=======================
class MatriculaGrupo(models.Model):
grupoclase = models.ForeignKey(GrupoClase, on_delete=models.PROTECT)
estudiante = models.ForeignKey(Estudiante, on_delete=models.PROTECT, related_name="matriculas")
estado = models.ForeignKey(Estado, on_delete=models.PROTECT)
#Показать свойства в админ-панели Django.
def __str__(self):
return f"{self.grupoclase}, {self.estudiante}"
class Meta:
verbose_name_plural = "Matriculas Grupos"
#=======================ТАБЛИЦА АКАДЕМИЧЕСКИХ БИЛЕТОВ=======================
class BoletaAcademica(models.Model):
CodigoBoleta = models.CharField(max_length=20)
NoBoleta = models.IntegerField()
FechaBoleta = models.DateTimeField()
grado = models.ForeignKey(Grado, on_delete=models.PROTECT)
seccion = models.ForeignKey(Seccion, on_delete=models.PROTECT)
asignatura = models.ForeignKey(Asignatura, on_delete=models.PROTECT)
docente = models.ForeignKey(User, on_delete=models.PROTECT)
nota = models.ForeignKey(Nota, on_delete=models.PROTECT)
estudiante = models.ForeignKey(Estudiante, on_delete=models.PROTECT)
semestre = models.ForeignKey(Semestre, on_delete=models.PROTECT)
anio = models.ForeignKey(AnioEscolar, on_delete=models.PROTECT, default=date.today().year)
class Meta:
verbose_name_plural = "Boletas Académicas"
Это работает правильно, когда студент имеет оценки только по одному предмету в своей группе, но когда студент имеет оценки по разным предметам, я получаю ошибку.
Ошибка, которую я получаю, это MultipleObjectsReturned, которая возникает потому, что запрос Accumulated.objects.get(student=student, subject=subject) возвращает более одного объекта Accumulated. Это происходит потому, что существует несколько объектов Accumulated, которые соответствуют условию наличия одного и того же студента и предмета.
Ответ или решение
Ошибка MultipleObjectsReturned
в Django возникает, когда запрос, ожидающий вернуть один объект, фактически возвращает несколько. В вашем случае это происходит из-за того, что запрос Acumulado.objects.get(estudiante=estudiante, asignatura=asignatura)
возвращает несколько объектов Acumulado
, соответствующих одному и тому же студенту и предмету.
Причины возникновения ошибки
Ваша модель Acumulado
описывает информацию о накопленных оценках для студентов, и у вас может быть несколько записей для одного и того же студента по одной и той же предмету (например, в разных семестрах или на различных уровнях). Это является причиной того, что метод .get()
не может однозначно идентифицировать один объект и вместо этого возвращает несколько записей.
Возможные решения
Для решения этой проблемы вы можете рассмотреть несколько подходов:
-
Использовать
.filter()
вместо.get()
:
Если вам нужно работать со всеми записямиAcumulado
для данного студента и предмета, вы можете использоватьfilter()
:acumulados = Acumulado.objects.filter(estudiante=estudiante, asignatura=asignatura) if not acumulados.exists(): messages.warning(request, 'Должны быть зарегистрированыAccumulated на "0" для этого студента перед регистрацией его накопленных оценок.') return redirect('RegistroCalificaciones', grupo_clase_id=grupo_clase.id)
Далее, вы можете обрабатывать полученные записи в любом нужном вам формате (например, выбрать последнюю запись или работать с несколькими записями).
-
Добавление уникальности в модель:
Если логика вашего приложения подразумевает, что для каждого студента и каждого предмета может быть только одна запись, вы можете рассмотреть возможность добавления уникального ограничения в вашу модельAcumulado
:class Acumulado(models.Model): ... class Meta: unique_together = ('estudiante', 'asignatura', 'semestre') # Уникальность по студенту, предмету и семестру
В результате при попытке создания нового
Acumulado
с одинаковыми значениями полейestudiante
,asignatura
иsemestre
будет возвращаться ошибка. -
Работа с дополнительными параметрами:
Если ваши данные включают различные семестры или другие параметры, вам следует убедиться, что вы также учитываете эти параметры в своем запросе. Например, добавлениеsemestre
в вашу выборку:acumulado = Acumulado.objects.get(estudiante=estudiante, asignatura=asignatura, semestre=semestre)
Это гарантирует, что вы получите уникальный объект на основе всех необходимых полей.
Заключение
Выбор подходящего решения зависит от ответов на следующие вопросы:
- Необходимо ли вам хранить несколько записей для одного студента и предмета?
- Если да, то как вы планируете обрабатывать эти записи в вашей логике приложения?
- Есть ли необходимость в реализации уникальных ограничений для вашей модели?
Обеспечив правильную архитектуру данных, вы сможете избежать подобных ошибок в будущем и обеспечить стабильную работу вашего приложения.