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.
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
Very, very new to Django. I'm attempting to create a user registration process with custom form inputs. However, I have a few issues with the general persisting of new users to the database. This is what I have defined in my forms.py:
class UserRegistrationForm(UserCreationForm):
required_css_class = 'required'
email = forms.EmailField()
first_name = forms.CharField()
last_name = forms.CharField()
class Meta:
model = User
fields = ('username', 'email', 'first_name', 'last_name')
def __init__(self, *args, **kwargs):
super(UserRegistrationForm, self).__init__(*args, **kwargs)
self.fields['username'].widget = TextInput(attrs={'placeholder': 'Username'})
self.fields['username'].required = True
self.fields['username'].error_messages = {'required': 'Please enter your username'}
self.fields['email'].widget = EmailInput(attrs={'placeholder': 'Email'})
self.fields['email'].required = True
self.fields['email'].error_messages = {'required': 'Please enter your email'}
self.fields['first_name'].widget = TextInput(attrs={'placeholder': 'Forename'})
self.fields['first_name'].required = True
self.fields['first_name'].error_messages = {'required': 'Please enter your first_name'}
self.fields['last_name'].widget = TextInput(attrs={'placeholder': 'Surname'})
self.fields['last_name'].required = True
self.fields['last_name'].error_messages = {'required': 'Please enter your last_name'}
self.fields['password1'].widget = PasswordInput(attrs={'placeholder': 'Password'})
self.fields['password1'].required = True
self.fields['password1'].error_messages = {'required': 'Please enter your Password'}
self.fields['password2'].widget = PasswordInput(attrs={'placeholder': 'Confirm password'})
self.fields['password2'].required = True
self.fields['password2'].error_messages = {'required': 'Please confirm your Password'}
I also have the following in my views.py file:
class UserRegistrationView(FormView):
disallowed_url = ''
form_class = UserRegistrationForm
success_url = '/blog'
template_name = 'oauth/user/registration_form.html'
def registration_allowed(self):
return getattr(settings, 'REGISTRATION_OPEN', True)
def register(self, request):
if request.method == 'POST':
form = UserRegistrationForm(request.POST)
if form.is_valid():
new_user = form.save(commit=False)
new_user.set_password(form.cleaned_data['password1'])
new_user.save()
return render(request, 'blog/post/list.html', { 'new_user': new_user })
else:
return render(request, 'oauth/user/registration_form.html', { 'form': form })
def get_success_url(self, user=None):
return super(UserRegistrationView, self).get_success_url()
What's worrying:
It doesn't create a new user in the database to be logged in (should I expect to be able to see the newly created user in the general admin dashboard?)
It is able to sign in with the superuser! (I really don't get that one bit, like, seriously couldn't write that if I tried!)
Any help in where I may have gone wrong would be much appreciated!
You're confusing class based and function based views. Class based views don't have a method called "register"; yours will never be called.
Actually the method you have written is a complete function based view in its own right. Extract it from the class, remove the self parameter, and change the urlconf to call the function directly, and it should work.
Create a post method (or change the name of register to post) and add it to your class so that as_view() knows what to do with your class. then add it to your urlconf as a view.
class UserRegistrationView(FormView):
disallowed_url = ''
form_class = UserRegistrationForm
success_url = '/blog'
template_name = 'oauth/user/registration_form.html'
def post(self, request, *arg, **kwargs):
return self.register(request)
def registration_allowed(self):
return getattr(settings, 'REGISTRATION_OPEN', True)
def register(self, request):
if request.method == 'POST':
form = UserRegistrationForm(request.POST)
if form.is_valid():
new_user = form.save(commit=False)
new_user.set_password(form.cleaned_data['password1'])
new_user.save()
return render(request, 'blog/post/list.html', {'new_user': new_user})
else:
return render(request, 'oauth/user/registration_form.html', {'form': form})
def get_success_url(self, user=None):
return super(UserRegistrationView, self).get_success_url()
Then, in your urls:
urlpatterns = [
# . . .
url(r'^register_user/$', UserRegistrationView.as_view(), name='register_user'),
# . . .
]
You could also add a get method and use it to render your form if you are interested in keeping all of your form logic contained to one view class.
I'm making a social network in django and i've a trouble with the User Creation Form.
Here's my code:
views.py
def register(request):
if request.method == 'POST':
form = UserProfileForm(request.POST) #
if form.is_valid():
u = UserProfile.objects.create(first_name = form.cleaned_data['first_name'],...)
u.set_password(u.password)
u.save()
return HttpResponseRedirect(...)
else:
return Http404
else:
form = UserProfileForm()
return render_to_response(...,
{'form': form},
context_instance=RequestContext(request))
forms.py
class UserProfileForm(ModelForm):
first_name = forms.CharField(help_text="Por favor, introduzca su nombre", required=True)
...
class Meta:
model = UserProfile
fields = ['first_name',...]
def __init__(self, request=None, *args, **kwargs):
self.user = kwargs.pop('user', None)
super(UserProfileForm, self).__init__(*args, **kwargs)
def validate_user(self):
first_name_length = len(self.cleaned_data['first_name'])
...
if not first_name_length > 0:
raise ValidationError("Por favor, introduzca nombre valido")
...
models.py
class UserProfile(models.Model):
first_name = models.CharField(max_length=50, blank=True)...
and i'm using templates with form.as_p, but my form instance is never valid. What am I doing wrong? Can anyone help me?
Look at the signature of your form:
def __init__(self, request=None, *args, **kwargs)
and now look at how you instantiate it:
form = UserProfileForm(request.POST)
So, you're passing request.POST as the request parameter.
You should really avoid changing the signature of a subclass like this. In particular in your case as you are not using or passing the request at all. If you do need to do this, you should do the same as with the user value which you get from kwargs (but again you're not using or passing that either, so I don't know why you're bothering to do it.)
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!
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.