Using django's built in User model I have a basic update password view:
class ChangePasswordView(PasswordChangeView):
form_class = ChangePasswordForm
success_url = reverse_lazy('password_success')
def form_valid(self, form):
account = self.request.user.account
email = account.user.email
update_password_notification_task.delay(email)
return redirect(self.success_url)
def password_success(request):
return render(request, 'accounts/password_success.html')
its form:
class ChangePasswordForm(PasswordChangeForm):
class Meta:
model = User
and its url:
path('password/',ChangePasswordView.as_view(template_name='accounts/change_password.html')),
And finally, the template:
<h1>Change Password...</h1>
<form method="POST">
{% csrf_token %}
{{form.as_p}}
<button>Change Password</button>
</form>
There are no errors, and everything seems to work, but the password is not being updated. I don't know why, I think I did everything, but for some reason after I update the password and get redirected to the reverse_url if I try logging out and in, the new password doesn't work.
I don't know what's causing this issue or in what file, so it's hard to troubleshoot.
Embarrassingly enough all I needed to do add was a form.save():
class ChangePasswordView(PasswordChangeView):
form_class = ChangePasswordForm
success_url = reverse_lazy('password_success')
def form_valid(self, form):
form.save()
account = self.request.user.account
email = account.user.email
update_password_notification_task.delay(email)
return redirect(self.success_url)
Related
I am trying to allow users to save details of a workout for a specific exercise through submitting a form. My ExerciseDetailView displays the form how I'd like it to:
class ExerciseDetailView(DetailView):
model = Exercise
template_name = 'workouts/types.html'
def get_context_data(self, **kwargs):
context = super(ExerciseDetailView, self).get_context_data(**kwargs)
context['form'] = WorkoutModelForm
return context
But my problem is with saving the inputted data in the database. I have tried making both a FormView and a CreateView but am clearly missing something:
class ExerciseFormView(FormView):
form_class = WorkoutModelForm
success_url = 'workouts:exercise_detail'
def form_valid(self, form):
form.save()
return super(ExerciseFormView, self).form_valid(form)
Here is my referenced WorkoutModelForm:
class WorkoutModelForm(forms.ModelForm):
class Meta:
model = Workout
fields = ['weight', 'reps']
My template:
<form action="{% url 'workouts:workout' exercise.id %}" method="post">
{% csrf_token %}
{{ form }}
<button type="submit">Save</button>
</form>
Urls:
path('exercise/<int:pk>/detail/', ExerciseDetailView.as_view(), name='exercise_detail'),
path('exercise/<int:pk>/detail/', ExerciseFormView.as_view(), name='workout'),
And for context here is my Workout model which contains a get_absolute_url method:
class Workout(models.Model):
weight = models.FloatField(default=0)
reps = models.PositiveIntegerField(default=0)
created = models.DateField(auto_now_add=True)
updated = models.DateField(auto_now=True)
exercise = models.ForeignKey(Exercise, on_delete=models.CASCADE, default=None)
def get_absolute_url(self):
return reverse('exercise_detail', args=[str(self.pk)])
I am not receiving any errors, but when I submit the form my url remains the same, as I hoped, however the page just appears blank and the objects are not recorded. Can anybody please help me see what the problem is?
The problem is not your view, the Django logic will never trigger this view, the URLs are perfectly overlapping, so that means that for a URL, it will always trigger the first view (here the ExerciseDetailView), you should make the paths non-overlapping, for example with:
path('exercise/<int:pk>/detail/', ExerciseDetailView.as_view(), name='exercise_detail'),
path('exercise/<int:pk>/workout/', ExerciseFormView.as_view(), name='workout'),
Triggering the logic will however not be sufficient, since it will not link the Workout to the necessary exercise, you can alter the logic to:
from django.urls import reverse
class ExerciseFormView(CreateView):
form_class = WorkoutModelForm
def form_valid(self, form):
form.instance.exercise_id = self.kwargs['pk']
return super().form_valid(form)
def get_success_url(self):
return reverse('workouts:exercise_detail', kwargs={'pk': self.kwargs['pk']})
Need use CreateView
from django.views.generic.edit import CreateView
class ExerciseFormView(CreateView):
form_class = WorkoutModelForm
...
A user has a form where he can save sensitive data. I am capable of crypting this data and store it in the database using modelforms. However, if the user wants to modify this data, it appears in the TextInput from the form.
I've read this post, and this one. The answer seems to be there, but I can't manage to suceed in the implementation. Here is what I have:
Models
class CustomUser(AbstractUser):
api_key = models.CharField(max_length=256, default='null')
Forms
class APIForm(forms.ModelForm):
class Meta:
model = CustomUser
fields = ('api_key')
widgets = {
'api_key': TextInput(attrs={'size': 10, 'placeholder': 'Your key'}),
}
Html
<form method="post">
{% csrf_token %}
{{ form|crispy }}
<button type="submit">Save changes</button>
</form>
It seems that the form is not detecting the widget, since if I change the value of the attribute size, the form will not change.
Am I missing something?
Thanks in advance
UPDATE.
Here is my view code simplified:
Views
class KeyView(LoginRequiredMixin, UpdateView):
model = CustomUser
form_class = APIForm
template_name = 'account/api_form.html'
success_url = reverse_lazy('pages:home')
def get_object(self):
return self.request.user
def form_valid(self, form):
self.object = form.save(commit=False)
key=botcrypt.encrypt_val(self.object.api_key)
self.object.api_key =key.decode("utf-8")
self.object.save()
messages.success(self.request, 'key updated with success!')
return super().form_valid(form)
I'm using allauth for accounts, in case this information is important
You can try alernative for above method
api_key = forms.CharField(_(u'API Key'), required=False)
api_key.widget = forms.TextInput(attrs={'size': 10, 'title': 'API Key',})
or
api_key = forms.CharField(
_(u'API Key'),
required=False,
widget=forms.TextInput(attrs={'size': 10, 'title': 'API Key',})
)
So actually the solution was pretty simple, as expected....
All the code from Models, Forms and html was right.
I only had to empty the value of the key within the get_object from views:
def get_object(self):
self.request.user.api_key = ""
return self.request.user
I have my custom user model:
class RemoteUser(AbstractUser):
class Meta:
verbose_name= 'MyUser'
verbose_name_plural = 'MyUsers'
# Custom fields here...
How do I set up my user admin form to edit user details hiding the password?
class RemoteUserForm(UserCreationForm):
# What do I put here?
Even if I exclude password from fields it keeps giving me KeyError 'password1'.
Use ModelForm, its very simple using that. You create a form class for editing user objects, in fields you can specifiy whichever you want to edit. Use this form in view to save the input data and use the context in template to complete the cycle.
Forms.py
from django.contrib.auth.models import User
class EditUserForm(forms.ModelForm):
class Meta:
model = User
fields = {'username'}
Views.py
def update_userprofile(request, pk):
user = User.objects.get(pk=pk)
user_form = EditUserForm(instance=user)
if request.user.id == user.id:
if request.method == "POST":
user_form = EditUserForm(request.POST, instance=user)
if user_form.is_valid():
created_user = user_form.save(commit=False)
return redirect('someview') #wherever you want
return render(request, "app_name/update_userprofile.html", {
"noodle": pk,
"noodle_form": user_form,}
else:
raise PermissionDenied
Update_userprofile.html
<form method="post">
{% csrf_token %} {{ noodle_form.as_p }}
<button type="submit">Submit</button>
</form>
In my app, I have Users create Post objects. Each Post has a User
class Post(models.Model):
user = models.ForeignKey(User, on_delete = models.CASCADE)
...
I want to create a post-submission form for editing and submission, so I plan to use Django's ModelForm functionality.
class PostForm(ModelForm):
class Meta:
model = Post
fields = "__all__"
However, if I do this, then whoever is viewing the form will be able to set who the Post author is. I want to make sure that the resulting user field is them. But, if I exclude the user field from the ModelForm,
class PostForm(ModelForm):
class Meta:
model = Post
exclude = 'user'
then the user will not be set on form submission. I've hacked my way around this by making a custom form and updating the post field
def submit_view(request):
....
request.POST = request.POST.copy()
request.POST.update({
'user' : request.user.id
})
form = PostForm(request.POST, request.FILES)
....
but then I lose automatic UI generation and form validation, which in some ways defeats the purpose of the Form class. Could somebody point me to the idiomatic way of setting the user field without including it in the Form?
Try this view:
def submit_view(request):
form = PostForm(request.POST or None)
if form.is_valid():
new_post = form.save(commit=False)
new_post.user = request.user
new_post.save()
view.py
from django.views.generic import CreateView
from .models import Post
class PostCreate(CreateView):
model = Post
template_name ="new_Post_form.html"
fields = ['text']
def form_valid(self, form):
object = form.save(commit=False)
object.user = self.request.user
object.save()
return super(PostCreate, self).form_valid(form)
def get_success_url(self):
return "/"
url.py
url(r'^newpost$',views.PostCreate.as_view(),name='post_new',),
new_post_form.html
<form method="post" enctype="multipart/form-data" class="form" action="newpost" id="new-post-form">
<div class="modal-body">
{% csrf_token %}
{{ form.as_p }}
<input type="submit" value="Submit" />
</div>
Thanks for read :/
When I remove the "get" from the view, the form works, but when I put it back, it doesn't render.
URL at the browser:
http://127.0.0.1:8000/activate?tpr=1140277&idpr=42
URLs:
url(r'^activate', Activation_vw.as_view(), name='activate')
VIEW:
class Activation_vw(FormView):
template_name = 'activate.html'
form_class = Activation_Form
success_url = '/dashboard/'
def get(self, request, *args, **kwargs):
tokenProspect_v = request.GET.get('tpr')
idProspect_v = request.GET.get('idpr')
USER = USERS.objects.filter(
id=idProspect_v).values('id', 'email', 'token')
if int(tokenProspect_v) != int(USER[0]['token']):
message = "Check the URL"
else:
message = USER[0]['email']
context = {'msg': message}
return self.render_to_response(context)
def form_valid(self, form):
# No code yet
return super(Activation, self).form_valid(form)
FORM:
class Activation_Form(forms.Form):
email = forms.EmailField()
password = forms.CharField(widget=forms.PasswordInput())
TEMPLATE:
Hello, {{ msg }}
<form action="" method="POST">
{%csrf_token%}
{{ form.as_p }}
<button type="submit">Activate</button>
</form>
All of them have the imports at the top of each file.
The get function works perfectly, I receive the tpr and the idpr but the form doesn't and because of that the form form_valid and the success_url doesn't work neither.
I suspect something is wrong in the return of my get, but can't figured out.
It is because your form is not being passed in the context:
context = {'msg': message}
return self.render_to_response(context)
You can use get_context_data to get the context and update it as you go:
context = self.get_context_data(msg=message)
return self.render_to_response(context)
This will call the get_context_data from the super class and include the form to the context along with adding the message.