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

'list' object has no attribute 'queryset' #1320

Open
stcbus opened this issue Mar 7, 2023 · 7 comments
Open

'list' object has no attribute 'queryset' #1320

stcbus opened this issue Mar 7, 2023 · 7 comments

Comments

@stcbus
Copy link

stcbus commented Mar 7, 2023

Tried with pypi latest version and latest dev git install. I have seen this error a few times from previous searches (such as #790 , so maybe it came back?

Simple model called FundraisingTeam with a team name, that I reference as a ForeignKey field here:

class EventRegistration(models.Model):
    ...
    fundraising_team = models.ForeignKey(
        FundraisingTeam, on_delete=models.SET_NULL, blank=True, null=True
    )

urls.py

 path(
        "team-autocomplete/<int:pk>/",
        views.FundraisingTeamAutocomplete.as_view(),
        name="team_autocomplete",
    ),

note: I am able to call the url and retrieve the JSON data

Then, in my view the query (removed the filter to just double check):

class FundraisingTeamAutocomplete(autocomplete.Select2QuerySetView):
    def get_queryset(self):
        # Don't forget to filter out results depending on the visitor !

        qs = FundraisingTeam.objects.all()

        if self.q:
            qs = qs.filter(name__istartswith=self.q)

        return qs

And finally, I override the get_form() in my createview to set the widget:

class RegisterView(generic.edit.CreateView):
    model = EventRegistration
    fields = ["data", "fundraising_team"]
    template_name = "app/temp.html"

    def get_form(self):
        form = super(RegisterView, self).get_form()
        fundraising_enabled = (
            EventInstance.objects.get(pk=self.kwargs.get("event_instance_id"))
        ).enable_fundraising
        if fundraising_enabled:
            form.fields["fundraising_team"].widget = autocomplete.ModelSelect2(
                url=reverse(
                    "app:team_autocomplete",
                    kwargs={"pk": self.kwargs.get("event_instance_id")},
                ),
                attrs={
                    # Set some placeholder
                    "data-placeholder": "Type to start a search",
                    # Only trigger autocompletion after 3 characters have been typed
                    "data-minimum-input-length": 3,
                },
            )
        else:
            del form.fields["fundraising_team"]

        return form

My example seems pretty vanilla so far from the tutorial, so not sure what's happening. Latest released Django 4.1.

Appreciate any help!

@stcbus
Copy link
Author

stcbus commented Mar 8, 2023

I'm sure this isn't a priority and/or everyone is very busy, but is it possible to get some help on this? I am trying to use this module for a project with a tight deadline. I appreciate any help you all can provide!

@stcbus
Copy link
Author

stcbus commented Mar 11, 2023

I ended up working around this by switching to ListSelect2 instead of the ModelSelect2. Would be nice to know why this didn't work, though.

@Actionb
Copy link

Actionb commented Mar 13, 2023

I assume you're running into this exception? (please, always include a stack trace if you have one)

[...] in filter_choices_to_render
    self.choices.queryset = self.choices.queryset.filter(
AttributeError: 'list' object has no attribute 'queryset'

The formfield sets the choices on the widget when the formfield is instantiated:

    def _set_queryset(self, queryset):
        self._queryset = None if queryset is None else queryset.all()
        self.widget.choices = self.choices

But you override the widget after and never set the choices - so this widget has the default value for choices, an empty list.
It's the same mistake as in the issue you've linked: #790 (comment)

So either explicitly set the choices on the widget after it has been created in your get_form method, or declare the widget via form.Meta.widgets like described and let Django take care of that. I suggest the latter.

@stcbus
Copy link
Author

stcbus commented Mar 15, 2023

Thanks, so much @Actionb - not sure how I missed that difference. I blame being a newbie and also seeing another issue with it in the view that I lost context when copying/pasting.

Checking now, I am actually forwarding 2 values so that I can create objects, including a kwarg. How would I do this in the model meta without access to self?

 form.fields["fundraising_team"].widget = autocomplete.ListSelect2(
                url=reverse(
                    "endurance:team_autocomplete", kwargs={"pk": event_instance.id}
                ),
                forward=["email", forward.Const(event_instance.id, "event_id")],
                attrs={
                    # Set some placeholder
                    "data-placeholder": "Type to start a search",
                    # Only trigger autocompletion after 3 characters have been typed
                },
            )

One final thing - I am actually wondering what do i gain/lose by using the ModelSelect vs ListSelect if I have it working now?

@Actionb
Copy link

Actionb commented Mar 16, 2023

I don't quite follow what you are trying to do with the limited information you have given - and I don't think the issue tracker is the right place to ask for implementation advice like that (try asking on stackoverflow).
If you want to keep your current code, you can set the widget's choices after declaring it in get_form (#790 (comment)) and it should work.

One final thing - I am actually wondering what do i gain/lose by using the ModelSelect vs ListSelect if I have it working now?

I think (I'm not a dal developer) the choice of widget only matters for the initial rendering of the form. After that, the choices are served by your autocomplete view. That's why the choice of what widget class to use doesn't seem to matter once the form is up and rendered.

It comes down to what type of object choices is.
The default formfield for many-to-one and many-to-many model fields is an instance of ModelChoiceField.
To enable lazy evaluation of the choices queryset (and to facilitate enumerating over the queryset items), the formfield 'wraps' the widget choices in a ModelChoiceIterator object.

In the dal workflow, ModelSelect2 can handle that kind of object whereas ListSelect cannot.
So if you want to override the widget of a ModelChoiceField in Form.Meta.widgets, you should use a ModelSelect2 widget, because a ListSelect widget won't be able to handle the choices set by the formfield.

@yangwk4464
Copy link

I encounter the same issue, is that because of some logic conflicts with django-filter?

@Actionb
Copy link

Actionb commented Aug 15, 2023

What do you mean by conflict? This issue comes from overriding the widget of a ModelChoiceField formfield after the field has already been instantiated. The ModelChoiceField sets the widget's choices only once. If you override the widget after that, you have to set the choices yourself. Or use the form's meta options to override the widget for a given formfield, as described by the Django docs.

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

No branches or pull requests

3 participants