CardListView

Render objects as cards instead of table rows. Each card shows the object name and configurable action buttons. The card body template is overridable per view.

Quick Reference

View class Use for
CardListView Card grid (no permission check)
CardListViewPermissionRequired Card grid with model-level permission check
GuardianCardListViewPermissionRequired Card grid with per-object permissions (django-guardian)

Minimal Pattern

from crud_views.lib.view import CardAction
from crud_views.lib.views import CardListViewPermissionRequired

class AuthorCardListView(CardListViewPermissionRequired):
    cv_viewset = cv_author
    cv_card_actions = [
        CardAction(key="detail", label="Details", variant="primary", flex=True),
        CardAction(key="update", label="Edit"),
        CardAction(key="delete", no_label=True, variant="tertiary"),
    ]

URLs auto-register at /<prefix>/card/.

CardAction Fields

Field Type Default Description
key str (required) View key in the ViewSet ("detail", "update", "delete", etc.)
label str \| None None Explicit button label. If None, falls back to the target view's short label.
no_label bool False Render as icon-only button
variant str "secondary" Button style: "primary", "secondary", "tertiary"
flex bool False If True, button gets flex-grow-1
child_name str \| None None Child viewset name for cross-viewset links (like LinkChildColumn). When set, key is ignored for URL resolution.
child_key str "list" View key within the child viewset (e.g., "list", "card")

Card Container Class

Control the Bootstrap grid class on each card's wrapper <div>. Defaults to col-md-6 (two cards per row).

class ProjectCardListView(CardListViewPermissionRequired):
    cv_viewset = cv_project
    cv_card_container_class = "col-md-12"  # full-width cards
    cv_card_actions = [...]
Value Layout
col-md-4 Three cards per row
col-md-6 Two cards per row (default)
col-md-12 One card per row

Child ViewSet Actions

Link to a child viewset from a card button — the card equivalent of LinkChildColumn in tables.

from crud_views.lib.view import CardAction

class PublisherCardListView(CardListViewPermissionRequired):
    cv_viewset = cv_publisher
    cv_card_actions = [
        CardAction(key="detail", label="Details", variant="primary", flex=True),
        CardAction(child_name="book", child_key="card", label="Books"),
    ]

Child actions always render (no permission check on the card). The target view handles its own permissions. This matches LinkChildColumn behavior for tables.

Field Required Description
child_name Yes The child viewset's name
child_key No (default "list") Which view in the child viewset to link to
label Yes (for child actions) Button text — no auto-label for child actions

Custom Card Template

Override cv_card_template to render model-specific content:

class ProjektCardListView(CardListViewPermissionRequired):
    cv_viewset = cv_projekt
    cv_card_template = "myapp/tags/projekt_card.html"
    cv_card_actions = [...]

The custom template receives object, view, and request in its context. Use {% cv_card_action action object %} to render action buttons:

{% load crud_views %}

<div class="card mb-3">
    <div class="card-body">
        <h5 class="card-title">{{ object.name }}</h5>
        <p>{{ object.description|truncatewords:20 }}</p>
        <div class="d-flex gap-2">
            {% for action in view.cv_card_actions %}
                {% cv_card_action action object %}
            {% endfor %}
        </div>
    </div>
</div>

Filter Integration

Add ListViewTableFilterMixin for django-filter support (same pattern as table list views):

from crud_views.lib.views import CardListViewPermissionRequired, ListViewTableFilterMixin

class AuthorCardListView(ListViewTableFilterMixin, CardListViewPermissionRequired):
    cv_viewset = cv_author
    filterset_class = AuthorFilter
    formhelper_class = AuthorFilterFormHelper
    cv_card_actions = [...]

List Key Fallback

When a ViewSet has a CardListView but no ListView, keys that reference "list" (such as cv_success_key, cv_cancel_key, and the default "home" context button) automatically fall back to "card". No manual overrides needed.

This means a ViewSet with only a CardListView works out of the box — CreateView, UpdateView, and DeleteView all redirect to the card view after success.

Guardian (Per-Object Permissions)

from crud_views_guardian.lib.views import GuardianCardListViewPermissionRequired

class AuthorCardListView(GuardianCardListViewPermissionRequired):
    cv_viewset = cv_author  # must be a GuardianViewSet
    cv_card_actions = [...]

The queryset is automatically filtered to objects the user has per-object view permission on.