class InterfaceCreateForm(ComponentForm): name_pattern = ExpandableNameField(label='Name') type = forms.ChoiceField(choices=VMInterfaceTypeChoices, initial=VMInterfaceTypeChoices.TYPE_VIRTUAL, widget=forms.HiddenInput()) enabled = forms.BooleanField(required=False) mtu = forms.IntegerField(required=False, min_value=1, max_value=32767, label='MTU') mac_address = forms.CharField(required=False, label='MAC Address') description = forms.CharField(max_length=100, required=False) mode = forms.ChoiceField( choices=add_blank_choice(InterfaceModeChoices), required=False, widget=StaticSelect2(), ) untagged_vlan = forms.ModelChoiceField(queryset=VLAN.objects.all(), required=False, widget=APISelect( api_url="/api/ipam/vlans/", display_field='display_name', full=True)) tagged_vlans = forms.ModelMultipleChoiceField( queryset=VLAN.objects.all(), required=False, widget=APISelectMultiple(api_url="/api/ipam/vlans/", display_field='display_name', full=True)) tags = TagField(required=False) def __init__(self, *args, **kwargs): # Set interfaces enabled by default kwargs['initial'] = kwargs.get('initial', {}).copy() kwargs['initial'].update({'enabled': True}) super().__init__(*args, **kwargs) # Limit VLan choices to those in: global vlans, global groups, the current site's group, the current site vlan_choices = [] global_vlans = VLAN.objects.filter(site=None, group=None) vlan_choices.append( ('Global', [(vlan.pk, vlan) for vlan in global_vlans])) for group in VLANGroup.objects.filter(site=None): global_group_vlans = VLAN.objects.filter(group=group) vlan_choices.append( (group.name, [(vlan.pk, vlan) for vlan in global_group_vlans])) site = getattr(self.parent.cluster, 'site', None) if site is not None: # Add non-grouped site VLANs site_vlans = VLAN.objects.filter(site=site, group=None) vlan_choices.append( (site.name, [(vlan.pk, vlan) for vlan in site_vlans])) # Add grouped site VLANs for group in VLANGroup.objects.filter(site=site): site_group_vlans = VLAN.objects.filter(group=group) vlan_choices.append( ('{} / {}'.format(group.site.name, group.name), [(vlan.pk, vlan) for vlan in site_group_vlans])) self.fields['untagged_vlan'].choices = [(None, '---------') ] + vlan_choices self.fields['tagged_vlans'].choices = vlan_choices
class TagForm(forms.Form): tags = TagField()
class DirectPeeringSessionForm(BootstrapMixin, forms.ModelForm): local_asn = forms.IntegerField( min_value=ASN_MIN, max_value=ASN_MAX, initial=settings.MY_ASN, label="Local ASN", help_text=f"ASN to be used locally, defaults to {settings.MY_ASN}", ) autonomous_system = forms.ModelChoiceField( queryset=AutonomousSystem.objects.all(), widget=APISelect(api_url="/api/peering/autonomous-systems/"), ) bgp_group = forms.ModelChoiceField( required=False, queryset=BGPGroup.objects.all(), label="BGP Group", widget=APISelect(api_url="/api/peering/bgp-groups/"), ) relationship = forms.ChoiceField(choices=BGP_RELATIONSHIP_CHOICES, widget=StaticSelect) router = forms.ModelChoiceField( required=False, queryset=Router.objects.all(), widget=APISelect(api_url="/api/peering/routers/"), ) import_routing_policies = FilterChoiceField( required=False, queryset=RoutingPolicy.objects.all(), widget=APISelectMultiple( api_url="/api/peering/routing-policies/", query_filters={"type": "import-policy"}, ), ) export_routing_policies = FilterChoiceField( required=False, queryset=RoutingPolicy.objects.all(), widget=APISelectMultiple( api_url="/api/peering/routing-policies/", query_filters={"type": "export-policy"}, ), ) password = PasswordField(required=False, render_value=True) comments = CommentField() tags = TagField(required=False) class Meta: model = DirectPeeringSession fields = ( "local_asn", "local_ip_address", "autonomous_system", "bgp_group", "relationship", "ip_address", "password", "multihop_ttl", "enabled", "router", "import_routing_policies", "export_routing_policies", "comments", "tags", ) labels = { "local_ip_address": "Local IP Address", "autonomous_system": "AS", "ip_address": "IP Address", } help_texts = { "local_ip_address": "IPv6 or IPv4 address", "ip_address": "IPv6 or IPv4 address", "enabled": "Should this session be enabled?", "router": "Router on which this session is configured", }
class LeadForm(PydiciCrispyModelForm): class Meta: model = Lead responsible = ConsultantChoices(required=False, label=_("Responsible")) salesman = SalesManChoices(required=False, label=_("Salesman")) business_broker = BusinessBrokerChoices(required=False, label=_("Business broker")) paying_authority = BusinessBrokerChoices(required=False, label=_("Paying authority")) client = ClientChoices() staffing = ConsultantMChoices(required=False) tags = TagField(label="", required=False) def __init__(self, *args, **kwargs): super(LeadForm, self).__init__(*args, **kwargs) self.helper.layout = Layout( TabHolder( Tab( _("Identification"), Field( "name", placeholder=mark_safe( _("Name of the lead. don't include client name"))), AppendedText( "client", "<a href='%s' target='_blank'><span class='glyphicon glyphicon-plus'></span></a>" % reverse("crm.views.client")), "subsidiary", "description", Field("action", placeholder=_("Next commercial action to be done"))), Tab( _("State and tracking"), Div( Column( "responsible", Field("due_date", placeholder=_("Due date for next step"), css_class="datepicker"), Field( "start_date", placeholder=_("Date of the operational start"), css_class="datepicker"), css_class='col-md-6'), Column(Field( "deal_id", placeholder=_("Leave blank to auto generate")), Field( "client_deal_id", placeholder=_("Internal client reference")), "state", css_class='col-md-6'))), Tab( _("Commercial"), Div( Column(AppendedText("sales", "k€"), "salesman", css_class='col-md-6'), Column(AppendedText( "business_broker", "<a href='%s' target='_blank'><span class='glyphicon glyphicon-plus'></span></a>" % reverse("businessbroker_add"), placeholder=_( "If the leads was brought by a third party")), AppendedText( "paying_authority", "<a href='%s' target='_blank'><span class='glyphicon glyphicon-plus'></span></a>" % reverse("businessbroker_add"), placeholder=_( "If payment is done by a third party")), css_class='col-md-6'))), Tab( _("Staffing"), Div(Field( "staffing", placeholder=_("People that could contribute...")), Field( "external_staffing", placeholder= _("People outside company that could contribute..." )), css_class="col-md-6"))), Fieldset("", "send_email"), Field("creation_date", type="hidden"), Field("tags", css_class="hide" ), # Don't use type=hidden, it breaks tag parsing. self.submit) def clean_sales(self): """Ensure sale amount is defined at lead when commercial proposition has been sent""" if self.cleaned_data["sales"] or self.data["state"] in ( 'QUALIF', 'WRITE_OFFER', 'SLEEPING', 'LOST', 'FORGIVEN'): # Sales is defined or we are in early step, nothing to say return self.cleaned_data["sales"] else: # We can't tolerate that sale amount is not known at this step of the process raise ValidationError( _("Sales amount must be defined at this step of the commercial process" )) def clean_deal_id(self): """Ensure deal id is unique. Cannot be done at database level because we tolerate null/blank value and all db engines are not consistent in the way they handle that. SQL ISO is really fuzzy about that. Sad""" if not self.cleaned_data["deal_id"]: # No value, no pb :-) return self.cleaned_data["deal_id"] else: if Lead.objects.filter( deal_id=self.cleaned_data["deal_id"]).exclude( id=self.instance.id).exists(): raise ValidationError( _("Deal id must be unique. Use another value or let the field blank for automatic computation" )) else: return self.cleaned_data["deal_id"]
class PrefixForm(BootstrapMixin, TenancyForm, CustomFieldForm): site = forms.ModelChoiceField( queryset=Site.objects.all(), required=False, label='Site', widget=APISelect( api_url="/api/dcim/sites/", filter_for={ 'vlan_group': 'site_id', 'vlan': 'site_id', }, attrs={ 'nullable': 'true', } ) ) vlan_group = ChainedModelChoiceField( queryset=VLANGroup.objects.all(), chains=( ('site', 'site'), ), required=False, label='VLAN group', widget=APISelect( api_url='/api/ipam/vlan-groups/', filter_for={ 'vlan': 'group_id' }, attrs={ 'nullable': 'true', } ) ) vlan = ChainedModelChoiceField( queryset=VLAN.objects.all(), chains=( ('site', 'site'), ('group', 'vlan_group'), ), required=False, label='VLAN', widget=APISelect( api_url='/api/ipam/vlans/', display_field='display_name' ) ) tags = TagField(required=False) class Meta: model = Prefix fields = [ 'prefix', 'vrf', 'site', 'vlan', 'status', 'role', 'is_pool', 'description', 'tenant_group', 'tenant', 'tags', ] widgets = { 'vrf': APISelect( api_url="/api/ipam/vrfs/" ), 'status': StaticSelect2(), 'role': APISelect( api_url="/api/ipam/roles/" ) } def __init__(self, *args, **kwargs): # Initialize helper selectors instance = kwargs.get('instance') initial = kwargs.get('initial', {}).copy() if instance and instance.vlan is not None: initial['vlan_group'] = instance.vlan.group kwargs['initial'] = initial super().__init__(*args, **kwargs) self.fields['vrf'].empty_label = 'Global'
class ProfileRegistrationForm(RegistrationForm): """ Override for the default registration form - adds new fields: * Avatar for allowing the user to upload a profile picture * and short_info, which allows the user to introduce herself to the other attendees/speakers. * Organisation name * Twitter handle * website (URL) * Number of accompanying children (dropdown selection) * Info about children's ages (free text) All these fields are publicly accessible and optional. """ avatar = forms.ImageField( widget=forms.FileInput, required=False, help_text=Profile()._meta.get_field_by_name('avatar')[0].help_text) short_info = forms.CharField(label=_("short info"), widget=forms.Textarea, required=False) organisation = forms.CharField(label=_("Organisation"), required=False) twitter = forms.CharField(label=_("Twitter"), required=False) website = forms.URLField(label=_("Website"), required=False) num_accompanying_children = forms.IntegerField( required=False, label=_('Number of accompanying children'), widget=forms.Select(choices=NUM_ACCOMPANYING_CHILDREN_CHOICES)) age_accompanying_children = forms.CharField( label=_("Age of accompanying children"), required=False) full_name = forms.CharField(required=False) display_name = forms.CharField( required=True, help_text=_('What name should be displayed to other people?')) addressed_as = forms.CharField( label=_("Address me as"), required=False, help_text= _('How should we call you in mails and dialogs throughout the website? If you leave this field blank, we will fallback to your display name.' )) accept_privacy_policy = forms.BooleanField( required=True, label=_('I hereby agree to the privacy policy.')) accept_pysv_conferences = forms.BooleanField( required=False, label= _('I hereby allow the Python Software Verband e.V. to re-use my profile information for upcoming conferences.' )) accept_ep_conferences = forms.BooleanField( required=False, label= _('I hereby allow the EuroPython Society to re-use my profile information for upcoming conferences.' )) accept_job_offers = forms.BooleanField( required=False, label=_( 'I hereby allow EuroPython 2014 sponsors to send me job offers.')) tags = TagField(label=_("Interests"), required=False, help_text=_("Please separate tags by comma.")) def __init__(self, *args, **kwargs): super(ProfileRegistrationForm, self).__init__(*args, **kwargs) account_fields = Fieldset(_('Login information'), Field('username', autofocus="autofocus"), 'email', 'password', 'password_repeat') profile_fields = Fieldset(_('Personal information'), 'full_name', 'display_name', 'addressed_as', 'avatar', 'short_info') profession_fields = Fieldset(_('Professional information'), 'organisation', 'twitter', 'website', Field('tags', css_class='tags-input'), 'accept_job_offers') privacy_fields = Fieldset( _('Privacy Policy'), HTML( _('{% load cms_tags %}<p class="control-group">Due to data protection ' 'regulations you need to explicitly accept our ' '<a href="{% page_url "privacy-policy" %}">privacy policy</a>.</p>' )), 'accept_privacy_policy', 'accept_pysv_conferences', 'accept_ep_conferences') self.helper = FormHelper() self.helper.form_class = 'form-horizontal' self.helper.layout = Layout( account_fields, profile_fields, profession_fields, privacy_fields, ButtonHolder( Submit('submit', _('Create account'), css_class='btn btn-primary'))) if settings.ACCOUNTS_FALLBACK_TO_GRAVATAR: self.fields['avatar'].help_text = _( """Please upload an image with a side length of at least 300 pixels.<br />If you don't upload an avatar your Gravatar will be used instead.""" ) def save(self, *args, **kwargs): with transaction.commit_on_success(): super(ProfileRegistrationForm, self).save(*args, **kwargs) def save_profile(self, new_user, *args, **kwargs): """ save_profile is used by django-userprofiles as a post-save hook. In this case we use it to create a new profile object for the user. """ Profile.objects.create( user=new_user, avatar=self.cleaned_data['avatar'], short_info=self.cleaned_data['short_info'], organisation=self.cleaned_data['organisation'], twitter=self.cleaned_data['twitter'], website=self.cleaned_data['website'], num_accompanying_children=self. cleaned_data['num_accompanying_children'] or 0, age_accompanying_children=self. cleaned_data['age_accompanying_children'] or '', full_name=self.cleaned_data['full_name'], display_name=self.cleaned_data['display_name'], addressed_as=self.cleaned_data['addressed_as'], accept_pysv_conferences=self. cleaned_data['accept_pysv_conferences'], accept_ep_conferences=self.cleaned_data['accept_ep_conferences'], accept_job_offers=self.cleaned_data['accept_job_offers']) def clean_twitter(self): """ Allow the user to enter either "@foo" or "foo" as their twitter handle. """ value = self.cleaned_data.get('twitter', '') value = value.lstrip('@') validators.twitter_username(value) # validates the max_length return value
class VirtualMachineForm(BootstrapMixin, TenancyForm, CustomFieldForm): cluster_group = forms.ModelChoiceField( queryset=ClusterGroup.objects.all(), required=False, widget=APISelect(api_url='/api/virtualization/cluster-groups/', filter_for={ "cluster": "group_id", }, attrs={ 'nullable': 'true', })) cluster = ChainedModelChoiceField( queryset=Cluster.objects.all(), chains=(('group', 'cluster_group'), ), widget=APISelect(api_url='/api/virtualization/clusters/')) tags = TagField(required=False) local_context_data = JSONField(required=False) class Meta: model = VirtualMachine fields = [ 'name', 'status', 'cluster_group', 'cluster', 'role', 'tenant', 'platform', 'primary_ip4', 'primary_ip6', 'vcpus', 'memory', 'disk', 'comments', 'tags', 'local_context_data', ] help_texts = { 'local_context_data': "Local config context data overwrites all sources contexts in the final rendered " "config context", } widgets = { "status": StaticSelect2(), "role": APISelect(api_url="/api/dcim/device-roles/", additional_query_params={"vm_role": "True"}), 'primary_ip4': StaticSelect2(), 'primary_ip6': StaticSelect2(), 'platform': APISelect(api_url='/api/dcim/platforms/') } def __init__(self, *args, **kwargs): # Initialize helper selector instance = kwargs.get('instance') if instance.pk and instance.cluster is not None: initial = kwargs.get('initial', {}).copy() initial['cluster_group'] = instance.cluster.group kwargs['initial'] = initial super().__init__(*args, **kwargs) if self.instance.pk: # Compile list of choices for primary IPv4 and IPv6 addresses for family in [4, 6]: ip_choices = [(None, '---------')] # Collect interface IPs interface_ips = IPAddress.objects.select_related( 'interface').filter( family=family, interface__virtual_machine=self.instance) if interface_ips: ip_choices.append(('Interface IPs', [ (ip.id, '{} ({})'.format(ip.address, ip.interface)) for ip in interface_ips ])) # Collect NAT IPs nat_ips = IPAddress.objects.select_related( 'nat_inside').filter( family=family, nat_inside__interface__virtual_machine=self.instance) if nat_ips: ip_choices.append(('NAT IPs', [ (ip.id, '{} ({})'.format(ip.address, ip.nat_inside.address)) for ip in nat_ips ])) self.fields['primary_ip{}'.format(family)].choices = ip_choices else: # An object that doesn't exist yet can't have any IPs assigned to it self.fields['primary_ip4'].choices = [] self.fields['primary_ip4'].widget.attrs['readonly'] = True self.fields['primary_ip6'].choices = [] self.fields['primary_ip6'].widget.attrs['readonly'] = True
class InternetExchangePeeringSessionForm(BootstrapMixin, forms.ModelForm): password = PasswordField(required=False, render_value=True) import_routing_policies = FilterChoiceField( required=False, queryset=RoutingPolicy.objects.all(), widget=APISelectMultiple( api_url="/api/peering/routing-policies/", query_filters={"type": "import-policy"}, ), ) export_routing_policies = FilterChoiceField( required=False, queryset=RoutingPolicy.objects.all(), widget=APISelectMultiple( api_url="/api/peering/routing-policies/", query_filters={"type": "export-policy"}, ), ) comments = CommentField() tags = TagField(required=False) def __init__(self, *args, **kwargs): super().__init__(*args, **kwargs) self.fields["autonomous_system"].widget.attrs["data-live-search"] = "true" def clean(self): # Do the regular cleanup cleaned_data = super().clean() # This should be cleaned up, ready to be used password = cleaned_data["password"] internet_exchange = cleaned_data["internet_exchange"] # Process to password check/encryption if we have what we need if internet_exchange.router and password: # Encrypt the password only if it is not already cleaned_data["password"] = internet_exchange.router.encrypt_string(password) return cleaned_data class Meta: model = InternetExchangePeeringSession fields = ( "autonomous_system", "internet_exchange", "ip_address", "password", "multihop_ttl", "is_route_server", "enabled", "import_routing_policies", "export_routing_policies", "comments", "tags", ) labels = { "autonomous_system": "AS", "internet_exchange": "IX", "ip_address": "IP Address", "is_route_server": "Route Server", } help_texts = { "ip_address": "IPv6 or IPv4 address", "is_route_server": "Define if this session is with a route server", } widgets = { "autonomous_system": APISelect(api_url="/api/peering/autonomous-systems/"), "internet_exchange": APISelect(api_url="/api/peering/internet-exchanges/"), }
class InterfaceForm(BootstrapMixin, forms.ModelForm): untagged_vlan = DynamicModelChoiceField(queryset=VLAN.objects.all(), required=False, widget=APISelect( display_field='display_name', full=True, additional_query_params={ 'site_id': 'null', }, )) tagged_vlans = DynamicModelMultipleChoiceField( queryset=VLAN.objects.all(), required=False, widget=APISelectMultiple( display_field='display_name', full=True, additional_query_params={ 'site_id': 'null', }, )) tags = TagField(required=False) class Meta: model = Interface fields = [ 'virtual_machine', 'name', 'type', 'enabled', 'mac_address', 'mtu', 'description', 'mode', 'tags', 'untagged_vlan', 'tagged_vlans', ] widgets = { 'virtual_machine': forms.HiddenInput(), 'type': forms.HiddenInput(), 'mode': StaticSelect2() } labels = { 'mode': '802.1Q Mode', } help_texts = { 'mode': INTERFACE_MODE_HELP_TEXT, } def __init__(self, *args, **kwargs): super().__init__(*args, **kwargs) # Add current site to VLANs query params site = getattr(self.instance.parent, 'site', None) if site is not None: # Add current site to VLANs query params self.fields['untagged_vlan'].widget.add_additional_query_param( 'site_id', site.pk) self.fields['tagged_vlans'].widget.add_additional_query_param( 'site_id', site.pk) def clean(self): super().clean() # Validate VLAN assignments tagged_vlans = self.cleaned_data['tagged_vlans'] # Untagged interfaces cannot be assigned tagged VLANs if self.cleaned_data[ 'mode'] == InterfaceModeChoices.MODE_ACCESS and tagged_vlans: raise forms.ValidationError({ 'mode': "An access interface cannot have tagged VLANs assigned." }) # Remove all tagged VLAN assignments from "tagged all" interfaces elif self.cleaned_data['mode'] == InterfaceModeChoices.MODE_TAGGED_ALL: self.cleaned_data['tagged_vlans'] = []
class IncidentForm(ModelForm): tags = TagField(required=False, widget=LabelWidget)
class DirectPeeringSessionForm(BootstrapMixin, forms.ModelForm): autonomous_system = forms.ModelChoiceField( queryset=AutonomousSystem.objects.all(), widget=APISelect(api_url="/api/peering/autonomous-systems/"), ) bgp_group = forms.ModelChoiceField( required=False, queryset=BGPGroup.objects.all(), label="BGP Group", widget=APISelect(api_url="/api/peering/bgp-groups/"), ) relationship = forms.ChoiceField( choices=BGP_RELATIONSHIP_CHOICES, widget=StaticSelect ) router = forms.ModelChoiceField( required=False, queryset=Router.objects.all(), widget=APISelect(api_url="/api/peering/routers/"), ) import_routing_policies = FilterChoiceField( required=False, queryset=RoutingPolicy.objects.all(), widget=APISelectMultiple( api_url="/api/peering/routing-policies/", query_filters={"type": "import-policy"}, ), ) export_routing_policies = FilterChoiceField( required=False, queryset=RoutingPolicy.objects.all(), widget=APISelectMultiple( api_url="/api/peering/routing-policies/", query_filters={"type": "export-policy"}, ), ) password = PasswordField(required=False, render_value=True) comments = CommentField() tags = TagField(required=False) def clean(self): # Do the regular cleanup cleaned_data = super().clean() # This should be cleaned up, ready to be used password = cleaned_data["password"] router = cleaned_data["router"] # Process to password check/encryption if we have what we need if router and password: # Encrypt the password only if it is not already cleaned_data["password"] = router.encrypt_string(password) return cleaned_data class Meta: model = DirectPeeringSession fields = ( "local_asn", "local_ip_address", "autonomous_system", "bgp_group", "relationship", "ip_address", "password", "multihop_ttl", "enabled", "router", "import_routing_policies", "export_routing_policies", "comments", "tags", ) labels = { "local_asn": "Local ASN", "local_ip_address": "Local IP Address", "autonomous_system": "AS", "ip_address": "IP Address", } help_texts = { "local_asn": "ASN to be used locally, defaults to {}".format( settings.MY_ASN ), "local_ip_address": "IPv6 or IPv4 address", "ip_address": "IPv6 or IPv4 address", "enabled": "Should this session be enabled?", "router": "Router on which this session is configured", }
class InternetExchangeForm(BootstrapMixin, forms.ModelForm): slug = SlugField(max_length=255) import_routing_policies = FilterChoiceField( required=False, queryset=RoutingPolicy.objects.all(), widget=APISelectMultiple( api_url="/api/peering/routing-policies/", query_filters={"type": "import-policy"}, ), ) export_routing_policies = FilterChoiceField( required=False, queryset=RoutingPolicy.objects.all(), widget=APISelectMultiple( api_url="/api/peering/routing-policies/", query_filters={"type": "export-policy"}, ), ) communities = FilterChoiceField( required=False, queryset=Community.objects.all(), widget=APISelectMultiple(api_url="/api/peering/communities/"), ) comments = CommentField() tags = TagField(required=False) class Meta: model = InternetExchange fields = ( "peeringdb_id", "name", "slug", "ipv6_address", "ipv4_address", "communities", "import_routing_policies", "export_routing_policies", "router", "check_bgp_session_states", "comments", "tags", ) labels = { "peeringdb_id": "PeeringDB ID", "ipv6_address": "IPv6 Address", "ipv4_address": "IPv4 Address", "check_bgp_session_states": "Poll Peering Session States", } help_texts = { "peeringdb_id": "The PeeringDB ID for the IX connection (can be left empty)", "name": "Full name of the Internet Exchange point", "ipv6_address": "IPv6 Address used to peer", "ipv4_address": "IPv4 Address used to peer", "router": "Router connected to the Internet Exchange point", "check_bgp_session_states": "If enabled, with a usable router, the state of peering sessions will be polled.", } widgets = {"router": APISelect(api_url="/api/peering/routers/")}
class ResourceForm(forms.ModelForm): purposes = forms.ModelMultipleChoiceField( widget=RespaCheckboxSelect, queryset=Purpose.objects.all(), required=True, ) equipment = forms.ModelMultipleChoiceField( required=False, widget=RespaCheckboxSelect, queryset=Equipment.objects.all(), ) name_fi = forms.CharField( required=True, label='Nimi [fi]', ) tags = TagField(required=False, label='Avainsanat') def __init__(self, *args, **kwargs): super().__init__(*args, **kwargs) self.fields['generic_terms'].queryset = TermsOfUse.objects.filter( terms_type=TermsOfUse.TERMS_TYPE_GENERIC) self.fields['payment_terms'].queryset = TermsOfUse.objects.filter( terms_type=TermsOfUse.TERMS_TYPE_PAYMENT) class Meta: model = Resource translated_fields = [ 'name_fi', 'name_en', 'name_sv', 'description_fi', 'description_en', 'description_sv', 'reservation_info_fi', 'reservation_info_en', 'reservation_info_sv', 'specific_terms_fi', 'specific_terms_en', 'specific_terms_sv', 'reservation_requested_notification_extra_fi', 'reservation_requested_notification_extra_en', 'reservation_requested_notification_extra_sv', 'reservation_confirmed_notification_extra_fi', 'reservation_confirmed_notification_extra_en', 'reservation_confirmed_notification_extra_sv', 'responsible_contact_info_fi', 'responsible_contact_info_en', 'responsible_contact_info_sv', 'reservation_additional_information_fi', 'reservation_additional_information_en', 'reservation_additional_information_sv', ] fields = [ 'unit', 'type', 'purposes', 'equipment', 'external_reservation_url', 'people_capacity', 'min_age', 'max_age', 'area', 'min_period', 'max_period', 'slot_size', 'cooldown', 'reservable_max_days_in_advance', 'reservable_min_days_in_advance', 'max_reservations_per_user', 'reservable', 'need_manual_confirmation', 'authentication', 'resource_staff_emails', 'access_code_type', 'max_price', 'min_price', 'price_type', 'generic_terms', 'payment_terms', 'public', 'reservation_metadata_set', 'reservation_home_municipality_set', 'tags' ] + translated_fields widgets = { 'min_period': forms.Select(choices=(thirty_minute_increment_choices)), 'max_period': forms.Select(choices=(thirty_minute_increment_choices)), 'slot_size': forms.Select(choices=(thirty_minute_increment_choices)), 'cooldown': forms.Select(choices=(hour_increment_choices)), 'need_manual_confirmation': RespaRadioSelect(choices=((True, _('Yes')), (False, _('No')))), 'public': forms.Select(choices=((False, _('Hidden')), (True, _('Published')))), 'reservable': forms.Select(choices=((False, _('Can not be reserved')), (True, _('Bookable')))), }
class PostForm(forms.ModelForm): tags = TagField(required=False, widget=LabelWidget) class Meta: model = Post exclude = []
class TagForm(forms.Form): """ Simple TagForm class to validate tags in a request. """ tags = TagField()
class TestForm(forms.Form): tag = TagField(disabled=True)
class ProfileForm(BaseProfileForm): avatar_size = (settings.THUMBNAIL_SIZE, settings.THUMBNAIL_SIZE) avatar_help_text = Profile()._meta.get_field_by_name('avatar')[0].help_text avatar = forms.ImageField(widget=AvatarWidget(size=avatar_size), required=False, help_text=avatar_help_text) organisation = forms.CharField(label=_("Organisation"), required=False) num_accompanying_children = forms.IntegerField( required=False, label=_('Number of accompanying children'), widget=forms.Select(choices=NUM_ACCOMPANYING_CHILDREN_CHOICES)) age_accompanying_children = forms.CharField( label=_("Age of accompanying children"), required=False) tags = TagField(label=_("Interests"), required=False, help_text=_("Please separate tags by comma.")) class Meta(BaseProfileForm.Meta): fields = ('full_name', 'display_name', 'addressed_as', 'avatar', 'short_info', 'organisation', 'twitter', 'website', 'tags', 'accept_job_offers', 'accept_pysv_conferences', 'accept_ep_conferences') if not settings.CHILDREN_DATA_DISABLED: fields = fields + ('num_accompanying_children', 'age_accompanying_children') def __init__(self, *args, **kwargs): super(ProfileForm, self).__init__(*args, **kwargs) self.helper = FormHelper() profile_fields = Fieldset(_('Personal information'), 'full_name', 'display_name', 'addressed_as', 'avatar', 'short_info') profession_fields = Fieldset(_('Professional information'), 'organisation', 'twitter', 'website', Field('tags', css_class='tags-input'), 'accept_job_offers') privacy_fields = Fieldset(_('Privacy Policy'), 'accept_pysv_conferences', 'accept_ep_conferences') self.helper.form_class = 'form-horizontal' self.helper.layout = Layout( profile_fields, profession_fields, privacy_fields, (HTML('') if settings.CHILDREN_DATA_DISABLED else Div( Field('num_accompanying_children'), Field('age_accompanying_children'))), ButtonHolder(Submit('save', _('Change'), css_class='btn-primary'))) if settings.ACCOUNTS_FALLBACK_TO_GRAVATAR: self.fields['avatar'].help_text = _( """Please upload an image with a side length of at least 300 pixels.<br />If you don't upload an avatar your Gravatar will be used instead.""" ) # children fields - may be disabled on registration form (see above) # and removed completely from profile form if settings.CHILDREN_DATA_DISABLED: del self.fields['num_accompanying_children'] del self.fields['age_accompanying_children'] def clean_accept_pysv_conferences(self): value = self.cleaned_data['accept_pysv_conferences'] if not value and self.instance.accept_pysv_conferences: self.data['accept_pysv_conferences'] = True raise ValidationError( _("You previously agreed to this option" " which can no longer be revoked.")) return value def clean_accept_ep_conferences(self): value = self.cleaned_data['accept_ep_conferences'] if not value and self.instance.accept_ep_conferences: self.data['accept_ep_conferences'] = True raise ValidationError( _("You previously agreed to this option" " which can no longer be revoked.")) return value
class TestForm(forms.Form): tag = TagField()
class PozaTagsForm(CrispyBaseModelForm): class Meta(object): model = Imagine fields = ["tags", "titlu", "descriere", "published_status"] tags = TagField(required=False, widget=TaggitTagsInput, label=u"Tag-uri")
class ItemForm(TeachForm): # def __init__(self, *args, **kwargs): # super(TeachForm, self).__init__(*args, **kwargs) # for field in self.fields: # self.fields[field].widget.attrs['class'] = 'controls' # print type(self.fields[field].label) MY_CHOICES = ( (False, u'Selling'), (True, u'Looking for'), ) item_sellOrLookFor = forms.ChoiceField( label=' I am ', required=True, choices=MY_CHOICES, ) CATEGORY_CHOICES = ( ('Item', u'Item'), ('Service/Job', u'Service/Job'), ('Housing', u'Housing'), ) item_category = forms.ChoiceField( label='Category', required=True, choices=CATEGORY_CHOICES, ) item_name = forms.CharField(required=True, label='Name', max_length=80 # attrs={'class':'special'} ) item_tags = TagField( label='Tags', required=False, # widget = TagWidget((attrs={'placeholder': 'Comma separated'}),) ) # item_price= forms.DecimalField( # required= True, # label = 'Price' # ) item_description = forms.CharField( required=False, widget=forms.Textarea( attrs={ 'placeholder': 'Please include the price in your description.' }), label='Description') item_negotiable = forms.BooleanField(required=False, label='Price Negotiable') image_first = forms.FileField(label='Upload an image (optional)', help_text='max. 42 megabytes', required=False) image_second = forms.FileField(label='Upload an image (optional)', help_text='max. 42 megabytes', required=False) image_third = forms.FileField(label='Upload an image (optional)', help_text='max. 42 megabytes', required=False)
def __init__(self, *args, **kwargs): super().__init__(*args, **kwargs) self.fields["add_tags"] = TagField(required=False) self.fields["remove_tags"] = TagField(required=False)
def __init__(self, *args, **kwargs): super().__init__(*args, **kwargs) # Add add/remove tags fields self.fields['add_tags'] = TagField(required=False) self.fields['remove_tags'] = TagField(required=False)
class DatasetForm(forms.ModelForm): class Meta(object): model = Dataset fields = ('broadcaster_email', 'broadcaster_name', 'categories', 'data_type', 'date_creation', 'date_modification', 'date_publication', 'description', 'geocover', 'granularity', 'keywords', 'license', 'organisation', 'owner_email', 'owner_name', 'published', 'support', 'thumbnail', 'update_frequency', 'title', 'slug') title = forms.CharField( label="Titre*", required=True, widget=forms.Textarea(attrs={ 'placeholder': "Titre de votre jeu de données", 'rows': 1, }, ), ) slug = forms.CharField( label="URL du jeu de données", required=False, max_length=100, widget=forms.TextInput(attrs={ 'addon_before': '{}/dataset/'.format(DOMAIN_NAME), 'addon_before_class': 'input-group-addon', 'addon_after': '<button class="btn btn-default" type="button" />', 'addon_after_class': 'input-group-btn', 'autocomplete': 'off', 'readonly': True, 'placeholder': '', }, ), ) description = forms.CharField( label="Description", required=False, widget=forms.Textarea(attrs={ 'placeholder': "Vous pouvez utiliser le langage Markdown ici", }, ), ) class CustomClearableFileInput(forms.ClearableFileInput): template_name = 'idgo_admin/widgets/file_drop_zone.html' thumbnail = forms.FileField( label="Illustration", required=False, widget=CustomClearableFileInput(attrs={ 'value': None, 'max_size_info': 1048576, }, ), ) keywords = TagField( label="Liste de mots-clés", required=False, widget=TagWidget(attrs={ 'autocomplete': 'off', 'class': 'typeahead', 'placeholder': "Utilisez la virgule comme séparateur", }, ), ) categories = forms.ModelMultipleChoiceField( label= "Catégories (sélectionnez dans la liste ci-dessous une ou plusieurs catégories)", required=False, queryset=Category.objects.all(), widget=forms.CheckboxSelectMultiple(), ) date_creation = forms.DateField( label="Date de création", required=False, widget=forms.DateInput(attrs={ 'autocomplete': 'off', 'class': 'datepicker' }, ), ) date_modification = forms.DateField( label="Date de dernière modification", required=False, widget=forms.DateInput(attrs={ 'autocomplete': 'off', 'class': 'datepicker' }, ), ) date_publication = forms.DateField( label="Date de publication", required=False, widget=forms.DateInput(attrs={ 'autocomplete': 'off', 'class': 'datepicker' }, ), ) update_frequency = forms.ChoiceField( label="Fréquence de mise à jour", required=False, choices=Dataset.FREQUENCY_CHOICES, ) geocover = forms.ChoiceField( label="Couverture géographique", required=False, choices=Dataset.GEOCOVER_CHOICES, ) granularity = forms.ModelChoiceField( label="Granularité de la couverture territoriale", empty_label="Sélectionnez une valeur", required=False, queryset=Granularity.objects.all().order_by('order'), ) organisation = forms.ModelChoiceField( label="Organisation à laquelle est rattaché ce jeu de données*", empty_label="Sélectionnez une organisation", required=True, queryset=Organisation.objects.all(), ) license = forms.ModelChoiceField( label="Licence*", empty_label="Sélectionnez une licence", required=True, queryset=License.objects.all(), ) support = forms.ModelChoiceField( label="Support technique", empty_label="Aucun", required=False, queryset=Support.objects.all(), ) data_type = DatatypeField( # Cf. définition plus haut ) owner_name = forms.CharField( label="Nom du producteur", required=False, ) owner_email = forms.EmailField( label="Adresse e-mail du producteur", required=False, error_messages={ 'invalid': "L'adresse e-mail est invalide.", }, ) broadcaster_name = forms.CharField( label="Nom du diffuseur", required=False, ) broadcaster_email = forms.EmailField( label="Adresse e-mail du diffuseur", required=False, error_messages={ 'invalid': "L'adresse e-mail est invalide.", }, ) published = forms.BooleanField( label="Publier le jeu de données", required=False, initial=True, ) def __init__(self, *args, **kwargs): self.include_args = kwargs.pop('include', {}) super().__init__(*args, **kwargs) instance = kwargs.get('instance', None) owner = instance \ and instance.editor or self.include_args.get('user') today = timezone.now().date().strftime('%d/%m/%Y') self.fields['date_creation'].widget.attrs[ 'placeholder'] = "%s (par défaut)" % today self.fields['date_modification'].widget.attrs[ 'placeholder'] = "%s (par défaut)" % today self.fields['date_publication'].widget.attrs[ 'placeholder'] = "%s (par défaut)" % today self.fields['organisation'].queryset = Organisation.objects.filter( liaisonscontributeurs__profile=owner.profile, liaisonscontributeurs__validated_on__isnull=False) self.fields['owner_name'].initial = owner.get_full_name() self.fields['owner_name'].widget.attrs['placeholder'] = \ '{} (valeur par défaut)'.format(owner.get_full_name()) self.fields['owner_email'].initial = owner.email self.fields['owner_email'].widget.attrs['placeholder'] = \ '{} (valeur par défaut)'.format(owner.email) self.fields['broadcaster_name'].widget.attrs['placeholder'] = \ instance and instance.support and instance.support.name or DEFAULT_PLATFORM_NAME self.fields['broadcaster_email'].widget.attrs['placeholder'] = \ instance and instance.support and instance.support.email or DEFAULT_CONTACT_EMAIL if instance and instance.thumbnail: self.fields['thumbnail'].widget.attrs[ 'value'] = instance.thumbnail.url if not instance: self.fields['granularity'].initial = 'indefinie' def clean(self): title = self.cleaned_data.get('title') if self.cleaned_data.get('slug') \ and not re.match('^[a-z0-9\-]{1,100}$', self.cleaned_data.get('slug')): self.add_error( 'slug', ("Seuls les caractères alphanumériques et le tiret sont " "autorisés (100 caractères maximum).")) raise ValidationError('KeywordsError') if self.include_args['identification']: dataset = Dataset.objects.get(id=self.include_args['id']) if title != dataset.title and Dataset.objects.filter( title=title).exists(): self.add_error('title', 'Ce nom est réservé.') raise ValidationError( "Dataset '{0}' already exists".format(title)) if not self.include_args['identification'] \ and Dataset.objects.filter(title=title).exists(): self.add_error('title', 'Le jeu de données "{0}" existe déjà'.format(title)) raise ValidationError("Dataset '{0}' already exists".format(title)) keywords = self.cleaned_data.get('keywords') if keywords: for keyword in keywords: if len(keyword) < 2: self.add_error( 'keywords', "La taille minimum pour un mot clé est de 2 caractères. " ) raise ValidationError("KeywordsError") keyword_match = re.compile("[\w\s\-.']*$", re.UNICODE) if not keyword_match.match(keyword): self.add_error( 'keywords', "Les mots-clés ne peuvent pas contenir de caractères spéciaux. " ) raise ValidationError('KeywordsError') return self.cleaned_data
class ResourceBaseForm(TranslationModelForm): """Base form for metadata, should be inherited by childres classes of ResourceBase""" abstract = forms.CharField( label=_("Abstract"), required=False, widget=TinyMCE()) purpose = forms.CharField( label=_("Purpose"), required=False, widget=TinyMCE()) constraints_other = forms.CharField( label=_("Other constraints"), required=False, widget=TinyMCE()) supplemental_information = forms.CharField( label=_('Supplemental information'), required=False, widget=TinyMCE()) data_quality_statement = forms.CharField( label=_("Data quality statement"), required=False, widget=TinyMCE()) owner = forms.ModelChoiceField( empty_label=_("Owner"), label=_("Owner"), required=False, queryset=get_user_model().objects.exclude(username='******'), widget=autocomplete.ModelSelect2(url='autocomplete_profile')) date = forms.DateTimeField( label=_("Date"), localize=True, input_formats=['%Y-%m-%d %H:%M %p'], widget=ResourceBaseDateTimePicker(options={"format": "YYYY-MM-DD HH:mm a"}) ) temporal_extent_start = forms.DateTimeField( label=_("temporal extent start"), required=False, localize=True, input_formats=['%Y-%m-%d %H:%M %p'], widget=ResourceBaseDateTimePicker(options={"format": "YYYY-MM-DD HH:mm a"}) ) temporal_extent_end = forms.DateTimeField( label=_("temporal extent end"), required=False, localize=True, input_formats=['%Y-%m-%d %H:%M %p'], widget=ResourceBaseDateTimePicker(options={"format": "YYYY-MM-DD HH:mm a"}) ) poc = forms.ModelChoiceField( empty_label=_("Person outside GeoNode (fill form)"), label=_("Point of Contact"), required=False, queryset=get_user_model().objects.exclude( username='******'), widget=autocomplete.ModelSelect2(url='autocomplete_profile')) metadata_author = forms.ModelChoiceField( empty_label=_("Person outside GeoNode (fill form)"), label=_("Metadata Author"), required=False, queryset=get_user_model().objects.exclude( username='******'), widget=autocomplete.ModelSelect2(url='autocomplete_profile')) keywords = TagField( label=_("Free-text Keywords"), required=False, help_text=_("A space or comma-separated list of keywords. Use the widget to select from Hierarchical tree."), # widget=TreeWidget(url='autocomplete_hierachical_keyword'), #Needs updating to work with select2 widget=TaggitSelect2Custom(url='autocomplete_hierachical_keyword')) """ regions = TreeNodeMultipleChoiceField( label=_("Regions"), required=False, queryset=Region.objects.all(), level_indicator=u'___') """ regions = RegionsMultipleChoiceField( label=_("Regions"), required=False, choices=get_tree_data(), widget=RegionsSelect) regions.widget.attrs = {"size": 20} def __init__(self, *args, **kwargs): super(ResourceBaseForm, self).__init__(*args, **kwargs) for field in self.fields: help_text = self.fields[field].help_text if help_text != '': self.fields[field].widget.attrs.update( { 'class': 'has-popover', 'data-content': help_text, 'data-placement': 'right', 'data-container': 'body', 'data-html': 'true'}) def disable_keywords_widget_for_non_superuser(self, user): if settings.FREETEXT_KEYWORDS_READONLY and not user.is_superuser: self['keywords'].field.disabled = True def clean_keywords(self): from html.entities import codepoint2name def unicode_escape(unistr): """ Tidys up unicode entities into HTML friendly entities Takes a unicode string as an argument Returns a unicode string """ escaped = "" for char in unistr: if ord(char) in codepoint2name: name = codepoint2name.get(ord(char)) escaped += '&%s;' % name if 'nbsp' not in name else ' ' else: escaped += char return escaped keywords = self.cleaned_data['keywords'] _unsescaped_kwds = [] for k in keywords: _k = ('%s' % re.sub(r'%([A-Z0-9]{2})', r'&#x\g<1>;', k.strip())).split(",") if not isinstance(_k, six.string_types): for _kk in [html.unescape(x.strip()) for x in _k]: # Simulate JS Unescape _kk = _kk.replace('%u', r'\u').encode('unicode-escape').replace( b'\\\\u', b'\\u').decode('unicode-escape') if '%u' in _kk else _kk _hk = HierarchicalKeyword.objects.filter(name__iexact='%s' % _kk.strip()) if _hk and len(_hk) > 0: _unsescaped_kwds.append(str(_hk[0])) else: _unsescaped_kwds.append(str(_kk)) else: _hk = HierarchicalKeyword.objects.filter(name__iexact=_k.strip()) if _hk and len(_hk) > 0: _unsescaped_kwds.append(str(_hk[0])) else: _unsescaped_kwds.append(str(_k)) return _unsescaped_kwds class Meta: exclude = ( 'contacts', 'name', 'uuid', 'bbox_polygon', 'srid', 'category', 'csw_typename', 'csw_schema', 'csw_mdsource', 'csw_type', 'csw_wkt_geometry', 'metadata_uploaded', 'metadata_xml', 'csw_anytext', 'popular_count', 'share_count', 'thumbnail', 'charset', 'rating', 'detail_url', 'tkeywords', )
class IPAddressForm(BootstrapMixin, TenancyForm, ReturnURLForm, CustomFieldForm): interface = forms.ModelChoiceField( queryset=Interface.objects.all(), required=False ) nat_site = forms.ModelChoiceField( queryset=Site.objects.all(), required=False, label='Site', widget=APISelect( api_url="/api/dcim/sites/", filter_for={ 'nat_rack': 'site_id', 'nat_device': 'site_id' } ) ) nat_rack = ChainedModelChoiceField( queryset=Rack.objects.all(), chains=( ('site', 'nat_site'), ), required=False, label='Rack', widget=APISelect( api_url='/api/dcim/racks/', display_field='display_name', filter_for={ 'nat_device': 'rack_id' }, attrs={ 'nullable': 'true' } ) ) nat_device = ChainedModelChoiceField( queryset=Device.objects.all(), chains=( ('site', 'nat_site'), ('rack', 'nat_rack'), ), required=False, label='Device', widget=APISelect( api_url='/api/dcim/devices/', display_field='display_name', filter_for={ 'nat_inside': 'device_id' } ) ) nat_inside = ChainedModelChoiceField( queryset=IPAddress.objects.all(), chains=( ('interface__device', 'nat_device'), ), required=False, label='IP Address', widget=APISelect( api_url='/api/ipam/ip-addresses/', display_field='address' ) ) primary_for_parent = forms.BooleanField( required=False, label='Make this the primary IP for the device/VM' ) tags = TagField( required=False ) class Meta: model = IPAddress fields = [ 'address', 'vrf', 'status', 'role', 'description', 'interface', 'primary_for_parent', 'nat_site', 'nat_rack', 'nat_inside', 'tenant_group', 'tenant', 'tags', ] widgets = { 'status': StaticSelect2(), 'role': StaticSelect2(), 'vrf': APISelect( api_url="/api/ipam/vrfs/" ) } def __init__(self, *args, **kwargs): # Initialize helper selectors instance = kwargs.get('instance') initial = kwargs.get('initial', {}).copy() if instance and instance.nat_inside and instance.nat_inside.device is not None: initial['nat_site'] = instance.nat_inside.device.site initial['nat_rack'] = instance.nat_inside.device.rack initial['nat_device'] = instance.nat_inside.device kwargs['initial'] = initial super().__init__(*args, **kwargs) self.fields['vrf'].empty_label = 'Global' # Limit interface selections to those belonging to the parent device/VM if self.instance and self.instance.interface: self.fields['interface'].queryset = Interface.objects.filter( device=self.instance.interface.device, virtual_machine=self.instance.interface.virtual_machine ) else: self.fields['interface'].choices = [] # Initialize primary_for_parent if IP address is already assigned if self.instance.pk and self.instance.interface is not None: parent = self.instance.interface.parent if ( self.instance.address.version == 4 and parent.primary_ip4_id == self.instance.pk or self.instance.address.version == 6 and parent.primary_ip6_id == self.instance.pk ): self.initial['primary_for_parent'] = True def clean(self): super().clean() # Primary IP assignment is only available if an interface has been assigned. if self.cleaned_data.get('primary_for_parent') and not self.cleaned_data.get('interface'): self.add_error( 'primary_for_parent', "Only IP addresses assigned to an interface can be designated as primary IPs." ) def save(self, *args, **kwargs): ipaddress = super().save(*args, **kwargs) # Assign/clear this IPAddress as the primary for the associated Device/VirtualMachine. if self.cleaned_data['primary_for_parent']: parent = self.cleaned_data['interface'].parent if ipaddress.address.version == 4: parent.primary_ip4 = ipaddress else: parent.primary_ip6 = ipaddress parent.save() elif self.cleaned_data['interface']: parent = self.cleaned_data['interface'].parent if ipaddress.address.version == 4 and parent.primary_ip4 == ipaddress: parent.primary_ip4 = None parent.save() elif ipaddress.address.version == 6 and parent.primary_ip6 == ipaddress: parent.primary_ip6 = None parent.save() return ipaddress
class InterfaceForm(BootstrapMixin, forms.ModelForm): untagged_vlan = forms.ModelChoiceField(queryset=VLAN.objects.all(), required=False, widget=APISelect( api_url="/api/ipam/vlans/", display_field='display_name', full=True)) tagged_vlans = forms.ModelMultipleChoiceField( queryset=VLAN.objects.all(), required=False, widget=APISelectMultiple(api_url="/api/ipam/vlans/", display_field='display_name', full=True)) tags = TagField(required=False) class Meta: model = Interface fields = [ 'virtual_machine', 'name', 'type', 'enabled', 'mac_address', 'mtu', 'description', 'mode', 'tags', 'untagged_vlan', 'tagged_vlans', ] widgets = { 'virtual_machine': forms.HiddenInput(), 'type': forms.HiddenInput(), 'mode': StaticSelect2() } labels = { 'mode': '802.1Q Mode', } help_texts = { 'mode': INTERFACE_MODE_HELP_TEXT, } def __init__(self, *args, **kwargs): super().__init__(*args, **kwargs) # Limit VLan choices to those in: global vlans, global groups, the current site's group, the current site vlan_choices = [] global_vlans = VLAN.objects.filter(site=None, group=None) vlan_choices.append( ('Global', [(vlan.pk, vlan) for vlan in global_vlans])) for group in VLANGroup.objects.filter(site=None): global_group_vlans = VLAN.objects.filter(group=group) vlan_choices.append( (group.name, [(vlan.pk, vlan) for vlan in global_group_vlans])) site = getattr(self.instance.parent, 'site', None) if site is not None: # Add non-grouped site VLANs site_vlans = VLAN.objects.filter(site=site, group=None) vlan_choices.append( (site.name, [(vlan.pk, vlan) for vlan in site_vlans])) # Add grouped site VLANs for group in VLANGroup.objects.filter(site=site): site_group_vlans = VLAN.objects.filter(group=group) vlan_choices.append( ('{} / {}'.format(group.site.name, group.name), [(vlan.pk, vlan) for vlan in site_group_vlans])) self.fields['untagged_vlan'].choices = [(None, '---------') ] + vlan_choices self.fields['tagged_vlans'].choices = vlan_choices def clean(self): super().clean() # Validate VLAN assignments tagged_vlans = self.cleaned_data['tagged_vlans'] # Untagged interfaces cannot be assigned tagged VLANs if self.cleaned_data[ 'mode'] == InterfaceModeChoices.MODE_ACCESS and tagged_vlans: raise forms.ValidationError({ 'mode': "An access interface cannot have tagged VLANs assigned." }) # Remove all tagged VLAN assignments from "tagged all" interfaces elif self.cleaned_data['mode'] == InterfaceModeChoices.MODE_TAGGED_ALL: self.cleaned_data['tagged_vlans'] = []
def validate(self, value): return TaggitTagField.validate(self, value)
class ProposalTagsForm(forms.Form): tags = TagField(required=False, help_text=_(u"Comma-separated list of tags"))