I have form (thanks Alasdair):
class PermissionsModelMultipleChoiceField(forms.ModelChoiceField):
def label_from_instance(self, obj):
return "%s" % obj.name
class UserForm(forms.ModelForm):
first_name = forms.CharField(label=u'First name', required=True)
last_name = forms.CharField(label=u'Last name', required=True)
permissions = PermissionsModelMultipleChoiceField(Permission.objects.none(), widget=forms.CheckboxSelectMultiple)
def __init__( self, *args, **kwargs ):
super( NewUserForm, self ).__init__( *args, **kwargs )
ctypes = ContentType.objects.filter(
Q(app_label='articles') |
Q(app_label='tags')
)
self.fields['permissions'].queryset = Permission.objects.filter(content_type__in=ctypes)
class Meta:
model = User
And in template permissions shows me permissions something like this:
[] Can change article
[] Can delete article
[] Can view article
[...]
calling form:
profile_user = User.objects.get(pk=user_id)
if request.method == 'POST':
form = UserForm(request.POST, instance=profile_user)
if form.is_valid():
form.save()
[... here will save permissions ...]
return HttpResponseRedirect(reverse('home'))
else:
form = UserForm(instance=profile_user)
How to set checked in template this permissions which are assigned to edited user?
Use the argument data to populate the checkboxes when you instantiate the form (only outside the POST request) like this:
profile_user = User.objects.get(pk=user_id)
if request.method == 'POST':
form = UserForm(request.POST, instance=profile_user)
if form.is_valid():
form.save()
[... here will save permissions ...]
return HttpResponseRedirect(reverse('home'))
else:
user_permissions_list = # place here the query to get the profile_user permissions
form = UserForm(instance=profile_user, data={'permissions':user_permissions_list})
Using data is the key to mark the checkboxes according to user actual permissions.
Hope it helps!
Related
I would like users to have the ability to update their email address. I created a profile that has fields, but the email address is in the users table. I created a form that adds a custom form field and it works for update. However, I can't find a way to pre-populate this field on a REQUEST.GET.
# forms.py
class ProfileForm(forms.ModelForm):
class Meta:
model = Profile
fields = ('name', 'timezone')
class ProfileUpdateForm(ProfileForm):
email = forms.EmailField(max_length=254)
class Meta(ProfileForm.Meta):
fields = ProfileForm.Meta.fields + ('email',)
# views.py
#login_required
#require_http_methods(["GET","POST"])
def profile_update_view(request):
context = {}
# Get the logged in users profile
profile_object = Profile.objects.get(user=request.user.id)
if request.method == 'GET':
profile_form = ProfileUpdateForm(None, instance=profile_object)
context["form"] = profile_form
# how can I add User.objects.get(id=request.user.id).email to the custom field
if request.method == 'POST':
profile_form = ProfileUpdateForm(request.POST or None, instance=profile_object)
context["form"] = profile_form
if profile_form.is_valid():
try:
# email address exists
user = User.objects.get(email=profile_form.cleaned_data.get('email'))
messages.error(request, 'Failed profile update. Email address already exists.')
except:
# email address available
# get user object
user = User.objects.get(id=request.user.id)
user.email = profile_form.cleaned_data.get('email')
# update user object
user.save()
profile_form.save()
messages.success(request, 'Successful profile update.')
return render(request, "profile.html", context)
I tend to favour class-based views, and things like this are where they come into their own. The form:
class ProfileUpdateForm(forms.ModelForm):
class Meta:
model = Profile
fields = ('name', 'timezone')
email = forms.EmailField(max_length=254) #add non-model form field
And a class-based view. Handle the initial value for email in get_initial(), and updating of self.request.user in form_valid():
class ProfileUpdateView( UpdateView):
model = Profile
form_class = ProfileUpdateForm
template_name = 'profile.html' # profiles/update_profile.html would be better
# other declarations ...?
def get_initial(self):
initial = super().get_initial()
initial['email'] = self.request.user.email
return initial
# #transaction.atomic might be a good idea
def form_valid(self, form):
new_email = form.cleaned_data['email']
user = self.request.user
if user.email != new_email: # don't do a pointless non-update save
user.email = new_email
user.save()
return super().form_valid( form) # will save the profile
# forms.py
def __init__(self, *args, **kwargs):
self.email = kwargs.pop("email")
super(ProfileUpdateForm, self).__init__(*args, **kwargs)
self.initial['email'] = self.email
# views.py
if request.method == 'GET':
profile_form = ProfileUpdateForm(None, instance=profile_object, email=request.user.email)
context["form"] = profile_form
if request.method == 'POST':
profile_form = ProfileUpdateForm(request.POST or None, instance=profile_object, email=request.POST.get('email'))
context["form"] = profile_form
what I am trying to accomplish is to send the "requester" model, using the logged-in user to a form ...
Mainly the problem that I have is that the views.py "class CreateOrderView(CreateView)" does not have a parameter "request" , so I cannot get the request.user, and therefore get requester_obj and automatically select this requester_obj in the form field "requester", when entering this page.
models.py Order:
DEFAULT_REQUESTER_ID= 1
requester = models.ForeignKey(Profile, on_delete=models.CASCADE, default=DEFAULT_REQUESTER_ID, verbose_name="usuario")
forms.py:
class OrderCreateForm(BaseForm, forms.ModelForm):
date = forms.DateField(label="Fecha" , widget=forms.DateInput(attrs={'type': 'date'}))
class Meta:
model = Order
fields = ['requester','title' , 'date', ]
views.py:
#method_decorator(staff_member_required, name='dispatch')
class CreateOrderView(CreateView):
template_name = 'form.html'
form_class = OrderCreateForm
model = Order
def get_success_url(self):
self.new_object.refresh_from_db()
return reverse('update_order', kwargs={'pk': self.new_object.id})
def form_valid(self, form):
object = form.save()
object.refresh_from_db()
self.new_object = object
return super().form_valid(form)
I get the requester like this:
#login_required
def create(request):
#return render(request, 'app_client/create.html')
if request.method == 'POST':
if request.POST['value'] and request.POST['products']:
logged_user = request.user
user_obj = get_object_or_404(User, username=logged_user)
requestor_obj = get_object_or_404(Profile, user=user_obj)
....
I just found a solution for my issue...
What I did was to remove the "requester" field in forms.py, and send the requester obj to the form after user presses the submit form button
def form_valid(self, form):
logged_user = self.request.user
user_obj = get_object_or_404(User, username=logged_user)
requester_obj = get_object_or_404(Profile, user=user_obj)
form.instance.requestor = requestor_obj
object = form.save()
object.refresh_from_db()
self.new_object = object
return super().form_valid(form)
form.instance.requestor = requestor_obj was the line that I needed to send it to form before saving it.
ref: Django CreateView Foreign key
I have my response form and view like this
class ResponseForm(ModelForm):
class Meta:
model = ResponseModel
exclude = ('author', 'title','submit_count')
# help_texts = {
# 'ans1': user.q1.value,
# }
#login_required
def ResponseFormView(request):
if request.method == "POST":
form = ResponseForm(request.POST)
if form.is_valid():
submission = form.save(commit=False)
submission.author = request.user
submission.save()
return render(request, 'thanks.html', {})
else:
form = ResponseForm()
return render(request, 'response_tem.html', {'form': form})
I want the help text for 'ans1' field to be the value of q1 field of request.user. How do I do it?
You can do it like this:
class ResponseForm(ModelForm):
def __init__(self, *args, **kwargs):
user = kwargs.pop('user', None) # popping user from known arguments
super(ResponseForm, self).__init__(*args, **kwargs)
if user:
self.fields['ans1'].help_text = "Help Text for {}".format(user.username)
class Meta:
model = ResponseModel
exclude = ('author', 'title','submit_count')
#login_required
def ResponseFormView(request):
if request.method == "POST":
form = ResponseForm(request.POST)
if form.is_valid():
submission = form.save(commit=False)
submission.author = request.user
submission.save()
return render(request, 'thanks.html', {})
else:
form = ResponseForm(user=request.user) # passing user as known argument
return render(request, 'response_tem.html', {'form': form})
Here, in the view I am passing the request.user as known argument when I am initiating Form Class's Object (marked with comment). Then in the Form, I am catching the user sent from view and updating the field's help text.
Related to this question, but expanding on it - How would I use this technique in a formset?
I'd like to use the current logged in user in a form, but I'm using the form in a formset. The referenced solution for a single form is to pass request.user to the form and process in init. How do I add to the kwargs for each form in the formset?
Example in my code:
in forms.py
class NewStudentForm (forms.Form):
username = forms.RegexField(label=_("Username"), max_length=30, regex=r'^\w+$',
help_text = _("Required. 30 characters or fewer. Alphanumeric characters only (letters, digits and underscores)."),
error_message = _("This value must contain only letters, numbers and underscores."))
first_name = forms.CharField(label=_('first name'), max_length=30 )
last_name = forms.CharField(label=_('last name'), max_length=30, )
email = forms.EmailField(label=_('e-mail address') )
password = forms.CharField(label=_('password'), max_length=64, )
class Meta:
model = User
fields = ("username","first_name", "last_name", "email", "password")
def __init__(self, *args, **kwargs):
self._user = kwargs.pop('user')
super(NewStudentForm, self).__init__(*args, **kwargs)
def save(self, commit=True):
user = super(NewStudentForm, self).save(commit=False)
user.set_password(self.cleaned_data["password"])
if commit:
user.save()
profile = Profile.objects.create_profile(user)
profile.activiation_key = profile.ACTIVATED_KEY
profile.authorized = True
profile.save()
user.is_active=True
user.save()
student = models.Student()
student.user = user
student.teacher = self._user
student.plaintext_pwd = self.cleaned_data["password"]
student.save()
return UserWarning
then in views.py
#login_required
def new_student(request):
from django.forms.formsets import formset_factory
try:
if request.method == 'GET':
newStudentFormset = formset_factory(forms.NewStudentForm, extra=2)
formset = newStudentFormset()
return shortcuts.render_to_response('NewStudent.html', { 'newStudentFormSet':formset, 'active_username': request.user.username })
elif request.method == 'POST':
if LOGIN_FORM_KEY in request.POST:
return _handle_login(request)
data = request.POST.copy()
newStudentFormset = formset_factory(forms.NewStudentForm)
formset = newStudentFormset(data) ### Pass current user to formset? ###
if formset.is_valid():
formset.save()
request.user.message_set.create(message="Save successful.")
return shortcuts.redirect(student)
else:
return shortcuts.render_to_response('NewStudent.html', { 'newStudentFormSet':formset, 'active_username': request.user.username, 'error_message':formset.errors})
return http.HttpResponseNotAllowed(['GET', 'POST'])
except models.Student.DoesNotExist:
return http.HttpResponseNotFound('<h1>Requested Student not found</h1>')
By adding a class that extends BaseFormSet you can add custom code to pass a parameter to the form.
in forms.py:
class NewStudentFormSet(BaseFormSet):
def __init__(self, *args, **kwargs):
self.user = kwargs.pop('user', None)
super(NewStudentFormSet, self).__init__(*args, **kwargs)
def _construct_forms(self):
self.forms = []
for i in xrange(self.total_form_count()):
self.forms.append(self._construct_form(i, user=self.user))
Then in views.py:
# ...
data = request.POST.copy()
newStudentFormset = formset_factory(forms.NewStudentForm, formset=forms.NewStudentFormSet)
formset = newStudentFormset(data, user=request.user)
# ...
Thanks to Ashok Raavi.
I rather to iterate forms directly in the view:
for form in formset.forms:
form.user = request.user
formset.save()
It avoid creating unecessary BaseFormSet
It is cleaner
Based on Paulo Cheque answer (which didn't really work for my case).
I loved the idea of not writing a custom BaseFormSet inherited class.
if formset.is_valid():
new_instances = formset.save(commit=False)
for new_instance in new_instances:
new_instance.user = request.user
new_instance.save()
I tried the solution of selfsimilar but the BaseFormSet didn't work in my Django 1.6.
I followed the steps in: https://code.djangoproject.com/ticket/17478 and the way that worked for me is:
class NewStudentFormSet(BaseFormSet):
def __init__(self, *args, **kwargs):
self.user = kwargs.pop('user',None)
super(NewStudentFormSet, self).__init__(*args, **kwargs)
for form in self.forms:
form.empty_permitted = False
def _construct_forms(self):
if hasattr(self,"_forms"):
return self._forms
self._forms = []
for i in xrange(self.total_form_count()):
self._forms.append(self._construct_form(i, user=self.user))
return self._forms
forms = property(_construct_forms)
Here is a similar question about passing form parameters to a formset:
Django Passing Custom Form Parameters to Formset
Personally, I like the second answer on there about building the form class dynamically in a function because it is very fast to implement and easy to understand.
I have a view that enables users to edit their profiles (usual name, username etc) and a image contained in an ÌmageField within the UserProfile:
#login_required
def editprofile(request):
user = request.user
if request.method == 'POST':
edit_form = EditProfileForm(data = request.POST, user = user)
if edit_form.is_valid():
user = edit_form.save()
request.user.message_set.create(message='Votre profil a été modifié.')
return HttpResponseRedirect('/')
else:
dict = {'first_name':user.first_name, 'last_name':user.last_name, 'email':user.email, 'username':user.username}
edit_form = EditProfileForm(user = user, data = dict)
tpl_dict = {'form' : edit_form}
return render_to_response('editprofile.html', tpl_dict, RequestContext(request))
and the form is:
class EditProfileForm(forms.Form):
first_name = forms.CharField(max_length = 100, required=False)
last_name = forms.CharField(max_length = 100, required=False)
email = forms.EmailField()
username = forms.CharField(max_length = 100)
avatar = forms.ImageField(required = False)
def __init__(self, user, *args, **kwargs):
super(EditProfileForm, self).__init__(*args, **kwargs)
self.user = user
def save(self):
user = self.user
user.email = self.cleaned_data['email']
user.username = self.cleaned_data['username']
user.first_name = self.cleaned_data['first_name']
user.last_name = self.cleaned_data['last_name']
user.save()
profile = user.get_profile()
profile.avatar = self.cleaned_data['avatar']
profile.save()
return user
the problem is that i need to pass a `request.FILES' to the form!
I've tried
edit_form = EditProfileForm(data = request.POST, request.FILES, user = user)
and other variants without succes.
When you are overriding the constructor of a form it is a good idea to pass the arguments named instead of just in order. So, I would do:
edit_form = EditProfileForm(user=user, data=request.POST, files=request.FILES)
That way it is clear to someone reading the code that you have a non-standard form that expects a user argument and it makes explicit which arguments you are passing.
Alternatively, if you'd insist on calling the constructor without naming the arguments, the correct way to do so is:
edit_form = EditProfileForm(user, request.POST, request.FILES)
since user is the first argument to your constructor.
Try
edit_form = EditProfileForm(request.POST, request.FILES, user = user)
The safest way to override a form's __init__ is to listen for extra kwargs:
class EditProfileForm(forms.Form):
# [fields]
def __init__(self, *args, **kwargs):
user = kwargs.pop('user', default_user) # fetch `user` and remove from kwargs
super(EditProfileForm, self).__init__(*args, **kwargs)
self.user = user
This leaves the form's original signature essentially untouched, and you can instantiate it as normal, with your extra argument tacked on the end:
EditProfileForm(request.POST, user=user)
EditProfileForm(request.POST, request.FILES, user=user)
etc.