============================= Context-dependant completions ============================= There's a common usecase: "Please display the items depending on the user context". In a multi-site context, you **have** to define a preemptive filter to gather informations based on the current context. **And** you also have to validate form data against this context. A malevolent user could forge a POST request with a data field that would be off-context. On the Autocomplete class side ============================== That's the reason why the ``django-agnocomplete`` views carry the request user into the Autocomplete class. This instance variable is ``self.user``. If you're using a custom authentication profile, it'll be an instance of your ``AUTH_USER_MODE`` class, hence it'll have access to its properties and methods. .. code-block:: python class AutocompletePersonQueryset(AgnocompleteModel): fields = ['first_name', 'last_name'] requires_authentication = True model = Person def get_queryset(self): email = self.user.email _, domain = email.split('@') return Person.objects.filter(email__endswith=domain) class AutocompletePersonSameSite(AgnocompleteModel): fields = ['first_name', 'last_name'] requires_authentication = True model = Person def get_queryset(self): return Person.objects.filter(site=self.user.site) .. important:: You may have noticed that these two classes have a ``model`` property and a ``requires_authentication`` property set to ``True``. Because we're using a user-based context, the ``requires_authentication`` will allow the general "out-of-context code" (form class creation) to instanciate the Agnocomplete class without the context, but will disallow it to return results based on the ``query``. This way, you can filter out unauthorized uses of the autocomplete, as you could do it with the ``@login_required`` decorator in your views. On the Form side ================ Nothing special to do. Just declare your fields exactly the same way. .. code-block:: python from django import forms from agnocomplete.forms import UserContextFormMixin class SearchContextForm(UserContextForm, forms.Form): search_person = fields.AgnocompleteModelField( 'AutocompletePersonSameSite') On the view side ================ Views that take care of the context to handle form display and form process need to be aware that they'll have to carry the user context to the form and that, when the ``POST`` request has to be processed using this context. We're providing a Mixin named :class:`UserContextFormViewMixin`: .. code-block:: python from agnocomplete.views import UserContextFormViewMixin class SearchContextFormView(UserContextFormViewMixin, FormView): form_class = SearchContextForm That's it. The view will pass the context to the fields, and this context will be used by the Agnocomplete field to validate against the queryset. If the POST data pushed to your form don't comply with the context (e.g., choosing a Category that doesn't belong to the user context), then the ``form.is_valid()`` method will return False. No need to redeclare the queryset, no need to override the ``clean_()`` method. Context-dependant multiple selects ================================== In the :ref:`model-multiple-selection` section, we've seen how to create multiple-select inputs, with or without enabling creation mode. It may happen that we want to create new model instances using the current context. Typically, let's imagine that we're on a multiple-client website, each logged user belongs to their own "domain". Now we want to tag items, but each tag catalog has to be isolated from the others. The tags of the *client A* are not the tags of the *client B*. Here are our models: .. code-block:: python class ContextTag(models.Model): name = models.CharField(max_length=50) domain = models.CharField(max_length=100) class ArticleContextTag(models.Model): article = models.ForeignKey(Article) tags = models.ManyToManyField(ContextTag) Here's the corresponding :class:`ModelForm` .. code-block:: python from agnocomplete.forms import UserContextFormMixin class ArticleContextTagModelForm(UserContextFormMixin, forms.ModelForm): article = fields.AgnocompleteModelField(AutocompleteArticle) tags = ModelMultipleDomainField( AutocompleteContextTag, create_field="name", required=False ) class Meta: model = ArticleContextTag fields = '__all__' You may have noticed that we have a dedicated :class:`ModelMultipleDomainField`. This specific field class uses the context passed through the form, and then the fields, to build extra arguments when creating the model instance. Here's the :class:`ModelMultipleDomainField`: .. code-block:: python class ModelMultipleDomainField(fields.AgnocompleteModelMultipleField): def extra_create_kwargs(self): """ Inject the domain of the current user in the new model instances. """ user = self.get_agnocomplete_context() if user: _, domain = user.email.split('@') return { 'domain': domain } return {} When the field will want to create new records in the :class:`ContextTag` table, here's what's going to happen: * the "name" will take the value of the string inserted into the input, * the "domain" will take the value of the current user email domain name. As a consequence, each tag creation could be written like this: .. code-block:: python ContextTag.objects.create(**{ 'name': input_value, 'domain': user_domain_name, }) Of course, you're free to extract whichever information out of the context (or not) and feed the extra_kwargs dictionary (the date and time, the weather, whatever). .. important:: Views that will use the :class:`ArticleContextTagModelForm` **must** inherit from the :class:`UserContextFormViewMixin`, exactly as above, otherwise, the context is not transmitted to the different elements of the view. Also, you **have** to decorate your ``form_valid`` method using the ``@method_decorator(allow_create)`` trick.