Updating User Password in Django with Class Based Views - python

I'm tring write a view to administrator update a password of another user, using Class Based Views and model SetPasswordForm of Django.
My views.py
class UserSetPasswordUpdateView(GroupRequiredMixin, FormView):
form_class = forms.SetPasswordForm
model = User
template_name = 'app/admin/object_update.html'
success_url = reverse_lazy('portal:admin_user')
group_required = u"Administrator"
def get_form_kwargs(self):
kwargs = super(UserSetPasswordUpdateView, self).get_form_kwargs()
kwargs['user'] = User.objects.filter(pk=self.kwargs['pk'])
return kwargs
update_change_password = UserSetPasswordUpdateView.as_view()
My urls.py
url(r'^app/admin/update-user-pass/(?P<pk>[0-9]+)$', update_views.update_change_password, name='update_change_password'),
And don't show any errors, just go to success_url, but the password don't updated.

Your view is based on FormView. This doesn't have any knowledge of model forms, and doesn't do anything with the data other than check that it is valid. SetPasswordForm changes the password when the form is saved, but this view never does this.
You could override form_valid to call form.save() explicitly, but it would be better to use a more appropriate base class such as UpdateView which will do that for you.

Related

How to track which user is creating object for a model and how to show the object details only to that user in django

I am doing an online classroom project in Django where I created a model named create_course which is accessible by teachers. Now I am trying to design this as the teacher who creates a class only he can see this after login another teacher shouldn't see his classes and how to add students into that particular class I created
the course model
class course(models.Model):
course_name = models.CharField(max_length=200)
course_id = models.CharField(max_length=10)
course_sec = models.IntegerField()
classroom_id = models.CharField(max_length=50,unique=True)
created_by = models.ForeignKey(User,on_delete=models.CASCADE)
here if I use "the created_by" field in forms it appears to be a drop-down menu where every user is showing but I want to automatically save the user who creates the object
views.py
def teacher_view(request, *args, **kwargs):
form = add_course(request.POST or None)
context = {}
if form.is_valid():
form.save()
return HttpResponse("Class Created Sucessfully")
context['add_courses'] = form
return render(request, 'teacherview.html', context)
forms.py
from django import forms
from .models import course
class add_course(forms.ModelForm):
class Meta:
model = course
fields = ('course_name', 'course_id', 'course_sec', 'classroom_id')
You can inject the logged in user to the .created_by of the .instance in the form, so:
from django.contrib.auth.decorators import login_required
from django.shortcuts import redirect
#login_required
def teacher_view(request, *args, **kwargs):
if request.method == 'POST':
form = add_course(request.POST, request.FILES)
if form.is_valid():
form.instance.created_by = request.user
form.save()
return redirect('name-of-some-view')
else:
form = add_course()
return render(request, 'teacherview.html', {'add_courses': form})
Note: It is normally better to make use of the settings.AUTH_USER_MODEL [Django-doc] to refer to the user model, than to use the User model [Django-doc] directly. For more information you can see the referencing the User model section of the documentation.
Note: You can limit views to a view to authenticated users with the
#login_required decorator [Django-doc].
Note: Usually a Form or a ModelForm ends with a …Form suffix,
to avoid collisions with the name of the model, and to make it clear that we are
working with a form. Therefore it might be better to use CourseForm instead of
add_course.
Note: Models in Django are written in PascalCase, not snake_case,
so you might want to rename the model from course to Course.
Note: In case of a successful POST request, you should make a redirect
[Django-doc]
to implement the Post/Redirect/Get pattern [wiki].
This avoids that you make the same POST request when the user refreshes the
browser.
In your view use commit=False to stop the form from saving until you add the created_by field.
def teacher_view(request, *args, **kwargs):
form = add_course(request.POST or None)
context = {}
if form.is_valid():
course = form.save(commit=False)
course.created_by = request.user
course.save()
return HttpResponse("Class Created Sucessfully")
context['add_courses'] = form
return render(request, 'teacherview.html', context)

How to configure Django generic view for both GET and POST using model form

I want to create a view function + template that displays a simple form (derived from a user model) and also captures the form submission. How do I do this using generic views in Django?
My user model is:
class User(models.Model):
email = models.EmailField(unique=True)
name = models.CharField(max_length=100)
I only need to capture the email field in the form.
I think there must be a simple way to do this using generic forms, however I'm not sure which one to use nor how to do it. The only other ways I know how to do it are:
1) Create UserForm explicitly and a single view function separating POST and GET requests. E.g., :
def contact(request):
if request.method == 'GET':
# display UserForm
....
elif request.method == 'POST':
# process form submission
....
2) Create two views (with seperate URLs) - one using generic view to display form and another view to receive form submission e.g.,:
class contact(generic.DetailView):
# display form from User model
model = User
....
def submit(request):
# process form submission
....
So, my two questions are:
can and how should this be implemented using ONLY a generic view?
which generic view should be used?
First part of the answer: use a single view. If you use a function view (which is by far the simplest solution), the canonical form-handling edit view looks like:
def myview(request, instance_id, ...):
instance = get_object_or_404(pk=instance_id)
if request.method == "POST":
form = MyForm(request.POST, ..., instance=instance)
if form.is_valid():
# assuming a ModelForm
form.save()
return redirect(somewhere)
# invalid forms will be re-rendered with the error messages
else:
form = MyForm(instance=instance)
return render(request, "myapp/mytemplate.html", {"form": form})
For a create view, you just remove all the instance_xxx parts. Or you can use the same view for both create and update making the instance_id optional:
def myview(request, instance_id=None, ...):
if instance_id is not None:
instance = get_object_or_404(pk=instance_id)
else:
instance = None
if request.method == "POST":
form = MyForm(request.POST, ..., instance=instance)
if form.is_valid():
# assuming a ModelForm
form.save()
return redirect(somewhere)
# invalid forms will be re-rendered with the error messages
else:
form = MyForm(instance=instance)
return render(request, "myapp/mytemplate.html", {"form": form})
If you want a class-based generic view they are documented here. I personally don't think there's much to gain from generic class-based views (except eventually headaches when you try to grasp the execution flow scattered amongst half a dozen base classes and mixins) but YMMV.
update
if I want to do some processing on the data (including adding in extra fields) before saving an instance to the DB, where would I do this?
Preferably in the form itself unless you need some other data that you don't want to pass to the form. For all forms you can process data at the validation stage. With a ModelForm you can also override the save() method itself:
class MyModelForm(forms.ModelForm):
class Meta:
model = MyModel
fields = ('whatever', 'something')
def save(self, commit=True):
""" Save user and create a pro account """
instance = super(MyModelForm, self).save(commit=False)
instance.something_else = 42
if commit:
instance.save()
return instance
CreateView can work perfectly according to your requirements, You only need to create a form of contact models and success_url where user will redirect after form submission. It'll save automatic user data into models
class ContactView(generic.CreateView):
form_class = ContactForm
template_name = 'contact/index.html'
success_url = '/homepage/' . #Mention URL here
This can also be done using only CreateView.Specify email in fields as you need only the email field in the form.You can also process submitted form in form_valid method.
class UserCreate(CreateView):
model = User
fields = ['email']
success_url = '/your_success_url/'
#transaction.atomic
def form_valid(self, form):
new_user = form.save(commit=False)
# process your submitted form here.
# eg. add any extra fields as:
# new_user.something = something
new_user.save()
return super().form_valid(form)

Django CBV Detailview

Hello Everybody excuse my english....
I am facing a problem with django.
I need to restrict object so only their owners can print it.
Model.py
class Post(models.Model):
title = models.CharField(max_length=50, blank=False)
prenom = models.CharField(max_length=255, blank=False)
user = models.ForeignKey(User, null=False)
View.py
class detailpost(DetailView):
model = Post
template_name = 'detail-post.html'
context_object_name = 'post'
url.py
url(r'detail-post/(?P<pk>[-\d]+)$', views.detailpost.as_view(), name='detailpost'),
This works properly but the problem is that every users can access to the post of another user (http://localhost:8000/detail-post/1). So my question is how can i do some stuff befor rendering the page and see if the post belongs to the current user if yes we print it else we redirect the user to another page.
You can use the LoginRequiredMixin (new in Django 1.9) to make sure that only logged in users can access the view.
Then override the get_queryset method, and filter the queryset so that it only includes posts by the logged-in user.
from django.contrib.auth.mixins import LoginRequiredMixin
class DetailPost(LoginRequiredMixin, DetailView):
model = Post
template_name = 'detail-post.html'
context_object_name = 'post'
def get_queryset(self):
queryset = super(DetailPost, self).get_queryset()
return queryset.filter(owner=self.request.user)
If the user views a post that does not belong to them, they will see a 404 page. If you must redirect the user instead of showing a 404, then you'll have to take a different approach.
Note that I have renamed your class DetailPost (CamelCase is recommended for classes in Django. You'll have to update your urls.py as well.
You can override get() or post() method in your view class.
from django.shortcuts import redirect
class detailpost(DetailView):
model = Post
template_name = 'detail-post.html'
context_object_name = 'post'
def get(self, request, *args, **kwargs):
self.post = Post.objects.get(pk=self.kwargs['pk'])
if self.post.user != request.user or not request.user.is_superuser:
return redirect('login')
else:
return super(detailpost, self).get(request, *args, **kwargs)
You should override 'get()' method in your 'detailpost' class, so that it would be something like below:
def get(self, request, *args, **kwargs):
queryset = self.model._default_manager.filter(user=request.user)
self.object = self.get_object(queryset)
context = self.get_context_data(object=self.object)
return self.render_to_response(context)
It seems like it is the only way to pass User from Request instance to filter queryset.
I did not find that DetailView uses self.request

Django Login Required to view

I am building a small application which needs user profiles, I've used the build in user system from Django. But I have a problem regarding that even if you are not logged in you can still view the profile also another thing is that each user should only see his profile not others I need some tips on this
views.py
class UserProfileDetailView(DetailView):
model = get_user_model()
slug_field = "username"
template_name = "user_detail.html"
def get_object(self, queryset=None):
user = super(UserProfileDetailView, self).get_object(queryset)
UserProfile.objects.get_or_create(user=user)
return user
class UserProfileEditView(UpdateView):
model = UserProfile
form_class = UserProfileForm
template_name = "edit_profile.html"
def get_object(self, queryset=None):
return UserProfile.objects.get_or_create(user=self.request.user)[0]
def get_success_url(self):
return reverse("profile", kwargs={"slug": self.request.user})
Since you are using the Class Based Generic View, you need to add decorator #login_required in your urls.py
#urls.py
from django.contrib.auth.decorators import login_required
from app_name import views
url(r'^test/$', login_required(views.UserProfileDetailView.as_view()), name='test'),
Have you checked out the login_required decorator? Docs are here.
Since it seems you are using Class Based Views, you need to decorate in the urlconf, see here for more info.
At this moment you can add LoginRequiredMixin for your custom view.
Example:
class MyListView(LoginRequiredMixin, ListView): # LoginRequiredMixin MUST BE FIRST
pass
Doc: https://docs.djangoproject.com/en/4.1/topics/auth/default/#the-loginrequiredmixin-mixin
The below is what you should typically do
#login_required
def my_view(request, uid):
# uid = user id taken from profile url
me = User.objects.get(pk=uid)
if me != request.user:
raise Http404

django form exclude a user instance from a queryset

I have the following model:
class UserProfile(models.Model):
user = models.OneToOneField(User, related_name="user")
people_interested = models.ManyToManyField(User, related_name="interested")
Now I want a form where I want to offer users a form where they can choose people_interested, so I add the following forms.py
class ChooseForm(forms.Form):
q_set = User.objects.all()
peers = forms.ModelMultipleChoiceField(widget=forms.CheckboxSelectMultiple, queryset = q_set)
and then in views:
form = ChooseForm(data = request.POST or None)
if request.POST and form.is_valid():
uprofile, created = UserProfile.objects.get_or_create(user=request.user)
uprofile.people_interested = form.cleaned_data['peers']
uprofile.save()
return HttpResponseRedirect("/")
else:
return render(request, "form_polls.html", {'form':form})
But the trouble with this is, the current user instance also gets displayed. So I tried the following in views.py:
form = ChooseForm(request.user.id, data = request.POST or None)
and then in forms.py
class ChooseForm(forms.Form):
def __init__(self, uid, *args, **kwargs):
super(ChooseForm, self).__init__(*args, **kwargs)
self.fields['peers'].queryset = User.objects.exclude(id=uid)
q_set = User.objects.all()
peers = forms.ModelMultipleChoiceField(widget=forms.CheckboxSelectMultiple, queryset = q_set)
But the above is not a clean implementation, is there a better method of doing it??
What makes you say this is not a clean implementation? Overwriting queryset on __init__ is perfectly acceptable.
The only things I'd do to improve your code is using a post_save signal on User to create it's UserProfile, then just do user.get_profile() on your view. See this question
You could also use a ModelForm for UserProfile instead of a regular form, and limit the fields to people_interested.

Categories