Editing image field using Django forms - python

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

Django database relation

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 }}

duplicate key value violates unique constraint "photo_photo_user_id_key" DETAIL: Key (user_id)=(102) already exists

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)

Upload image via ModelForm in django

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.

Django forms not working

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')

Restricting upload until the admin approved the uploaded file in Django

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.

Categories

Resources