0

So the title states my objective. Here's the code I wrote to achieve it:

#for each cycle in instructional cycles:
for cycle in Instructional_Cycle.objects.all():

    #for each standard in the currently selected cycle:
    for standard in cycle.standards.all():

        #generate random percentage of correct grade-level students to pass that standard, say between 20 and 90%
        percent_pass = randint(20,90)

        #calculate number of total students for that grade-level that percentage represents (percentage times total number of students in that grade)
        total_students_passing = (percent_pass/100) * Student.objects.filter(grade_level = standard.grade_level).count()

        already_selected = []

        #while length of list of already selected students < total needed
        while len(already_selected) < total_students_passing:

            #select a random student out of that grade
            count = Student.objects.filter(grade_level=standard.grade_level).count()
            random_student = Student.objects.all()[randint(0, count - 1)] #single random object

            #if that student isn't in list of already selected students
            if not random_student.id in already_selected:

                #create a passing progress report with the end date of that instructional cycle
                Progress_Report.objects.create(date=cycle.date_finished, student=random_student, standard_mastered=standard, mastery_status=True)

                #add that student to list of already selected students
                already_selected.append(random_student.id)

This ends with the following error:

django.db.utils.IntegrityError: UNIQUE constraint failed: 
student_progress_progress_report.standard_mastered_id

The progress_report table that I'm trying to fill is empty. I can add records to it using the admin interface. So I'm not sure where to look to fix my issue, because I'm not really sure what the problem is. Thanks for looking and any tips that you can provide. -- GH

Here are the models:

from django.db import models
from django.urls import reverse

gradeLevels = ((6,6), (7,7),(8,8),(9,9), (10,10), (11,11), (12,12))
subjects = (('Literacy','Literacy'), ('Math','Math'),
    ('Science','Science'), ('Social Studies','Social Studies'))


class Student(models.Model):

    student_id = models.CharField(max_length=8, unique=True)
    last_name = models.CharField(max_length=100)
    first_name = models.CharField(max_length=100)
    grade_level = models.IntegerField(choices=gradeLevels)
    active_status = models.BooleanField(default=True)

    class Meta:
        ordering = ['grade_level', 'last_name']

    def __str__(self):
        #Return a string representation of the model.
        return self.student_id + ' ' + self.last_name + ', ' + self.first_name

    def student_name(self):
        return self.last_name + ', ' + self.first_name

    def get_absolute_url(self):
        return reverse('student_progress:student_detail', args=[str(self.id)])

class Standard(models.Model):

    subject = models.CharField(max_length=14, choices=subjects)
    grade_level = models.IntegerField(choices=gradeLevels)
    descriptor = models.CharField(max_length=15)
    description = models.TextField()
    essential_status = models.BooleanField(default=False)


    class Meta:
        ordering = ["subject", "grade_level", "descriptor"]

    def __str__(self):
        return self.descriptor + ': ' + self.description[:100]

    def get_absolute_url(self):
        return reverse('student_progress:standard_detail', args=[str(self.id)])

class Milestone (models.Model):

    step_number = models.IntegerField()
    statement = models.CharField(max_length=250, default="I can ...")
    standard = models.ForeignKey(Standard, on_delete=models.CASCADE,         
        related_name='milestones', null=True, blank=True)

    def __str__(self):
        return str(self.step_number) + ': ' + self.statement[:50]

class Progress_Report(models.Model):

    date = models.DateField(null=True)
    student = models.OneToOneField(Student, on_delete=models.CASCADE)
    standard_mastered = models.OneToOneField(Standard, 
        on_delete=models.CASCADE)
    mastery_status = models.BooleanField(default=True)

    class Meta:
        ordering = ["date", "student"]

    def __str__(self):
        return self.date

class Instructional_Cycle(models.Model):
    date_started = models.DateField(blank=False)
    date_finished = models.DateField(blank=False)
    standards = models.ManyToManyField(Standard, related_name="standards")

    class Meta:
        ordering = ['date_started']

    def __str__(self):
        return str(self.date_started) + ' to ' + str(self.date_finished)

    def get_absolute_url(self):
        return reverse('student_progress:cycle_detail', args=[str(self.id)])
Glenn H.
  • 33
  • 5
  • 1
    See: https://stackoverflow.com/q/29373887/1531971 (Tell us why this does not apply, and there are others when you search for this error message.) –  Oct 17 '18 at 20:42
  • Possible duplicate of [django.db.utils.IntegrityError: UNIQUE constraint failed: rango\_category\_\_new.slug](https://stackoverflow.com/questions/29373887/django-db-utils-integrityerror-unique-constraint-failed-rango-category-new-sl) – Brisbe Oct 17 '18 at 23:40
  • As suggested in referred article, dropped the database, deleted migrations, reran makemigrations and migrate. Still getting error. I'm going to edit the question to add my models in case that will shed any light. Unfortunately, I read through as many of the posted questions as possible after searching on this error, and none of them give a concrete solution or adequate explanation of exactly what's going on. See below--I figured out the answer myself accidentally. – Glenn H. Oct 18 '18 at 01:58

2 Answers2

1

you've told the database you want to maintain a unique constraint! the data you're trying to insert would violate that constraint and therefore the transaction is failing.

Django provides various helpers for this. For example Progress_Report.objects.update_or_create(…) might help. For more info, see:

https://docs.djangoproject.com/en/2.1/ref/models/querysets/#update-or-create

The exact invocation would depend on the constraints you're wanting to enforce.

Sam Mason
  • 15,216
  • 1
  • 41
  • 60
  • I'm afraid I don't understand how I've done that. I read the page you recommended and it seems the take-away is: "Like get_or_create() and create(), if you’re using manually specified primary keys and an object needs to be created but the key already exists in the database, an IntegrityError is raised." The Progress_Report table is empty, so I don't understand how I'm adding non-unique data. – Glenn H. Oct 17 '18 at 20:20
  • I'd suggest searching for `unique` in the model definition and making sure that you're inserting data that is consistent with those constraints. If this is just test data, you could catch the exception and ignore it, maybe after logging it. `django.db.connection.queries` can also be useful to see what statements are actually being sent to the database – Sam Mason Oct 17 '18 at 20:26
0

I stumbled on the answer accidentally: I changed the field types of Progress_Report.student and Progress_Report.standard_mastered from OnetoOneField to ForeignKey. Now the python code runs perfectly and populates the database exactly as intended without error.

I have no idea why this solved the problem. I wish I understood better what the problem is, but I'm thankful it's fixed.

Glenn H.
  • 33
  • 5
  • OneToOne means that every student in Progress_Report can have one and only one associated Student and vice versa. Likewise, every standard_mastered in Progress_Report can have one and only one associated Standard and vice versa. Unfortunately, if you are creating multiple Progress_Report object instances and randomly selecting Students and Standards, you will eventually have situations where the same Student or Standard is selected for more than one Progress_Report, which you already told the ORM is not allowed (since you used OneToOne). – djangomachine Oct 19 '18 at 20:07