In my application, I used email and password for user authentication, which works fine. However, I want to offer the user the option of adding other information to their account like first names, last names, and dates of birth.
I have a change form in myapp.forms.py
class MyChangeForm(forms.ModelForm):
"""
Form for editing an account.
"""
first_name = forms.CharField(widget=forms.TextInput, label="First name")
last_name = forms.CharField(widget=forms.TextInput, label="Last name")
date_of_birth = forms.DateField(widget=forms.DateField, label="Date of birth")
class Meta:
model = MyUser
fields = ['first_name', 'last_name', 'date_of_birth']
def save(self, commit=True):
user = super(MyChangeForm, self).save(commit=False)
if commit:
user.save()
return user
in my views.py, I have the following method for updating
#login_required(login_url='/')
def update_user(request):
if request.method == 'POST':
form = MyChangeForm(request.POST, instance=request.user)
if form.is_valid():
user = form.save(commit=False)
user.save()
return HttpResponseRedirect('/')
else:
form = MyChangeForm(instance=request.user)
return render_to_response('update_user.html', context_instance=RequestContext(request))
and my update_user.html is as follows
{% extends 'user_base.html' %}
{% block content %}
<div class="col-sm-3 col-sm-offset-5">
<h1> Update User</h1>
<form method='POST' action='/update_user/'> {% csrf_token %}
<ul>
{{ form.as_table }}
</ul>
<input type='Submit' class='btn btn-primary btn-block'>
</form>
</div>
{% endblock %}
However, when I serve the file I see this:
As seen here, there's no way to enter my fields!
How can I fix this? It's probably easy, but I'm getting tunnel vision.
erip
Add form to the context, for example like this:
render('update_user.html', {'form': form})
Related
I have a problem, the urls form works but I can't see the records in url/admin, can I ask for help, thank you :D
SOF wants me to add more details otherwise it doesn't transfer, I don't know what more I can add, generally temapals and urls work.
class Note(models.Model):
"""..."""
notes = models.CharField(max_length=100, unique=True)
description = models.TextField()
class Meta:
verbose_name = "Note"
verbose_name_plural = "Notes"
def __str__(self):
return self.notes
class NoteView(View):
def get(self, request):
if request.method == 'POST':
textN = Note.objects.all().order_by('notes')
form = NoteAddForm(request.POST)
if form.is_valid():
form.save()
return redirect('Files/menu')
else:
textN = NoteAddForm()
return render(request, 'Files/note.html', {'textN': textN})
class NoteAddForm(forms.ModelForm):
"""New note add form"""
class Meta:
model = Note
fields = '__all__'
{% extends 'Files/base.html' %}
{% block title %}Notatnik{% endblock %}
<h2>Notatnik Dietetyka/ Zalecenia ręczne </h2>
{% block content %}
<form action="/send/" method="post">
{% csrf_token %}
{{ textN }}
<label>
<input type="text" class="btn btn-second btn-lg">
<button>Wyślij formularz</button>
</label>
</form>
<button type="button" class="btn btn-primary btn-lg">Powrót</button>
{% endblock %}
Within your NoteView class in views.py file is where the issue is.
I see you have an if statement checking for if request.method == 'POST' within the class-based view get(). The get() is equivalent to if request.method == 'GET'. Therefore, what you might want to do is to override the post() on the class instead. For example:
class NoteView(View):
template_name = 'Files/note.html'
# Use the get method to pass the form to the template
def get(self, request, *arg, **kwargs):
textN = NoteAddForm()
return render(request, self.template_name, {'textN': textN})
# Use the post method to handle the form submission
def post(self, request, *arg, **kwargs):
# textN = Note.objects.all().order_by('notes') -> Not sure why you have this here...
form = NoteAddForm(request.POST)
if form.is_valid():
form.save()
# if the path is... i.e: path('success/', SucessView.as_view(), name='success')
return redirect('success') # Redirect upon submission
else:
print(form.errors) # To see the field(s) preventing the form from being submitted
# Passing back the form to the template in the name 'textN'
return render(request, self.template_name, {'textN': form})
Ideally, that should fix the issue you're having.
Updates
On the form, what I'd suggest having is...
# Assuming that this view handles both the get and post request
<form method="POST"> # Therefore, removing the action attribute from the form
{% csrf_token %}
{{ textN }}
# You need to set the type as "submit", this will create a submit button to submit the form
<input type="submit" class="btn btn-second btn-lg" value="Submit">
</form>
views.py
def student_login(request):
form = StudentLoginForm()
if request.method == 'POST':
form = StudentLoginForm(data=request.POST)
print(form.is_valid())
if form.is_valid():
username = form.cleaned_data.get('username')
password = form.cleaned_data.get('password')
print(username)
user = authenticate(username=username,password=password)
if user is not None:
login(request,user)
return redirect('index')
else:
messages.error(request,'Invalid username or password!')
else:
messages.error(request,'Invalid username or password!')
context = {'form':form}
return render(request,'student_login.html',context)
models.py
class Student(models.Model):
name = models.CharField(max_length=100)
username = models.CharField(max_length=100,unique=True,default=None)
mobile = models.CharField(max_length=8)
email = models.CharField(max_length=200,unique=True)
password = models.CharField(max_length=200,default=None,unique=True)
total_books_due = models.IntegerField(default=0)
def __str__(self):
return self.name
forms.py
class StudentLoginForm(forms.ModelForm):
class Meta:
model = Student
fields = ['username','password']
widgets = {
'password':forms.PasswordInput(),
}
student_login.html
{% extends 'base.html' %}
<!-- {% load crispy_forms_tags %} -->
{% block content %}
<div class="container">
<br>
<h2>Student Login Form</h2>
<br>
<form method="POST" action="">
{% csrf_token %}
{% if messages %}
{% for message in messages %}
<div class="alert alert-danger" role="alert">
{{ message }}
</div>
{% endfor %}
{% endif %} <br>
{% for field in form %}
<p>{{ field.label }} </p>
<p>{{ field }} </p>
<br>
{% endfor %}
<br>
<input type="submit" class="btn btn-primary" value="Login">
</form>
</div>
{% endblock %}
I have been trying to change again and again but form.is_valid() is still returning False. I could not figure out the reason that the form is not valid because I have already specify the fields that I want to show and added the csrf_token. Could anyone help me to figure out where is the problem?
Try to use just a simple form and not a ModelForm, as ModelForms in the background work with creating and updating objects.
In this case, ModelForm is validating as if you are trying to create a new Student, causing the already exists failures.
For example, you can write a simple login form like this:
class StudentLoginForm(forms.Form):
username = forms.CharField(widget=forms.TextInput(attrs={'autofocus': True}))
password = forms.CharField(
label='Password',
strip=False,
widget=forms.PasswordInput,
)
And then in your views, use it as is:
def student_login(request):
form = StudentLoginForm()
if request.method == 'POST':
form = StudentLoginForm(data=request.POST)
if form.is_valid():
user = authenticate(
username=form.cleaned_data.get('username'),
password=form.cleaned_data.get('password'),
)
if user:
login(request, user)
return redirect('index')
messages.error(request, 'Invalid username or password!')
context = {'form':form}
return render(request,'student_login.html',context)
can you please change this line
form = StudentLoginForm(data=request.POST)
to
form = StudentLoginForm(request.POST)
and also why you are validating the fields as you already validating using .is_valid()
function?
Just create a simple form with username and password. Also, the password field should not be unique if you are using plain text.
Better to use inbuilt User model provided by django. If you want to extend fields in User model, use AbstractUser and override the User model. See in documentation https://docs.djangoproject.com/en/3.2/topics/auth/customizing/
I am trying to implement some functionality that allows a user to edit their personal information in a Django project using Django forms. When a user enters the new value in the form and hits enter, they are brought back to the main profile page which is correct however, the values remain the same as before. Below is how I have tried to implement the functionality:
Forms
class UpdateProfile(forms.ModelForm):
email = forms.EmailField(required=False)
first_name = forms.CharField(required=False)
last_name = forms.CharField(required=False)
age = forms.IntegerField(required=False)
height = forms.IntegerField(required=False)
weight = forms.IntegerField(required=False)
class Meta:
#Here are the fields that i want editable
model = User
fields = ('email', 'first_name', 'last_name', 'age', 'height', 'weight')
#Here im trying to commit the changes to the user and return the user
def save(self, commit=True):
super(UpdateProfile, self).__init__(commit)
if commit:
user.save()
return user
Views
def update_profile(request):
args = {}
if request.method == 'POST':
form = UpdateProfile(request.POST, instance=request.user)
if form.is_valid():
form.save()
return HttpResponseRedirect(reverse('account/profile.html'))
else:
form = UpdateProfile()
args['form'] = form
return render(request, 'account/edit_profile.html', args)
HTML
% block head %}
<title>Profile</title>
{% endblock %}
{% block body %}
<div class="container">
<form method="POST" action="{% url 'account:profile' %}">
{% csrf_token %}
{{ form.as_p }}
<button type="submit">Submit</button>
</form>
<br>
</div>
{% endblock %}
Your form is submitting directly to the view profile page. But that page is presumably not expecting to validate a form. You need to submit it back to the update_profile page, which you normally do by using an action of just "." in the form HTML element.
<form method="POST" action=".">
Once you've done that, you'll see some issues with your form save() method. That method does not do anything useful anyway; you should remove it and let the superclass one be called automatically.
This line seems wrong:
super(UpdateProfile, self).__init__(commit)
You're calling __init__ from the parent class, but the method being called is save()... Also you're refering to a user variable which is (hopefully) not defined in this scope.
I'm trying to create a custom form where the user can also enter his name for instance but I am facing an issue, when the registration is done the name is not saved and I can't show it on the template.
here is the code
views.py
def register_user(request):
if request.user.is_authenticated():
return HttpResponseRedirect('/user/')
else:
if request.method == 'POST':
form = MyRegistrationForm(request.POST)
if form.is_valid():
form.save()
return HttpResponseRedirect('/user/')
context = {}
context.update(csrf(request))
context['form'] = MyRegistrationForm()
return render(request, 'register.html', context)
forms.py
class MyRegistrationForm(UserCreationForm):
email = forms.EmailField(required=True)
name = forms.CharField(required=True)
class Meta:
model = User
fields = {'name', 'username', 'password1', 'password2', 'email'}
def save(self, commit=True):
user = super(MyRegistrationForm, self).save(commit=False)
user.email = self.cleaned_data['email']
user.name = self.cleaned_data['name']
if commit:
user.save()
return user
register.html
<form action="/user/register/" method="post" id="register" autocomplete="off">
{% csrf_token %}
<div class="fieldWrapper">
{{ form.name.errors }}
{{ form.name.label_tag }}
{{ form.name }}
</div>
[... other form fields ...]
</div>
<input type="submit" value="Register"/>
</form>
So when I submit the form, everything works but when I try to show {{ user.name }} in the template, nothing is shown, why is that? (it works for the email field)
The default User object doesn't have a name field (so you are actually just saving the content of your name field to the object, and not the database). It has a first_name and last_name so you can either use those fields instead or you can customize the User model to have a separate name field
Edit
Also, just so you know, if you use the first_name and last_name fields instead, the User model has a get_full_name() method built-in which might be useful
The User does not have a "name" field. Try:
{{ user.username }}
I used {{ form }} as seen here
template.html
<h4>Change your details</h4><br>
<form id="edit_form" method='post'>
{% csrf_token %}
{{ form }}
<div class='section group'>
<input id="update_details_button" type='submit' class='btn btn-primary wide' value='Change'/>
</div>
</form>
views.py
def user_view(request, is_admin):
user = request.user
form = myForm()
if request.method == 'POST' and is_admin:
form = myForm(request.POST)
if form.is_valid():
data = form.cleaned_data
user.name = data['name']
user.country = data['country']
user.save()
messages.success(request, 'You have successfully updated your details.')
return render(request, 'mysite/user.html', {
'user': user,
'is_admin': is_admin,
'form': form,
})
My form is as followed
class myForm(forms.Form):
name = forms.CharField(
label="Name",
widget=forms.TextInput(attrs={'placeholder': 'Name'}))
country = CountryField(blank_label='(select country)')
def __init__(self, *args, **kwargs):
super(myForm, self).__init__(*args, **kwargs)
The name field displayed fine on the page but there's no sign of the CountryField, could someone point out the error? The code compiled fine and gives no error while server is running.
CountryField is a model field, not a form field. You're supposed to add it to your user model, in which case a modelform based on that model will automatically generate a country field. Since it looks like you have actually added a field called country to the User model, that's where you should be using CountryField.
However, for reference, to do it manually on a non-model form is slightly more complicated:
from django_countries import widgets, countries
class myForm(forms.Form):
country = forms.ChoiceField(widget=CountrySelectWidget, choices=countries)
In fact it's simpler: https://pypi.org/project/django-countries/#custom-forms
from django_countries.fields import CountryField
class MyForm(forms.Form):
country = CountryField(blank=True).formfield()
If you have installed django_country and it added in installed app than no need to make it from just use like this
{% load countries %}
<select name="country">
{% countries %}
<option value="{{ code }}">{{ name }}</option>
{% endcountries %}
</select>