I have the following form:
class GroupForm(forms.ModelForm):
class Meta:
model = Group
def __init__(self, customer):
self.customer = customer
super(GroupForm, self).__init__()
def clean(self):
cleaned_data = super(GroupForm, self).clean()
email = cleaned_data.get('email')
print email
try:
groups = Group.objects.filter(email=email, customer=self.customer)
if groups:
messsge = u"That email already exists"
self._errors['email'] = self.error_class([messsge])
except:
pass
return cleaned_data
I call the form from the view like so:
if request.method == "POST":
form = GroupForm(request.POST, customer, instance=group)
if form.is_valid():
form.save()
The problem is that the validation is never triggered. Also the print of the email is never hit which means the clean function is never hit.
Why is this occurring?
I see this problem a lot here on SO, and the cause is usually the same. You have overridden the init method and changed the signature, so that the first element is now customer, not data. But when you instantiate it in your view, you pass request.POST first, so the parameters don't match up to the right variables.
In addition, you don't pass the parameters into the super method, so the POST is never even seen.
Do this instead:
class GroupForm(forms.ModelForm):
def __init__(self, *args, **kwargs):
self.customer = kwargs.pop('customer', None)
super(GroupForm, self).__init__(*args, **kwargs)
and in the view:
form = GroupForm(request.POST, customer=customer, instance=group)
Related
I have following code in my view of adding a new Item. Some fields are filled via user some fields are filled in the background. If form is valid then user is redirected to a url with a parameter (slug) from added object. How can I convert this code to django-bootstrap-modal-forms way?
def category_view(request, slug, *args, **kwargs):
...
if request.POST:
form = CreateItemForm(request.POST)
if form.is_valid():
if not request.user.is_authenticated:
raise PermissionDenied()
obj = form.save(commit=False)
obj.created_country = Constants.country_code
obj.created_by = request.user
obj.save()
return redirect('category:item_detail', slug=obj.slug)
I used django-bootstrap-modal-forms in the below way. but country and user fields are not null and must be filled. These fields are not part of the form.
class add_person(BSModalCreateView):
template_name = 'add_item.html'
form_class = CreateItemForm
success_message = 'Success: Item was created.'
success_url = reverse_lazy('category:item_detail') # slug needed
You are asking, how to modify the form and the only code you do not provide is the form. But try something like this:
forms.py
class BaseForm(forms.BaseForm):
def __init__(self, *args, **kwargs):
super().__init__(*args, **kwargs)
for field in self.fields.values():
if isinstance(field.widget, widgets.RadioSelect):
continue
elif isinstance(field.widget, widgets.Select):
field.widget.attrs.update({'class': 'form-select'})
continue
field.widget.attrs.update({'class': 'form-control'})
class CreateItemForm(BaseForm):
# your code
This itereates over your FormFields and adds the bootstrap class form-select, or much more important form-control to the widget of your field.
I changed the save method in the Django form.Then I inherited another save method from this method and made some changes to the child method ,that conflicted. I can't figure out how to fix the conflict so that my other uses of the parent method stay healthy and don't get spoiled.
Forms.py
class BaseModelForm(forms.ModelForm):
def save(self, commit=True, **kwargs):
"""
Save this form's self.instance object if commit=True. Otherwise, add
a save_m2m() method to the form which can be called after the instance
is saved manually at a later time. Return the model instance.
"""
if self.errors:
raise ValueError(
"The %s could not be %s because the data didn't validate." % (
self.instance._meta.object_name,
'created' if self.instance._state.adding else 'changed',
)
)
if commit:
# If committing, save the instance and the m2m data immediately.
self.instance.save(user=kwargs.pop('user'))
self._save_m2m()
else:
# If not committing, add a method to the form to allow deferred
# saving of m2m data.
self.save_m2m = self._save_m2m
return self.instance
class ChildForm(BaseModelForm):
def save(self, commit=True, **kwargs):
new_instance = super(ChildForm, self).save(commit=True)
# Some other codes goes here!
return new_instance
Models.py
class BaseFieldsModel(models.Model):
def save(self, *args, **kwargs):
user = kwargs.pop('user', None)
if user:
if self.pk is None:
self.created_by = user
self.updated_by = user
super(BaseFieldsModel, self).save(*args, **kwargs)
Views.py
def my_view(request,id):
if form.is_valid():
instance = form.save(commit=False)
# Some codes goes here!
instance.save(user=request.user)
And error is:
KeyError at /my/url
Request Method: POST
'user'
Exception Type: KeyError
Exception Value:
'user'
And Django Debug page separately highlight these three lines:
instance = form.save(commit=False)
new_instance = super(ChildForm, self).save(commit=True)
self.instance.save(user=kwargs.pop('user'))
You're trying to get user in BaseModelForm.save(), but you never passed the user to the form.save() calls. You need to add form.save(..., user=request.user):
def my_view(request,id):
...
instance = form.save(commit=False, user=request.user)
and also pass it along in super(ChildForm, self).save(..., **kwargs)
class ChildForm(BaseModelForm):
def save(self, commit=True, **kwargs):
new_instance = super(ChildForm, self).save(commit=True, **kwargs)
...
Also, you probably want to pass super(ChildForm, self).save(commit=commit, ...) in ChildForm:
new_instance = super(ChildForm, self).save(commit=True, **kwargs)
because otherwise the form class may not respect the commit flag being passed from the view (unless of course, you've already handled this in your elided code).
I want to make a custom form field validation to check if entered string is an email of user variable. Smth like this:
class FullEmailOrPhoneForm(forms.Form):
entered_string = forms.CharField()
class Meta:
model = User
fields = ('entered_string',)
def clean_entered_string(self):
email = self.cleaned_data['entered_string']
if email == user.email: # I need user variable for this comprasion
ans = email
else:
raise ValidationError('Incorrect email')
return ans
My view:
def reset_password_with_username(request, user):
if request.method == 'POST':
form = FullEmailOrPhoneForm(request.POST)
if form.is_valid():
pass
else:
form = FullEmailOrPhoneForm()
return render(request, 'registration/password_reset_with_username.html')
So how can I transfer user variable from view to form validation function?
You can override the __init__() method of your form so it can receive an extra argument, the user:
# inside FullEmailOrPhoneForm
def __init__(self, user, *args, **kwargs):
self.user = user # now you can use self.user anywhere in your form
super().__init__(*args, **kwargs)
def clean_entered_string(self):
...
if self.user and email == self.user.email:
...
# inside your view you have to specify `data=` since the first init arg is now the user.
form = FullEmailOrPhoneForm(user=request.user, data=request.POST)
# or with no data
form = FullEmailOrPhoneForm(user=request.user)
Note that you created a Form, not a ModelForm, so your Meta class is completely useless. If in fact, you wanted to have a ModelForm that models the user being edited (and the user you want to pass to the form is the same user as the one being edited), you should do this using the instance of the form:
class FullEmailOrPhoneForm(forms.ModelForm): # <-- note the ModelForm here
...
class Meta:
model = User
fields = ...
def clean_entered_string(self):
...
if self.instance and email == self.instance.email:
...
# then in your view:
form = FullEmailOrPhoneForm(request.POST, instance=user)
I want to use forms.ModelMultipleChoiceField in a form. I know it takes a queryset, however the query set I will be using take the param user which I normally pass in a view using request.user. However this is in a form, how do I pass request.user? do I need to?
Entry.objects.filter(request.user)
You should override your form's init method:
class MyForm(forms.ModelForm):
class Meta:
model = Entry
def __init__(self, user=None, *args, **kwargs):
super(MyForm, self).__init__(*args,**kwargs)
if user is not None:
form_choices = Entry.objects.filter(user)
else:
form_choices = Entry.objects.all()
self.fields['my_mfield'] = forms.ModelMultipleChoiceField(
queryset=form_choices
)
and in your views, when it's time to instantiate the form:
form = MyForm(request.user)
or
form = MyForm()
Can someone help me out with the following error below and explain the issue? All I'm trying to do it populate a group with a query set, but upon submitting the form I get the following error...
*TypeError at /sms/addbatch
int() argument must be a string or a number, not 'QueryDict'*
views.py
def add_batch(request):
# If we had a POST then get the request post values.
if request.method == 'POST':
form = BatchForm(request.POST)
# Check we have valid data before saving trying to save.
if form.is_valid():
# Clean all data and add to var data.
data = form.cleaned_data
groups = data['group'].split(",")
for item in groups:
batch = Batch(content=data['message'],
group=Group.objects.get(pk=item),
user=request.user
)
form.py
class BatchForm(forms.ModelForm):
class Meta:
model = Batch
def __init__(self, user=None, *args, **kwargs):
super(BatchForm, self).__init__(*args,**kwargs)
if user is not None:
form_choices = Batch.objects.for_user_pending(user)
else:
form_choices = Batch.objects.all()
self.fields['group'] = forms.ModelMultipleChoiceField(
queryset=form_choices
)
models.py
class BatchManager(models.Manager):
def for_user_pending(self, user):
return self.get_query_set().filter(user=user, status="Pending")
You are passing request.POST as the user parameter to your form. Do this:
form = BatchForm(data=request.POST)
# first parameter ---v
def __init__(self, user=None, ...
# first parameter ---v
form = BatchForm(request.POST)