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.