I want to write a form that will display the user's email but allow them to change their password by entering their current password and their new password twice. I want to use a ModelForm, too.
I have the User:
# models.py
class User(AbstractBaseUser):
email = models.EmailField(unique=True)
And I've started on a form:
# forms.py
class ChangeAccountDetailsForm(forms.ModelForm):
class Meta:
model = User
fields = ('email',)
current_password = forms.CharField(widget=forms.PasswordInput)
password = forms.CharField(widget=forms.PasswordInput)
confirm_password = forms.CharField(widget=forms.PasswordInput)
I've created a simple view that only works with a GET request at the minute:
# views.py
def edit_account_details(request):
if request.method == 'GET':
account_form = ChangeAccountDetailsForm()
return render(request, 'freelancestudent/edit_account_details.html', {
{'form': account_form}
})
else:
pass
And a simple HTML file that simply calls {{form.as_p}}. When I run it as is I get the error unhashable type: 'dict' at {'form': account_form}. How can I display, then, the current user's email and allow functionality for changing password. Is this possible with a ModelForm or will I have to roll my own form and do all the logic myself?
Here :
return render(
request,
'freelancestudent/edit_account_details.html', {
{'form': account_form}
})
You have two sets of {}. The inner one defines a dict, the outer one defines a set, so it's equivalent to:
context = {'form': account_form}
whatever = set(context)
Now sets needs their values to be hashable (just like dict keys), and a dict is not hashable, hence your error message.
But anyway: render expects a dict, not a set, so what you want is:
return render(
request,
'freelancestudent/edit_account_details.html',
{'form': account_form}
)
Related
I'm building a Django project for a client that requires me to not user a simple form.save() method to update a model field.
Basically, it looks like this:
I have this model with a CharField who has unique=True:
# models.py
class Course(models.Model):
name = models.CharField(max_length=20, unique=True)
other_field = models.CharField(max_length=10, null=True)
def __str__(self):
return self.name
That model has a form in forms.py:
# forms.py
class CourseCreateForm(forms.ModelForm):
class Meta:
model = Course
fields = ['name', 'other_field']
I need to update this field through a function view (can't be class based in this scenario. Of course, literally it can, but for my student's project requirements it can't be) and I can't use the simple form.save() function, so I need to do the full update code as if it was a normal form:
# views.py
def course_update(request, pk):
course = Course.objects.get(pk=pk)
course_queryset = Course.objects.filter(pk=pk)
if request.method == "POST":
form = CourseCreateForm(request.POST)
if form.is_valid():
name = form.cleaned_data['name']
other_field = form.cleaned_data['other_field']
course_queryset.update(name=name, other_field=other_field) # <-- Where I try to update
else:
print(form.errors)
return HttpResponseRedirect('../')
else:
form = CourseCreateForm(instance=course)
context = {
"form": form,
}
return render(request, 'base/course_update.html', context)
When I try to only update the other_field, the change isn't made and in the formerrors I receive the error "Course with this Name already exists.", so I can't only change the other_field data without also having to change the name field because the name field is unique.
How can you update a model instance's field that has unique=True without changing the unique value?
Hope this makes sense!
Thanks.
UPDATE:
Also wanted to add that it works perfectly fine when unique=True is turned off. I'm just curious how do you update a field normally when unique=True and you're adding in the same variable from a model form.
The reason that this will fail is because the ModelForm thinks you are creating a new record, and thus it will check if an object with the given name already exists, and if so, it of course finds out the name already exists, hence the form is not valid.
You can pass the instance to the form. In that case the form will exclude that item from the unique check:
def course_update(request, pk):
course = Course.objects.get(pk=pk)
course_queryset = Course.objects.filter(pk=pk)
if request.method == 'POST':
form = CourseCreateForm(request.POST, instance=course)
if form.is_valid():
name = name_form.cleaned_data['name']
other_field = course_form.cleaned_data['other_field']
course_queryset.update(name=name, other_field=other_field)
return HttpResponseRedirect('../')
else:
print(form.errors)
else:
form = CourseCreateForm(instance=course)
context = {
'name_form': name_form,
'course_form': course_form,
}
return render(request, 'base/course_update.html', context)
I want to edit my user data from template, bellow are my codes.
def guru_edit(request, id):
Guru = get_object_or_404(DataGuru, GuruUser_FK_id=id)
GuruUser = get_object_or_404(User, id=id)
if request.method == 'POST':
form_guru = dataguruform(request.POST, instance=Guru)
form_user = userform(request.POST, instance=GuruUser)
if form_guru.is_valid() and form_user.is_valid():
form_guru.save()
form_user.save()
return redirect('index_guru')
else:
form_guru = dataguruform(instance=Guru)
form_user = userform(instance=GuruUser)
return render(request, 'guru/guru_tambah.html', {'form_user': form_user,'form_guru':form_guru})
this is my forms.py
class userform(ModelForm):
class Meta:
model = User
fields = ('username','email', 'password','is_staff','is_active','is_superuser')
widgets={
'password':TextInput(attrs={'type':'password'})
}
But when i was save from template, the password is not encrypted like it used to be, but just plaintext.
How to make it encripted?
Do not set the password via a form field. Set the password with User.set_password() method which accepts your unencrypted password:
user_form = UserForm(request.POST, instance=user)
if user_form.is_valid():
user = user_form.save()
user.set_password('unencrypted_password') # replace with your real password
user.save()
return redirect('index_guru')
I have named the variables and forms in a bit more Django-ish way here, as you can see.
Background: The password in Django is stored as a (most commonly PBKDF2) hash in your database. set_password takes care of seeking the correct hashing method and salting and hashing your passwords correctly.
Forms should merely contain something like password and password_check fields that are used to check if your user inputs his or her password correctly. They should not be used to save a plain password into your database, which I suspect is happening here by default.
You can use set_password inside your forms as well by overriding the UserForm.save() method.
Take the time to read through this document:
https://docs.djangoproject.com/en/dev/topics/auth/passwords/
Just create a view using Django's built in forms and views:
In your views.py:
from django.contrib.auth.views import PasswordChangeView
from django.contrib.auth.forms import PasswordChangeForm
class UpdatePassword(PasswordChangeView):
form_class = PasswordChangeForm
success_url = '/user/edit-profile'
template_name = 'app/change-password.html'
Inside your urls.py:
from . import views
urlpatterns = [
path('/change-password', views.UpdatePassword.as_view(), name="update_password"),
]
This is my form:
class FriendRequestForm(forms.ModelForm):
class Meta:
model = Friend
my model:
class Friend(models.Model):
user1 = models.ForeignKey(User, related_name='frienduser1')
user2 = models.ForeignKey(User, related_name='frienduser2')
date = models.DateTimeField(auto_now=True)
accepted = models.BooleanField(default=False)
class Meta:
unique_together = ('user1', 'user2',)
In the template, how can I set user2 to be the ID based on who's profile page I am on? So if I am user1, and I am on user2's page (where this form loads in the html), I want user2 in the form to be set properly from the html template. Thanks!
You can't set anything in templates but assume that you actually meant corresponding view. If so, you can set initial value for the form easily:
def profile_view(request, user_id):
user = User.objects.get(id=user_id)
form = FriendRequestForm(initial={'user2': user})
I do not know which User model class you are using, but I'm assuming it is django's default User class. In that case, you can get the currently logged in user using request.user, and use that to populate the template field.
# views.py
def foo(request):
user = User.objects.get(id=request.user.id)
# TODO: check this is a valid user
render(
request,
'template.html',
{
# pass this user's id as a template variable
# or pass the entire user object itself
'user1_id': user.id,
})
# template.html
<div id='user1_field'>{{ user1_id }}</div>
IMHO, explicitly passing in required data is better than passing the entire request object in to the template like this:
from django.template import RequestContext
def foo(request):
return render_to_response(
'index.html', {'form': form, },
context_instance = RequestContext(request))
I am not able to understand why and how RegistrationForm class method clean_username is evoked when I am creating class instance in my view.py
my form.py
class RegistrationForm(forms.Form):
firm_name = forms.CharField()
username = forms.CharField()
def clean_username(self):
print "inside clean_username ..I don know how this called"
if db.firm.find(username =self.cleaned_data['username']):
raise forms.ValidationError((u'This username is already taken. Please choose another.'))
return self.cleaned_data['username']
My view.py
def main(request):
if request.method == 'POST':
form = RegistrationForm(request.POST)
if form.is_valid():
new_user = form.save()
return HttpResponseRedirect("/thanks/")
else:
form = RegistrationForm()
return render(request, "registration.html", {
'form': form,
})
this gets printed "inside clean_username ..I don know how this called"
According to my understanding the class method clean_username() should not be run until evoked like form.clean_username()
The method is called automatically by django, as part of form and field validation.
See the django documentation here:
Validation of a Form is split into several steps, which can be
customized or overridden:
The clean_<fieldname>() method in a form subclass – where <fieldname> is replaced with the name of the form field attribute.
This method does any cleaning that is specific to that particular
attribute, unrelated to the type of field that it is. This method is
not passed any parameters.
I'm trying to learn Django, and I am reading this link right now:
https://docs.djangoproject.com/en/1.5/topics/forms/modelforms/
If you scroll down in the link provided, it says that there are two main steps involved in validating a form and that the first step is 'validating the form' which leads to this link:
https://docs.djangoproject.com/en/1.5/ref/forms/validation/#form-and-field-validation
It says that the first step in every validation is to use the to_python() method on a field. I don't understand what they mean when they say
"It coerces the value to correct datatype and raises ValidationError if that is not possible. This method accepts the raw value from the widget and returns the converted value."
So suppose I have a model like this
class User(models.Model):
user_id = models.AutoField(unique=True, primary_key=True)
username = models.SlugField(max_length=50, unique=True)
first_name = models.CharField(max_length=50)
I created a form like so
class UserForm(forms.ModelForm):
class Meta:
model = User
now, how exactly do I use to_python() method? Do I use it in a view? Or do I have to use it in the forms.py file? If I use it in a view, what would the function be called?
Django does validates and deserializes fields input automatically.
Example view when posting form:
def my_view(request):
form = UserForm()
if request.method == 'POST':
form = UserForm(request.POST)
if form.is_valid(): # here to_python() is run for each field
form.save()
# redirect
return render_to_response('home.html', { 'form': form })
You don't need to worry about the to_python() unless you are creating a custom field. If you are going to use the ModelForm to create simple forms, you can use the clean methods.
If you want to validate only one field, you can do this:
class UserForm(forms.ModelForm):
def clean_username(self):
username = self.cleaned_data['username']
if len(username) > 10:
raise forms.ValidationError("Please shorten your username")
# Always return the cleaned data, whether you have changed it or
# not.
return username
if you want to clean multiple fields, you can do this:
class Userform(forms.Form):
# Everything as before.
...
def clean(self):
cleaned_data = super(UserForm, self).clean()
username = cleaned_data.get("username")
first_name = cleaned_data.get("first_name")
if len(username) > 10:
raise forms.ValidationError("Please shorten your username")
if len(first_name) < 1:
raise forms.ValidationError("First name is too short")
# Always return the full collection of cleaned data.
return cleaned_data