Skip to content

Conversation

@gagarski
Copy link
Member

Since building linked autocomplete hierarchy is a quite common case, I have added support for automatic clearing autocomplete on forward field change. To use it you should add something like feedback=('field_to_clear1', 'field_to_clear2') to forwarded field Select2 widget arguments.

Here is a form example that uses this feature:

class TestForm(forms.ModelForm):
    def clean_test(self):
        owner = self.cleaned_data.get('owner', None)
        value = self.cleaned_data.get('test', None)

        if value and owner and value.owner != owner:
            raise forms.ValidationError('Wrong owner for test')

        return value

    class Meta:
        model = TestModel
        fields = ('name', 'owner', 'test')
        widgets = {
            'owner': autocomplete.ModelSelect2(url='linked_data_feedback_users',
                                               feedback=('test',)),
            'test': autocomplete.ModelSelect2(url='linked_data_feedback',
                                              forward=('owner',))
        }

This example is also provided in the test project.

This also works with with non-Select2 forwarded fields but for them you should explicitly pass comma-separated HTML-attribute (data-autocomplete-light-forward-feedback="field_to_clear1,field_to_clear2").

Here is an example with a standard HTML select being forwarded:

class TestForm(forms.ModelForm):
    def clean_test(self):
        owner = self.cleaned_data.get('owner', None)
        value = self.cleaned_data.get('test', None)

        if value and owner and value.owner != owner:
            raise forms.ValidationError('Wrong owner for test')

        return value

    class Meta:
        model = TestModel
        fields = ('name', 'owner', 'test')
        widgets = {
            'owner': Select(attrs={
                "data-autocomplete-light-forward-feedback": "test"
            }, choices=get_user_model().objects.all()),
            'test': autocomplete.ModelSelect2(url='linked_data_feedback',
                                              forward=('owner',))
        }

Also, some modifiers for feedback fields are supported:

  • $test - all fields ending with "test" are cleared on update (may be useful with formsets or prefixed forms);
  • ^test - all fields starting with "test" are cleared on update;
  • *test - all fields containing "test" are cleared on update.

Here is an example using modifiers:

class TestForm(forms.ModelForm):
    def clean_test(self):
        owner = self.cleaned_data.get('owner', None)
        value = self.cleaned_data.get('test', None)

        if value and owner and value.owner != owner:
            raise forms.ValidationError('Wrong owner for test')

        return value

    class Meta:
        model = TestModel
        fields = ('name', 'owner', 'test')
        widgets = {
            'owner': autocomplete.ModelSelect2(url='linked_data_feedback_users',
                                               feedback=('$test',)),
            # Or without autocomplete ...
            # 'owner': Select(attrs={
            #     "data-autocomplete-light-forward-feedback": "$test"
            # }, choices=get_user_model().objects.all()),
            'test': autocomplete.ModelSelect2(url='linked_data_feedback',
                                              forward=('owner',))
        }

@gagarski
Copy link
Member Author

Seems like I need a proper test suite for an example in the test project.

@gagarski
Copy link
Member Author

gagarski commented Sep 6, 2016

Need something better that $test to find fields to clean in inline forms.

Probably the following should be done for fields that match $('[name$=test]'):

  • check that the field name is exactly form-prefix_test
  • check if the field owner is forwarded by this field

The latter leads to thoughts about autodiscovering fields that forward current field.

@jpic
Copy link
Member

jpic commented Sep 21, 2016

I really love this feature, but I don't think it should be part of DAL.

If we integrate this in DAL, it will only work for DAL fields.

Such a feature could be useful for any kind of form fields, not just DAL.

What do you think ?

@gagarski
Copy link
Member Author

Such a feature could be useful for any kind of form fields, not just DAL.

Field cleaning can be induced by any form field that emits change event. But yes, the cleaned field can only be a DAL field. Since only DAL fields can use forward, this is kind of dual feature to forward.

But now I think the implementation should be reconsidered. Maybe we should declare feedback on the side of the field that should be cleaned (smth like "please forward continent field for country autocomplete" and clean it when the continent is changed" or Field("country", clean_on_change=True)). Declaring feedback on the "inducer" side is a bit ugly, especially for non-DAL fields.

Maybe sometimes i'll try to reimplement it from scratch in a more nice way.

@jpic
Copy link
Member

jpic commented Sep 21, 2016

Perhaps you could add the change callback in your own project, and manually feed the feedback data attribute yourself in the mean time ? Then your project could still be using upstream dal instead of your branch ? I really don't want you to have to go over extra efforts maintaining your branch, but I see the work you did here and it's really cool.

In DAL v1 and v2, we had the "add another" feature in DAL, for v3 it has been extracted in django-add-another, and took a life of its own. I highly encourage you to do that and to add support for all fields. However, I wonder if it would be better to just provide the script and let users do things like:

driver=forms.ModelChoiceField(attrs={'data-clear-on-change': ['car']})
car=forms.Select2(forward=['driver'])

Then, you could even derive on that and add more callbacks on field change, such as toggling a field display, ie. fieldA requires fieldB to have that value or something.

@gagarski
Copy link
Member Author

Yes, probably this feature is weakly related to autocomplete. Seems like this PR should be closed.

@gagarski gagarski closed this Sep 21, 2016
@jpic
Copy link
Member

jpic commented Oct 6, 2016

Hi @gagarski, perhaps you might want to consider django-dynamic-fields. Let me know what you think.

🎸

@gagarski
Copy link
Member Author

gagarski commented Oct 8, 2016

@jpic looks cool for me at first glance and more general than this PR. I'll try to look and play around with it later.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants