barseghyanartur / django-fobi

Form generator/builder application for Django done right: customisable, modular, user- and developer- friendly.
https://pypi.python.org/pypi/django-fobi
485 stars 112 forks source link

email and email confirm #310

Closed i-salameh95 closed 1 year ago

i-salameh95 commented 1 year ago

Hello, How to create email and email confirmation field in django-fobi ? or in general two fields related to each other: 1st case: the two fields must be identical 2nd case: if field number 1 is filled with yes ( yes/no field) , so the user must be fill the field number 2 and vice versa ?

barseghyanartur commented 1 year ago

You should create a plugin that does it. Simply put - a muliti field plugin.

i-salameh95 commented 1 year ago

thanks, is there example on this? I have read the doc. but there is nothing about multi field plugin

barseghyanartur commented 1 year ago

Well, look. Each plugin returns a list of elements. Usually it's just one, but you can return two or more. It's quite trivial.

If you don't manage to set it up, let me know. I'll make one (useful to have anyway). If you do, please share it using a PR. :)

i-salameh95 commented 1 year ago

Do u mean that from get_form_field_instances that I should return 2 instances of the same field? I'm trying from yesterday to handle this, but with no luck. please could you help me on this ?

barseghyanartur commented 1 year ago

Check this plugin:

https://github.com/barseghyanartur/django-fobi/blob/main/src/fobi/contrib/plugins/form_elements/fields/email/base.py

It only returns one field. You should return two and take care of the validation.

i-salameh95 commented 1 year ago

I have done this: but this doesn't return anything, it shows that successfully my plugin added to the form, but the form is empty...

   def get_form_field_instances(
            self, request=None, form_entry=None, form_element_entries=None, **kwargs
    ):
        """Get form field instances."""
        widget_attrs = {
            "class": theme.form_element_html_class,
            "type": "email",
            "placeholder": self.data.placeholder,
        }

        field_kwargs = {
            "label": self.data.label,
            "help_text": self.data.help_text,
            "initial": self.data.initial,
            "required": self.data.required,
            "widget": TextInput(attrs=widget_attrs),
        }

        field2_kwargs = {
            "label_confirm": self.data.label_confirm,
            "help_text": self.data.help_text,
            "initial": self.data.initial,
            "required": self.data.required,
            "widget": TextInput(attrs=widget_attrs),
        }

        if self.data.max_length:
            try:
                field_kwargs["max_length"] = int(self.data.max_length)
            except ValueError:
                field_kwargs["max_length"] = None
        else:
            field_kwargs["max_length"] = None

        field_kwargs["min_length"] = None

       return [(self.data.name, EmailField, field_kwargs), (self.data.name_confirm, EmailField, field2_kwargs)]
barseghyanartur commented 1 year ago

OK. I'll take a look.

i-salameh95 commented 1 year ago

FINALLY, I have successfully added the plugin that will make the email and confirmation email: here is the code of the plugin:

fobi_form_elements.py file

class MultiFieldPlugin(FormFieldPlugin):
    """SampleTextareaPlugin."""

    uid = "confirm_another"
    name = "Confirm Email"
    form = MultiFieldForm
    group = "Samples"  # Group to which the plugin belongs to

    def get_form_field_instances(
            self, request=None, form_entry=None, form_element_entries=None, **kwargs
    ):
        """Get form field instances."""
        widget_attrs = {
            "class": theme.form_element_html_class,
            "type": "email",
            "placeholder": self.data.placeholder,
        }

        field_kwargs = {
            "label": self.data.label,
            "help_text": self.data.help_text,
            "initial": self.data.initial,
            "required": self.data.required,
            "widget": TextInput(attrs=widget_attrs),
        }

        field2_kwargs = {
            "label": self.data.label_confirm,
            "help_text": self.data.help_text,
            "initial": self.data.initial,
            "required": self.data.required,
            "widget": TextInput(attrs=widget_attrs),
        }

        if self.data.max_length:
            try:
                field_kwargs["max_length"] = int(self.data.max_length)
            except ValueError:
                field_kwargs["max_length"] = None
        else:
            field_kwargs["max_length"] = None

        field_kwargs["min_length"] = None

        return [(self.data.name, forms.EmailField, field_kwargs), (self.data.name_confirm, forms.EmailField, field2_kwargs)]

form_element_plugin_registry.register(MultiFieldPlugin)

forms.py file

class MultiFieldForm(forms.Form, BasePluginForm):
    """Form for ``MultiFieldForm``."""

    plugin_data_fields = [
        ("label", ""),
        ("name", ""),
        ("label_confirm", ""),
        ("name_confirm", ""),
        ("help_text", ""),
        ("initial", ""),
        ("max_length", str(DEFAULT_MAX_LENGTH)),
        ("required", False),
        ("placeholder", ""),
    ]

    label = forms.CharField(
        label=_("Label"),
        required=True,
        widget=forms.widgets.TextInput(
            attrs={"class": theme.form_element_html_class}
        ),
    )
    name = forms.CharField(
        label=_("Name"),
        required=True,
        widget=forms.widgets.TextInput(
            attrs={"class": theme.form_element_html_class}
        ),
    )

    label_confirm = forms.CharField(
        label=_("Label of Confirmation email"),
        required=True,
        widget=forms.widgets.TextInput(
            attrs={"class": theme.form_element_html_class}
        ),
    )

    name_confirm = forms.CharField(
        label=_("Name of Confirmation email"),
        required=True,
        widget=forms.widgets.TextInput(
            attrs={"class": theme.form_element_html_class}
        ),
    )

    help_text = forms.CharField(
        label=_("Help text"),
        required=False,
        widget=forms.widgets.Textarea(
            attrs={"class": theme.form_element_html_class}
        ),
    )
    initial = forms.EmailField(
        label=_("Initial"),
        required=False,
        widget=forms.widgets.TextInput(
            attrs={"class": theme.form_element_html_class}
        ),
    )
    max_length = forms.IntegerField(
        label=_("Max length"),
        required=True,
        widget=NumberInput(
            attrs={
                "class": theme.form_element_html_class,
                "min": str(DEFAULT_MIN_LENGTH),
            }
        ),
        initial=DEFAULT_MAX_LENGTH,
        validators=[MinValueValidator(DEFAULT_MIN_LENGTH)],
    )
    required = forms.BooleanField(
        label=_("Required"),
        required=False,
        widget=forms.widgets.CheckboxInput(
            attrs={"class": theme.form_element_checkbox_html_class}
        ),
    )
    placeholder = forms.CharField(
        label=_("Placeholder"),
        required=False,
        widget=forms.widgets.TextInput(
            attrs={"class": theme.form_element_html_class}
        ),
    )

    def clean(self):
        super(MultiFieldForm, self).clean()

        max_length = self.cleaned_data.get("max_length", DEFAULT_MAX_LENGTH)

        if self.cleaned_data["initial"]:
            len_initial = len(self.cleaned_data["initial"])
            if len_initial > max_length:
                self.add_error(
                    "initial",
                    _(
                        "Ensure this value has at most {0} characters "
                        "(it has {1}).".format(max_length, len_initial)
                    ),
                )

        email_name = self.cleaned_data["name"]
        email_confirm_name = self.cleaned_data["name_confirm"]
        print(email_confirm_name)
        print(email_name)
        if email_name == email_confirm_name:
            self.add_error(
                "name_confirm",
                _(
                    "email and email confirm must have different names"
                ),
            )

I think I must have also validate the data of the two fields on submit ( the data must match ) and according to doc. this is done in _submit_plugin_formdata , am I right ?

i-salameh95 commented 1 year ago

Hello, I'm struggling to find where I should add the validation code of the two email if they matched or not. should it be in submit_plugin_form_data , I have tried that, but the form is submitted successfully ! even the two emails are not the same.

  def submit_plugin_form_data(self,
                                form_entry,
                                request,
                                form,
                                form_element_entries=None,
                                **kwargs):

        email = form.cleaned_data.get(self.data.name, None)
        email_confirm = form.cleaned_data.get(self.data.name_confirm, None)
        if email and email_confirm and email != email_confirm:
            form.add_error(self.data.name, ValidationError("Emails are not the same"))
        return form

please if you could direct me, I will be so thankful..

barseghyanartur commented 1 year ago

You are in the right direction. I'll answer you today, later on, OK?

barseghyanartur commented 1 year ago

I started in dev branch: https://github.com/barseghyanartur/django-fobi/tree/dev/src/fobi/contrib/plugins/form_elements/fields/email_repeat

It's not yet working, but shows the intention.

i-salameh95 commented 1 year ago

Ok, thanks a lot for your intention, I will follow the branch updates.

barseghyanartur commented 1 year ago

What is important to know, is that the solution will be based on this form field and widget. Those parts take care of the validation.

i-salameh95 commented 1 year ago

I will pull the branch and use it, I really appreciate your time and help a lot.

btw: I'm trying to use django-fobi in Jobs project, every job ( Apply Form) has different form than the other, so the django-fobi suite my needs, but the admin needs some extra fields, like email match, and related fields ( like when the user press Yes from choices, so another text field should be filled)..

So I'm trying to test everything in this package.

barseghyanartur commented 1 year ago

So, it's finished in the dev branch. Instructions are left in the README. Please, evaluate it for a couple of days. If all is well, I'll make a new release shortly.

i-salameh95 commented 1 year ago

is there something error on the dev branch? I have add this to requirements.txt: django-fobi@git+https://github.com/barseghyanartur/django-fobi@dev every time I try to pull the git branch, it gives me error: UnicodeDecodeError: 'charmap' codec can't decode byte 0x90 in position 69342: character maps to <undefined>

barseghyanartur commented 1 year ago

Sorry, this looks like a windows issue with docker. Alternatively, you could go for classic virtualenv/sqlite setup.

i-salameh95 commented 1 year ago

Ok. I have fix the issue by delete fobi- main branch from site-packages and replace it with one from dev branch. at all, the Email repeat works very well, but I think there should be 2 labels for each input when you insert the Email Repeat plugin. ( e.g: email, confirm email address) but what appear is one label for the two input. which is the label of Email repeat element. See the screenshot for what I mean:

labels issue

barseghyanartur commented 1 year ago

It might depend on how are you going to use it. See the example below. I think it's just fine.

Screenshot from 2023-07-09 21-51-29

i-salameh95 commented 1 year ago

Ok, I have test it. I think its totally works well, you could please close this ticket.

barseghyanartur commented 1 year ago

OK. I'll make a release today.