Home > Enterprise >  'NoneType' object has no attribute 'profile'
'NoneType' object has no attribute 'profile'

Time:03-12

Trying to update profile picture for student models but i get this error. 'NoneType' object has no attribute 'profile'. Idk if it has something to do with the views.py but it probably does.

My views.py for user app:

from django.contrib.auth.decorators import login_required
from .forms import UpdateProfileForm
from django.contrib import messages
from main.models import Student
from .models import Profile



@login_required
def profile(request):
    if request.method == 'POST':
        student = request.POST.get('student')
        p_form = UpdateProfileForm(
            request.POST, request.FILES, instance=student.profile)
        if p_form.is_valid():
            p_form.save()
            messages.success(request, f'Student picture has been updated!')
            return redirect('student_profiles')
    else:
        p_form = UpdateProfileForm()

    context = {
        'p_form': p_form,
        'students': Student.objects.all()
    }

    return render(request, 'user/profiles.html', context)

Signals.py:

from django.dispatch import receiver
from main.models import Student
from django.contrib.auth.models import User
from .models import Profile


@receiver(post_save, sender=Student)
def create_profile(sender, instance, created, **kwargs):
    if created:
        Profile.objects.create(student=instance)

@receiver(post_save, sender=Student)
def update_profile(sender, instance, created, **kwargs):
    if created == False:
        instance.profile.save()

Profile model:

from main.models import Student
from PIL import Image


class Profile(models.Model):
    student = models.OneToOneField(
        Student, default=None, null=True, on_delete=models.CASCADE, related_name="profile")
    image = models.ImageField(default='default.jpg', upload_to='profile_pics')

    def __str__(self):
        return f'{self.student.firstname} {self.student.surname} Profile'

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

        img = Image.open(self.image.path)

        if img.height > 300 or img.width > 300:
            output_size = (300, 300)
            img.thumbnail(output_size)
            img.save(self.image.path)

https://i.stack.imgur.com/sXJEk.png

And this is my profile.html:

{% load crispy_forms_tags %}
{% block content %}
    {% for student in students %}
        <div >
            <div >
            <img  src="{{ student.profile.image.url }}">
            <div >
                <h2 ></h2>
                <p ></p>
            </div>
        </div>
            <form method="POST" enctype='multipart/form-data'>
                {% csrf_token %}
                <fieldset >
                    <legend >{{ student.firstname }} {{ student.surname }}</legend>
                    {{ p_form|crispy }}
                </fieldset>
                <div >
                    <button  type="submit">Update</button>
                </div>
            </form>
        </div>
    {% endfor %}
{% endblock content %}

CodePudding user response:

The solution I'd try is to hide the Student id at the template level and get this information to filter the object later. For the template I'd add this input:

{% block content %}
    {% for student in students %}
        <div >
            <div >
            <img  src="{{ student.profile.image.url }}">
            <div >
                <h2 ></h2>
                <p ></p>
            </div>
        </div>
            <form method="POST" enctype='multipart/form-data'>
                {% csrf_token %}
                <fieldset >
                    <legend >{{ student.firstname }} {{ student.surname }}</legend>

                    <!-- pass student.id for every object at students -->
                    <input type="hidden" name="student_id" value="{{ student.id }}" />

                    {{ p_form|crispy }}
                </fieldset>
                <div >
                    <button  type="submit">Update</button>
                </div>
            </form>
        </div>
    {% endfor %}
{% endblock content %}

Then at the view:

@login_required
def profile(request):
    if request.method == 'POST':
        # student_id comes from the hidden input at the html
        student_id = request.POST.get('student_id')
        # get the student object with its relationships, so now student.profile should work
        student = Student.objects.get(pk=student_id)

        p_form = UpdateProfileForm(
            request.POST, request.FILES, instance=student.profile)
        if p_form.is_valid():
            p_form.save()
            messages.success(request, f'Student picture has been updated!')
            return redirect('student_profiles')
    else:
        p_form = UpdateProfileForm()

    context = {
        'p_form': p_form,
        'students': Student.objects.all()
    }

    return render(request, 'user/profiles.html', context)

I hope this is a simple solution for your issue. I didn't test it, but you get the general idea.

  • Related