I have this app and its working but i'm confused whether to use form method or POST.get method. with form i'm getting so many challenges like rendering form on custom html
suppose i have this change password screen, for that i need to create form then use this on html template and with custom html it gets more complicated to use form fields.
forms.py:
class ChangePasswordForm(PasswordChangeForm):
old_password = forms.CharField(label="Old Password", strip=False, widget=forms.PasswordInput(
attrs={'class': 'formField password-genrInput'}))
new_password1 = forms.CharField(label="New Password", strip=False, widget=forms.PasswordInput(
attrs={'class': 'formField password-genrInput'}))
new_password2 = forms.CharField(label="Confirm Password", strip=False, widget=forms.PasswordInput(
attrs={'class': 'formField password-genrInput'}))
class Meta:
model = User
fields = ('old_password', 'new_password1', 'new_password2')
views.py:
# Password Change View
def changePassword(request):
if request.method == 'POST':
form = ChangePasswordForm(request.user, request.POST)
print(form)
if form.is_valid():
print("form valid")
user = form.save()
update_session_auth_hash(request, user)
messages.success(request, "Password Changed Successfully")
return redirect('changePassword')
else:
messages.error(request, "Something Went Wrong, Please Try Again ")
return redirect('changePassword')
else:
form = ChangePasswordForm(request.user)
return render(request, 'admin/user_auth/change_password.html', {
'form': form
})
html:
{% extends "admin/layouts/default.html" %}
{% load static %}
{% block content%}
<div class="row">
<div class="col">
<div class="titleBlock">
<h1><i class="fas fa-chevron-circle-left mr-3"></i>Back</h1>
</div>
<div class="card">
{% if messages %}
<ul class="messages">
{% for message in messages %}
<li {% if message.tags %} class=" {{ message.tags }} " {% endif %}> {{ message }} </li>
{% endfor %}
</ul>
{% endif %}
<form method="post">
{% csrf_token %}
<div class="formBlock">
<div class="row password-genr mt-5">
{% for field in form %}
<div class="col-md-7">
<div class="formControl static ">
<label for="" class="formLabel">{{field.label}}</label>
{{field}}
</div>
</div>
{%endfor%}
<div class="col-md-7">
<div class="btnBlock mt-5">
<button type="submit" class="btn btn-md">Save</button>
</div>
</div>
</div>
</div>
</form>
</div>
</div>
</div>
{%endblock content%}
but with simple method i would have checked first if new password and confirm password are matching then
old_password = request.POST.get('old_password')
new_password = request.POST.get('new_password')
check the old password are matching with db then save the new password on db.
so which method should i use and why?
TO do This you can do something like this.
from django.contrib.auth.hashers import check_password
current_password = request.user.password
old_password = request.POST.get('old_password')
matchcheck= check_password(old_password, current_password) #this returns True or False
if matchcheck:
#change your password
else:
#tell the user the password is wrong.
The method above is useful by example if a user wants to delete his account to do so you can use the idea to check whether he knows his password or not ,if he knows it then he can delete his account.
And if you do not want to implement it by yourself you can use the built-in in Django(i really recommend this method for changing password because it well done and less headache).
Related
Am working on this app where users of the app can make payment to other users, but am currently challenge by the search functionality, since i can only search users on the app by their foreignKey only, but instead i would want to be able to search by users full name, email and username, when i search user by name i get this exception (Field 'id' expected a number but got 'Hong'. ). why does my query only limit me to search by user number(foreignKey).Here is my view to search user for payment.
class RequestSearchUser(LoginRequiredMixin, ListView):
model = FriendList
form_class = SearchUserForm
template_name = 'wallet/request_search_user_form.html'
def get_queryset(self):
try:
username = self.kwargs['search_user']
except:
username = ''
if username != '':
object_list = self.model.objects.filter(username=username)
else:
object_list = self.model.objects.all()
return object_list
def get_context_data(self, **kwargs):
context = super(RequestSearchUser, self).get_context_data(**kwargs)
query = self.request.GET.get("search_user")
context['user'] = self.request.user
if query:
queryset = (Q(user=query))
search_user = FriendList.objects.filter(queryset)
if not search_user:
messages.error(self.request, 'No user found.')
else:
search_user = []
context['search_user'] = search_user
context['nbar'] = 'request'
return context
Here is my search form.py
class SearchUserForm(forms.ModelForm):
class Meta:
model = User
fields = ('username',)
def clean_username(self):
username = self.cleaned_data['username'].strip()
return username
Here is the Html form
<div class="blog_details">
<h2>Request Money</h2><hr>
<form class="form-contact contact_form" action="{% url 'wallet:request' %}" method="get" novalidate="novalidate">
<div class="row">
<div class="col-12">
{% if messages %}
{% for message in messages %}
<div class="alert alert-danger" role="alert">
{{ message }}
</div>
{% endfor %}
{% endif %}
</div>
<div class="col-12">
{% for search_user in search_user %}
{% if search_user == request.user %}
<div class="alert alert-danger" role="alert">
Cannot input your username.
</div>
{% else %}
<meta http-equiv="refresh" content="0; url={% url 'wallet:request_money' search_user.id %}" />
{% endif %}
{% endfor %}
</div>
<div class="col-12">
<div class="form-group">
<input class="form-control" name="search_user" id="search_user" type="text" maxlength="45"
onfocus="this.placeholder = ''" onblur="this.placeholder = 'Username'" placeholder="Username" required/>
</div>
</div>
</div>
<div class="form-group mt-3">
<button type="submit" class="button button-contactForm boxed-btn">Search </button>
</div>
</form>
</div>
</article>
Error is here
object_list = self.model.objects.filter(user=username)
and here
queryset = (Q(user=query))
If you store username of user in field called username, you should filter by user__username
object_list = self.model.objects.filter(user__username=username)
queryset = (Q(user__username=query))
It's nicely described here Many-to-one
You can use contains so your query will look like this
if query:
queryset = (
Q(user__username__contains=query)|
Q(user__first_name__contains=query)|
Q(user__email__contains=query)
)
search_user = FriendList.objects.filter(queryset)
if not search_user:
messages.error(self.request, 'No user found.')
I am struggling passing data to a form using pytest. I wish to pass the username and password to a login form. Here is what I have tried:
conftest.py
import pytest
from wahoo_connect import init_app, db
from wahoo_connect.models import User
from dotenv import load_dotenv
load_dotenv('.flaskenv')
#pytest.fixture(scope='module')
def app():
app = init_app()
with app.app_context():
db.create_all()
user = User(username='testuser', email='test#gmail.com', forename='Test', surname='User', confirmed=True)
user.set_password('testing')
db.session.add(user)
db.session.commit()
yield app
#pytest.fixture(scope='module')
def client(app):
return app.test_client()
this sets up a client fixture which is used by my test:
def test_index_page__logged_in(client):
with client:
formdata = {'username': 'testuser', 'password': 'testing', 'remember_me': False}
client.post('/auth/login', data=formdata)
assert current_user.username == 'testuser'
the route is defined in a blueprint as:
#auth_bp.route('/login', methods=['GET', 'POST'])
def login():
# Login route logic goes here
if current_user.is_authenticated:
return redirect(url_for('home_bp.index'))
form = LoginForm()
if form.validate_on_submit():
user = User.query.filter_by(username=form.username.data).first()
if user is None or not user.check_password(form.password.data):
flash('Invalid username or password', 'warning')
return redirect(url_for('auth_bp.login'))
login_user(user, remember=form.remember_me.data)
next_page = request.args.get('next')
if not next_page or url_parse(next_page).netloc != '':
next_page = url_for('home_bp.index')
return redirect(next_page)
return render_template('auth/login.html', title='Sign In', form=form)
and here is the form:
class LoginForm(FlaskForm):
username = StringField('Username', validators=[DataRequired()])
password = PasswordField('Password', validators=[DataRequired()])
remember_me = BooleanField('Remember Me')
submit = SubmitField('Sign In')
and the template:
{% extends "base.html" %}
{% block content %}
<div class="form-signin text-center">
<form action="" method="post" novalidate>
<h3>Please sign in</h3>
<div class="form-floating">
{{ form.username(type="text", class="form-control", id="floatingInput", placeholder="Username") }}
{% for error in form.username.errors %}
<span style="color: red;">[{{ error }}]</span>
{% endfor %}
<label for="floatingInput">{{ form.username.label.text }}</label>
</div>
<div class="form-floating">
{{ form.password(type="password", class="form-control", id="floatingPassword", placeholder="Password") }}
{% for error in form.password.errors %}
<span style="color: red;">[{{ error }}]</span>
{% endfor %}
<label for="floatingPassword">{{ form.password.label.text }}</label>
</div>
<div class="checkbox mb-3">
<label>
{{ form.remember_me() }} {{ form.remember_me.label.text }}
</label>
</div>
<button class="btn btn-lg btn-primary btn-block" type="submit">{{ form.submit.label.text }}</button>
{{ form.hidden_tag() }}
</form>
</div>
<p class="text-center">Forgot Your Password? Click to Reset</p>
<p class="text-center pt-3">Create an Account</p>
{% endblock %}
I can see the data before the "if form.validate_on_submit():" statement in the view and it is correct, but I can never get beyond this point in the submission process. I seem to be missing something!
Thank you
Martyn
It seems that I was missing an important flag:
WTF_CSRF_ENABLED = False
it now works!
First error :I want to check if group_name field value is unique in a modelforms
I tried this code but seems not working as if i put a new value in this field, there is no message but data as not been add to table
Second error: i nether have any message. Maybe, I should not redirect pages ?
my code in views.py:
def group_create(request):
group_form = GroupForm()
if request.method == "POST":
group_form = GroupForm(request.POST)
if group_form.is_valid():
group_name = group_form.cleaned_data.get('group_name')
if Group.objects.filter(group_name=group_name).exists:
messages.error(request, 'this group already exists')
else:
group_form.save()
messages.success(request, 'Group created')
return redirect('group_list')
return render(request, 'imports/group_create.html', {
"group_form": group_form,
})
my model:
class Group(models.Model):
group_id = models.AutoField(primary_key=True)
groupParent_id = models.ForeignKey('self', blank=True, null=True, related_name='Parent', on_delete=models.CASCADE)
group_name = models.CharField(max_length=100, null=False, blank=False, unique=True)
my html:
<div class="container-fluid">
<!-- Code pour message type toaster -->
{% if messages %}
<div class="row">
<div class="col-xs-12">
<ul class="alert" role="alert">
{% for message in messages %}
<p {% if message.tags %} class="{{ message.tags }}"{% endif %}>{{ message }}</p>
{% endfor %}
</ul>
</div>
</div>
{% endif %}
<!-- fin messages -->
<div class="row">
<div class="col-lg-2 col-md-2 col-sm-2 sidebar">
{% block sidebar %}{% endblock %}
</div>
<div class="col-lg-10 col-lg-offset-2 col-md-10 col-md-offset-2 col-sm-10 col-sm-offset-2 content">
{% block heading %}{% endblock %}
{% block page %}{% endblock %}
</div>
</div>
</div>
Django's ModelForm [Django-doc] already validates the uniqness of fields that you marked unique=True [Django-doc], as is specified in the documentation on Interaction with model validation [Django-doc]:
(...)
The model's clean() method will be called before any uniqueness
checks are made. (...).
If the field is not valid, than it will add that error to the group_form.errors [Django-doc]. The reason why you did not see those is that you, regardless whether the form is valid or not, just redirect to a view, and thus the form is discarded.
If you render the form accordingly, it will add extra messages to the fields with errors:
def group_create(request):
if request.method == "POST":
group_form = GroupForm(request.POST)
if group_form.is_valid():
group_form.save()
messages.success(request, 'Group created')
# only in case of success
return redirect('group_list')
else:
group_form = GroupForm()
return render(request, 'imports/group_create.html', {
"group_form": group_form,
})
I have a problem whit a function, when I use the tag {{form.image_cliente}} always return a "form is invalid" and don't save nothing but if I use the tag {{form.as_p}} return a form is valid and save the field image_cliente, and I dont know why, I just want to display only the form.image_cliente in the template.
Mys form Cliente:
class ClienteForm(forms.ModelForm):
id_tipo_cliente = forms.ModelChoiceField(queryset=Tipo_cliente.objects.filter(status=1), label="Tipo de cliente" ,empty_label="Seleciona tipo", widget=forms.Select(attrs={'value':'form-control'}))
password = forms.CharField(label="Password", required=False,
widget=forms.PasswordInput)
class Meta:
model = Cliente
exclude = ['status', 'id_usuario_alt', 'fecha_creacion', 'fecha_modificacion', 'fecha_cancelacion']
fields = [
'nombres',
'apellidos',
'usuario',
'password',
'correo_electronico',
'direccion',
'telefono',
'telefono_celular',
'id_tipo_cliente',
'image_cliente',
]
labels = {'nombres':'Nombres', 'apellidos':'Apellidos', 'usuario':'Usuario', 'correo_electronico':'Correo', 'direccion':'Direccion', 'telefono':'Telefono', 'telefono_celular':'Celular', }
widgets = {
'nombres': forms.TextInput(attrs={'class':'form-control'}),
'apellidos': forms.TextInput(attrs={'class':'form-control'}),
'usuario': forms.TextInput(attrs={'class':'form-control'}),
'correo_electronico': forms.TextInput(attrs={'class':'form-control'}),
'direccion': forms.TextInput(attrs={'class':'form-control'}),
'telefono': forms.TextInput(attrs={'class':'form-control'}),
'telefono_celular': forms.TextInput(attrs={'class':'form-control'}),
'image_cliente':forms.ClearableFileInput(attrs={'multiple': True, 'class':'file', 'data-show-upload':'false', 'data-show-preview':'false'})
}
This is my def function in my views:
def subirIMGCliente(request, pk):
clientes = get_object_or_404(Cliente, pk=pk)
if request.method == 'POST':
form = ClienteForm(request.POST, request.FILES, instance=clientes)
if form.is_valid():
clientes = form.save(commit=False)
if clientes.password == '':
clientes.save(update_fields=['image_cliente'])
print('yes');
return redirect('BackEnd:cliente')
else:
print('form in not valid');
else:
form = ClienteForm(instance=clientes)
return render(request, 'back/Modulo_cliente/imagenes_cliente.html', {'form': form })
My Template
<form method="post" enctype="multipart/form-data">
{% csrf_token %}
<div class="container">
<div class="row">
<div class="col-md-8 col-md-offset-2">
<div class="form-group">
{{form.image_cliente}}
</div>
</div>
</div>
</br>
<br> <div class="row">
<div class="col-md-8 col-md-offset-2">
<button class="btn btn-success" type="submit">Guardar</button>
<span>Regresar</span>
</div>
</div>
</div>
</form>
the following fields are required:
usuario
nombres
direccion
apellidos
id_tipo_cliente
correo_electronico
telefono
telefono_celular
Add a required=False on them like you have on the password field, and you'll be on your way.
You add an instance for the object, and that object has values for all the required form fields, so when you load form.as_p, or any other tag that outputs the entire form, it loads with all the required fields filled out. That way, when the form is submitted, there are no validation errors. Validity checks are done over request.POST and not on the original model instance, so when the form is submitted without some of the required fields, you get validation errors.
To debug these sorts of issues, add a {{form.errors}} somewhere in your template. That's how I found the errors in your form.
It's considered best practice to display all non-field related errors in a list at the top of the form and field-related errors next to each form field.
So you'd add something like this at the top of the template:
<ol>
{% for error in form.non_field_errors %}
<li><strong>{{ error|escape }}</strong></li>
{% endfor %}
</ol>
And something like this for the image_cliente form field:
{% if form.image_cliente.errors %}
<ol>
{% for error in form.image_cliente.errors %}
<li><strong>{{ error|escape }}</strong></li>
{% endfor %}
</ol>
{% endif %}
I want to show custom error messages, if some field is not valid. I have following model:
class Test(models.Model):
name = models.IntegerField(max_length=10)
class TestForm(forms.ModelForm):
class Meta:
model = Test
fields = '__all__'
error_messages = {
'name': {
'max_length': ("This user's name is too long."),
},
}
The view is:
def test(request):
if request.method == 'POST':
print "The form is submitted successfully."
form = TestForm(request.POST)
if form.is_valid():
print request.POST.get("name")
return render(request, 'test.html',{'form' : TestForm()})
else:
print "Something wrong with inputs."
return render(request, 'test.html',{'form' : form})
else:
return render(request,'test.html',{'form' : TestForm()})
and template is:
{% extends "base.html" %}
{% block title %}
Test Form
{% endblock title %}
{% load widget_tweaks %}
{% block body_block %}
<h1>hello from test</h1>
<form class='form-horizontal' role='form' action="." method="POST">
<div class='form-group'>
<label class='control-label col-md-2 col-md-offset-2' for='id_name'>Name</label>
<div class='col-md-6'>
{% render_field form.name class="form-control" placeholder="Full Name" type="text" %}
{{ form.name.error_messages }}
{# I want to add here classes for alert-error etc #}
</div>
</div>
{% csrf_token %}
<div class='form-group'>
<div class='col-md-offset-4 col-md-6'>
<button type="submit" class="btn btn-success">Submit</button>
</div>
</div>
</form>
{% endblock body_block %}
But, I am not getting any messages in the template. Please help me to solve this.
Change form.name.error_messages to form.name.errors in your template.
It seems you render fields/errors one by one manually, explained here:
You may want to consider a more automatic approach using a {% for %} template tag.
EDIT: To change the default error message, you need to update your error_messages in the form Meta and overwrite the key used by django, in this case it is key invalid, based on IntegerField source:
class Meta:
model = Test
fields = '__all__'
error_messages = {
'some_integer_field': {
'invalid': 'some custom invalid message',
},
}