Private data

Search endpoints are public by default, so restrict any lookup over private data. If every form with a LookupWidget is behind a login, require one for all lookups:

IZAYA_LOGIN_REQUIRED = True

Anonymous searches then get a 403, so a session that expires while a form is open shows login_required_text in the results. This reads request.user, which needs AuthenticationMiddleware. The system check izaya.W001 reports if it’s missing from MIDDLEWARE. To keep one lookup public, for example on a public page, override has_access:

class GenreLookup(ModelLookup):
    model = Genre
    search_fields = ["name__icontains"]

    def has_access(self, request):
        return True

A has_access override replaces the setting, so call super().has_access(request) to add a rule on top of it.

Django’s LoginRequiredMiddleware takes over for anonymous users: it redirects their searches to the login page before izaya sees them. The setting, has_access and login_required_text then have no effect on those searches, so a lookup can’t opt out to stay public.

Only a False from has_access shows a message. A PermissionDenied raised by a lookup still gets the project’s 403 page, which the widget ignores, so the results stay closed.

The setting only controls who can search, not what they find. A per-user get_queryset only limits what can be searched. Limit the form field’s queryset to the same records, usually in the form’s __init__, as well:

class ListForm(forms.ModelForm):
    def __init__(self, *args, user, **kwargs):
        super().__init__(*args, **kwargs)
        self.fields["previous"].queryset = ReadingList.objects.manageable_by(user)

The field rejects submitted values outside that queryset, and the widget won’t render their labels. A stored value outside it isn’t shown either, so saving the form without that value clears it.

On a field with choices, such as a ChoiceField or a model field with choices, the widget likewise renders only labels for keys among those choices. A field with neither a queryset nor choices, such as a CharField, gives the widget nothing to check against. A bound form then renders the label get_selected returns for any submitted key, so anyone can read the labels of records they can’t search by submitting their keys. get_selected has no request to limit by, so use a model or choice field for private data rather than a CharField.