-1

I have Detail Model, which have the ForeignKey of the Django's default User Model. I created a Form to take the input from user and update the Detail Model fields if it exists else create a new detail.

For this purpose, I am filtering the Detail.objects.all() on the username of the user which was selected in the Form at Front-End. Now, I need a ID of the username that was selected in order to update the Detail Model. How can I get the ID? If I just pass the username, it says Field 'id' expected a number but got 'std1'.

My models.py:

class Detail(models.Model):
    user = models.ForeignKey(User, on_delete=models.CASCADE, limit_choices_to={'is_superuser': False, 'is_staff': False}, verbose_name="Select a Student")
    subject = models.CharField(max_length=50, verbose_name='Subject Name', help_text='Write the name of the subject.')
    skype_session_attendance = models.FloatField(validators=[MinValueValidator(0), MaxValueValidator(20)], verbose_name="Skype Session Attendances (of this subject, in numbers)", help_text="Enter the numbers of skype sessions of this subject, the student attended out of 20.")
    internal_course_marks = models.FloatField(validators=[MinValueValidator(0), MaxValueValidator(40)], verbose_name="Internal Course Marks (of this subject, in numbers)", help_text="Enter the total internal course marks of this subject, the student obtained out of 40.")
    programming_lab_activity = models.FloatField(validators=[MinValueValidator(0), MaxValueValidator(25)], verbose_name="Programming Lab Activities (of this subject, in numbers)", help_text="Enter the total numbers of programming lab activities of this subject, the student participated in, out of 25.")
    mid_term_marks = models.FloatField(validators=[MinValueValidator(0), MaxValueValidator(45)], verbose_name="Mid_Term Marks (of this subject, in numbers)", help_text="Enter the total mid-term marks of this subject, the student obtained out of 45.")
    final_term_marks = models.FloatField(validators=[MinValueValidator(0), MaxValueValidator(90)], verbose_name="Final_Term Marks (of this subject, in numbers)", help_text="Enter the total final-term marks of this subject, the student obtained out of 90.")

    def __str__(self):
        return f'{self.user.username}-{self.subject}'

    def save(self, *args, **kwargs):
        super().save(*args, **kwargs)

My forms.py:

class UpdateStudentDetailsForm(forms.Form):
    stds = forms.CharField(widget=forms.Select(choices=STUDENTS_LIST), label='Select a Student')
    subject = forms.CharField(max_length=50)
    skype_session_attendance = forms.FloatField(min_value=0, max_value=20, label="Skype Session Attendances (of this subject, in numbers)", help_text="Enter the numbers of skype sessions of this subject, the student attended out of 20.")
    internal_course_marks = forms.FloatField(min_value=0, max_value=40, label="Internal Course Marks (of this subject, in numbers)", help_text="Enter the total internal course marks of this subject, the student obtained out of 40.")
    programming_lab_activity = forms.FloatField(min_value=0, max_value=25, label="Programming Lab Activities (of this subject, in numbers)", help_text="Enter the total numbers of programming lab activities of this subject, the student participated in, out of 25.")
    mid_term_marks = forms.FloatField(min_value=0, max_value=45, label="Mid_Term Marks (of this subject, in numbers)", help_text="Enter the total mid-term marks of this subject, the student obtained out of 45.")
    final_term_marks = forms.FloatField(min_value=0, max_value=90, label="Final_Term Marks (of this subject, in numbers)", help_text="Enter the total final-term marks of this subject, the student obtained out of 90.")
    print(stds)
    class Meta:
        fields = ['stds', 'subject', 'skype_session_attendance', 'internal_course_marks', 'programming_lab_activity', 'mid_term_marks', 'final_term_marks']

My views.py:

def updateStudentDetails(request):
    if request.method == 'POST':
        std_form = UpdateStudentDetailsForm(request.POST)

        if std_form.is_valid():
            details = std_form.cleaned_data
            usr = details['stds']

            old_details = Detail.objects.get(user=usr)      # HERE I NEED TO PUT ID INSTEAD OF 'usr'
            old_details.subject = details['subject']
            old_details.skype_session_attendance = details['skype_session_attendance']
            old_details.internal_course_marks = details['internal_course_marks']
            old_details.programming_lab_activity = details['programming_lab_activity']
            old_details.mid_term_marks = details['mid_term_marks']
            old_details.final_term_marks = details['final_term_marks']
            old_details.save()

            messages.success(request, 'Success! Academic Record/Details of ' + usr.first_name + usr.last_name + '(' + usr.username + ')' ' has been updated.')

            return redirect('update_student_details')
    else:
        std_form = UpdateStudentDetailsForm()

    return render(request, 'users/update_student_details.html', {'std_form': std_form})
Khubaib Khawar
  • 51
  • 1
  • 11

1 Answers1

0

I think design wise it has some flaws. Like here an User can have multiple Detail object as it is a ForeignKey relation. So calling old_details = Detail.objects.get(user=usr) will cause error when you have multiple instances of User. Now you can change it to OneToOne relation to avoid this problem:

class Detail(models.Model):
    user = models.OneToOneField(User, on_delete=models.CASCADE, limit_choices_to={'is_superuser': False, 'is_staff': False}, verbose_name="Select a Student")

Instead of Form, use forms.ModelForm. You can try like this:

class UpdateStudentDetailsForm(forms.Form):
    class Meta:
        model = Details
        exclude = 'user'

And instead of getting the stds data form, I would recommend using url to manage it. For example:

# view
def updateStudentDetails(request, user_id):  # Please use snake_case as per PEP-8 Standards
    user = get_object_or_404(User, user_id)
    if request.method == 'POST':
        std_form = UpdateStudentDetailsForm(request.POST, instance=user.detail)
        if std_form.is_valid():
            std_form.save()
            # redirect
    else:
       std_form = UpdateStudentDetailsForm(instance=user.detail) 
    return render(request, 'users/update_student_details.html', {'std_form': std_form})

# url:
path('student/update/<int:user_id>/', updateStudentDetails, name="update_student_detail"),

Update

If you want to have One to Many relation between User and Detail object, then I would recommend getting Detail objects using pk.

If you insist on having a dropdown to select student, then use a different form to redirect to this updateStudentDetails page like this:

# form

Class StudentDetailChoiceForm(forms.Form):
    student = forms.ModelChoiceField(queryset=Detail.objects.all(), label="Choose a student")

# view

def redirect_to_student_update(request):
    form = StudentDetailChoiceForm(request.GET or request.POST)
    if form.is_valid():
        return redirect('update_student_detail', pk=form.cleaned_data['student'].pk)

And update the view of updateStudentDetails:

def updateStudentDetails(request, pk):  # Please use snake_case as per PEP-8 Standards
    detail = get_object_or_404(Detail, pk)
    if request.method == 'POST':
        std_form = UpdateStudentDetailsForm(request.POST, instance=detail)

BTW, you can use ModelChoiceField with your existing implementation, like:

class UpdateStudentDetailsForm(forms.Form):
    stds = forms.ModelChoiceField(queryset=Detail.objects.all(), label="Choose a student")

And rest of your implementation should work, but it won't be a clean solution as you will not be using DRY principle and you are getting data from form manually etc, where with ModelForm, the initial values would be visible in your template, less codes and Django takes care of the update itself.

ruddra
  • 50,746
  • 7
  • 78
  • 101
  • That's what I want, I want the User to have multiple Detail objects, one per subject. If I will use OneToOneField, it will not let me have a single user has multiple Detail Object (in other words, multiple subjects). It is basically a OneToMany relation from `User` to `Detail`. – Khubaib Khawar May 27 '20 at 05:58
  • Is not there any way to get the ID of the user, whose username was selected in the `Form` at FrontEnd? – Khubaib Khawar May 27 '20 at 05:59
  • But you did not tell me that how will I get the ID of the user that was selected from the dropdown list at FrontEnd? Because I have to put the id there in `views.py` order to get the details. – Khubaib Khawar May 27 '20 at 07:07