I am making a form in which the user can edit their profile, so currentley I have 2 forms, one that edits the User model(first_name, Username and Email) and other one that edits the Profile model(biography). The problem is that everytime I edit, just the User model is the one that gets saved while the Profile model doesnt. I think that the error is on the views.py file.
views.py
def edit_profile(request):
if request.method == 'POST':
form = EditProfileForm(request.POST, instance=request.user)
form1 = UpdateProfileForm(request.POST, instance=request.user)
if form.is_valid:
form.save()
form1.save()
return redirect('profile')
else:
form = EditProfileForm(instance=request.user)
form1 = UpdateProfileForm(instance=request.user)
args = {
'form': form,
'form1': form1,
}
return render(request, 'profile-edit.html', args)
forms.py
class EditProfileForm(UserChangeForm):
class Meta:
model = User
fields = (
'first_name',
'username',
'email',
)
exclude = ('password',)
class UpdateProfileForm(forms.ModelForm):
class Meta:
model = Profile
fields = (
'bio',
'profile_pic',
)
exclude = ('user',)
models.py
class Profile(models.Model):
user = models.OneToOneField(User, on_delete=models.CASCADE)
bio = models.CharField(max_length=400, default=1)
def __str__(self):
return f'{self.user.username} Profile'
profile-edit.html (I replaced the {{form.as_p}} and {{ form1.as_p }} to the following html code)
<form method="post" enctype="multipart/form-data">
{% csrf_token %}
<div class="edit-profile">
<h3>Name</h3>
<input type="text" name="first_name" value="{{ user.first_name }}" maxlength="30" id="id_first_name">
<h3>Username</h3>
<input type="text" name="username" value="{{ user.username }}" maxlength="150" required="" id="id_username">
<h3>Bio</h3>
<input type="text" name="bio" value="{{ user.profile.bio }}" maxlength="400" id="id_bio">
<h3>Email</h3>
<input type="email" name="email" value="{{ user.email }}" maxlength="254" id="id_email">
<button type="submit">Submit</button>
</div>
</form>
I found the error on my code, i missed to pass the .profile from the model in the views.py file
Bad
form1 = UpdateProfileForm(request.POST, instance=request.user)
Good
form1 = UpdateProfileForm(request.POST, instance=request.user.profile)
Related
In my django project I have a form VisitorRegForm which collects scanned_id and body_temp and submits data to my database table ClinicalData, since the current user is already logged in, how can I make this form to also be able grab current logged in user's username and email (Note: username and email were both created from the built-in django User model) and submit these four items: 'scanned_id', 'body_temp', 'username', 'email' into the database table ClinicalData?
Don't mind the missing details and inports, the form works and am able to submit 'scanned_id', 'body_temp'. Help me on the easiest way I will be able to submit these four items: 'scanned_id', 'body_temp', 'username', 'email' looking at the details shown in the files below:
Where should it be easier to achieve this; in the views.py or the template file?
forms.py
class VisitorRegForm(UserCreationForm):
class Meta:
model = User
fields = ['username', 'email', 'password1', 'password2']
class ScannerForm(ModelForm):
class Meta:
model = ClinicalData
fields = ['scanned_id', 'body_temp']
models.py
class ClinicalData(models.Model):
...
scanned_id = models.CharField(verbose_name="Scanned ID", max_length=50, null=True)
body_temp = models.DecimalField(verbose_name="Body Temperature", max_digits=3, decimal_places=1, null=True)
username = models.CharField(verbose_name="Facility Name", max_length=200, null=True)
email = models.EmailField(verbose_name="Visitor Email", max_length=200, null=True)
...
views.py
Am assuming I might need like two variables here in the views that should help me like the ones i've commented out, if it should start from here, help me how it should succeed:
#login_required(login_url='login')
def scanEnter(request):
#grab_username = function_to_grab_current_logged_in_username_HELP_ME_HERE
#grab_email = function_to_grab_current_logged_in_user_email_HELP_ME_HERE
form = ScannerForm(request.POST)
if request.method == 'POST':
form = ScannerForm(request.POST)
if form.is_valid():
form.save()
return redirect('scanenter')
context = {'form': form}
return render(request, 'scan_enter_fac.html', context)
scan_enter_fac.html
<form action="" method="post">
{% csrf_token %}
<div class="">scanned_id<input type="text" id="multiple" class="form-control" placeholder="or Type ID here; e.g: ACCTS-147" name="scanned_id"></div>
<div class="">body_temp<input type="text" class="form-control" placeholder="e.g: 36.5" name="body_temp"></div>
<button type="submit" class="btn btn-danger btn-block">Submit</button>
</form>
views.py:
#login_required(login_url='login')
def scanEnter(request):
#grab_username = function_to_grab_current_logged_in_username_HELP_ME_HERE
#grab_email = function_to_grab_current_logged_in_user_email_HELP_ME_HERE
form = ScannerForm(request.POST)
if request.method == 'POST':
form = ScannerForm(request.POST)
if form.is_valid():
form = form.save(commit=False)
form.username = request.user.username #new
form.email = request.user.email #new
form.save()
return redirect('scanenter')
context = {'form': form}
return render(request, 'scan_enter_fac.html', context)
scan_enter_fac.html
<form action="" method="post">
{% csrf_token %}
<div class="">{{ form.scanned_id }}</div>
<div class="">{{ form.body_temp }}</div>
<button type="submit" class="btn btn-danger btn-block">Submit</button>
</form>
I have a model in models.py like this:
class UserProfile(models.Model):
user = models.OneToOneField(User, on_delete=models.DO_NOTHING)
phone_number = models.CharField(max_length=50, default='')
Birthday = models.CharField(max_length=50, default='')
city = models.CharField(max_length=50, default='')
school = models.CharField(max_length=100, default='')
course = models.CharField(max_length=50, default='')
I want to allow my users to make edits, so I made a function like this in my views.py:
if request.method == 'POST':
profil = UserProfile.objects.get(user=request.user)
profil.phone_number = models.CharField(max_length=50, default='')
profil.Birthday = models.CharField(max_length=50, default='')
profil.city = models.CharField(max_length=50, default='')
profil.school = models.CharField(max_length=100, default='')
profil.course = models.CharField(max_length=50, default='')
profil.save()
return redirect('profile')
return render(request, 'edit_profile.html')
My template is:
<form action="." method="post">
{% csrf_token %}
Phone Number:
<input type="text" name="phone_number" value="{{ profil.phone_number }}" /><br />
Birthday:
<input type="text" name="Birthday" value="{{ profil.Birthday }}" /><br />
city:
<input type="text" name="city" value="{{ profil.city }}" /><br />
school:
<input type="text" name="school" value="{{ profil.school }}" /><br />
course:
<input type="text" name="course" value="{{ profil.course }}" /><br />
<input type="submit" value="Save Changes" name="save" />
<input type="reset" value="Cancel" name="cancel" />
<br/>
</form>
I do not know why, but after filling the form I get an error saying page not found. And if I check the existing user profile nothing got updated. Help please.
you have an error
profil = UserProfile.objects.get(user=request.user)
profil.phone_number = models.CharField(max_length=50, default='')
you get an UserProfile object and then instead of saving str into phone_number you try to save an object models.CharField.
use ModelForm to achieve what you need.
class UserProfileForm(forms.ModelForm):
class Meta:
model = UserProfile
fields = '__all__'
then in your view you can use:
userprofile_form = UserProfileForm(request.POST if request.POST else None, instance = UserProfile.objects.get(user=request.user))
if request.method == 'POST':
if form.is_valid():
form.save()
return redirect('profile')
return render(request, 'edit_profile.html', context={'userprofile_form': userprofile_form)
and in html use:
<form action="." method="post">
{% csrf_token %}
Phone Number: {{ userprofile_form.phone_number }}
Birthday: {{ userprofile_form.Birthday }}
city: {{ userprofile_form.city }}
school: {{ userprofile_form.school }}
course: {{ userprofile_form.course }}
<input type="submit" value="Save Changes" name="save" />
<input type="reset" value="Cancel" name="cancel" />
<br/>
</form>
or just
<form action="." method="post">
{% csrf_token %}
{{ userprofile_form }}
<input type="submit" value="Save Changes" name="save" />
<input type="reset" value="Cancel" name="cancel" />
<br/>
</form>
if you want to use upload files, don't forget to put your files into form you can do it
UserProfileForm(request.POST, request.FILES, instance = UserProfile.objects.get(user=request.user))
and in html you need to put <form enctype="multipart/form-data" method="post" action="...">
Reproduce these steps, you might need slight modifications:
1. Create a UserEditForm
in forms.py
from .models import UserProfile
from django.forms import ModelForm
class UserEditForm(ModelForm):
class Meta:
model = UserProfile
fields = '__all__' # or ['phone_number', 'Birthday', 'city', 'only_fields_you_want_to_edit']
2. Create a View:
in views.py
from django.shortcuts import render, HttpResponseRedirect, get_object_or_404
from django.urls import reverse
from django.forms.models import model_to_dict
from .models import UserProfile
from .forms import UserEditForm
def userEdit(request, user_id):
user = get_object_or_404(Question, pk=user_id)
if request.method == "GET":
form = UserEditForm(initial=model_to_dict(user))
return render(request, 'yourapp/useredit_form.html', {'form':form)
elif request.method == "POST":
form = UserEditForm(request.POST, instance=user)
if form.is_valid():
form.save()
return HttpResponseRedirect(reverse('user_profile', kwargs={'uid':user.id}))
else:
return HttpResponseRedirect(reverse('some_fail_loc'))
3. Create template
in yourapp/useredit_form.html
<form method="POST">
{% csrf_token %}
{{form}}
<button type="submit">Submit</button>
</form>
4. Set a URL:
in urls.py
urlpatterns += [
path('edit_user/<int:user_id>/', views.userEdit, name='user_edit'),
]
Now try visiting yourapp.com/edit_user/1. You are good to go. :D
I'm trying to create a custom messaging platform between users. For some reason it says that my form is not valid when I submit it, and when I print the error with print(messageform.errors), I get:
<ul class="errorlist"><li>receiver<ul class="errorlist"><li>Select a valid choice. That choice is not one of the available choices.</li></ul></li></ul>
This is strange because I don't believe that I'm using a dropdown anywhere in the form. Does anyone know why this might be the case?
forms.py
class MessageForm(forms.ModelForm):
class Meta:
model = Message
fields = ['text', 'receiver']
html file
<form action="" method="post">
{% csrf_token %}
<input type="text" name="text" value="" />
<label for="text">Enter your message here</label><br/>
<input type="text" name="receiver" value="" />
<label for="receiver">id of receiver</label><br/>
<input type="submit" value="Send" />
</form>
models.py
class Message(models.Model):
text = models.TextField(max_length=10000, blank=True)
sender = models.ForeignKey(
settings.AUTH_USER_MODEL,
on_delete=models.CASCADE,
null = True,
related_name="sender"
)
receiver = models.ForeignKey(
settings.AUTH_USER_MODEL,
on_delete=models.CASCADE,
null = True,
related_name="receiver"
)
views.py
usermodel = get_user_model()
if request.method == "POST" and not signupform.is_valid():
messageform = MessageForm(request.POST)
print(messageform.errors)
if messageform.is_valid():
receiverid = messageform.cleaned_data['receiver']
newmessage = messageform.save(commit=False)
newmessage.sender = request.user
newmessage.receiver = usermodel.objects.filter(username=receiverid)
newmessage.text = messageform.cleaned_data['text']
new_profile.save()
return HttpResponseRedirect('/profile/')
So when the form is invalid and I click submit, it just redirects to /home because of return redirect('/'). So what do I have to return here to prevent form submission and raise an error message? Here's my code:
views.py
class BoxesView(ListView, FormMixin):
template_name = 'polls.html' # extends base.html
form_class = UserRegistrationForm
def get_context_data(self, **kwargs):
context = super(BoxesView, self).get_context_data()
question_list = Question.objects.all().order_by('-date')
choice = Choice.objects.all()
context['question_list'] = question_list
context['choice'] = choice
q_list = []
returned_list = []
for i in question_list:
q_list.append(i)
for a, b in CATEGORY_CHOICES:
name = resolve(self.request.path_info).url_name
if b == name:
category = a
search = self.request.GET.get('search')
posts = Post.objects.all().filter(category=category).order_by('-date')
if search:
posts = posts.filter(
Q(title__icontains=search) |
Q(content__icontains=search)
)
else:
posts = Post.objects.all().filter(category=category).order_by('-date')
context['posts'] = posts
total = 0
for post in posts:
returned_list.append(post)
total += 1
if total == 4:
total = 0
for i in q_list:
returned_list.append(i)
q_list.remove(i)
break
paginator = Paginator(returned_list, 14)
page = self.request.GET.get('page')
try:
lst = paginator.page(page)
except PageNotAnInteger:
# If page is not an integer, deliver first page.
lst = paginator.page(1)
except EmptyPage:
# If page is out of range (e.g. 9999), deliver last page of results.
lst = paginator.page(paginator.num_pages)
context['lst'] = lst
return context
def get_queryset(self):
pass
def register(request):
form = UserRegistrationForm(request.POST)
if form.is_valid():
username = form.cleaned_data['username']
password = form.cleaned_data['password']
email = form.cleaned_data['email']
user = User.objects.create_user(username=username, password=password, email=email)
user.save()
return redirect('/')
else:
print(form.errors)
form = UserRegistrationForm()
return redirect('/')
And in my forms.py it raises an exception error to the terminal but the form still submits.
forms.py
class UserRegistrationForm(forms.ModelForm):
email = forms.EmailField()
username = forms.CharField(max_length=25)
password = forms.CharField(widget=forms.PasswordInput)
confirm_password = forms.CharField(widget=forms.PasswordInput)
class Meta:
model = User
fields = [
'username',
'email',
'password',
]
def clean(self):
email = self.cleaned_data.get('email')
current_emails = User.objects.filter(email=email)
if current_emails.exists():
raise forms.ValidationError("That email is taken")
base.html
<form method="post" enctype="multipart/form-data" action="{% url 'register' %}">{% csrf_token %}
<div class="registerBox">
<p><label for="id_username"></label> <input id="id_username" type="text" name="username" maxlength="30" placeholder="username"/></p>
<p><label for="id_email"></label> <input type="email" name="email" id="id_email" placeholder="email"/></p>
<p><label for="id_password"></label> <input type="password" name="password" id="id_password" placeholder="password"/></p>
<p><label for="id_confirm_password"></label> <input type="password" name="confirm_password" id="id_confirm_password" placeholder="confirm password"/></p>
<input type="submit" value="register" />
</div>
</form>
Any idea?
You shouldn't be trying to prevent submission when the form is invalid. What you should be doing is accepting the submission, checking the errors, then returning the errors and the filled-in form to the template.
But you are doing three things that prevent that: you are always re-instantiating the form when it is invalid, you always redirect, and you don't show errors or previous values in the template.
So you should do this:
def register(request):
form = UserRegistrationForm(request.POST)
if form.is_valid():
username = form.cleaned_data['username']
password = form.cleaned_data['password']
email = form.cleaned_data['email']
user = User.objects.create_user(username=username, password=password, email=email)
user.save()
return redirect('/')
return render('mytemplate.html', {"form": form})
and in the template:
<div class="registerBox">
{{ form.non_field_errors }}
<p>{{ form.username.label_tag }} {{ form.username }} {{ form.username.errors }}</p>
<p>{{ form.email.label_tag }} {{ form.email }} {{ form.email.errors }}</p>
<p><label for="id_password"></label> <input type="password" name="password" id="id_password" placeholder="password"/></p>
<p><label for="id_confirm_password"></label> <input type="password" name="confirm_password" id="id_confirm_password" placeholder="confirm password"/></p>
<input type="submit" value="register" />
</div>
have you tried
if form.is_valid():
...
else:
return form_invalid(form)
You should use ajax validation if you do not want to submit invalid form.
I am using the Django authentication system and have another model which is about user:
class user(models.Model):
user = models.OneToOneField(User)
user_code=models.IntegerField(max_length=3)
phone=models.IntegerField(max_length=10)
task=models.ForeignKey(task)
class Meta:
unique_together = ('user_code', 'task')
def __unicode__(self):
return str(self.user_code)
so I made two forms for user registeration(form.py):
class userForm(forms.ModelForm):
class Meta:
model=User
widgets = {'password': forms.PasswordInput(),}
class Form4(forms.ModelForm):
class Meta:
model=user
Now in my view I want to send two forms to template and submit both of them. Everything is well but after submitting I can't check form validation? this is my view:
def add_new_user(request):
if request.method == 'POST': # If the form has been submitted...
user_form = userForm(request.POST) # django User
form = Form4(request.POST) # my model user
if user_form.is_valid() and form.is_valid(): # All validation rules pass **edited** is_valide
user_name=user_form.cleaned_data['username']
password = user_form.cleaned_data['password']
name=user_form.cleaned_data['first_name']
user_id = form.cleaned_data['user_code']
email = user_form.cleaned_data['email']
phone = form.cleaned_data['phone']
task = form.cleaned_data['task']
else: # **added** form invalid statement
return render(request, 'student/form4.html', {'form': form,'user_form':user_form})
try: #**moved from valid statement**
do somthing...
except ValueError :
do somthing...
else:
form = Form4()
user_form=userForm()
# An unbound form
return render(request, 'student/form4.html', {'form': form,'user_form':user_form})
this is my template(form4.html):
<form name="myForm" action="{% url 'student:add_new_user' %}" onsubmit="return validateForm4();" method="post">
{% csrf_token %}
<div id="form-data">
{{ form.non_field_errors }}
{{ user_form.username }}{{ user_form.username.errors }}
<label for="id_user_name">user name:</label><br>
{{ user_form.password }}{{ user_form.password.errors }}
<label for="id_password">password:</label><br>
{{ user_form.first_name }}{{ user_form.first_name.errors }}
<label for="id_name">first name:</label><br>
{{ user_form.email }}{{ user_form.email.errors }}
<label for="id_email">email:</label><br>
{{ form.user_code }}{{ form.user_code.errors }}
<label for="id_email">use ID:</label><br>
{{ form.phone }}{{ form.phone.errors }}
<label for="id_phone">phone number:</label><br>
{{ form.task }}{{ form.task.errors }}
<label for="id_task">task:</label><br>
<input type="submit" value="register" /><br>
</div>
</form>
But it returns False for both validation. How can I check validation?
ANSWER:
I add which field I want from User and user. and change my "form.py" to :
class userForm(forms.ModelForm):
class Meta:
model=User
fields = ('username', 'password','first_name','email' )
widgets = {'password': forms.PasswordInput(),}
class Form4(forms.ModelForm):
class Meta:
model=user
fields = ('user_code', 'phone','task' )
thanks for all comments and answer.
Try add different prefixes to each of forms
# GET
user_form = userForm(prefix="one")
form = Form4(prefix="two")
# POST
user_form = userForm(request.POST, prefix="one")
form = Form4(request.POST, prefix="two")