Search code examples
pythondjangoattributeerror

AttributeError: 'NoneType' object has no attribute 'get' - How to solve it when I even have the data and no field is None?


I am creating a Django app. I will write the short summary of the system how it is supposed to work and then I will share my code.

When user (student) will go to the performance_calculator.html he/she will see a textbox where he/she will have to enter the relevant subject name. After entering the subject name, he/she will click 'Calculate' button, on the other side (server-side) the entered name will be taken and the system will search the detail (progress) of that subject of that user (student) from the Detail Model using the subject name and that user name as filters. After getting the the progress detail, each value in the detail will be assigned to a separate single variable. Those variables will be passed as parameters to fuzz_algo() function (a Fuzzy Logic Algorithm to calculator Performance), after getting the result, result will be forward to Client Side (Front End) using Django Messages Module.

This was the summary, now I get the error at the step when the system tries to get the progress details of the subject (whose name was entered by user) of the user (student who is using the system). and the Error is AttributeError: 'NoneType' object has no attribute 'get'. I know this error happens when we receive None from somewhere and we try to get something from None. But I have populated the Detail Model along with the Subject Model using the Admin Panel (screenshots are attached). I don't know why I am getting None and due to None, this AttributeError.

My views.py

def performanceCalculator(request):
    skype = 0
    internal_course = 0
    prg_lab = 0
    mid_marks = 0
    final_marks = 0
    sub = 0
    if request.method == 'POST':
        performance_form = PerformanceCalculatorForm(request.POST)

        if performance_form.is_valid():
            performance_form.save()

            sub = performance_form.cleaned_data.get('subject')
            skype = Detail.objects.filter(subject__subject=sub, user__username=User.username).values().first().get('skype_session_attendance')
            internal_course = Detail.objects.filter(subject__subject=sub, user__username=User.username).values().first().get('internal_course_marks')
            prg_lab = Detail.objects.filter(subject__subject=sub, user__username=User.username).values().first().get('programming_lab_activity')
            mid_marks = Detail.objects.filter(subject__subject=sub, user__username=User.username).values().first().get('mid_term_marks')
            final_marks = Detail.objects.filter(subject__subject=sub, user__username=User.username).values().first().get('final_term_marks')

            result = fuzz_algo(skype, internal_course, prg_lab, mid_marks, final_marks)

            messages.success(request, result)

            return redirect('performance_calculator')
    else:
        performance_form = PerformanceCalculatorForm()

    context = {
        'performance_form': performance_form
    }

    return render(request, 'users/performance_calculator.html', context)

My models.py

class Subject(models.Model):
    user = models.ForeignKey(User, on_delete=models.CASCADE)
    subject = models.CharField(max_length=100)

    def __str__(self):
        return '{} ({})'.format(self.subject, self.user.username)


class Detail(models.Model):
    user = models.ForeignKey(User, on_delete=models.CASCADE)
    subject = models.OneToOneField(Subject, on_delete=models.CASCADE)
    skype_session_attendance = models.FloatField()
    internal_course_marks = models.FloatField()
    programming_lab_activity = models.FloatField()
    mid_term_marks = models.FloatField()
    final_term_marks = models.FloatField()

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

class Sub(models.Model):
    s = models.CharField(max_length=100

My forms.py

class PerformanceCalculatorForm(forms.ModelForm):
    subject = forms.CharField(max_length=100)

    class Meta:
        model = Sub
        fields = ['subject']

My template (users/performance_calculator.html)

{% if not request.user.is_superuser and not request.user.is_staff %}
                        <div class="account-heading">
                            <h2>
                                Performance Calculator
                            </h2>
                        </div>

                        <div class="content-section">
                            <form method="POST">
                                {% csrf_token %}
                                <fieldset class="form-group">
                                    <legend class="border-bottom mb-4"></legend>
                                    {{ performance_form|crispy }}
                                </fieldset>
                                <div class="from-group">
                                    <button class="btn btn-outline-info" type="submit">Calculate</button>
                                </div>
                            </form>
                        </div>
                    {% endif %}

This is the full error that I get:

AttributeError at /esacp/performance-calculator/
'NoneType' object has no attribute 'get'
Request Method: POST
Request URL:    http://localhost:8000/esacp/performance-calculator/
Django Version: 3.0.3
Exception Type: AttributeError
Exception Value:    
'NoneType' object has no attribute 'get'
Exception Location: C:\Users\khubi\OneDrive\Desktop\FYP\test_phase\users\views.py in performanceCalculator, line 61
Python Executable:  C:\environments\bsse_fyp\Scripts\python.exe
Python Version: 3.8.1
Python Path:    
['C:\\Users\\khubi\\OneDrive\\Desktop\\FYP\\test_phase',
 'C:\\Users\\khubi\\AppData\\Local\\Programs\\Python\\Python38-32\\python38.zip',
 'C:\\Users\\khubi\\AppData\\Local\\Programs\\Python\\Python38-32\\DLLs',
 'C:\\Users\\khubi\\AppData\\Local\\Programs\\Python\\Python38-32\\lib',
 'C:\\Users\\khubi\\AppData\\Local\\Programs\\Python\\Python38-32',
 'C:\\environments\\bsse_fyp',
 'C:\\environments\\bsse_fyp\\lib\\site-packages']
Server time:    Thu, 14 May 2020 11:55:41 +0000

Now, below are the screenshots of Admin Panel showing the populated Subject and Detail model: This is the main Django Admin Panel, see the actions at the right side? enter image description here

This is the Subject Model, see 4 subjects are added, 2 of one student and 2 of other. enter image description here

This is the Detail Model of one Subject of the user (student). See the details are entered and fields are not None or empty enter image description here

Now I don't understand, why am I getting the error where there is nothing empty.

P.S. Yes, I logged in with the same user (student) whose subject detail is populated in the screenshot above. If you see in Template, I have if condition at start that if the user is not staff and not superuser, only then that form will be shown. So it's obvious with the Django Admin Panel logged in, i won't see that Performance Calculator form, I will only see the form if I am logged in with the regular account. But I am still getting the error.


Solution

  • Clarify: By looking this time at your entire code, you have data inserted to the database already, and your form is to only perform a GET to those data ... So for performance, the form should not be saved (as it will be creating new object every time you POST). And this means that a better approach for this should be coded as a GET instead of a POST...

    but lets say this is a special case and you want to do that because you want to... This time I will directly modify your code, it is a headache at first watch because its querying too much and therefore VERY inefficient.

    views.py

    def performanceCalculator(request):
        if request.method == 'POST':
            performance_form = PerformanceCalculatorForm(request.POST)
    
            if performance_form.is_valid():
                #performance_form.save() #dont save because you dont need to.
                #just process it by getting the data
                sub = performance_form.cleaned_data['subject']
    
                detail = Detail.objects.all().filter(user=request.user, subject=sub).first()
                #get the unique object that matches the filters
                #and I think what is causing you the Nonetype error is your queries
                #(I saw filter(subject__subject=...) and the OneToOneField doesn't 
                #behave the same as a ForeignKey and that may be the case.)
    
                #now fetch every field into the function
                result = fuzz_algo(detail.skype_session_attendance, detail.internal_course_marks, detail.programming_lab_activity, detail.mid_term_marks, detail.final_term_marks)
    
                messages.success(request, result) #if it raises int related error, include the str filter later.
    
                return redirect('performance_calculator')
        else:
            performance_form = PerformanceCalculatorForm()
    
        context = {
            'performance_form': performance_form
        }
    
        return render(request, 'users/performance_calculator.html', context)
    

    EDIT: Forgot to add this part

    And in your models.py ... It doesn't make much sense to have the model Subject and it is cause to have 2 user relation...

    so a cleaner model would be:

    #removed the Subject model
    
    class Detail(models.Model):
        user = models.ForeignKey(User, on_delete=models.CASCADE)
        subject = models.CharField(max_length=15) #modified to be a CharField
        skype_session_attendance = models.FloatField()
        internal_course_marks = models.FloatField()
        programming_lab_activity = models.FloatField()
        mid_term_marks = models.FloatField()
        final_term_marks = models.FloatField()
    
        def __str__(self):
            return f'{self.subject, (self.user.username)} Details'
    
    #also the Sub class
    

    EDIT: Add the .first() to get the first object on queryset