class MembershipTaskForm(forms.ModelForm): """Form intended to use in formset for creating multiple membership members.""" helper = BootstrapHelper( add_cancel_button=False, form_tag=False, add_submit_button=False ) helper_empty_form = BootstrapHelper( add_cancel_button=False, form_tag=False, add_submit_button=False ) class Meta: model = MembershipTask fields = [ "person", "role", ] widgets = { "person": ModelSelect2Widget(data_view="person-lookup"), "role": ModelSelect2Widget(data_view="membershippersonrole-lookup"), } def __init__(self, *args, **kwargs): super().__init__(*args, **kwargs) # set up layout objects for the helpers - they're identical except for # visibility of the delete checkbox self.helper.layout = self.helper.build_default_layout(self) self.helper_empty_form.layout = self.helper.build_default_layout(self) self.helper.layout.append(Field("id")) self.helper.layout.append(Field("DELETE")) # visible; formset adds it self.helper_empty_form.layout.append(Field("id")) self.helper_empty_form.layout.append( Div(Field("DELETE"), css_class="d-none") # hidden )
class TrainingProgressForm(forms.ModelForm): trainee = forms.ModelChoiceField(label='Trainee', required=True, queryset=Person.objects.all(), widget=ModelSelect2(url='person-lookup')) evaluated_by = forms.ModelChoiceField( label='Evaluated by', required=False, queryset=Person.objects.all(), widget=ModelSelect2(url='admin-lookup')) event = forms.ModelChoiceField(label='Event', required=False, queryset=Event.objects.all(), widget=ModelSelect2(url='event-lookup')) # helper used in edit view helper = BootstrapHelper(duplicate_buttons_on_top=True, submit_label='Update', add_delete_button=True, additional_form_class='training-progress', add_cancel_button=False) # helper used in create view create_helper = BootstrapHelper(duplicate_buttons_on_top=True, submit_label='Add', additional_form_class='training-progress', add_cancel_button=False) class Meta: model = TrainingProgress fields = [ 'trainee', 'evaluated_by', 'requirement', 'state', 'discarded', 'event', 'url', 'notes', ] widgets = { 'state': RadioSelect, } def clean(self): cleaned_data = super().clean() trainee = cleaned_data.get('trainee') # check if trainee has at least one training task training_tasks = trainee.get_training_tasks() if not training_tasks: raise ValidationError("It's not possible to add training progress " "to a trainee without any training task.")
class MemberForm(EditableFormsetFormMixin, forms.ModelForm): """Form intended to use in formset for creating multiple membership members.""" helper = BootstrapHelper( add_cancel_button=False, form_tag=False, add_submit_button=False, # formset gathers media, so there's no need to include them in every individual # form (plus this prevents an unnecessary bug when multiple handlers are # attached to the same element) include_media=False, ) helper_empty_form = BootstrapHelper(add_cancel_button=False, form_tag=False, add_submit_button=False) class Media: js = ("member_form.js", ) class Meta: model = Member fields = [ # The membership field is required for uniqueness validation. We're making # it hidden from user to not confuse them, and to discourage from changing # field's value. The downside of this approach is that a) user can provide # different ID if they try hard enough, and b) tests get a little bit # harder as additional value has to be provided. "membership", "organization", "role", ] widgets = { "membership": forms.HiddenInput, "organization": ModelSelect2Widget(data_view="organization-lookup"), "role": ModelSelect2Widget(data_view="memberrole-lookup"), } def __init__(self, *args, **kwargs): super().__init__(*args, **kwargs) # set up layout objects for the helpers - they're identical except for # visibility of the delete checkbox self.helper.layout = self.helper.build_default_layout(self) self.helper.layout.append("id") self.helper.layout.append("DELETE") # visible; formset adds it self.helper_empty_form.layout = self.helper.build_default_layout(self) self.helper_empty_form.layout.append("id") self.helper_empty_form.layout.append( Div("DELETE", css_class="d-none") # hidden ) # remove EDITABLE checkbox from empty helper form pos_index = self.helper_empty_form.layout.fields.index("EDITABLE") self.helper_empty_form.layout.pop(pos_index)
class BulkChangeTrainingRequestForm(forms.Form): """Form used to bulk discard training requests or bulk unmatch trainees from trainings.""" requests = forms.ModelMultipleChoiceField( queryset=TrainingRequest.objects.all()) # TODO: add training-requests lookup? # requests = forms.ModelMultipleChoiceField( # label='Requests', # required=False, # queryset=TrainingRequest.objects.all() # widget=ModelSelect2(url='???-lookup'), # ) helper = BootstrapHelper(add_submit_button=False, form_tag=False, display_labels=False, add_cancel_button=False) helper.layout = Layout( # no 'requests' -- you should take care of generating it manually in # the template where this form is used # We use formnovalidate on submit buttons to disable browser # validation. This is necessary because this form is used along with # BulkMatchTrainingRequestForm, which have required fields. Both # forms live inside the same <form> tag. Without this attribute, # when you click one of the following submit buttons, the browser # reports missing values in required fields in # BulkMatchTrainingRequestForm. FormActions( Div( Submit('discard', 'Discard selected requests', formnovalidate='formnovalidate', css_class="btn-danger"), Submit('accept', 'Accept selected requests', formnovalidate='formnovalidate', css_class="btn-success"), css_class="btn-group", ), Submit('unmatch', 'Unmatch selected trainees from training', formnovalidate='formnovalidate'), HTML('<a bulk-email-on-click class="btn btn-info text-white">' 'Mail selected trainees</a> '), )) # When set to True, the form is valid only if every request is matched to # one person. Set to True when 'unmatch' button is clicked, because # unmatching makes sense only if each selected TrainingRequest is matched # with one person. check_person_matched = False def clean(self): super().clean() unmatched_request_exists = any( r.person is None for r in self.cleaned_data.get('requests', [])) if self.check_person_matched and unmatched_request_exists: raise ValidationError('Select only requests matched to a person.')
class InvoiceRequestForm(forms.ModelForm): helper = BootstrapHelper(add_cancel_button=False) class Meta: model = InvoiceRequest fields = ( 'organization', 'reason', 'reason_other', 'date', 'event', 'event_location', 'item_id', 'postal_number', 'contact_name', 'contact_email', 'contact_phone', 'full_address', 'amount', 'currency', 'currency_other', 'breakdown', 'vendor_form_required', 'vendor_form_link', 'form_W9', 'receipts_sent', 'shared_receipts_link', 'notes', ) widgets = { 'reason': forms.RadioSelect, 'currency': forms.RadioSelect, 'vendor_form_required': forms.RadioSelect, 'receipts_sent': forms.RadioSelect, }
class InstructorRecruitmentCreateForm(forms.ModelForm): helper = BootstrapHelper(add_cancel_button=False, submit_label="Add sign up page") class Meta: model = InstructorRecruitment fields = ("notes", )
class TrainingRequestUpdateForm(forms.ModelForm): person = forms.ModelChoiceField(label='Matched Trainee', required=False, queryset=Person.objects.all(), widget=ModelSelect2(url='person-lookup')) score_auto = forms.IntegerField( disabled=True, label=TrainingRequest._meta.get_field('score_auto').verbose_name, help_text=TrainingRequest._meta.get_field('score_auto').help_text, ) helper = BootstrapHelper(duplicate_buttons_on_top=True, submit_label='Update') class Meta: model = TrainingRequest exclude = () widgets = { 'occupation': forms.RadioSelect(), 'domains': forms.CheckboxSelectMultiple(), 'gender': forms.RadioSelect(), 'previous_involvement': forms.CheckboxSelectMultiple(), 'previous_training': forms.RadioSelect(), 'previous_experience': forms.RadioSelect(), 'programming_language_usage_frequency': forms.RadioSelect(), 'teaching_frequency_expectation': forms.RadioSelect(), 'max_travelling_frequency': forms.RadioSelect(), 'state': forms.RadioSelect() }
class DebriefForm(forms.Form): '''Represent general debrief form.''' begin_date = forms.DateField(label='Begin date', help_text='YYYY-MM-DD', input_formats=[ '%Y-%m-%d', ]) end_date = forms.DateField(label='End date', help_text='YYYY-MM-DD', input_formats=[ '%Y-%m-%d', ]) MODE_CHOICES = ( ('all', 'List all events'), ('TTT', 'List only TTT events'), ('nonTTT', 'List only non-TTT events'), ) mode = forms.ChoiceField( choices=MODE_CHOICES, widget=forms.RadioSelect, initial='all', ) helper = BootstrapHelper(use_get_method=True, add_cancel_button=False)
class GenericEmailScheduleForm(forms.ModelForm): body_template = MarkdownxFormField( label="Markdown body", widget=AdminMarkdownxWidget, required=True, ) helper = BootstrapHelper( wider_labels=True, add_cancel_button=False, add_submit_button=False, form_tag=False, ) class Meta: model = EmailTemplate fields = [ "slug", "subject", "to_header", "from_header", "cc_header", "bcc_header", "reply_to_header", "body_template", ]
class EventSubmitForm(EventSubmitFormNoCaptcha, PrivacyConsentMixin): captcha = ReCaptchaField() helper = BootstrapHelper(wider_labels=True) class Meta(EventSubmitFormNoCaptcha.Meta): exclude = ('state', 'event') + EventSubmitFormNoCaptcha.Meta.exclude
class BaseModelAddFormSet(forms.models.BaseModelFormSet): can_delete = True can_order = False min_num = forms.formsets.DEFAULT_MIN_NUM max_num = forms.formsets.DEFAULT_MAX_NUM absolute_max = 2 * max_num validate_max = False validate_min = False helper = BootstrapHelper(form_tag=False, add_submit_button=False, add_cancel_button=False) def __init__(self, *args, **kwargs): # Override the default form helper super().__init__(*args, **kwargs) self.form.helper = self.helper def add_fields(self, form, index): # Change label of DELETE checkbox super().add_fields(form, index) form[forms.formsets.DELETION_FIELD_NAME].label = 'Do not import' def get_queryset(self): # Do not show any existing model in the formset return self.model.objects.none() def total_form_count(self): # Restrict the total number of forms to number of initial forms if self.data or self.files: return super().total_form_count() else: return len(self.initial_extra)
class SearchForm(forms.Form): """Represent general searching form.""" term = forms.CharField(label="Term", max_length=100) no_redirect = forms.BooleanField(required=False, initial=False, widget=forms.HiddenInput) helper = BootstrapHelper(add_cancel_button=False, use_get_method=True)
class BulkAddTrainingProgressForm(forms.ModelForm): event = forms.ModelChoiceField( label='Training', required=False, queryset=Event.objects.filter(tags__name='TTT'), widget=ModelSelect2(url='ttt-event-lookup')) trainees = forms.ModelMultipleChoiceField(queryset=Person.objects.all()) # TODO: add trainees lookup? # trainees = forms.ModelMultipleChoiceField( # label='Trainees', # required=False, # queryset=Person.objects.all(), # widget=ModelSelect2(url='person-lookup'), # ) helper = BootstrapHelper(additional_form_class='training-progress', submit_label='Add', form_tag=False, add_cancel_button=False) helper.layout = Layout( # no 'trainees' -- you should take care of generating it manually in # the template where this form is used 'requirement', 'state', 'event', 'url', 'notes', ) class Meta: model = TrainingProgress fields = [ # no 'trainees' 'requirement', 'state', 'event', 'url', 'notes', ] widgets = { 'state': RadioSelect, 'notes': TextInput, } def clean(self): cleaned_data = super().clean() trainees = cleaned_data.get('trainees') # check if all trainees have at least one training task for trainee in trainees: training_tasks = trainee.get_training_tasks() if not training_tasks: raise ValidationError("It's not possible to add training " "progress to a trainee without any " "training task.")
class DCSelfOrganizedEventRequestForm(DCSelfOrganizedEventRequestFormNoCaptcha, PrivacyConsentMixin): captcha = ReCaptchaField() helper = BootstrapHelper(wider_labels=True) class Meta(DCSelfOrganizedEventRequestFormNoCaptcha.Meta): exclude = ('state', 'event') \ + DCSelfOrganizedEventRequestFormNoCaptcha.Meta.exclude
class AutoUpdateProfileForm(forms.ModelForm): username = forms.CharField(disabled=True, required=False) github = forms.CharField( disabled=True, required=False, help_text='If you want to change your github username, please email ' 'us at <a href="mailto:[email protected]">' '[email protected]</a>.') country = CountryField().formfield( required=False, help_text='Your country of residence.', widget=ListSelect2(), ) languages = forms.ModelMultipleChoiceField( label='Languages', required=False, queryset=Language.objects.all(), widget=ModelSelect2Multiple(url='language-lookup') ) helper = BootstrapHelper(add_cancel_button=False) class Meta: model = Person fields = [ 'personal', 'middle', 'family', 'email', 'gender', 'may_contact', 'publish_profile', 'lesson_publication_consent', 'country', 'airport', 'github', 'twitter', 'url', 'username', 'affiliation', 'domains', 'lessons', 'languages', 'occupation', 'orcid', ] readonly_fields = ( 'username', 'github', ) widgets = { 'gender': forms.RadioSelect(), 'domains': forms.CheckboxSelectMultiple(), 'lessons': forms.CheckboxSelectMultiple(), 'airport': ListSelect2(), }
def __init__(self, *args, **kwargs): form_tag = kwargs.pop("form_tag", True) add_submit_button = kwargs.pop("add_submit_button", True) super().__init__(*args, **kwargs) self.helper = BootstrapHelper( add_cancel_button=False, form_tag=form_tag, add_submit_button=add_submit_button, ) # set up a layout object for the helper self.helper.layout = self.helper.build_default_layout(self) # set up `*WithOther` widgets so that they can display additional # fields inline self["gender"].field.widget.other_field = self["gender_other"] # remove additional fields self.helper.layout.fields.remove("gender_other")
class DCEventRequestForm(DCEventRequestNoCaptchaForm): captcha = ReCaptchaField() helper = BootstrapHelper(wider_labels=True, add_cancel_button=False, duplicate_buttons_on_top=False) class Meta(DCEventRequestNoCaptchaForm.Meta): exclude = ('state', 'event') \ + DCEventRequestNoCaptchaForm.Meta.exclude
class BulkAddTrainingProgressForm(forms.ModelForm): event = forms.ModelChoiceField( label="Training", required=False, queryset=Event.objects.filter(tags__name="TTT"), widget=ModelSelect2Widget(data_view="ttt-event-lookup", attrs=SELECT2_SIDEBAR), ) trainees = forms.ModelMultipleChoiceField(queryset=Person.objects.all()) helper = BootstrapHelper( additional_form_class="training-progress", submit_label="Add", form_tag=False, add_cancel_button=False, ) helper.layout = Layout( # no 'trainees' -- you should take care of generating it manually in # the template where this form is used "requirement", "state", "event", "url", "notes", ) class Meta: model = TrainingProgress fields = [ # no 'trainees' "requirement", "state", "event", "url", "notes", ] widgets = { "state": RadioSelect, "notes": TextInput, } def clean(self): cleaned_data = super().clean() trainees = cleaned_data.get("trainees") # check if all trainees have at least one training task for trainee in trainees: training_tasks = trainee.get_training_tasks() if not training_tasks: raise ValidationError("It's not possible to add training " "progress to a trainee without any " "training task.")
class SWCEventRequestNoCaptchaForm(PrivacyConsentMixin, forms.ModelForm): workshop_type = forms.CharField(initial='swc', widget=forms.HiddenInput()) understand_admin_fee = forms.BooleanField( required=True, initial=False, label='I understand the Software Carpentry Foundation\'s ' 'administration fee.', help_text='<a href="http://software-carpentry.org/blog/2015/07/changes' '-to-admin-fee.html" target="_blank" rel="noreferrer">' 'Look up administration fees</a>.', ) language = forms.ModelChoiceField( label='Language', required=False, queryset=Language.objects.all(), widget=ModelSelect2Widget(data_view='language-lookup') ) helper = BootstrapHelper(wider_labels=True, add_cancel_button=False, duplicate_buttons_on_top=True) class Meta: model = EventRequest exclude = ('created_at', 'last_updated_at', 'assigned_to', 'data_types', 'data_types_other', 'attendee_data_analysis_level', 'fee_waiver_request', ) widgets = { 'event': Select2Widget, 'approx_attendees': forms.RadioSelect(), 'attendee_domains': CheckboxSelectMultipleWithOthers( 'attendee_domains_other'), 'attendee_academic_levels': forms.CheckboxSelectMultiple(), 'attendee_computing_levels': forms.CheckboxSelectMultiple(), 'travel_reimbursement': RadioSelectWithOther( 'travel_reimbursement_other'), 'admin_fee_payment': forms.RadioSelect(), 'country': Select2Widget, } def __init__(self, *args, **kwargs): super().__init__(*args, **kwargs) # set up a layout object for the helper self.helper.layout = self.helper.build_default_layout(self) # set up RadioSelectWithOther widget so that it can display additional # field inline self['attendee_domains'].field.widget.other_field = \ self['attendee_domains_other'] self['travel_reimbursement'].field.widget.other_field = \ self['travel_reimbursement_other'] # remove that additional field self.helper.layout.fields.remove('attendee_domains_other') self.helper.layout.fields.remove('travel_reimbursement_other')
class SponsorshipForm(WidgetOverrideMixin, forms.ModelForm): helper = BootstrapHelper(submit_label='Add') class Meta: model = Sponsorship fields = '__all__' widgets = { 'organization': ModelSelect2(url='organization-lookup'), 'event': ModelSelect2(url='event-lookup'), 'contact': ModelSelect2(url='person-lookup'), }
class DCEventRequestNoCaptchaForm(SWCEventRequestNoCaptchaForm): workshop_type = forms.CharField(initial='dc', widget=forms.HiddenInput()) understand_admin_fee = forms.BooleanField( required=True, initial=False, label='I understand the Data Carpentry\'s administration fee.', help_text='There is a per-workshop fee for Data Carpentry to cover ' 'administrative and core development costs. The per-workshop fee is ' 'currently $2500. We work to find local instructors when possible, but' ' the host institute will also need to pay for instructors travel and' ' lodging if they need to travel. Therefore overall workshop costs are' ' $2500 - $6000.', ) helper = BootstrapHelper(wider_labels=True, add_cancel_button=False, duplicate_buttons_on_top=True) class Meta(SWCEventRequestNoCaptchaForm.Meta): exclude = ('created_at', 'last_updated_at', 'assigned_to', 'admin_fee_payment', 'attendee_computing_levels', ) widgets = { 'event': Select2Widget, 'approx_attendees': forms.RadioSelect(), 'attendee_domains': CheckboxSelectMultipleWithOthers( 'attendee_domains_other'), 'data_types': RadioSelectWithOther('data_types_other'), 'attendee_academic_levels': forms.CheckboxSelectMultiple(), 'attendee_data_analysis_level': forms.CheckboxSelectMultiple(), 'travel_reimbursement': RadioSelectWithOther( 'travel_reimbursement_other'), } def __init__(self, *args, **kwargs): super().__init__(*args, **kwargs) # set up a layout object for the helper self.helper.layout = self.helper.build_default_layout(self) # set up RadioSelectWithOther widget so that it can display additional # field inline self['attendee_domains'].field.widget.other_field = \ self['attendee_domains_other'] self['data_types'].field.widget.other_field = \ self['data_types_other'] self['travel_reimbursement'].field.widget.other_field = \ self['travel_reimbursement_other'] # remove that additional field self.helper.layout.fields.remove('attendee_domains_other') self.helper.layout.fields.remove('data_types_other') self.helper.layout.fields.remove('travel_reimbursement_other')
class TrainingRequestsSelectionForm(forms.Form): trainingrequest_a = forms.ModelChoiceField( label='Training request A', required=True, queryset=TrainingRequest.objects.all(), widget=ModelSelect2(url='trainingrequest-lookup')) trainingrequest_b = forms.ModelChoiceField( label='Training request B', required=True, queryset=TrainingRequest.objects.all(), widget=ModelSelect2(url='trainingrequest-lookup')) helper = BootstrapHelper(use_get_method=True, add_cancel_button=False)
class AssignmentForm(forms.Form): assigned_to = forms.ModelChoiceField( label="Assigned to:", required=False, queryset=Person.objects.filter( Q(is_superuser=True) | Q(groups__name="administrators")).distinct(), widget=Select2Widget(), ) helper = BootstrapHelper(add_submit_button=False, add_cancel_button=False, wider_labels=True, use_get_method=True, form_id="assignment-form")
class SendHomeworkForm(forms.ModelForm): url = forms.URLField(label='URL') requirement = forms.ModelChoiceField( queryset=TrainingRequirement.objects.filter(name__endswith="Homework"), label="Type", required=True, ) helper = BootstrapHelper(add_cancel_button=False) class Meta: model = TrainingProgress fields = [ 'requirement', 'url', ]
def get_context_data(self, **kwargs): context = super().get_context_data(**kwargs) context['title'] = 'Workshop inquiry #{}'.format(self.get_object().pk) person_lookup_form = AdminLookupForm() if self.object.assigned_to: person_lookup_form = AdminLookupForm( initial={'person': self.object.assigned_to}) person_lookup_form.helper = BootstrapHelper(form_action=reverse( 'workshopinquiry_assign', args=[self.object.pk]), add_cancel_button=False) context['person_lookup_form'] = person_lookup_form return context
def get_context_data(self, **kwargs): context = super().get_context_data(**kwargs) context["title"] = "Workshop inquiry #{}".format(self.get_object().pk) person_lookup_form = AdminLookupForm() if self.object.assigned_to: person_lookup_form = AdminLookupForm( initial={"person": self.object.assigned_to}) person_lookup_form.helper = BootstrapHelper( form_action=reverse("workshopinquiry_assign", args=[self.object.pk]), add_cancel_button=False, ) context["person_lookup_form"] = person_lookup_form return context
class AllActivityOverTimeForm(forms.Form): start = forms.DateField( label='Begin date', help_text='YYYY-MM-DD', input_formats=[ '%Y-%m-%d', ], ) end = forms.DateField( label='End date', help_text='YYYY-MM-DD', input_formats=[ '%Y-%m-%d', ], ) helper = BootstrapHelper(use_get_method=True, add_cancel_button=False)
def get_context_data(self, **kwargs): context = super().get_context_data(**kwargs) context['title'] = 'DC self-organized workshop request #{}'.format( self.get_object().pk) person_lookup_form = AdminLookupForm() if self.object.assigned_to: person_lookup_form = AdminLookupForm( initial={'person': self.object.assigned_to} ) person_lookup_form.helper = BootstrapHelper( form_action=reverse('dcselforganizedeventrequest_assign', args=[self.object.pk])) context['person_lookup_form'] = person_lookup_form return context
class OrganizationForm(forms.ModelForm): domain = forms.CharField( max_length=Organization._meta.get_field('domain').max_length, validators=[ RegexValidator( r'[^\w\.-]+', inverse_match=True, message='Please enter only the domain (such as "math.esu.edu")' ' without a leading "http://" or a trailing "/".') ], ) helper = BootstrapHelper(add_cancel_button=False, duplicate_buttons_on_top=True) class Meta: model = Organization fields = ['domain', 'fullname', 'country']
class MembershipCreateForm(MembershipForm): comment = MarkdownxFormField( label="Comment", help_text="This will be added to comments after the membership is " "created.", widget=forms.Textarea, required=False, ) helper = BootstrapHelper( submit_label="Save membership and go to next screen", add_cancel_button=True ) class Meta(MembershipForm.Meta): fields = MembershipForm.Meta.fields.copy() fields.append("comment") def __init__(self, *args, **kwargs): super().__init__(*args, **kwargs) ORGANIZATIONS_FORMSET_WARNING = ( "You will be able to select organisations for this membership on the " "next screen." ) self.helper.layout.insert( 0, Div( Div( HTML(ORGANIZATIONS_FORMSET_WARNING), css_class="alert alert-info offset-lg-2 col-lg-8 col-12", ), css_class="form-group row", ), ) def save(self, *args, **kwargs): res = super().save(*args, **kwargs) create_comment_signal.send( sender=self.__class__, content_object=res, comment=self.cleaned_data["comment"], timestamp=None, ) return res