I'm trying to create an edit form for existing users, I have the User model and I associated to it a profile.
The problem is that the fields of profile are empty in the rendered html, however when I created a new user I filled these fields, and when I enter to administration I find the fields are filled.
models.py
from django.db import models
from django.contrib.auth.models import User
class Profile(models.Model):
DEPARTMENT_CHOICES = (('MI', 'Math et info'),
('ST', 'Science et Tech'),
('SM', 'Science de la matiere'))
user = models.OneToOneField(User, on_delete=models.CASCADE)
teacher = models.BooleanField(default=False)
description = models.TextField(blank=True)
department = models.CharField(max_length=35, choices=DEPARTMENT_CHOICES, blank=True)
picture = models.ImageField(upload_to='profile-images', blank=True)
def __str__(self):
return self.user.username
views.py
def profile_view(request):
if request.method == 'POST':
user_form = EditUserForm(request.POST, instance=request.user)
ins = Profile.objects.get(pk=5)
profile_form = EditProfileForm(request.POST, request.FILES, instance=ins)
if user_form.is_valid() and profile_form.is_valid():
user = user_form.save()
user.save()
profile = profile_form.save(commit=False)
profile.user = user
if 'picture' in request.FILES:
profile.picture = request.FILES['picture']
profile.save()
return redirect(home)
else:
user_form = EditUserForm(instance=request.user)
profile_form = EditProfileForm(request.FILES, instance=request.user)
return render(request, 'account/profile.html', {'user_form': user_form,
'profile_form': profile_form})
forms.py
class EditProfileForm(forms.ModelForm):
class Meta:
model = Profile
fields = ('description', 'department', 'picture', )
class EditUserForm(forms.ModelForm):
class Meta:
model = User
fields = ('username', 'email', )
profile.html
{% extends 'manhal/base.html' %}
{% load staticfiles %}
{% load crispy_forms_tags %}
{% block content %}
<div class="col-md-6">
<form method="post" enctype="multipart/form-data" action="{% url 'profile' %}" class="form-horizontal">{% csrf_token %}
<fieldset>
<legend>User Profile</legend>
{{ user_form|crispy }}
{{ profile_form|crispy}}
<input type="submit" value="Save" class="btn btn-primary">
</fieldset>
</form>
</div>
{% endblock %}
First, your if/else block is checking if the request is a POST. Since the else block is not a POST, you do not want to pass any POST data into your form. This will make the form think it's bound with no data.
Also, it looks like you are passing the request.user to your ProfileForm as the instance, but the model on the ProfileForm meta class is expecting a Profile object.
Can you fix those two things and see if it works or not? If it doesn't work, please post some more code (like your templates).
Related
When I try to create a new user, I also want to create their profile at the same time. But there is no data coming from the Profile form. User is created but in Profile creation form I am getting an error.
Here is my models.py
class Profile(models.Model):
user = models.OneToOneField(User, on_delete=models.CASCADE)
StudentID = models.IntegerField(primary_key=True, verbose_name='SID')
image = models.ImageField(default='default.jpeg', upload_to='profile_pics', blank=True)
def __str__(self):
return self.Branch
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)
forms.py
class UserRegisterForm(UserCreationForm):
email = forms.EmailField()
first_name = forms.CharField()
last_name = forms.CharField()
class Meta:
model = User
fields = ['username', 'email', 'first_name', 'last_name', 'password1', 'password2']
class ProfileCreationForm(forms.ModelForm):
class Meta:
model = Profile
fields = ['StudentID', 'Branch', 'YearOfStudy', 'ContactNumber']
views.py
when I try to print StudentID, I get None value. I think there is data loss.
def register(request):
print("hello")
if request.method == 'POST':
form = UserRegisterForm(request.POST)
form1 = ProfileCreationForm(request.POST)
if form.is_valid() and form1.is_valid():
user = form.cleaned_data.get('username')
StudentID = form.cleaned_data.get('StudentID')
print(StudentID)
profile = form1.save(commit=False)
profile.user = user
form.save()
profile.save()
messages.success(request, f'Your account has been created! You are now able to log in')
print("reached here")
return redirect('login')
else:
form = UserRegisterForm()
form1 = ProfileCreationForm()
context = {
'form': form,
'form1': form1
}
return render(request, 'user/register.html', context)
register.html
{% 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 }}
{{ form1|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="{% url 'login' %}">Sign In</a></small>
</div>
</div>
{% endblock content %}
StudentID is on form1 not form
StudentID = form1.cleaned_data.get('StudentID')
print(StudentID)
You should also save the User first since it's a required relation for the Profile model
user = form.save()
profile = form1.save(commit=False)
profile.user = user
profile.save()
Apart from what #Iain has pointed out, I think your code in the views has some problem after you run is_valid. Try to fix it like this:
if form.is_valid() and form1.is_valid():
profile = form1.save(commit=False)
user = form.save()
profile.user = user
profile.save()
messages.success(request, 'Your account has been created! You are now able to log in')
return redirect('login')
Let me explain why you might get error, that is profile.user is a User instance, not a string. So putting username will not work. But you can get the user object when you save the form. Like user=form.save(), so use that in profile.user.
I'm using the following line to fill in data into the form.
form = EditProfileForm(request.POST, instance=request.user)
However, no data fills into the form. I just get the empty form. Not sure what's going wrong. I have a profile html where the data from each user shows up, but the line above is not working on the edit page. The model is the default user model. Django version 2.2.3.
#views.py
def editprofile(request):
if request.method == 'POST':
form = EditProfileForm(request.POST, instance=request.user)
if form.is_valid():
agree = form.save(commit=False)
agree.save()
args = {'form': form}
else:
form = EditProfileForm(request.POST)
args = {'form': form}
return render(request, 'profile_edit.html', {'form':form})
Here is my forms.py:
class EditProfileForm(UserChangeForm):
username = forms.CharField(label='Username', widget=forms.TextInput(attrs={'class': "form-control"}))
first_name = forms.CharField(label='First Name', widget=forms.TextInput(attrs={'class': "form-control"}))
last_name = forms.CharField(label='Last Name', widget=forms.TextInput(attrs={'class': "form-control"}))
email = forms.CharField(label= 'Email', widget=forms.EmailInput(attrs={'class': "form-control"}))
class Meta:
model = User
fields = ['username', 'first_name', 'last_name', 'email', 'password']
def save(self, commit=True):
user = super(EditProfileForm, self).save(commit=False)
user.username = self.cleaned_data['username']
user.first_name = self.cleaned_data['first_name']
user.last_name = self.cleaned_data['last_name']
user.email = self.cleaned_data['email']
if commit:
user.save()
return user
template
{% block content %}
<h3> Please edit your profile here </h3>
<div class="container">
<form method="post">
{% csrf_token %}
{{ form.as_p }}
<button href="{% url 'profilepage' %}" type="submit" >Submit</button>
</form>
<br>
</div>
{% endblock %}
Added urls.py
path('profile/', views.profile, name = 'profilepage'),
path('profile/edit/', views.editprofile, name = 'editprofile')
Try adding action to your form tag in template.
Like this:
<form action= "{% url 'editprofile' %}" method="post">
'editprofile' is the url_name of your editprofile view. Also the class Meta and method def save() should be inside your class EditProfileForm() block.
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
When I update the user profile via the view everything is saving to the db except the image. The forms are validating but image isn't being saved. I can log in the admin portal and successfully add an image to an existing instance. I assume my problem lies in my html template but I can't figure out what it is.
**Btw I've read multiple similiar post but none I believe addresses my issue.
form.py
class EditUserForm(forms.ModelForm):
template_name='/something/else'
class Meta:
model = User
fields = (
'email',
'first_name',
'last_name',
)
class EditProfileForm(forms.ModelForm):
template_name='/something/else'
class Meta:
model = UserProfile
fields = (
'description',
'city',
'website',
'phone',
'image',
)
views.py
#transaction.atomic
def edit_profile(request):
if request.method == 'POST':
form = EditUserForm(request.POST, instance=request.user)
form2 = EditProfileForm(request.POST, instance=request.user.userprofile)
if form.is_valid() and form2.is_valid():
form.save()
form2.save()
return redirect(reverse('accounts:view_profile'))
else:
form = EditUserForm(instance=request.user)
form2 = EditProfileForm(instance=request.user.userprofile)
args = {'form': form, 'form2':form2}
return render(request, 'accounts/edit_profile.html', args)
models.py
class UserProfile(models.Model):
user = models.OneToOneField(User, on_delete=models.CASCADE)
description = models.CharField(max_length=100, default='')
city = models.CharField(max_length=100, default='')
website = models.URLField(default='')
phone = models.IntegerField(default=0)
image = models.ImageField(upload_to='profile_image', blank=True)
def __str__(self):
return self.user.username
edit_profile.html
<div class="container">
{% if form.errors %}
<ol>
{% for field in form %}
<H3 class="title">
<p class="error"> {% if field.errors %}<li>{{ field.errors|striptags }}</li>{% endif %}</p>
</H3>
{% endfor %}
</ol>
{% endif %}
<form method="post" enctype="multipart/form-data">
{% csrf_token %}
{{ form.as_p }}
{{ form2.as_p }}
<button type="submit">Submit</button>
</form>
<br>
</div>
If you are uploading files, you must instantiate the form with request.POST and request.FILES.
form2 = EditProfileForm(request.POST, request.FILES, instance=request.user.userprofile)
See the docs on file uploads for more info.
Okay so it appears I'm in way over my head on this small task. I'd like to ask how exactly does one submit to a form, without relying on the URL values?
In my example, the user has to log in before they can see their gallery pictures. Determining this is via "context", which has the active user (as logged in) assigned to it. Props to #chem1st & #Daniel-Roseman for the assistance earlier in helping me figure that out yesterday. Now it can display their own user gallery in the homepage after they log in.
I prefer not uploading with "blahblah.com/bobby/upload", because it doesn't seem very secure. I'd like to let logged in users upload via "blahblah.com/upload/". Which means the form in the view.py would have to get the context of the user who's logged in, somehow, and save the data to the database under that account.
I've been toying around, and searching for answers, but can't find anything. Can someone help point me in the right direction?
Here's my models.py
class UserProfile(models.Model):
user = models.OneToOneField(User)
activation_key = models.CharField(max_length=40, blank=True)
key_expires = models.DateTimeField(default=datetime.date.today())
def __str__(self):
return self.user.username
class Meta:
verbose_name_plural='User profiles'
class ImageDoc(models.Model):
user = models.ForeignKey(UserProfile)
imgfile = models.ImageField(upload_to='images/')
forms.py:
class RegistrationForm(UserCreationForm):
email = forms.EmailField(required=True, widget=forms.TextInput(attrs={'placeholder': 'E-mail address'}))
first_name = forms.CharField(required=True)
last_name = forms.CharField(required=True)
class Meta:
model = User
fields = ('first_name', 'last_name', 'email', 'username', 'password1', 'password2')
class ImgDocForm(forms.Form):
user_file = forms.ImageField()
def clean_user_file(self, *args, **kwargs):
cleaned_data = super(ImgDocForm,self).clean()
user_file = cleaned_data.get("user_file")
if user_file:
if user_file.size > 5 * 1024 * 1024:
raise forms.ValidationError("Filesize is too big.")
if not os.path.splitext(user_file.name)[1].strip().lower() in ['.jpg','.png','.gif','.jpeg']:
raise forms.ValidationError("File does not look like as picture.")
return user_file
class UserForm(forms.Form):
class Meta:
model = User
fields = ['first_name', 'last_name', 'password', 'email', 'username']
My views.py file (EDIT: Changed the Index to display the user details, the gallery, and a quick upload function):
def sign_in(request):
context = RequestContext(request)
if request.method == 'POST':
username = request.POST['username']
password = request.POST['password']
user = authenticate(username=username, password=password)
if user:
if user.is_active:
login(request, user)
return HttpResponseRedirect('/', context)
else:
return HttpResponse("Verify your account!")
else:
return HttpResponse("Invalid login details supplied.")
def populateContext(request, context):
context['authenticated'] = request.user.is_authenticated()
if context['authenticated'] == True:
context['username'] = request.user.username
def index(request):
user_details = UserProfile.objects.get(user=request.user)
gallery = ImageDoc.objects.filter(user_id=request.user.id)
if request.method == 'POST':
form = ImgDocForm(request.POST, request.FILES)
if form.is_valid():
origin_form = form.cleaned_data["user_file"]
origin_name = origin_form.name
original_name = ImageDoc(user_id=request.user.id, imgfile=origin_name)
original_name.save()
return HttpResponse('Saved!')
else:
form = ImgDocForm()
documents = ImageDoc.objects.all()
return render(request, 'test.html', {'documents': documents, 'form': form, 'user_details': user_details, 'gallery': gallery})
def upload(request):
data = {}
thumb_size = (100,100)
micro_thumb_size = (50,50)
if request.method == 'POST':
userform = ImgDocForm(request.POST, request.FILES)
if userform.is_valid():
origin_form = userform.cleaned_data["user_file"]
origin_name = origin_form.name
original_file = os.path.join(settings.MEDIA_ROOT, origin_name)
.
.
.
original_name = ImageDoc(imgfile=origin_name)
original_name.save()
.
.
.
userform = ImgDocForm()
else:
return HttpResponse('Nooo!')
else:
userform = ImgDocForm()
data.update(image_gallery = ImageDoc.objects.only('imgfile'))
data.update(userform=userform)
data.update(csrf(request))
return render(request, 'upload.html', data)
EDIT: I'm sure folks can clean up the index file significantly. Also, not very elegant at the bottom there, but it works.
And the upload.html document:
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="utf-8">
</head>
<body>
<div>
<form method="post" action="" enctype="multipart/form-data">
{% csrf_token %}
{{ userform.as_p }}
<input type="submit">
</form>
<br><br>
<h2>{{ origin_name }} (original)</h2>
{% if origin_name %}
<img src="{{ MEDIA_URL }}{{ origin_name }}">
{% endif %}
<br><br>
{% if image_gallery %}
{% for image in image_gallery %}
<img src="/{{ image.thumbfile }}">
{% endfor %}
{% endif %}
</div>
</body>
</html>
Thank you!
You can get currently logged in user inside the view as request.user.