User avatar not displayed in Django - django

I want to user have a avatar. So I created the model and the form.
Image is not displaying on the page. But form saves the image to folder. Where is mistake?
request.user.avatar.url doesn't work. Maybe the image is not attached to User? Thanks for the help
P.S. djagno-avatar is not good for me.
models.py
class Avatar(models.Model):
user = models.OneToOneField(User, on_delete=models.PROTECT, null=True)
avatar = models.ImageField(upload_to='user_avatars', null=True)
forms.py
class UserPhotoForm(forms.ModelForm):
class Meta:
model = Avatar
fields = ('avatar', )
widgets = {
'avatar': forms.FileInput(attrs={'class': 'input'}),
}
views.py
def cabinet(request):
user = request.user
if request.method == 'POST':
form = UserPhotoForm(request.POST, request.FILES, instance=user)
if form.is_valid():
form.save()
else:
form = UserPhotoForm()
return render(request, 'account/cabinet/cabinet.html', {'form': form})
cabinet.html
<div class="avatar">
{{ request.user.avatar.url }} #there is trying
{{ request.user.avatar.avatar.url }}
<img src="{{ request.user.avatar.avatar.url }}" alt="" width="80px" height="80px">
<form method="POST" enctype="multipart/form-data">
{% csrf_token %}
{{ form.as_p }}
<input type="submit" id="file"/>
</form>

try this
<img src="{{ request.user.avatar.url }}" alt="" width="80px" height="80px">
https://docs.djangoproject.com/en/3.1/ref/models/fields/#django.db.models.ImageField

Found the main problem you are never storing your avatar model in db. As you are giving it the instance of your user. Because of that your Avatar model is not stored.
You have to use
def cabinet(request):
user = request.user
if request.method == 'POST':
form = UserPhotoForm(request.POST,request.FILES)
if form.is_valid():
temp=form.save(commit=False)
temp.user = user
temp.save()
else:
form = UserPhotoForm()
return render(request, 'account/cabinet/cabinet.html', {'form': form})
Try that

Related

Django inlineformset_factory. Request.FILES not returning filename to form when formset.is_valid() = False

I'm trying to allow the user to upload images tied to a project. I'm doing this via an inlineformset_factory.
Im serving the form to the user trough a function based view. When the user fails to fill in one (or more) of the formsets correctly formset.is_valid() returns false and the bound formsets get returned to the user along with error messages. What i'm having trouble with is that the imagefield is not returned to the bound form. So if the user has filled in 2 out of 3 forms correctly then neither of the formsets have a bound imagefield after the failed validation. I can't manage to figure out if this is working as intended or if i'm doing something wrong. If i print out request.FILES i get the following:
<MultiValueDict: {'image_set-0-filename': [<InMemoryUploadedFile: IMG_0017.jpg (image/jpeg)>], 'image_set-1-filename': [<InMemoryUploadedFile: sprite.svg (image/svg+xml)>]}>
I would like the imagefield to be bound and show the user what value the field has.
before incorrect form submission
after incorrect form submission
Hopefully you can see where my mistake is or tell me if i'm thinking about this the wrong way.
Thank you in advance
forms.py
class EndUserImageForm(forms.ModelForm):
class Meta:
model = Image
fields = ["filename", "description"]
widgets = {
"filename": forms.FileInput(attrs={"class": "form__field no--outline"}),
"description": forms.Textarea(attrs={"class": "form__field no--outline", "placeholder": "Description"})
}
EndUserImageInlineFormset = inlineformset_factory(
Project, Image, form=EndUserImageForm, extra=2)
views.py
def image_formset(request, pk):
project = get_object_or_404(Project, pk=pk)
image_formset = EndUserImageInlineFormset(request.POST or None, request.FILES or None)
context = {
"image_formset": image_formset
}
if request.method == "POST":
if image_formset.is_valid():
images = image_formset.save(commit=False)
for image in images:
image.project = project
image.save()
return HttpResponseRedirect(reverse("projects:home"))
return render(request, "projects/test.html", context)
template
<form method="POST" enctype="multipart/form-data">
{% csrf_token %}
<h4>Add Images</h4>
{{ image_formset.non_form_errors }}
{{ image_formset.management_form }}
<div id="image-form-list">
{% for form in image_formset %}
{{ form.errors }}
<div class="image_form">
{{ form.filename }}
{{ form.description }}
</div>
{% endfor %}
</div>
<input type="submit" value="Save" class="button button--main activate"/>
</form>
models.py
class Image(models.Model):
project = models.ForeignKey(Project, on_delete=models.CASCADE)
filename = models.ImageField(upload_to=project_directory_path, blank=False)
description = models.CharField(max_length=350, blank=True)
created = models.DateTimeField(auto_now_add=True)
def image_formset(request, pk):
project = get_object_or_404(Project, pk=pk)
image_formset = EndUserImageInlineFormset()
if request.method == "POST":
image_formset = EndUserImageInlineFormset(request.POST or None, request.FILES or None)
if image_formset.is_valid():h
images = image_formset.save(commit=False)
for image in images:
image.project = project
image.save()

django model based forms - why isn't form valid?

I'm trying to make model based form but something went wrong.
model:
class Topic(models.Model):
name = models.CharField(max_length=200)
icon = models.ImageField(upload_to = 'images/')
form:
class TopicCreationForm(ModelForm):
class Meta:
model = Topic
fields = '__all__'
view:
def TopicCreateView(request):
form = TopicCreationForm()
if request.method == 'POST':
form = TopicCreationForm(request.POST)
if form.is_valid():
form.save()
return redirect('home')
else:
print('aaa') # It displays in console
context = {'form':form}
return render(request, 'blog/topic_form.html', context)
my form html part
<form method="POST">
{% csrf_token %}
<fieldset >
<legend> New Topic</legend>
{{ form|crispy }}
</fieldset>
<div>
<input type="submit" value="submit" class="button-33" role="button">
</div>
</form>
where did i make mistake ?
You need to pass both request.POST and request.FILES [Django-doc], so:
def topic_create(request):
if request.method == 'POST':
form = TopicCreationForm(request.POST, request.FILES)
if form.is_valid():
form.save()
return redirect('home')
else:
print('aaa') # It display in console
else:
form = TopicCreationForm()
context = {'form':form}
return render(request, 'blog/topic_form.html', context)
In the HTML form, you need to specify that the files should be encoded with the enctype="…" attribute [mdn]:
<form method="post" enctype="multipart/form-data">
…
</form>

Django Requiring Optional Form Field Error

To new readers: Neverwalkaloner's solution solved the initial error but the photo upload is still required and making required false in forms.py gives me a MultiValueDictKeyError. Any help on making it optional would be greatly appreciated.
I have a model and form to upload either a picture and text, or just text. My intention, actually was to make it a choice between an image, text or both and any help with that would be appreciated, but I digress. Uploading only works when an image is included, if it is just text, I get the error:
The view lesyeux.views.posts didn't return an HttpResponse object. It
returned None instead.The view lesyeux
My model is:
class Post(models.Model):
image = models.ImageField(upload_to='uploaded_images', blank=True,
null=True)
text_post = models.CharField(max_length=1000)
author = models.ForeignKey(User)
My form is:
class PostForm(forms.ModelForm):
image = forms.FileField(label='Select an image file',
help_text='Please select a photo to upload')
text_post = forms.CharField(help_text="Please enter some text.")
class Meta:
model = Post
fields = ('image', 'text_post',)
exclude = ('author',)
My view is:
def posts(request, id=None):
neighborhood = get_object_or_404(Neighborhood, id=id)
form = PostForm()
if request.method == 'POST':
form = PostForm(request.POST, request.FILES)
if form.is_valid():
post = Post(image = request.FILES['image'])
post = form.save(commit=False)
post.author = request.user
post = post.save()
next = request.POST.get('next', '/')
return HttpResponseRedirect(next)
else:
form = PostForm()
posts = Post.objects.all().order_by('-id')
return render(request, 'posts.html', context = {'form':form,
'posts':posts, 'neighborhood':neighborhood})
and my form is:
<form id="PostForm" method="post" action="/view/{{ neighborhood.id }}/posts/" enctype="multipart/form-data">
{% csrf_token %}
{% for hidden in form.hidden_fields %}
{{ hidden }}
{% endfor %}
{% for field in form.visible_fields %}
{{ field.errors }}
{{ field.help_text }}
{{ field }}
{% endfor %}
<input type="hidden" name="next" value="{{ request.path }}">
<input type="submit" name="submit" value="Post" />
</form>
Your view doesnt return response if form is not valid. To fixt it rewrite view like this:
def posts(request, id=None):
neighborhood = get_object_or_404(Neighborhood, id=id)
form = PostForm()
if request.method == 'POST':
form = PostForm(request.POST, request.FILES)
if form.is_valid():
post = Post(image = request.FILES['image'])
post = form.save(commit=False)
post.author = request.user
post = post.save()
next = request.POST.get('next', '/')
return HttpResponseRedirect(next)
else:
form = PostForm()
posts = Post.objects.all().order_by('-id')
return render(request, 'posts.html', context = {'form':form, 'posts':posts, 'neighborhood':neighborhood})

How to update an ImageField in Django

I am making a website and would like to make it possible for each user to upload and update his own profile picture.
The update-profile template loads perfectly fine. I can update all the other fields, except for the image field. It can only be updated from the /admin page, but not from the update-profile page.
This is my image field in the Profile model:
class Profile(models.Model):
user = models.OneToOneField(User, on_delete=models.CASCADE)
dob = models.DateField(default=datetime.date(1989, 12, 25))
gender = models.CharField(max_length=12, choices=GENDER_CHOICES, default='Unspecified')
city = models.CharField(max_length=100, default='Braşov')
image = models.ImageField(upload_to='profilepics', blank=True)
Here is my ProfileForm:
class ProfileForm(forms.ModelForm):
class Meta:
model = Profile
fields = ('dob', 'gender', 'city', 'image')
This is the html template:
{% block body %}
<form method="post">
<div class="container">
{% csrf_token %}
{{ user_form.as_p }}
{{ profile_form.as_p }}
<button type="submit">Update</button>
Cancel
</form>
</div>
{% endblock %}
And this is the update_profile view:
#login_required
#transaction.atomic
def update_profile(request):
if request.method == 'POST':
user_form = UserForm(request.POST, instance=request.user)
profile_form = ProfileForm(request.POST, request.FILES, instance=request.user.profile)
if user_form.is_valid() and profile_form.is_valid():
user_form.save()
profile_form.save()
messages.success(request, ('Your profile was successfully updated!'))
return redirect('myprofile')
else:
messages.error(request, ('Please correct the error below.'))
else:
user_form = UserForm(instance=request.user)
profile_form = ProfileForm(instance=request.user.profile)
return render(request, 'filter/updateprofile.html', {
'user_form': user_form,
'profile_form': profile_form
})
Thank you very much for reading!
Add the attribute enctype to your form tag in your html document enctype="multipart/form-data"
See this question
Django ModelForm Imagefield Upload
And as is documented in w3schools
https://www.w3schools.com/tags/att_form_enctype.asp

Django model form for user profile won't pre-populate

I've made a profile model in models.py:
class Profile(models.Model):
user = models.OneToOneField(User)
title = models.CharField(max_length=20, default='title')
firstname = models.CharField(max_length=40, default='firstname')
lastname = models.CharField(max_length=40, default='lastname')
blurb = models.CharField(max_length=500, default='tell us about yourself')
#work out how to make filename equal the username
pic = models.ImageField(default="static/profile/blank.png", upload_to='static/profile/%d%m%y.jpg') #+ user.pk + '.jpg')
def __unicode__(self):
return self.user.username
and here is the view for a page to edit the profile of a logged in user:
def editprofile(request):
u_p = request.user.profile
template = loader.get_template('registration/editprofile.html')
if request.method == 'POST':
form = ProfileForm(request.POST, instance=u_p)
if form.is_valid():
form.save()
else:
# todo
None
else:
#todo
context = RequestContext(request, {'form': form})
return HttpResponse(template.render(context))
The template fragment reads:
<form method="POST" action=".">
{% csrf_token %}
<div class="regformout">
<div class="regform">
{% for field in form %}
<div class='cell'> {{ field.label_tag }} </div>
<div class='nogin'> {{ field.errors }} </div>
<div class='cell'> {{ field }} </div>
{% endfor %}
</div>
</div>
<input class="btn btn-large btn-primary" type="submit" value="Save Your Profile" ></input>
</form>
I want the form fields to automatically populate with the data for the current user on the corresponding page for editing the profile. However, no matter what I try I cannot make this happen. What am I doing wrong here?
Your main problem is you are only populating the form if the user hits the submit button, so when the view is requested initially, your form is empty.
from django.shortcuts import render, redirect
def editprofile(request):
u_p = request.user.profile
form = ProfileForm(request.POST or None, instance=u_p)
if request.method == 'POST':
if form.is_valid():
form.save()
return redirect('/')
return render(request,
'registration/editprofile.html', {'form': form})