# Views and Templates Patterns for passing tenants to forms, rendering options in templates, and registering models in the Django admin. ## Passing `tenant` to forms Every form that uses a `django-tenant-options` mixin requires a `tenant` argument. How you determine the current tenant depends on your application architecture. ### Function-based views ```python from django.shortcuts import get_object_or_404, redirect, render def task_create(request): if request.method == "POST": form = TaskForm(request.POST, tenant=request.user.tenant) if form.is_valid(): form.save() return redirect("task_list") else: form = TaskForm(tenant=request.user.tenant) return render(request, "tasks/form.html", {"form": form}) def task_update(request, task_id): task = get_object_or_404(Task, id=task_id) if request.method == "POST": form = TaskForm(request.POST, instance=task, tenant=request.user.tenant) if form.is_valid(): form.save() return redirect("task_list") else: form = TaskForm(instance=task, tenant=request.user.tenant) return render(request, "tasks/form.html", {"form": form}) ``` ### Class-based views Override `get_form_kwargs` to inject the tenant: ```python from django.views.generic import CreateView, UpdateView from django.contrib.auth.mixins import LoginRequiredMixin class TaskCreateView(LoginRequiredMixin, CreateView): model = Task form_class = TaskForm template_name = "tasks/form.html" def get_form_kwargs(self): kwargs = super().get_form_kwargs() kwargs["tenant"] = self.request.user.tenant return kwargs class TaskUpdateView(LoginRequiredMixin, UpdateView): model = Task form_class = TaskForm template_name = "tasks/form.html" def get_form_kwargs(self): kwargs = super().get_form_kwargs() kwargs["tenant"] = self.request.user.tenant return kwargs ``` ### Determining the current tenant Your approach depends on your tenant architecture: ```python # Direct ForeignKey on User tenant = request.user.tenant # Through a profile model tenant = request.user.profile.organization # From middleware that sets it on the request tenant = request.tenant # From the URL (e.g., subdomain-based) tenant = Tenant.objects.get(subdomain=request.subdomain) ``` The package doesn't impose a particular tenant resolution strategy. As long as you pass a valid tenant model instance to the form, it works. ## Template patterns ### Displaying option types Options have an `option_type` field with values `"dm"` (Mandatory), `"do"` (Optional), or `"cu"` (Custom). Use `get_option_type_display` for human-readable labels: ```html ``` ### Color-coding by type Apply CSS classes based on option type for visual distinction: ```html {% for priority in priorities %}
  • {{ priority.name }} {% if priority.option_type == "dm" %} Mandatory {% elif priority.option_type == "do" %} Optional {% else %} Custom {% endif %}
  • {% endfor %} ``` > **Accessibility note:** The visible text ("Mandatory", "Optional", "Custom") is what > conveys meaning here - color is supplementary, satisfying WCAG 1.4.1. Do not drop the > text and rely on color alone. Also verify badge contrast (WCAG 1.4.3, 4.5:1 minimum): > Bootstrap's `badge-info` with white text fails contrast in common themes - prefer a > class whose color/background pair you have measured (e.g. `badge-dark`). ### Listing options for a tenant In your view, use the queryset methods to get the right set of options: ```python def priority_list(request): # All options available to this tenant (mandatory + optional + custom) options = TaskPriorityOption.objects.options_for_tenant(request.user.tenant) # Only selected options (what users will see in forms) selections = TaskPrioritySelection.objects.selected_options_for_tenant( tenant=request.user.tenant ) return render(request, "priorities/list.html", { "options": options, "selections": selections, }) ``` ### Simple form template ```html
    {% csrf_token %} {# Error summary - announced on submit because of role="alert". #} {% if form.errors %}

    Please correct the following:

    {% endif %} {{ form.as_p }}
    ``` > **Accessibility note:** `{{ form.as_p }}` does not attach `aria-invalid` or > `aria-describedby` to individual errored inputs. Pick one of these approaches (they are > alternatives, not complements): > > - Mix `django_tenant_options.forms.AccessibleFormMixin` into your form (it sets > `aria-invalid` and points `aria-describedby` at `id_{{ field.html_name }}_errors`) **and** > hand-render an error container with that exact id, e.g. > `