I am currently creating an application that allows users to view and edit their own personal profile. I've recently added in the ability for a user to add a profile picture to their profile. I can add a profile in the admin page and it will show up on the selected users profile no problem. The problem is when the user tries to update their picture I am getting an ValueError telling me that the image attribute has no file associated. Below is how I have tried to implement the functionality.
Models
class UserProfileModel(models.Model):
user = models.OneToOneField(User, on_delete=models.CASCADE)
age = models.PositiveIntegerField(blank=True, null=True)
email = models.EmailField(max_length=254, null=True, blank=True, unique=True)
height = models.PositiveIntegerField(blank=True, null=True)
weight = models.PositiveIntegerField(blank=True, null=True)
bio = models.CharField(max_length=100, blank=True, default='')
image = models.ImageField(upload_to='profile_image', blank=True)
forms
class UpdateProfile(forms.ModelForm):
class Meta:
model = UserProfileModel
fields = ('email', 'age', 'height', 'weight', 'bio', 'image')
views
def update_profile(request):
args = {}
if request.method == 'POST':
form = UpdateProfile(request.POST, instance=request.user.userprofilemodel)
if form.is_valid():
form.save()
return HttpResponseRedirect(reverse('account:profile'))
# return render(request, 'account/profile.html')
else:
form = UpdateProfile()
if request.user.is_authenticated():
form = UpdateProfile(instance=request.user.userprofilemodel)
args['form'] = form
return render(request, 'account/edit_profile.html', args)
HTML
<!DOCTYPE html>
<html>
<body>
{#<h2 style="text-align:center">User Profile Card</h2>#}
<div class="container">
<h1>{{ user }}s Profile </h1>
<div style="margin: 24px 0;">
<p>Username: {{ user }}</p>
<p>Email: {{ user.userprofilemodel.email }}</p>
<p>Age: {{ user.userprofilemodel.age }}</p>
<p>Height: {{ user.userprofilemodel.height }} CM </p>
<p>Weight: {{ user.userprofilemodel.weight }} KG </p>
<p>User Bio: {{ user.userprofilemodel.bio }} </p>
<img src="{{ user.userprofilemodel.image.url }}" width="240">
</body>
</html>
Looks like you may have forgotten to pass request.FILES to your form?
form = UpdateProfile(request.POST, request.FILES, instance=request.user.userprofilemodel)
Also, you haven't shown the template containing the form used to upload an image, but ensure that the form's enctype is set to multipart/form-data.
<form method="post" enctype="multipart/form-data">
Related
I am working on a forum using django, I have a problem accessing user fullname and bio, from a model class I have. I have no problem accessing the user.username or user.email, but not from the Author class..
This is from the models.py in the forum app
User = get_user_model()
class Author(models.Model):
user = models.ForeignKey(User, on_delete=models.CASCADE)
fullname = models.CharField(max_length=40, blank=True)
slug = slug = models.SlugField(max_length=400, unique=True, blank=True)
bio = HTMLField()
points = models.IntegerField(default=0)
profile_pic = ResizedImageField(size=[50, 80], quality=100, upload_to="authors", default=None, null=True, blank=True)
def __str__(self):
return self.fullname
My form is in the user app, where i have a profile update site, and the form is like this
from forums.models import Author
class UpdateForm(forms.ModelForm):
class Meta:
model = Author
fields = ('fullname', 'bio', 'profile_pic')
Then here is some of the update site, however nothing let me get access to the bio or fullname, I've tried so many combos. and I am lost here..
{% block content %}
<section class="section" id="about">
<!-- Title -->
<div class="section-heading">
<h3 class="title is-2">Hey {{ user.username }}</h3>
<div class="container">
<p>{{ user.bio }}bio comes here</p>
</div>
</div>
Here is the view.py from the user app
from .forms import UpdateForm
def update_profile(request):
context = {}
user = request.user
instance = Author.objects.filter(user=user).first()
if request.method == 'POST':
form = UpdateForm(request.POST, request.FILES, instance=user)
if form.is_valid():
form.instance.user = user
form.save()
return redirect('/')
else:
form = UpdateForm(instance=user)
context.update({
'form': form,
'title': 'update_profile',
})
return render(request, 'register/update.html', context)
The form html
<form method="POST" action="." enctype="multipart/form-data">
{% csrf_token %}
{{form|crispy}}
<hr>
<button class="button is-block is-info is-large is-fullwidth">Update <i class="fa fa-sign-in" aria-hidden="true"></i></button>
</form>
If there is some relation i am missing please help
Your view currently creates a new Author record each time you "update" the model. I would advise to first clean up the database and remove all authors.
Then you can convert the ForeignKey into a OneToOneField here: that way we know that each user has at most one Author:
from django.conf import settings
class Author(models.Model):
user = models.OneToOneField(
settings.AUTH_USER_MODEL,
on_delete=models.CASCADE
)
# …
Now we can alter the view to create a record in case there is no such record, or update an existing record if there is one:
from .forms import UpdateForm
def update_profile(request):
context = {}
user = request.user
instance = Author.objects.filter(user=user).first()
if request.method == 'POST':
form = UpdateForm(request.POST, request.FILES, instance=instance)
if form.is_valid():
form.instance.user = user
form.save()
return redirect('/')
else:
form = UpdateForm(instance=instance)
context.update({
'form': form,
'title': 'update_profile',
})
return render(request, 'register/update.html', context)
In the template, you can render data of the related Author model for a user user with:
{{ user.author.fullname }}
{{ user.author.bio }}
I receive the error above whenever users want to change their already existing profile.
This is the views.py
from django.shortcuts import render
from django.contrib.auth.decorators import login_required
from django.db import transaction
from .models import Photo
from .forms import PhotoForm
Create your views here.
#login_required
#transaction.atomic
def upload(request, pk=None):
instance = Photo.objects.get(pk=pk) if pk else None
context = dict(save_pk=pk or "")
if request.method == 'POST':
# Only backend upload should be posting here
context['backend_form'] = form = PhotoForm(request.POST, request.FILES, instance=instance)
if form.is_valid():
# Uploads image and creates a model instance for it
context['posted'] = form.save(commit=False)
context['posted'].user = request.user
context['posted'].save()
instance = Photo.objects.get(pk=pk) if pk else None
else:
# Form demonstrating backend upload
context['backend_form'] = PhotoForm(instance=instance)
return render(request, 'photo/upload.html', context)
from the stack trace, the problem is coming from this line
context['posted'].save()
How do I go about it? I want the users to be able to change their profiles without trying to create a new instance afresh.
This is the HTML template
<main class="mt-5 pt-5 container">
<!--A standard form for sending the image data to your server -->
<div class="mt-5 pt-5 container bg-white">
<h3 class="text-center">Upload</h3>
<div class="mb-3">
<div class="mb-auto p-2 bd-highlight">
<form action="{% url 'upload' %}" method="post" enctype="multipart/form-data" class="text-center mt-5 ">
{% csrf_token %}
{{ backend_form }}
</div>
<div class="p-2 bd-highlight text-center">
<input type="submit" value="Upload" class="text-center xbtn btn btn-primary">
</div>
</form>
</div>
{% if posted %}
<div class="results">
{% if posted.errors %}
Errors: {{ posted.errors }}
{% else %}
<div class="uploaded_info">
<div class="img-responsive">
{% cloudinary posted.image THUMBNAIL %}
</div>
</div>
{% endif %}
</div>
{% endif %}
</div>
</main>
<footer class="footerofupload">
{% block footer %} {% include 'footer-min.html' %} {% endblock footer %}
</footer>
models.py
from django.db import models
from cloudinary.models import CloudinaryField
from xpro.models import User
# Create your models here.
class Photo(models.Model):
# Misc Django Fields
user = models.OneToOneField(User, on_delete=models.CASCADE, null=True, related_name='photo')
create_time = models.DateTimeField(auto_now_add=True)
title = models.CharField("Title (optional)", max_length=150, blank=True)
# Points to a Cloudinary image
image = CloudinaryField('image')
""" Informative name for mode """
def __unicode__(self):
try:
public_id = self.image.public_id
except AttributeError:
public_id = ''
return "Photo <%s:%s>" % (self.title, public_id)
Below is the user model
class User(AbstractUser):
GENDER_CHOICES = (
('O', 'Other'),
('M', 'Male'),
('F', 'Female'),
)
is_student = models.BooleanField(default=False)
is_school = models.BooleanField(default=False)
is_sponsor = models.BooleanField(default=False)
email = models.EmailField(unique=True)
email_confirmed = models.BooleanField(default=False)
phone_number = models.CharField(max_length=12)
gender = models.CharField(max_length=1, choices=GENDER_CHOICES, default='O')
organization = models.ForeignKey(Organization, on_delete=models.CASCADE, null=True, editable=False)
# avatar = models.ImageField(upload_to='media/avarta', blank=True, default= '/media/avarta/default.png', null=True,)
You have a OneToOneField between User and Photo as here:
user = models.OneToOneField(User, on_delete=models.CASCADE, null=True, related_name='photo')
This essentially means for one User there can only be one Photo (and vice-versa). If you want multiple photos for one user change it to a ForeignKey:
user = models.ForeignKey(User, on_delete=models.CASCADE, null=True, related_name='photos')
If you want only one photo for a user delete any previous photos before adding any new one:
Photo.objects.filter(user=request.user).delete()
context['posted'] = form.save(commit=False)
When I try to submit my form it says "This field is required."
for an image even though I provide the image and other details to it.
forms.py file
from django.forms import ModelForm
from .models import Status
class CreatePost(ModelForm):
class Meta:
model=Status
fields = ["username","text","privacy","image"]
models.py file
class Status(models.Model):
title=models.CharField(max_length=20,default="updated status")
username = models.ForeignKey('User',on_delete=models.CASCADE)
#username = models.CharField(max_length=20)
text = models.TextField(blank=True, null=True)
image = models.ImageField(upload_to="media/image",null=True)
time = models.DateTimeField(auto_now=True)
privacy = models.CharField(max_length=5, blank=True, null=True)
gid = models.IntegerField(blank=True, null=True)
dp = models.SmallIntegerField(blank=True, null=True)
class Meta:
#unique_together = (('username', 'dp'),)
#managed = False
db_table = 'status'
view.py
def create_post(request):
form=CreatePost(request.POST or None)
if request.method=="POST":
if form.is_valid():
instance=form.save(commit=False)
instance.time=time.time()
instance.save()
return redirect('post',)
return render(request,"uposts/createpost.html",{'form':form})
createpost.html
{% extends "friendsbook/structure.html" %}
{% block content %}
<form action="" method="post">
{%csrf_token %}
{{ form.as_p }}
<input type="submit" value="Save">
</form>
{% endblock %}
What it said after clicking on save button
I am only taking 4 fields in form because all other fields can be null. For time field I took care of that in views.py by giving the time there.
You have to modify the template like this adding multipart/form-data:
<form method="post" enctype="multipart/form-data">
{% csrf_token %}
<input type="file" name="myfile">
<button type="submit">Upload</button>
</form>
and in views.py, you can access the uploaded file using request.FILES.
Hi I have a simple django form, which enables the users to signup to the website. but am confused how can I submit my form fields. Am new to Django. Please help me on it. Thanks in advance.
Forms.py:
from django import forms
from django import forms
from django.contrib.auth.models import User # fill in custom user info then save it
# from django.contrib.auth.forms import UserCreationForm
class UserForm(forms.Form):
email = forms.EmailField(max_length=100, null=True, blank=False)
first_name = forms.CharField(max_length=20)
password = forms.CharField(max_length=20, required=True, label="password", widget=forms.PasswordInput)
last_name = forms.CharField(max_length=20)
date_joined = forms.DateTimeField(auto_now_add=True, auto_now=False)
date_ = forms.DateTimeField(auto_now_add=False, auto_now=True)
Views.py
def register_user(request):
if request.method == 'POST':
print "Saisisis"
form = UserForm(request.POST) # create form object
if form.is_valid():
form.save()
return HttpResponseRedirect('/accounts/register_success')
print "blah"
args = {}
args.update(csrf(request))
args['form'] = UserForm()
# import pdb
# pdb.set_trace()
print args
return render(request, 'pages/signup.html', args)
and my html:
{% extends 'pages/base.html' %}
{% block additional_styles %}
<style>
body{
background:url(static/img/nose.jpg) no-repeat center center fixed;
-webkit-background-size: cover
-moz-background-size: cover;
-o-background-size: cover;
background-size: cover;
}
</style>
{% endblock %}
{% block contentblock %}
<div class="container well">
<h1> Please Sign Up fellas</h1>
<form method="POST" action="login.html">
{% csrf_token %}
{{ form.as_table }}
<input type="submit" value="OK">
</form>
</div>
{% endblock %}
To do what you've got there, you'd need to have a ModelForm so that when you call form.save() Django knows what the model you are creating an instance of. For example;
Forms.py:
from django import forms
from django.contrib.auth.models import User
class UserForm(forms.ModelForm):
email = forms.EmailField(max_length=100, null=True, blank=False)
first_name = forms.CharField(max_length=20)
password = forms.CharField(max_length=20, required=True, label="password", widget=forms.PasswordInput)
last_name = forms.CharField(max_length=20)
date_joined = forms.DateTimeField(auto_now_add=True, auto_now=False)
date_ = forms.DateTimeField(auto_now_add=False, auto_now=True)
class Meta:
model = User
But going from what you've got you'd need to create the model instance yourself, then set the data, then save it;
def register_user(request):
if request.method == 'POST':
form = UserForm(request.POST) # create form object
if form.is_valid():
email = form.cleaned_data['email']
user = User(email=email)
user.save()
return HttpResponseRedirect('/accounts/register_success')
i am new in django.I am doing a project where user can upload image.But i want to make sure that the user can not upload image or file until the admin approved it.now how can i do this,have prepared models for this,but for inexperience in django i can not complete it or you can say i have no clue,now help me if you can, i am giving a very simple code here.
This is my forms.py for the upload image...
from django import forms
class DocumentForm(forms.Form):
photo = forms.ImageField(
label='Select a file'
)
and this is my models.py...
from django.db import models
from django.contrib.auth.models import User
class Photo(models.Model):
name = models.CharField(max_length = 100)
photo = models.ImageField(upload_to = 'photos', blank=False,null=True)
approved = models.nullBooleanField()
uploaded_time = models.DateTimeField(auto_now_add = True,auto_now = False)
description = models.CharField(max_length = 80 , blank = False , null = True)
approved_by = models.CharField(max_length = 100)
user = models.ForeignKey(User)
here i make a field for approved as a Boolean field.
and this is view for file/image upload...
def UserImageUpload(request):
if request.method == 'POST':
form = DocumentForm(request.POST,request.FILES)
if form.is_valid():
if approved:
newdoc = Photo(photo = request.FILES['photo'],user = request.user)
newdoc.save()
else:
else:
form = DocumentForm()
uploaded_image = Photo.objects.all()
return render_to_response('myprofile/user_image_upload.html',{'uploaded_image':uploaded_image,'form':form},context_instance = RequestContext(request))
and this is the template for file or image upload...
{% extends 'base.html'%}
{% block title%}User Image Upload {% endblock %}
{%block content%}
<form action="" method="post" enctype="multipart/form-data">
{% csrf_token %}
<p>{{ form.non_field_errors }}</p>
<p>{{ form.photo.label_tag }} {{ form.photo.help_text }}</p>
<p>
{{ form.photo.errors }}
{{ form.photo }}
</p>
<p><input type="submit" value="Upload" /></p>
</form>
{%endblock%}
I think it should be like this:
1.User upload image and you set approved to false.
2.On admin page you can change approved to true.
3.Before you display image on site you check if it is approved.