Create a form to activate user permission - python

I want a django form which is an instance of a User and get an html form as constitued by radio buttons to activate or deactivate a permission.
A html code of this kind is expected at the end:
<div class="form-group">
<label for="change_user">Can edit user:</label>
<div class="labeled" id="change_user">
<input class="with-gap" name="change_user_False" type="radio" id="change_user_False" value="False">
<label for="change_user_False">No</label>
<input class="with-gap" name="change_user_True" type="radio" id="change_user_True" value="True" checked="">
<label for="change_user_True">Yes</label>
</div>
</div>
The example permission here will be "change_user" and the goal is to handle all the process in a clean django form. I do not know what is the most appropriate way...
Use on a simple form and manage everything in the clean function by passing in parameter a User object.
from django import forms
class PermissionForm(forms.Form):
change_user = forms.ChoiceField(widget=forms.RadioSelect, choices=((True, 'No'), (False, 'Yes')), required=True)
def __init__(self, *args, **kwargs):
self.fields['change_user'].initial = select_user.has_permission('auth.change_user ')
def clean(self, select_user):
if self.cleaned_data['change_user']:
select_user.permissions.add('change_user')
Or use a form of the User instance:
from django.contrib.auth.models import User
from django import forms
class ProfileForm(forms.ModelForm):
class Meta:
model = User
fields = []
widget = ...
But how to generate a widget in radioselect on a permission and catch errors when returned data wrong ?

You can resolve your question using Django UpdateView with a ModelForm like this example (See the comments in order to understand what's going on):
forms.py:
from django import forms
from YOUR_APP_NAME import models
class UserPermissionsForm(forms.ModelForm):
change_user = forms.ChoiceField(
widget=forms.RadioSelect,
choices=[(True, 'Yes'), (False, 'no')],
required=True # It's required ?
)
class Meta:
model = models.YOUR_MODEL_NAME
fields = ('change_user',) # I'll use only one field
views.py:
from django.views.generic import UpdateView
from django.urls import reverse_lazy
from django.contrib import messages
from django.contrib.auth.models import Permission
from YOUR_APP_NAME import models, forms
class UserPermissionView(UpdateView):
model = models.YOUR_MODEL_NAME
template_name = 'user_permission.html' # Your template name
form_class = forms.UserPermissionsForm
initial = {} # We'll update the form's fields by their initial values
def get_initial(self):
"""Update the form_class's fields by their initials"""
base_initial = super().get_initial()
# Here we'll check if the user has the permission of 'change_user'
user_has_permission = self.request.user.user_permissions.filter(
codename='change_user'
).first()
base_initial['change_user'] = True if user_has_permission else False
return base_initial
def form_valid(self, form):
"""
Here we'll update the user's permission based on the form choice:
If we choose: Yes => Add 'change_user' permission to the user
No => Remove 'change_user' permission from the user
"""
change_user = form.cleaned_data['change_user']
permission = Permission.objects.get(codename='change_user')
if change_user == 'True':
self.request.user.user_permissions.add(permission)
# Use django's messaging framework
# We'll render the results into the template later
messages.success(
self.request,
'Updated: User [{}] Can change user'.format(self.request.user.username)
)
else:
self.request.user.user_permissions.remove(permission)
messages.success(
self.request,
'Updated: User [{}] Cannot change user'.format(self.request.user.username)
)
return super().form_valid(form)
def get_success_url(self):
"""
Don't forget to add your success URL,
basically use the same url's name as this class view
"""
# Note here, we'll access to the URL based on the user pk number
return reverse_lazy('user_permissions', kwargs={'pk': self.request.user.pk})
urls.py:
from django.urls import path
from YOUR_APP_NAME import views
urlpatterns = [
path(
'client/<int:pk>/', # Access the view by adding the User pk number
views.UserPermissionView.as_view(),
name='user_permissions'
),
... # The rest of your paths
]
And finally the template:
user_permissions.html:
{% if messages %}
{% for message in messages %}
{{ message }}
{% endfor %}
<br><br>
{% endif %}
<div>User: {{ user.username }}</div>
<form method="POST">
{% csrf_token %}
{{ form.as_p }}
<button type='submit'>Submit</button>
</form>
And here are some screenshots of the flow of this solution:
And of course, you can check if the add/remove actions of the permission under the Django Admin Panel.

Related

Dynamically add field to a form python django

I want to have a Django form in which a user can add multiple stop_name,stop_longitude,stop_latitude using the Add more button inside. Let's suppose a user has 3 stop_names so he will have to click on the Add more twice. And on each add more above fields will populate again.
I am new to Django so I need some help.
This is my model
class SupplyChainStops(models.Model):
ingredient = models.ForeignKey(Ingredients, null=True, on_delete=models.CASCADE)
stop_name = ArrayField(models.CharField(max_length=1024, null=True, blank=True))
stop_longitude = ArrayField(models.CharField(max_length=500, null=True, blank=True))
stop_latitude = ArrayField(models.CharField(max_length=500, null=True, blank=True))
Okey using formset factory i very straight forward and simple to render multiple fields for user to fill in their information. First of all you would need to create forms.py in your project and then import django's formset_factory in forms. We would do something like so:
from django.forms import formset_factory
from .models import SupplyChainStops
# Here we are creating a formset to handle the maniplations of our form to
# have extra field by using the extra parameter to formset_factory
# and also we can add a delete function to allow users to be able to delete
Formset = formset_factory(SupplyChainStops, fields=[' stop_name',' stop_longitude','stop_latitude'], extra=4, can_delete=True)
# I have set the formset to give us an extra field of four and enable users
# to delete
Now we are going to work on the view to handle our formset.
from django.views.generic.edit import FormView
from .forms import Formset
class formsetView( FormView):
template_name = 'formset.html'
form_class = Formset
success_url = '/'
In our template we will do something like this .
<form method="post">{% csrf_token %}
{{ form.as_p }}
<input type="submit" value="Add">
</form>
Doing this in a function base view
from .forms import Formset
def formset_view(request):
if request.method == 'POST':
formset = Formset(request.POST,)
if formset.is_valid():
formset.save()
else:
formset = ()
return render (request, 'formset.html',{'formset':formset})
In your HTML Template
<form method="post">{% csrf_token %}
{{ formset.as_p }}
<input type="submit" value="Add">
</form>

How to take boolean field input from HTML into my Django model?

I have used Django forms for creating users and I extended the default User model by adding a boolean field, so I defined a new form for it. But I couldn't take input from HTML form to this boolean field. Shall I change my HTML form code?
Following are my code samples:
models.py
# accounts.models.py
from django.db import models
from django.contrib.auth.models import User
class SpecialUser(models.Model):
user = models.OneToOneField(User, on_delete=models.CASCADE)
flag = models.BooleanField()
def __str__(self):
return self.title
forms.py
from django import forms
from django.contrib.auth.forms import UserCreationForm
from django.contrib.auth.models import User
from django.forms.widgets import CheckboxInput
from .models import SpecialUser
class RegisterForm(UserCreationForm):
email = forms.EmailField()
class Meta:
model = User
fields = ["username", "email", "password1", "password2"]
class SuperUserForm(forms.ModelForm):
class Meta:
model = SpecialUser
fields = ['flag']
widgets = {
'flag': CheckboxInput(attrs={'class': 'flag'}),
}
views.py
def register(request):
if request.method == 'POST':
form = RegisterForm(request.POST)
sp_form = SuperUserForm(request.POST)
if form.is_valid() and sp_form.is_valid():
user = form.save()
sp_form = sp_form.save(commit=False)
sp_form.user = user
sp_form.save()
messages.success(request, 'Account created!')
return redirect('login')
else:
form = RegisterForm()
sp_form = SuperUserForm()
messages.warning(request, 'Your account cannot be created.')
return render(request, 'register.html', {'form': form})
HTML form code:
<form method="post" class="form-group">
{% csrf_token %}
{{ form|crispy }}
<label for="flag">Special User: </label>
<input id="flag" class="flag" type="checkbox" name="flag">
<button type="submit" class="btn btn-success">Sign up</button>
</form>
In your views.py you're creating a local variable for a SpecialUser form, sp_form, that is neither loaded into the context data nor templated in the HTML form code.
You can load sp_form into the context data by adding it to the context dict passed to render(). This will allow the template to see the variable. For example:
return render(request, 'register.html', {'form': form, 'sp_form': sp_form})
And then you can render it in the template. For example, underneath the main form:
{{ form|crispy }}
{{ sp_form|crispy }}
For starters this is generally not how you would want to extend the user model in a Django application. You would want to inherit from AbstractUser and add your fields to that model and run migrations. At least in this case, that would be ideal, then you could simply define the field on your RegisterForm.fields and let {{ form|crispy }} render the form for you. Naturally, you could call form.save() and move on with your life.
To clarify why this may not be working, it is generally not good practice to render your own fields for a form unless absolutely necessary. If you insist on doing it this way, note that Django prefixes the id with id_ so in your case it would be <label for="id_name">...</label> and <input id="id_flag" ...

Restrict `UpdateView` dataset for authenticated user in Class Based Views

I have a Django project where I extended the User to have a Profile using a OneToOneField. I'm using CBV UpdateView which allows users to update their profile. The URL they visit for this is ../profile/user/update. The issue I have is that if a user types in another users name they can edit the other persons profile. How can I restrict the UpdateView so the authenticated user can only update their profile. I was trying to do something to make sure user.get_username == profile.user but having no luck.
Models.py
from django.db import models
from django.contrib.auth.models import User
from django.db.models.signals import post_save
from django.core.urlresolvers import reverse
class Profile(models.Model):
# This field is required.
SYSTEM_CHOICES = (
('Xbox', 'Xbox'),
('PS4', 'PS4'),
)
system = models.CharField(max_length=5,
choices=SYSTEM_CHOICES,
default='Xbox')
user = models.OneToOneField(User)
slug = models.SlugField(max_length=50)
gamertag = models.CharField("Gamertag", max_length=50, blank=True)
f_name = models.CharField("First Name", max_length=50, blank=True)
l_name = models.CharField("Last Name", max_length=50, blank=True)
twitter = models.CharField("Twitter Handle", max_length=50, blank=True)
video = models.CharField("YouTube URL", max_length=50, default='JhBAc6DYiys', help_text="Only the extension!", blank=True)
mugshot = models.ImageField(upload_to='mugshot', blank=True)
def __unicode__(self):
return u'%s' % (self.user)
def create_user_profile(sender, instance, created, **kwargs):
if created:
Profile.objects.create(user=instance, slug=instance)
post_save.connect(create_user_profile, sender=User)
def get_absolute_url(self):
return reverse('profile-detail', kwargs={'slug': self.slug})
Views.py
from django.shortcuts import render
from django.views.generic import DetailView
from django.views.generic.edit import UpdateView
from django.views.generic.list import ListView
from profiles.models import Profile
class ProfileDetail(DetailView):
model = Profile
def get_context_data(self, **kwargs):
context = super(ProfileDetail, self).get_context_data(**kwargs)
return context
class ProfileList(ListView):
model = Profile
queryset = Profile.objects.all()[:3]
def get_context_data(self, **kwargs):
context = super(ProfileList, self).get_context_data(**kwargs)
return context
class ProfileUpdate(UpdateView):
model = Profile
fields = ['gamertag', 'system', 'f_name', 'l_name', 'twitter', 'video', 'mugshot']
template_name_suffix = '_update'
def get_context_data(self, **kwargs):
context = super(ProfileUpdate, self).get_context_data(**kwargs)
return context
Admin.py
from django.contrib import admin
from models import Profile
class ProfileAdmin(admin.ModelAdmin):
prepopulated_fields = {'slug': ('user',), }
admin.site.register(Profile, ProfileAdmin)
Urls.py for Profiles app
from django.conf.urls import patterns, url
from django.contrib.auth.decorators import login_required
from profiles.views import ProfileDetail, ProfileUpdate
urlpatterns = patterns('',
url(r'^(?P<slug>[-_\w]+)/$', login_required(ProfileDetail.as_view()), name='profile-detail'),
url(r'^(?P<slug>[-_\w]+)/update/$', login_required(ProfileUpdate.as_view()), name='profile-update'),
)
Profile_update.html
{% extends "base.html" %} {% load bootstrap %}
{% block content %}
{% if user.is_authenticated %}
<h1>Update your profile</h1>
<div class="col-sm-4 col-sm-offset-4">
<div class="alert alert-info alert-dismissible" role="alert">
<button type="button" class="close" data-dismiss="alert" aria-label="Close">
<span aria-hidden="true">×</span>
</button>
<strong>Heads up!</strong> Other users can find you easier if you have a completed profile.
</div>
<form enctype="multipart/form-data" method="post" action="">{% csrf_token %}
{{ form|bootstrap }}
<input class="btn btn-default" type="submit" value="Update" />
</form>
</div>
{% else %}
<h1>You can't update someone elses profile.</h1>
{% endif %}
{% endblock %}
How about something like this:
from django.contrib.auth.views import redirect_to_login
class ProfileUpdate(UpdateView):
[...]
def user_passes_test(self, request):
if request.user.is_authenticated():
self.object = self.get_object()
return self.object.user == request.user
return False
def dispatch(self, request, *args, **kwargs):
if not self.user_passes_test(request):
return redirect_to_login(request.get_full_path())
return super(ProfileUpdate, self).dispatch(
request, *args, **kwargs)
In this example, the user is redirected to default LOGIN_URL. But you can easily change it . to redirect user to their own profile.
To avoid access to data unrelated to the connected user when using Class Based View (CBV), you can use Dynamic filtering and define queryset instead on model attributes.
If you have a book.models with a ForeignKey (named user here) on auth.models.user you can easily restrict acces like this :
# views.py
from django.contrib.auth.mixins import LoginRequiredMixin
from django.views.generic import ListView
from books.models import Book
class BookList(LoginRequiredMixin, ListView):
def get_queryset(self):
return Book.objects.filter(user=self.request.user)
See more explanation in the documentation about CBV - Viewing subsets of objects
Specifying model = Publisher is really just shorthand for saying queryset = Publisher.objects.all(). However, by using queryset to define a filtered list of objects you can be more specific about the objects that will be visible in the view.
[…]
Handily, the ListView has a get_queryset() method we can override. Previously, it has just been returning the value of the queryset attribute, but now we can add more logic.
The key part to making this work is that when class-based views are called, various useful things are stored on self; as well as the request (self.request) this includes the positional (self.args) and name-based (self.kwargs) arguments captured according to the URLconf.
your template.html:
{% if request.user.is_authenticated and profile.user == request.user %}
your form
{% else %}
u cannot edit that profile - its not yours...
{% endif %}

How to customize the appearance of the Django-Userena change password form

I am using Django Userena for the first time.So can not able to customize the appearance of the change password form,as we know that userena used the default change password form from django.contrib.auth.forms (if i am not wrong).Now this is becoming tough for me to customize the appearance of the change password form template cause in the change password template, each and every field is rendered as {{ form.as_p }} like that
<form action = "" method="post" role = "form">
<fieldset>
<legend>{% trans "Change Password" %}</legend>
{% csrf_token %}
{{ form.as_p }}
</fieldset>
<input type="submit" value="{% trans "Change password" %}" class="btn btn-success" />
</form>
in mention,i have already been able to format the appearance of other forms provided by userena.for example i have changed the appearance of the Edit Profile form by adding css classes in the forms.py like that
class EditProfileForm(forms.ModelForm):
""" Base form used for fields that are always required """
first_name = forms.CharField(label=_(u'First name'),
max_length=30,
widget=forms.TextInput(attrs={'class' : 'form-control'}),
required=False)
last_name = forms.CharField(label=_(u'Last name'),
max_length=30,
widget=forms.TextInput(attrs={'class' : 'form-control'}),
required=False)
background = forms.CharField(label=(u'Background'),
max_length=500,
widget=forms.Textarea(attrs={'class' : 'form-control'}),
required=True)
and worked, change password form has been rendered from django.contrib.auth.forms,so i don't know how to add css classes in each field of that that file as it is a core file of Django.May be there alternative way to do this ,but i am inexperience in django and also the django userena,i don't know how do this.
It's too late, but for the new visitors,
you can create a new form in your forms.py as
# forms.py
from django.contrib.auth.forms import PasswordChangeForm
...
class MyPasswordChangeForm(PasswordChangeForm):
def __init__(self, *args, **kwargs):
super().__init__(*args, **kwargs)
self.fields["old_password"].widget = forms.PasswordInput(attrs={"class": "form-control"})
self.fields["new_password1"].widget = forms.PasswordInput(attrs={"class": "form-control"})
self.fields["new_password2"].widget = forms.PasswordInput(attrs={"class": "form-control"})
# other customization
and in your views.py you could use PasswordChangeView with your form as
# views.py
...
from django.contrib.auth.views import PasswordChangeView
from .forms import MyPasswordChangeForm
...
class ChangePasswordView(PasswordChangeView):
form_class = MyPasswordChangeForm
template_name = "path/to/your/template.html"
That's all.
You actually need to override the userena view altogether because it passes its own form in the view
urls.py:
# Change password
url(r'^(?P<username>[\#\.\w-]+)/password/$',
accounts.views.my_own_password_change_view,
name='userena_password_change'),
In your views.py:
#secure_required
#permission_required_or_403('change_user', (get_user_model(), 'username', 'username'))
def my_own_password_change_view(request, username, template_name='userena/password_form.html',
pass_form=YourPasswordChangeForm, success_url=None, extra_context=None):
""" Change password of user.
This view is almost a mirror of the view supplied in
:func:`contrib.auth.views.password_change`, with the minor change that in
this view we also use the username to change the password. This was needed
to keep our URLs logical (and REST) across the entire application. And
that in a later stadium administrators can also change the users password
through the web application itself.
:param username:
String supplying the username of the user who's password is about to be
changed.
:param template_name:
String of the name of the template that is used to display the password
change form. Defaults to ``userena/password_form.html``.
:param pass_form:
Form used to change password. Default is the form supplied by Django
itself named ``PasswordChangeForm``.
:param success_url:
Named URL that is passed onto a :func:`reverse` function with
``username`` of the active user. Defaults to the
``userena_password_complete`` URL.
:param extra_context:
Dictionary of extra variables that are passed on to the template. The
``form`` key is always used by the form supplied by ``pass_form``.
**Context**
``form``
Form used to change the password.
"""
user = get_object_or_404(get_user_model(),
username__iexact=username)
form = pass_form(user=user)
if request.method == "POST":
form = pass_form(user=user, data=request.POST)
if form.is_valid():
form.save()
# Send a signal that the password has changed
userena_signals.password_complete.send(sender=None,
user=user)
if success_url: redirect_to = success_url
else: redirect_to = reverse('userena_password_change_complete',
kwargs={'username': user.username})
return redirect(redirect_to)
if not extra_context: extra_context = dict()
extra_context['form'] = form
extra_context['profile'] = get_user_profile(user=user)
return ExtraContextTemplateView.as_view(template_name=template_name,
extra_context=extra_context)(request)
And finally
class YourPasswordChangeForm(forms.ModelForm):
""" Base form used for fields that are always required """
first_name = forms.CharField(label=_(u'First name'),
max_length=30,
widget=forms.TextInput(attrs={'class' : 'form-control'}),
required=False)
last_name = forms.CharField(label=_(u'Last name'),
max_length=30,
widget=forms.TextInput(attrs={'class' : 'form-control'}),
required=False)
background = forms.CharField(label=(u'Background'),
max_length=500,
widget=forms.Textarea(attrs={'class' : 'form-control'}),
required=True)
do even more customization on the html template
<form action="" method="post" id="password_change_form">
{% csrf_token %}
{% for field in form %}
<div class="profile-input w33">
<div class="profile-label" for="{{ field.name }}">{{ field.label }}</div>
{{ field }}
{{ field.errors }}
</div>
{% endfor %}
<div class="profile-input w33">
<input type="submit" class="input updatebtn" value="{% trans "UPDATE" %}"/>
</div>
</form>
If you are going to use Bootstrap and jQuery you can also customize all templates in your userena base file with jQuery.
In my case it saved me a lot of messy code in multiple files across the project.
Just change the desired parts with jQuery or pure JS and CSS for example:
$( "input" ).addClass( "form-control" );

Django form.is_valid() not validating

I can't seem to get my form to validate with .is_valid()
The associated View
def edit_social_media(request, user_id):
#verify a user is allowed to access the page
# user is saving the form
if request.POST:
form = SocialMediaForm(request.POST)
if form.is_valid():
...
return HttpResponseRedirect(reverse(users.profile,
args = (request.user.id,)))
# displaying the initial form
else:
try:
form = SocialMediaForm(instance = SocialMedia.objects.get(user = request.user))
except SocialMedia.DoesNotExist:
form = SocialMediaForm()
return render_to_response('users/edit_social_media.html', {'form': form, 'user' : user},
context_instance = RequestContext(request))
Forms.py
class SocialMediaForm(forms.ModelForm):
class Meta:
model = SocialMedia
fields = {'twitter', 'facebook', 'linkedin'}
the template
<form method="post" action=''>
{% csrf_token %}
<div class="form-col-left">
<p>
{{ form.as_p }}
</p>
</div>
<div class="submitbutton">
<input type="submit" class="green button" value="Save Social Media Settings" />
</div>
</form>
The model
from django.db import models
from django.contrib.auth.models import User
class SocialMedia(models.Model):
class Meta:
app_label = 'dashboard'
#the user associated with the data
user = models.ForeignKey(User)
#Twitter, Facebook, and Linkedin pages
twitter = models.URLField("Twitter")
facebook = models.URLField("Facebook")
linkedin = models.URLField("Linkedin")
Some help would be greatly appreciated. I'm rather new to django so I've been missing some of the smaller nuances. I've found some similar issues on stackoverflow related to bound vs unbound forms, but from reading the documentation I think I properly bound the data to the form.
Since this is a ModelForm it would help seeing the actual Model that this form is using, the SocialMedia class. Also I am not sure what you mean by not getting it to validate but I am assuming that the is_valid() method returns False.
My guess is that the method returns False because you are not setting the user on the form. In the POST dictionary you don't have the user or user_id. Add the following line before the "if form.is_valid()" statement:
form.instance.user = self.request.user
Hope this works.

Categories