performing different functionality on modelform buttons django - django

I have a modelform with 2 buttons and i want to perform different functionality on them.
My modelform:
class jobpostForm(ModelForm):
class Meta:
model = jobpost
fields = ('job_title','job_type','job_location','job_description','start_date','end_date','country','how_to_apply')
widgets = {
'job_type':RadioSelect(),
'job_location':TextInput(attrs={'size':'70'}),
'job_description':Textarea(attrs={'cols':200, 'rows':10}),
'start_date':TextInput(attrs={
'class': 'datepicker',
'data-date-format': 'mm/dd/yyyy',
}),
'end_date':TextInput(attrs={
'class': 'datepicker',
'data-date-format': 'mm/dd/yyyy',
}),
}
def __init__(self, *args, **kwargs):
#super(jobpostForm, self).__init__(*args, **kwargs)
#self.fields['start_date'].widget.attrs['class'] = 'datepicker'
super(jobpostForm, self).__init__(*args, **kwargs)
#self.fields['ref_id'].widget = forms.HiddenInput()
self.helper = FormHelper()
self.helper.form_class = 'horizontal-form'
self.helper.form_id = 'id-jobpostform'
self.helper.form_class = 'blueForms'
self.helper.form_method = 'post'
self.helper.form_action = '/portal/next/post/'
self.helper.add_input(Submit('submit_addcontent', 'Preview'))
self.helper.add_input(Submit('submit_addcontent', 'Submit'))
super(jobpostForm, self).__init__(*args, **kwargs)
I want to perform different functionality on submit and preview.How can i access them in my view?

A django form really handles two things:
Displaying the intitial form on a GET request
Processing POST requests with data
You can approach your situation in multiple ways. One way would be to have both buttons submit your form. The preview button would fill in a hidden field named preview. Your form would process the submitted data. If the data included a value in the POST field named preview it would render a preview. Otherwise, it would process the form normally.

Related

Custom button classes in django crispy forms

The technical issue of styling buttons in django crispy forms. I would like to apply my own class, without using the primary button class.
class MyForm(Form):
def __init__(self, *args, **kwargs):
super().__init__(*args, **kwargs)
self.helper = FormHelper()
self.helper.layout = Layout(
Fields("field_1"),
Fields("field_2"),
Submit('submit', u'On', css_class='own-css-class'),
)
Basically, I solved this by adding self.helper.form_tag = False and inserting the button code directly into the html template. In addition, I deleted the submit button from the layout.
class MyForm(Form):
def __init__(self, *args, **kwargs):
super().__init__(*args, **kwargs)
self.helper = FormHelper()
self.helper.layout = Layout(
Fields("field_1"),
Fields("field_2"),
)
self.helper.form_tag = False
Is this solution correct and will it be compatible in the long term?
I'd recommend that you create your own custom button and use that when creating your layout.
class CustomButton(BaseInput)
input_type = 'submit'
field_classes = 'my custom css'

get parameter from url into hidden parameter of form

I have a CreateView where I want to create a new price for a product. The product detail page has a button to change the price which leads to a new page like ...\partprice\X where X is the id of the product. A product can have multiple prices, which is the reason why I am using a CreateView. Is the only way to grab the part_id from the URL (like here)?
In the forms.py:
class PPriceFormset(ModelForm):
required_css_class = "required"
class Meta:
model = PPrice
fields = ("price", "customer", "date", "part")
def __init__(self, *args, **kwargs):
super().__init__(*args, **kwargs)
self.helper = FormHelper()
self.helper.form_method = 'POST'
self.helper.layout = Layout(Row(Column('price'),
Column('customer')),
Submit('submit', 'Save price', css_class='btn-primary'),
Field("date", type="hidden"),
Field("part", type="hidden"))
I want to add the X from above into the hidden field "part". In the view I already have it:
class PPrice(CreateView):
model = PPrice
template_name ="gap/pprice_update.html"
form_class = PPriceFormset
success_url = reverse_lazy('part-list')
def get_form_kwargs(self, *args, **kwargs):
kwargs = super(PPriceCreateView, self).get_form_kwargs(*args, **kwargs)
kwargs['part_id'] = self.kwargs.get("part_pk")
return kwargs
The solution was quite easy, the class needed an initial value that could be passed on to the form via get_initial.
class PPriceCreateView(CreateView):
model = PPrice
template_name ="gap/pprice_update.html"
form_class = PPriceFormset
success_url = reverse_lazy('part-list')
def get_initial(self):
return {"part": self.kwargs["part_pk"]}

crispy forms bootstrap 3 submit problems

This has really stumped me. I can write forms OK in django but want to use crispy-forms bootstrap3. I can get the forms to render using this form code:
class NewGuestForm(forms.ModelForm):
def __init__(self, *args, **kwargs):
super(NewGuestForm, self).__init__(*args, **kwargs)
self.helper = FormHelper(self)
self.helper.form_id = 'id-newGuestForm'
self.helper.form_class = 'form-horizontal'
self.helper.label_class = 'col-lg-2'
self.helper.field_class = 'col-lg-6'
self.helper.form_method = 'post'
self.helper.form_action = 'guest-list'
self.helper.layout = Layout(
Fieldset (
'New Guest',
'first_name',
'last_name',
'num_child',
'landline',
'mobile',
'email'
),
FormActions(
Submit('save', 'Save changes',css_class='btn-primary'),
Button('cancel', 'Cancel')
)
)
class Meta:
model = Guest
class BookingForm(forms.Form):
class Meta:
model = Booking
def __init__(self, *args, **kwargs):
self.helper = FormHelper()
self.helper.form_method = 'POST'
self.helper.form_id = 'add-booking'
self.helper.form_class = 'form-horizontal'
self.helper.label_class = 'col-lg-2'
self.helper.field_class = 'col-lg-6'
self.helper.layout = Layout(
Fieldset(
'',
'guest',
'guest_status',
'start_date',
'end_date',
'dep_recd',
'bkd_child',
'bkd_adult',
'bal_recd',
'sec_recd',
'keys_sent',
'sec_retn',
'notes'
),
FormActions(
Submit('submit', "Save changes"),
Submit('cancel', "Cancel"),
)
)
super(BookingForm, self).__init__(*args, **kwargs)
This renders the form OK, but when I click 'submit' the browser goes white. The return (form_action) is correctly shown in the address bar, but isn't loaded. The data is not written to the database. The form renders with only the fields I need.
My view is:
class NewGuestView(CreateView):
model = Guest
template_name = 'new_guest.html'
form_class = NewGuestForm
If I change 'form_class' to 'form' the form renders with all fields and ignores the bootstrap column instructions. Also, the 'submit' and 'cancel' buttons do not appear.
I must be doing something wrong, but can't for the life of me see what. Any suggestions gratefully received.
This is probably a bit late for you, but I've been looking at django-crispy-forms for the first time today and ran into the same problem as you. If I have the form_action defined, then upon submission of the form the browser is redirected to the correct url, but the page is blank - even upon refreshing. This also happens whether the form is valid or not, so clearly this is a pretty fundamental problem and there must be something we're both doing wrong.
I got around this by using the success_url attribute of the view. So you could have tried:
from django.core.urlresolvers import reverse_lazy
class NewGuestView(CreateView):
...
success_url = reverse_lazy("guest-list")
As for the buttons, I haven't yet gotten to defining the layout and have used this approach:
self.helper.add_input(Submit('submit', 'Submit'))
self.helper.add_input(Button('cancel', 'Cancel'))
Although it's worth noting that the 'Cancel' button doesn't actually do anything at this stage, I'll need to look into that further.
Did you manage to get this working or find another way around?
Update:
The redirect fails with a 405 Method Not Allowed error. I tried defining the post() method in my view as per this SO question and this solves the HTTP error, but doesn't process the data (a new record isn't saved and validation errors are not caught). So I'm sticking with the success_url method until I can find out what I'm doing wrong.
Resolved now. I had not realised that I needed to add the fields to the class Meta as well as listing them in Layout. I added the following
class Meta:
model = Guest
fields = ['first_name', 'last_name', 'email', 'landline', 'mobile']
Now the form saves OK.

Django, set ChoiceField in form, after creation

I want to display a form with some customized user data in it. More specifically I want to fill a forms.ChoiceField with different data for each user.
This is my Form:
class WallPostForm(forms.Form):
text = forms.CharField(label=u'', widget=TinyMCE(attrs={'cols': 70, 'rows': 5}))
relates_to = forms.ChoiceField(label=u'Relates to', choices=[], widget=forms.Select(), required=False)
def __init__(self, data):
self.fields['relates_to'] = forms.ChoiceField(label=u'Relates to', choices=data, widget=forms.Select(), required=False)
super(WallPostForm, self).__init__()
And this is how I am calling this:
user = get_object_or_404(User, username=username)
data = UserTopics.objects.filter(user=user, result=0).values('id', 'topic__name')[:10]
form = WallPostForm(data)
I get a 'WallPostForm' object has no attribute 'fields' error.
What am I doing wrong?
As an addition to Jack's answer, you're probably better off just replacing the choices attribute, rather than the whole field:
def __init__(self, *args, **kwargs):
relates_to_choices = kwargs.pop('relates_to_choices')
super(WallPostForm, self).__init__(*args, **kwargs)
self.fields['relates_to'].choices = relates_to_choices
(I renamed the variable, it won't be a queryset.)
Django sets up the form's fields property in the __init__.
So just swap your code:
def __init__(self, data):
super(WallPostForm, self).__init__()
self.fields['relates_to'] = forms.ChoiceField(label=u'Relates to', choices=data, widget=forms.Select(), required=False)
Though, you probably shouldn't override a Form's __init__ like that. Django's form system expects the data arg in init to contain the data for the form, not a queryset you're using for a choices field.
I'd override it differently:
def __init__(self, *args, **kwargs):
relates_to_queryset = kwargs.pop('relates_to_queryset')
super(WallPostForm, self).__init__(*args, **kwargs)
self.fields['relates_to'] = forms.ChoiceField(label=u'Relates to', choices=relates_to_queryset, widget=forms.Select(), required=False)
Then call it:
form = WallPostForm(request.POST or None, relates_to_queryset=data)
You can use "initial" parameter, see the docs: https://docs.djangoproject.com/en/dev/ref/forms/api/#django.forms.Form.initial

use Crispy form with ModelForm

I've been running into crispy form, and it seems to do exactly what I want: render forms with bootstrap layout.
Now, the example talk about using forms.Form. This is ok, I can create mine by writing the code like this:
class TemplateCreateForm(forms.Form):
title = forms.CharField(label=(u'Task name'))
description = forms.CharField(label=(u'Task description'))
url_start = forms.CharField(label=(u'Start page url'))
url_end = forms.CharField(label=(u'Final page url'))
def __init__(self, *args, **kwargs):
self.helper = FormHelper()
self.helper.form_method = 'post'
self.helper.add_input(Submit('submit', 'Submit'))
super(TemplateCreateForm, self).__init__(*args, **kwargs)
But, how to do the update? because if I put this in the view:
form = TemplateCreateForm(request.POST or None, instance=template)
it does not work because instance is only for ModelForm.
Now, can I substitute the forms.Form with ModelForm and use crispy form for ModelForm?
I did this
class TemplateCreateForm(ModelForm):
title = forms.CharField(label=(u'Task name'))
description = forms.CharField(label=(u'Task description'))
url_start = forms.CharField(label=(u'Start page url'))
url_end = forms.CharField(label=(u'Final page url'))
def __init__(self, *args, **kwargs):
self.helper = FormHelper()
self.helper.form_method = 'post'
self.helper.add_input(Submit('submit', 'Submit'))
super(TemplateCreateForm, self).__init__(*args, **kwargs)
class Meta:
model = Template
exclude = ('user')
Here I added the Meta class.
Now: it works, but is it correct to use it like this?
The update works as well in this way.
What's the correct way to use forms for doing the update?
I'm the lead developer of django-crispy-forms. I'm not sure I follow your question as it's a bit poorly formatted. What exactly are you trying to do?
django-crispy-forms does work with ModelForms, the same way as with simple forms. It sits on top of Django, so it doesn't mess with it. It only controls your form rendering, but doesn't change how validation works, how to create form instances and so on.
EDIT:
I'm adding an example on how to do a ModelForm with crispy-forms.
class ExampleModelForm(forms.ModelForm):
def __init__(self, *args, **kwargs):
super(ExampleModelForm, self).__init__(*args, **kwargs)
# If you pass FormHelper constructor a form instance
# It builds a default layout with all its fields
self.helper = FormHelper(self)
# You can dynamically adjust your layout
self.helper.layout.append(Submit('save', 'save'))
class Meta:
model = ExampleModel
I believe your first problem is that you were subclassing forms.Form instead of forms.ModelForm. That's why I said that your problem was Django related, not crispy-forms related.
Later in your view:
form = ExampleModelForm()
In your template:
{% load crispy_forms_tags %}
{% crispy form %}