Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Does not work with to_field #100

Open
rez0n opened this issue Oct 30, 2021 · 2 comments
Open

Does not work with to_field #100

rez0n opened this issue Oct 30, 2021 · 2 comments
Assignees
Labels
bug Something isn't working

Comments

@rez0n
Copy link

rez0n commented Oct 30, 2021

Hi @codingjoe, I faced an issue using to_field option in the model field. Form can't be validated at all.

class ModelA(models.Model):
    id = models.AutoField(primary_key=True)
    slug = models.CharField(max_length=4, unique=True)


class ModelB(models.Model):
    id = models.AutoField(primary_key=True)
    a_slug = models.ForeignKey(ModelA, verbose_name=_("Select A"), on_delete=models.CASCADE, to_field='slug')
    note = models.CharField(_("Note"), max_length=50)


class ModelAPickWidget(s2forms.ModelSelect2Widget):
    model = ModelA
    search_fields = [
        "slug__icontains",
    ]

    def label_from_instance(self, obj):
        return str(f'{obj.slug}')


class ModelBForm(ModelForm):

    def __init__(self, *args, **kwargs):
        super().__init__(*args, **kwargs)

        # self.fields['a_slug'].widget = ModelAPickWidget()
        # self.fields['a_slug'].widget.attrs['class'] = 'form-control'
        # self.fields['a_slug'].to_field_name = 'slug'
        # self.fields['a_slug'].queryset = ModelA.objects.all()

    class Meta:
        model = ModelB
        fields = ['a_slug', 'note', ]
        widgets = {
            "a_slug": ModelAPickWidget(attrs={
                'class': 'form-control',
            }),
            "note": widgets.TextInput(attrs={
                'class': 'form-control',
            }),
        }

If I going to disable ModelAPickWidget to use default Django's select widget it works and saves the form.

There is some debugging information.

  1. ModelSelect2Mixin in the optgroups method value is always pk
  2. ModelSelect2Mixin in the optgroups line
query = Q(**{"%s__in" % field_name: selected_choices})
print(query)
>>> (AND: ('slug__in', {'196'}))
  1. I tried to change
query = Q(**{"%s__in" % field_name: selected_choices})

to

query = Q(**{"%s__in" % 'pk': selected_choices})

And I got some success, form get failed validation on first submission but saves if I re-submit it again.

Form validation error raised by Django's ModelChoiceField in the to_python method

Select a valid choice. That choice is not one of the available choices.

because it gets pk value instead slug.

@rez0n rez0n added the bug Something isn't working label Oct 30, 2021
@rez0n
Copy link
Author

rez0n commented Oct 30, 2021

I performed a lot experiments with the optgroups method but have no success, by some reason first form submission get failed validation and validates successfully on the next submit.

A very dirty hack to make it works (nothing related to select2 module, just maybe it will helps to faster understand core of the issue.

class ModelChoiceField2(ChoiceField):

    def to_python(self, value):
        if value in self.empty_values:
            return None
        try:
            key = self.to_field_name or 'pk'
            if isinstance(value, self.queryset.model):
                value = getattr(value, key)
            value = self.queryset.get(**{'pk': value})   <<<<< HERE changed key to 'pk'
            value = value
        except (ValueError, TypeError, self.queryset.model.DoesNotExist):
            raise ValidationError(self.error_messages['invalid_choice'], code='invalid_choice')
        return value

    def valid_value(self, value):
        # Override validation to check is selected value exists in the db
        text_value = str(value)
        if not self.queryset.model(slug=text_value):
            return False
        
        return True

class ModelBForm(ModelForm):
    a_slug = ModelChoiceField2()
...

@codingjoe
Copy link
Owner

I recently fixed the same issue in Django's autocomplete field. I'd be delighted if you could try to backport this patch to this repo as well. I am pretty overwhelmed with work right now, so I'd appreciate the help.

You can find my Django commit here django/django@3071660

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
bug Something isn't working
Projects
None yet
Development

No branches or pull requests

2 participants