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})
Related
I am (attempting to) implement the ability for a user to edit and update their email address on their profile page. I am getting no errors when doing this end to end but the new email is not being saved to the DB.
Everything seems to be working, even the redirect to the profile page in the edit_profile function, but the save() doesn't seem to be working, the users email doesn't update and when I am redirected back to the profile page, the email is still the current value.
Thanks!
Model:
class CustomUser(AbstractUser):
email = models.EmailField(_('email address'), unique=True)
is_pro = models.BooleanField(default=False)
is_golfer = models.BooleanField(default=False)
def __str__(self):
return self.email
Form
class EditProfileForm(forms.Form):
email = forms.EmailField(
label='', widget=forms.TextInput(attrs={'class': 'form-field'}))
View
#login_required
def edit_profile(request):
if request.method == "POST":
form = EditProfileForm(request.POST)
if form.is_valid():
email = form.cleaned_data["email"]
user = CustomUser.objects.get(id=request.user.id)
user.save()
return redirect("typeA", username=user.username)
else:
form = EditProfileForm()
return render(request, "registration/edit_profile.html", {'form': form})
URLS
urlpatterns = [
path('type_a_signup/', ASignUpView.as_view(), name='a_signup'),
path('type_b_signup/', BSignUpView.as_view(), name='b_signup'),
path('login/', LoginView.as_view(), name='login'),
path('password_reset', PasswordResetView.as_view(), name='password_reset'),
path('typea/<username>/', typeA, name='typeA'),
path('typeb/<username>/', typeB, name='typeB'),
path('login_success/', login_success, name='login_success'),
path('edit_profile/', edit_profile, name='edit_profile'),
]
Template
<div class="container">
<div class="form-container">
<h2>Edit profile</h2>
<form method="post" enctype="multipart/form-data">
{% csrf_token %}
<div>
{{ form.email.label_tag }}
<input type="text" class="form-control {% if form.email.errors %}is-invalid{% endif %}" id="id_email"
name="email" value='{{ form.email.value|default:user.email }}'>
{% if form.email.errors %}
<div>{{ form.email.errors }}</div>
{% endif %}
</div>
<button type="submit">Submit</button>
</form>
<br>
</div>
You never set the email field of the object. You should set this with:
#login_required
def edit_profile(request):
if request.method == "POST":
form = EditProfileForm(request.POST)
if form.is_valid():
email = form.cleaned_data["email"]
user = request.user
user.email = email # 🖘 set the email field
user.save()
return redirect("typeA", username=user.username)
else:
form = EditProfileForm()
return render(request, "registration/edit_profile.html", {'form': form})
You should only redirect in case the form is successful. If it is not, Django will rerender the form with the errors.
So I'm making a to-do list and I made a booleanfield modelform which has attribute "complete". I want that user to check it when it's complete and I tried wriring if task.complete == True cross out the item and it didn't work(it only worked when I checked it from the admin panel). Then I tried form.complete instead of task.complete and it doesn't do anything.
models:
class Task(models.Model):
title = models.CharField(max_length=200)
complete = models.BooleanField(default=False)
created = models.DateTimeField(auto_now_add=True)
def __str__(self):
return self.title
forms:
from .models import *
class TaskForm(forms.ModelForm):
title = forms.CharField(widget= forms.TextInput(attrs={'placeholder':'Add new task...'}))
class Meta:
model = Task
fields = '__all__'
html:
<div class="main-container">
<form method="POST" action="/">
{% csrf_token %}
<input type="submit"/>
{{ form.title }}
</form>
{% for task in tasks %}
{{ form.complete }}
{% if form.complete == True %}
<h1><strike>{{task.title}}</strike></h1>
{% else %}
<h1>{{task.title}}</h1>
{% endif %}
{% endfor %}
</div>
views:
def index(request):
tasks = Task.objects.order_by('-created')
form = TaskForm()
if request.method == 'POST':
form = TaskForm(request.POST)
if form.is_valid():
form.save()
return redirect('/')
context = {
'tasks': tasks,
'form': form,
}
return render(request, 'to-do.html', context)
There are some problems with your code I don't know how to explain. Try this. It should work.
<div class="main-container">
<form method="POST" action="/"> # create new task
{% csrf_token %}
{{ form.title }}
<input type="submit" name="new-task"/>
</form>
<form method="post"> # update task status
{% csrf_token %}
{% for task in tasks %}
{% if task.complete %}
<input type="checkbox" name="if_completed" checked value="{{ task.pk }}">
<h1><strike>{{task.title}}</strike></h1>
{% else %}
<input type="checkbox" name="if_completed" value="{{ task.pk }}">
<h1>{{task.title}}</h1>
{% endif %}
{% endfor %}
<input type="submit" name="update-task"/>
</form>
</div>
view.py (Only for form, add your other code with it)
from django.http import HttpResponseRedirect
from django.urls import reverse
def index(request):
tasks = Task.objects.order_by('-created')
form = TaskForm()
if request.method == 'POST':
if 'new-task' in request.POST:
form = TaskForm(request.POST)
if form.is_valid():
form.save()
return HttpResponseRedirect(reverse('home')) # replace home with url name where you want to redirect
elif 'update-task' in request.POST:
task_pk = request.POST.getlist("if_completed")
for i in task_pk:
Task.objects.filter(pk=i).update(complete=True) # I have replace pk with i here
return HttpResponseRedirect(reverse('home')) # replace home with url name where you want to redirect
context = {
'tasks': tasks,
'form': form,
}
return render(request, 'to-do.html', context)
in forms.py
class TaskForm(forms.ModelForm):
class Meta:
model = Task
fields = ('title',)
widgets = {
'title': forms.TextInput(attrs={'placeholder':'Add new task...'})
}
This should work. There may be be some error because of typos or indentation. Let me know if you get any issue
def index(request):
tasks = Task.objects.order_by('-created')
form = TaskForm()
context = {
'tasks': tasks,
'form': form,
}
if request.method == 'POST':
if 'new-task' in request.POST:
form = TaskForm(request.POST)
if form.is_valid():
form.save()
elif 'update-task' in request.POST:
task_pk = request.POST.getlist("if_completed")
for i in task_pk:
Task.objects.filter(pk=pk).update(complete=True)
return render(request, 'to-do.html', context)
Sorry if this is too much code, but I believe it is all relevant to the question at hand.
Long story short, on my series_detail page, all episodes belonging to each series is shown, as well as forms to add a new episode or edit an existing one.
The edit episode form, however, requires an instance, which always returns the very first object of the episodes queryset. This is presumably because of the .first(), but I used this since you can only have one object passed as an instance.
What I am trying to achieve is:
after showing the edit modal next to each episode, show the instance of each episode instead of only the first episode.
save only that episode's instance after the form is filled
achieve this without redirecting to an edit page
models.py
class Series(models.Model):
name = models.CharField(max_length=100)
class Episode(models.Model):
series = models.ForeignKey(Series, on_delete=models.CASCADE)
name = models.CharField(max_length=100)
episode_no = models.IntegerField(null=True)
description = models.TextField(max_length=500)
image = models.ImageField(upload_to='pics/episodes',)
forms.py
class EpisodeForm(forms.ModelForm):
name = forms.CharField()
description = forms.CharField(widget=forms.Textarea, required=False)
episode_no = forms.IntegerField()
class Meta:
model = Episode
fields = ['name', 'description', 'episode_no' ,'image']
views.py
def series_detail(request, pk):
try:
series = Series.objects.get(pk=pk)
except:
return render(request, 'error_404.html')
episodes = Episode.objects.filter(series=series).first()
if request.method == 'POST':
if 'addepisodeform' in request.POST:
e_form = EpisodeForm(request.POST, request.FILES, prefix='addepisode')
e_form.instance.series = Series.objects.get(pk=pk)
if e_form.is_valid():
e_form.save()
return redirect('series_detail', pk=pk)
messages.success(request, 'Episode was created')
else:
return redirect('series_detail', pk=pk)
messages.error(request, 'Episode was not created')
elif 'editepisodeform' in request.POST:
edit_e_form = EpisodeForm(request.POST, request.FILES, instance=episodes, prefix='editepisode')
edit_e_form.instance.series = Series.objects.get(pk=pk)
if edit_e_form.is_valid():
edit_e_form.save()
return redirect('series_detail', pk=pk)
messages.success(request, 'Episode was updated')
else:
return redirect('series_detail', pk=pk)
messages.error(request, 'Episode was not updated')
else:
e_form = EpisodeForm(prefix='addepisode')
edit_e_form = EpisodeForm(instance=episodes, prefix='editepisode')
context = {
'episodes': episodes,
'e_form': e_form,
'edit_e_form': edit_e_form
}
return render(request, 'series/series_detail.html', context)
def delete_episode(request, pk1, pk2):
try:
series = Series.objects.get(pk=pk1)
except:
return render(request, 'error_404.html')
try:
episode = Episode.objects.get(series=series, episode_no=pk2)
except:
return render(request, 'error_404.html')
episode.delete()
return redirect('series_detail', pk=pk1)
urls.py
path('series/<int:pk>/', views.series_detail, name='series_detail'),
path('series/<int:pk1>/episode/<int:pk2>/delete/', views.delete_episode, name='delete_episode'),
series_detail.html
<button type="submit" name="addepisodeform">
Add Episode
<form method="POST" enctype="multipart/form-data">
{% csrf_token %}
{{ e_form }}
</form>
</button>
{% for episode in episodes %}
{{ episode.name }}
{{ episode.description}}
<img src="{{ episode.image.url }}" height="125px" width="300px" style="object-fit: cover;">
<button type="submit" name="editepisodeform">
Edit Episode
<form method="POST" enctype="multipart/form-data">
{% csrf_token %}
{{ edit_e_form }}
</form>
</button>
{% endfor %}
Okay, so it turns out that formsets were the way to go after all. Thanks to Willem Van Onsem's answer, I decided to go that route after all and it worked like a charm.
A form can only edit one instance, but with formsets, I was able to not only edit each episode rather than just the first instance, but even create a new object and delete multiple objects at the same time!
views.py
#import the formset
from django.forms import modelformset_factory, inlineformset_factory
#formset
EpisodeFormset = inlineformset_factory(Series, Episode, fields=('name', 'episode_no', 'description', 'image'), can_delete=True, extra=1)
#post call
if request.method == 'POST':
if 'editepisodeform' in request.POST:
formset = EpisodeFormset(request.POST, request.FILES, instance=series, prefix='editepisode')
if formset.is_valid():
formset.save()
return redirect('series_detail', pk=pk)
else:
return redirect('series_detail', pk=pk)
else:
formset = EpisodeFormset(instance=series)
series_detail.html
<div>
<form method="POST" enctype="multipart/form-data">
{% csrf_token %}
{{ formset.management_form }}
{% for form in formset %}
<button type="submit" class="btn btn-primary" name="editepisodeform">Edit</button>
{{ form.as_p }}
{% for episode in episodes %}
{% if episode.episode_no == form.episode_no.value %}
Episode: {{ episode.episode_no }} <br>
Name: {{ episode.name }} <br>
<img src="{{ episode.image.url }}" height="125px" width="300px" style="object-fit: cover;"> <br> {% endif %}
{% endif %}
{% endfor %}
{% endfor %}
</form>
</div>
I have two models with two different forms. One has a ForeignKey for another model, making a 1-N relationship. The problem is when i try to add images , is not working. The form where i change the (name/TabletForm2) is working so , only when i try to add (image/TabletFormImagem) is not working.
The problem is that
My model's
def get_image(instance, filename):
return os.path.join('intervencao/fotografias', str(instance.intervencao), filename)
class Intervencao(models.Model):
name= models.CharField(verbose_name="Name", max_length=200, blank=True, null=True)
class Imagem(models.Model):
intervencao = models.ForeignKey(Intervencao, related_name='intervencaoObjectsImagem', on_delete=models.CASCADE)
imagem = models.ImageField(upload_to=get_image, blank=True, null=True, verbose_name="Fotografia")
def __str__(self, ):
return str(self.intervencao)
My View
def project_detail_chefe(request, pk):
form = TabletForm2(request.POST)
form2 = TabletFormImagem(request.POST, request.FILES)
if request.method == "POST":
if form.is_valid():
form.save()
return redirect('index')
else:
form = TabletForm2(request.POST)
if form2.is_valid():
//when i print something here to see if the form2 is valid , never enter here.
form2.save()
return redirect('index')
else:
form2 = TabletFormImagem()
context = {
'form':form,
'form2':form2,
}
return render(request, 'tablet/info_chefes.html', context)
tablet/info_chefes.html
<div class="col-md-12">
<div class='form-group'>
<label for="{{ form.subject.id_for_label }}" id="titulo">Name:</label>
<em>{{ form.name}}</em>
</div>
</div>
<div class="col-md-12">
<form method="post" enctype="multipart/form-data">
{% csrf_token %}
{{ form2.as_p }}
<button type="submit">Upload</button>
</form>
</div>
Forms
class TabletForm2(forms.ModelForm):
class Meta:
model=Intervencao
fields = ['data_intervencao_fim','ferramenta']
class TabletFormImagem(forms.ModelForm):
class Meta:
model=Imagem
fields = ['imagem']
def project_detail_chefe(request, pk):
form = TabletForm2()
form2 = TabletFormImagem()
if request.method == "POST":
form = TabletForm2(request.POST)
form2 = TabletFormImagem(request.POST, request.FILES)
if form.is_valid() and form2.is_valid():
instance_form1 = form.save()
instance_form2 = form2.save(commit=False)
instance_form2.intervencao = instance_form1
instance_form2.save()
return redirect('index')
else:
form = TabletForm2()
form2 = TabletFormImagem()
context = {
'form':form,
'form2':form2,
}
return render(request, 'tablet/info_chefes.html', context)
HTML
<form method="post" enctype="multipart/form-data">
<div class="col-md-12">
<div class='form-group'>
<label for="{{ form.subject.id_for_label }}" id="titulo">Name:</label>
<em>{{ form.name}}</em>
</div>
</div>
<div class="col-md-12">
{% csrf_token %}
{{ form2.as_p }}
<button type="submit">Upload</button>
</div>
</form>
I have a standard Form to edit an object that has a file field in it. When I add a new profile, everything works fine, but when I want to modify a field, then problems happen: when I don't include request.FILES, all fields get retrieved in the form and can be updated with no problems, except the avatar (filefield), but when I add the request.FILES, fields are no longer retrieved in the inputs, and the form is not valid. What am I doing wrong?
models.py:
from django.db import models
class Profile(models.Model):
name = models.CharField(max_length=45)
email = models.EmailField()
avatar = models.FileField(upload_to="avatars/")
def __str__(self):
return self.name
forms.py:
from django import forms
from .models import Profile
class EditUserForm(forms.ModelForm):
class Meta:
model = Profile
fields = ["name", "email", "avatar"]
profile.html:
<form method="POST">
{% csrf_token %}
<div class="col-md-12">
<div class="row border m-3 p-5">
<div class="col-md-6">Name</div>
<div class="col-md-6">{{ form.name }}</div>
</div>
<div class="row border m-3 p-5">
<div class="col-md-6">Email</div>
<div class="col-md-6">{{ form.email }}</div>
</div>
<div class="row border m-3 p-5">
<div class="col-md-6">Avatar</div>
<div class="col-md-6">
<img src="{{ profile.avatar.url }}" width="100rem" />
{{ form.avatar }}
</div>
</div>
<div class="row">
<button type="submit" class="btn btn-success">Save</button>
</div>
</div>
</form>
views.py:
def profile(request, pk):
profile_instance = Profile.objects.get(id=pk)
form = EditUserForm(request.POST, request.FILES, instance = profile_instance)
context = {
'profile': profile_instance,
'form': form,
}
if request.method == "POST":
if form.is_valid():
profile = form.save(commit=False)
profile.save()
else:
print("form not valid")
else:
return render(request, "app/profile.html", context)
return render(request, "app/profile.html", context)
Special thank's to #bruno desthuilliers and #dirkgroten.
Here is the answer to solve the 2 problems:
def profile(request, pk):
if request.method == 'POST':
form = EditUserForm(request.POST, request.FILES, instance = Profile.objects.get(id=pk))
if form.is_valid():
profile = form.save(commit=False)
profile.save()
return redirect('profile', pk)
else:
form = EditUserForm(instance = Profile.objects.get(id=pk))
context = {
'profile': Profile.objects.get(id=pk),
'form': form,
}
return render(request, "app/profile.html", context)
Try refactoring the code as follows: (edit it if I missed something. After form.is_valid, the same form should be sent in the context to display the errors.
def profile(request, pk):
profile_instance = Profile.objects.get(id=pk)
form = EditUserForm(instance = profile_instance)
if request.method == "POST":
form = EditUserForm(request.POST, request.FILES, instance = profile_instance)
if form.is_valid():
profile = form.save(commit=False)
profile.save()
form = EditUserForm(instance = profile_instance)
context = {
'profile': profile_instance,
'form': form,
}
return render(request, "app/profile.html", context)