Can't Upload Image In Django - python

I have a profile page where I would like to allow a user to upload a profile picture. I can edit all of the text but cannot upload an image. It works if i add the image via the Admin, but not via the user's profile page on the website. Note that when created via admin--it is uploading correctly to the directory (profile_image) that i've specified in media folder. I've created some error handling on template page, but the error generated is this: "The 'image' attribute has no file associated with it." Below is my code:
models.py
class UserProfile(models.Model):
user = models.OneToOneField(User)
first_name = models.CharField(default='',max_length=100 )
last_name = models.CharField(default='',max_length=100)
email = models.CharField(max_length=100, default='')
date_birth = models.DateField(default=datetime.datetime.now())
bio = models.TextField(default='')
image = models.ImageField(upload_to='profile_image', blank=True)
def create_profile(sender, **kwargs):
if kwargs['created']:
user_profile = UserProfile.objects.create(user=kwargs['instance'])
post_save.connect(create_profile, sender=User)
views.py
#login_required
def edit_profile(request):
profile = get_object_or_404(models.UserProfile)
if request.method == 'POST':
form = forms.EditProfileForm(data=request.POST, instance=profile)
if form.is_valid():
form.save()
return redirect('/accounts/profile')
else:
form = forms.EditProfileForm(instance=profile)
args = {'form':form}
return render(request, 'accounts/edit_profile.html', args)
forms.py
from django import forms
from django.contrib.auth.models import User
from django.contrib.auth.forms import UserChangeForm
from . import models
class UserProfileForm(forms.ModelForm):
class Meta:
model = models.UserProfile
fields = [
'first_name',
'last_name',
'email',
'date_birth',
'bio',
'image',
]
class EditProfileForm(UserProfileForm):
model = models.UserProfile
fields = [
'first_name',
'last_name',
'email',
'date_birth',
'bio',
'image',
]
settings.py
MEDIA_URL = '/media/'
MEDIA_ROOT = os.path.join(BASE_DIR, 'accounts/media')
edit_profile.html
{% extends "layout.html" %}
{% block title %}User Profile | {{ user }}{{ super }}{% endblock %}
{% block body %}
<h1>My Profile</h1>
<form action="" method="POST" enctype="multipart/form-data">{% csrf_token %}
{{ form.as_p}}
<button type="submit">Save</button>
</form>
{% endblock %}

The file comes in request.FILES, not request.POST. Do the following in the view:
form = forms.EditProfileForm(data=request.POST, files=request.FILES, instance=profile)
See the documentation on file uploads:
A view handling this form will receive the file data in request.FILES, which is a dictionary containing a key for each FileField (or ImageField, or other FileField subclass) in the form.

Please check your folder permissions also, DRF is not throwing any permission errors for me.
For Mac use : sudo chmod 777 *
For Live Server don't use this just check what is relevant and secure for your Os.

Related

Can't change user profile image in django

I am really new to django and I am building a website for internal use in my company. I have extended the user model with another model called "profile" in order to store extra information about each user, including a profile picture.
I have set up a form.py class with the data i'd like to be able to modify:
class UserForm(forms.ModelForm):
class Meta:
model = Profile
fields = ['office','role', 'userImage']
The form in the html is as follows:
<form class="form" action="" method="POST" enctype="multipart/form-data">
{% csrf_token %}
{% for field in form %}
<div class="form__group">
<label for="Profile.userImage">{{ field.label }}</label>
{{ field }}
</div>
{% endfor %}
<div class="form__action">
<a class="btn btn--dark" href="{% url 'user-profile' request.user.id%}">Cancel</a>
<button class="btn btn--main" type="submit">Update</button>
</div>
</form>
And in the views.py, here is the function that takes care of this:
def update_user(request):
user = request.user
profile = request.user.profile
if request.method == 'POST':
form = UserForm(request.POST, request.FILES, instance=profile)
if form.is_valid():
form.save()
return redirect('user-profile', pk=user.id)
else:
form = UserForm(instance = profile)
return render(request, 'base/update-user.html', {'form': form})
And the profile model is:
class Profile(models.Model):
user = models.OneToOneField(User, on_delete=models.CASCADE)
job= models.TextField(max_length=50, blank=True, verbose_name="Centro")
role= models.TextField(null=True, max_length=50, blank=True, verbose_name="Cargo")
userImage = models.ImageField(upload_to='profileImages/', default='profileImages/happy.jpg', verbose_name="Profile Image")
#receiver(post_save, sender=User)
def create_user_profile(sender, instance, created, **kwargs):
if created:
Profile.objects.create(user=instance)
#receiver(post_save, sender=User)
def save_user_profile(sender, instance, **kwargs):
instance.profile.save()
in addition, the MEDIA_ROOT and MEDIA_URL are as follows:
MEDIA_ROOT = os.path.join(BASE_DIR, 'static/images') MEDIA_URL = '/images/'
The form works fine for all fields, they both dispplay and update all the information regarding the "office" and "job" fields, but not the profile image.. It neither uploads a file to the folder I have assigned it to upload to. Even when I manually insert another image in the folder, it doesn't change it. Everything works through django admin though. Images are uploaded and changed correctly. It just wont do it via the form, no errors come up and the terminal doesn't display any issues, neither does the console in the browser.
I don't know what to do, please let me know if you need any extra information in order to diagnose the problem.
I appreciate any help provided!

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

Link Username To Post Created By User

I would like to link the user who created the post by their username that way people know who it was posted by but I can't seem to get it to work and yes I am logged in and I have a working register and login form already.
Every time I go to submit some news from the form when logged in I get this error NOT NULL constraint failed: news_news.author_id
models.py
from django.db import models
from django.contrib.auth.models import User
from markdownx.models import MarkdownxField
from markdownx.utils import markdownify
from taggit.managers import TaggableManager
class News(models.Model):
author = models.ForeignKey(User, unique=True, on_delete=models.CASCADE)
title = models.CharField(max_length=150)
short_desc = models.CharField(max_length=500)
content = MarkdownxField()
tags = TaggableManager()
slug = models.SlugField(unique=True)
created_at = models.DateTimeField(auto_now_add=True)
updated_at = models.DateTimeField(auto_now=True)
def __str__(self):
return self.title
views.py
from django.shortcuts import render, redirect, get_object_or_404
from .models import Category, News
from .forms import NewNewsForm
from django.shortcuts import render
def show_news_view(request):
news = News.objects.values('author', 'title', 'short_desc', 'tags', 'created_at', 'updated_at')
context = {
'news': news
}
return render(request, "news/news_home.html", context)
def new_news_form_view(request):
if request.method == "POST":
form = NewNewsForm(request.POST or None)
if form.is_valid():
form.save()
form = NewNewsForm()
return redirect('/news')
else:
form = NewNewsForm()
context = {
'form': form
}
return render(request, "news/news_form.html", context)
EDIT:
forms.py
from django import forms
from .models import News
class NewNewsForm(forms.ModelForm):
class Meta:
model = News
fields = ['title', 'short_desc', 'content', 'category', 'tags', 'slug']
news_form.html
{% extends "base.html" %}
{% block content %}
{% if user.is_authenticated %}
<form action="" method="POST">
{% csrf_token %}
{{ form.as_p }}
<button type="submit">Submit</button>
</form>
{% else %}
<p>Please login before you can submit a news story.</p>
{% endif %}
{% endblock content %}
You are not passing the user.
Later edit with a simpler solution:
Simply pass commit=False when saving the form. Which will create your News object without commiting it to DB. Simply set the author afterwards and save the object.
def new_news_form_view(request):
if request.method == "POST":
form_data = request.POST or None
form = NewNewsForm(form_data)
if form.is_valid():
news = form.save(commit=False)
news.author = request.user
news.save()
return redirect('/news')
else:
form = NewNewsForm()
context = {
'form': form
}
return render(request, "news/news_form.html", context)

How to extend django UserCreationForm model to include phone number field

I cant seem to find any posts here regarding extending the Django UserCreationForm model to include a phone number field for users to enter their number and then validate the phone number using phonenumbers.parse in the backend to check if the number is in the respective format and whether it exists or not. I need to know what code I should include in my forms.py under my "users" app.
I've tried including normal html text field for the phonenumbers and it does not belong to the default UserCreationForm model in Django and neither can it be stored in the database. (I need it to be stored in the database for the phone numbers). I am only using forms.py, views.py and register.html to be rendered in views as shown below, currently I am not using models.py.
/* forms.py */
from django import forms
from django.contrib.auth.models import User
from django.contrib.auth.forms import UserCreationForm
# from validate_email import validate_email
class UserRegisterForm(UserCreationForm):
email = forms.EmailField()
phone_number = forms.IntegerField(required=True)
class Meta:
model = User
fields = ['username', 'email', 'password1', 'password2']
def save(self, commit=True):
user = super(UserCreationForm, self).save(commit=False)
user.email = self.cleaned_data['email']
if commit:
user.save()
return user
/* views.py */
from django.shortcuts import render, redirect
from django.contrib import messages
from .forms import UserRegisterForm
def register(request):
if request.method == 'POST':
form = UserRegisterForm(request.POST)
if form.is_valid():
form.save()
username = form.cleaned_data.get('username')
messages.success(request, f'Account created for {username}!')
return redirect('blog-home')
else:
form = UserRegisterForm()
return render(request, 'users/register.html', {'form': form})
/* register.html */
{% extends "blog/base.html" %}
{% load crispy_forms_tags %}
{% block content %}
<div class="content-section">
<form method="POST">
{% csrf_token %}
<fieldset class="form-group">
<legend class="border-bottom mb-4">Join Today</legend>
{{ form|crispy }}
</fieldset>
<div class="form-group">
<button class="btn btn-outline-info" type="submit">Sign
Up</button>
</div>
</form>
<div class="border-top pt-3">
<small class="text-muted">
ALready Have An Account? <a class="ml-2" href="#">Sign In</a>
</small>
</div>
</div>
{% endblock content %}
I need to include a phone number field as part of the UserCreationForm in django and validate the number to check if it exists or not and then save the number in the database.
I usually extend Django User Model using OneToOneLink
models.py
from django.db import models
from django.contrib.auth.models import User
class UserProfile(models.Model):
user = models.OneToOneField(User, on_delete=models.CASCADE)
phone = models.CharField(max_length=11, blank=True) # change the field to watever works for you
# This will auto create a profile of user with blank phone number that can be updated later.
#receiver(post_save, sender=User)
def create_user_profile(sender, instance, created, **kwargs):
if created:
UserProfile.objects.create(user=instance)
forms.py
class UserForm(forms.ModelForm):
class Meta:
model = User
fields = ('first_name', 'last_name', 'email')
class UserProfileForm(forms.ModelForm):
class Meta:
model = UserProfile
fields = ('phone')
views.py
def create_user(request):
if request.method == 'POST':
user_form = UserForm(request.POST, instance=request.user)
profile_form = ProfileForm(request.POST, instance=request.user.user_profle)
if user_form.is_valid() and profile_form.is_valid():
user_form.save()
profile_form.save()
messages.success(request, _('New user created successfully'))
return redirect('settings:profile')
else:
messages.error(request, _('Please correct the error below.'))
else:
user_form = UserForm(instance=request.user)
profile_form = ProfileForm(instance=request.user.user_profile)
return render(request, 'template_name.html', {
'user_form': user_form,
'profile_form': profile_form
})
template:
<form method="post">
{% csrf_token %}
{{ user_form.as_p }}
{{ profile_form.as_p }}
<button type="submit">Save changes</button>
</form>
UPDATE to initial answer. I decided while the solution below worked it was preferable to inherit the AbstractUser model and add my own requirements in my own bespoke user model. Once in place it's far more straight forward dealing with views and templates. I hesitated at first as I didn't feel confident enough to mess around with the default User model, but it is actually very simple. It also helped me understand abstracting models in general via.
class Meta:
abstract = True
The posts here were very helpful.
How to Extend Django User model using AbstractUser
Extending User Model
Previous post:
I've been struggling with this issue also. I've found a solution that works ok, but may have some pitfalls, which it would be good to get views on. My solution is a combination of the other answer to this question, with two modifications. Firstly the code above should be used to update a user rather than create one, because at registration no user profile exists so can't be called. Secondly, I removed the create_user_profile method on the model and used the answer posted here How to Extend UserCreateForm
to save the extended user information at registration. The reason for removing the create_user_profile was to prevent interference with the save() method on the form. The extended model i'm using is called Account.
I also found this article useful extending the django user model, and I'm still considering whether one of the other options might be more appropriate.
My code looks like this:
Views:
def register_view(request):
form = AccountRegisterForm(request.POST or None)
if form.is_valid():
form.save()
return redirect("accounts:login")
context = {"form": form}
return render(request, "accounts/register.html", context)
def user_update_view(request):
user_obj = User.objects.get(username=request.user)
account_obj = Account.objects.get(user=request.user)
user_form = UserForm(request.POST or None, instance=user_obj)
account_form = AccountForm(request.POST or None, instance=account_obj)
if user_form.is_valid() and account_form.is_valid():
user_form.save()
account_form.save()
return redirect(reverse("accounts:detail"))
context = {
"account_form": account_form,
"user_form": user_form,
}
return render(request, "accounts/account_update.html", context)
Forms
class AccountRegisterForm(UserCreationForm):
group = forms.ModelChoiceField(queryset=Group.objects)
dir = forms.ModelChoiceField(queryset=Directorate.objects)
class Meta:
model = User
fields = (
"username",
"first_name",
"last_name",
"group",
"dir",
)
def save(self, commit=True):
if not commit:
raise NotImplementedError(
"Can't create User and UserProfile without database save"
)
user = super(AccountRegisterForm, self).save(commit=True)
user_account = Account(
user=user,
group=self.cleaned_data["group"],
dir=self.cleaned_data["dir"],
)
user_account.save()
class UserForm(forms.ModelForm):
class Meta:
model = User
fields = ("username", "first_name", "last_name")
class AccountForm(forms.ModelForm):
class Meta:
model = Account
fields = (
"group",
"dir",
)

Image not being uploaded//something wrong with python code in my views.py probably

Hello I followed steps here;Need a minimal Django file upload example I'm not sure what I did wrong. I'm trying to add a feature that user to be able to post pictures as well. Here's my try
settings.py
MEDIA_ROOT = os.path.join(os.path.dirname(BASE_DIR), "static_in_env", "media_root")
MEDIA_URL = '/media/'
models.py
class Category(models.Model):
image = models.ImageField(upload_to='images',blank=True, null=True)
forms.py
class CategoryForm(forms.ModelForm):
name = forms.CharField(max_length=128)
description = forms.CharField(max_length=300)
image = forms.ImageField()
class Meta:
model = Category
my views.py
#login_required
def add_category(request):
if not request.user.is_superuser and Category.objects.filter(author=request.user).exists():
return render(request,'main/category_already_exists.html')
if request.method == 'POST':
category = Category(author=request.user)
form = CategoryForm(request.POST, instance=category)
if form.is_valid():
form.save(commit=True)
return redirect('index')
else:
form = CategoryForm()
return render(request, 'main/add_category.html', {'form':form})
category.html
{% load staticfiles %}
{{category.image}}
I am assuming you are missing enctype form attribute in your template.
<form method="POST" enctype="multipart/form-data">
{{ form.as_p }}
<input type="submit" value="Submit" />
</form>
Also, in your views, instantiating your form should be
form = CategoryForm(request.POST, request.FILES, instance=category)

Categories

Resources