diff --git a/docs/configuration/conditional-fields.md b/docs/configuration/conditional-fields.md index 424ee1541..5026bcafa 100644 --- a/docs/configuration/conditional-fields.md +++ b/docs/configuration/conditional-fields.md @@ -66,3 +66,7 @@ class UserAdmin(ModelAdmin): "address": "different_address == true" } ``` + +## Support + +`conditional_fields` can be used in `ModelAdmin`, `TabularInline` and `StackedInline`. When used with `TabularInline`, the table column containing a hidden field will still be shown, but the field itself will be hidden per row. diff --git a/src/unfold/mixins/base_model_admin.py b/src/unfold/mixins/base_model_admin.py index a718fe2f9..a027035ee 100644 --- a/src/unfold/mixins/base_model_admin.py +++ b/src/unfold/mixins/base_model_admin.py @@ -18,6 +18,8 @@ class BaseModelAdminMixin: + conditional_fields: Optional[dict[str, str]] = None + def __init__(self, model: models.Model, admin_site: AdminSite) -> None: overrides = copy.deepcopy(FORMFIELD_OVERRIDES) diff --git a/src/unfold/templates/admin/edit_inline/stacked.html b/src/unfold/templates/admin/edit_inline/stacked.html index c0193f305..ad2c05d97 100644 --- a/src/unfold/templates/admin/edit_inline/stacked.html +++ b/src/unfold/templates/admin/edit_inline/stacked.html @@ -23,7 +23,7 @@

+ diff --git a/src/unfold/templates/admin/edit_inline/tabular.html b/src/unfold/templates/admin/edit_inline/tabular.html index eb9f25f4a..acc0a25a9 100644 --- a/src/unfold/templates/admin/edit_inline/tabular.html +++ b/src/unfold/templates/admin/edit_inline/tabular.html @@ -23,7 +23,7 @@

+ {% include "unfold/helpers/edit_inline/tabular_error.html" %} {% include "unfold/helpers/edit_inline/tabular_title.html" %} diff --git a/src/unfold/templates/unfold/helpers/edit_inline/tabular_row.html b/src/unfold/templates/unfold/helpers/edit_inline/tabular_row.html index 1d9547c8e..1399374b0 100644 --- a/src/unfold/templates/unfold/helpers/edit_inline/tabular_row.html +++ b/src/unfold/templates/unfold/helpers/edit_inline/tabular_row.html @@ -1,3 +1,4 @@ +{% load unfold %} {% spaceless %} {% for fieldset in inline_admin_form %} @@ -14,11 +15,15 @@ {% for fieldset in inline_admin_form %} {% for line in fieldset %} {% for field in line %} - {% if field.is_readonly or not field.field.is_hidden %} - - {% include "unfold/helpers/edit_inline/tabular_field.html" %} - - {% endif %} + {% with inline_admin_form.model_admin.conditional_fields|index:field.field.name as conditional_display %} + {% if field.is_readonly or not field.field.is_hidden %} + {% with field|changeform_condition as field %} + + {% include "unfold/helpers/edit_inline/tabular_field.html" %} + + {% endwith %} + {% endif %} + {% endwith %} {% endfor %} {% endfor %} {% endfor %} diff --git a/tests/server/example/admin.py b/tests/server/example/admin.py index f3958d689..e1c6fa2e6 100644 --- a/tests/server/example/admin.py +++ b/tests/server/example/admin.py @@ -5,12 +5,16 @@ from django.shortcuts import redirect from django.urls import reverse_lazy -from unfold.admin import ModelAdmin, StackedInline +from unfold.admin import ModelAdmin, StackedInline, TabularInline +from unfold.contrib.inlines.admin import ( + NonrelatedStackedInline, + NonrelatedTabularInline, +) from unfold.decorators import action from unfold.forms import AdminPasswordChangeForm, UserChangeForm, UserCreationForm from unfold.sections import TableSection, TemplateSection -from .models import ActionUser, SectionUser, Tag, User +from .models import ActionUser, NotableUser, SectionUser, Tag, User, UserNote admin.site.unregister(Group) @@ -21,6 +25,59 @@ class UserTagInline(StackedInline): collapsible = True +class UserNoteTabularInline(TabularInline): + model = UserNote + conditional_fields = { + "note": "type == 'note'", + "tag": "type == 'tag'", + } + + +class UserNoteStackedInline(StackedInline): + model = UserNote + conditional_fields = { + "note": "type == 'note'", + "tag": "type == 'tag'", + } + + +class UserTagUnrelatedInlineBase: + model = UserNote + conditional_fields = { + "note": "type == 'note'", + "tag": "type == 'tag'", + } + + def get_form_queryset(self, obj: User): + return self.model.objects.all() + + def save_new_instance(self, parent, instance): + pass + + +class UserTagUnrelatedStackedInline( + UserTagUnrelatedInlineBase, NonrelatedStackedInline +): + pass + + +class UserTagUnrelatedTabularInline( + UserTagUnrelatedInlineBase, NonrelatedTabularInline +): + pass + + +@admin.register(NotableUser) +class NotableUserAdmin(ModelAdmin): + fields = ("username",) + inlines = ( + UserNoteTabularInline, + UserNoteStackedInline, + UserTagUnrelatedStackedInline, + UserTagUnrelatedTabularInline, + ) + + @admin.register(User) class UserAdmin(BaseUserAdmin, ModelAdmin): form = UserChangeForm diff --git a/tests/server/example/migrations/0005_notableuser_usernote.py b/tests/server/example/migrations/0005_notableuser_usernote.py new file mode 100644 index 000000000..fa6b23126 --- /dev/null +++ b/tests/server/example/migrations/0005_notableuser_usernote.py @@ -0,0 +1,57 @@ +# Generated by Django 4.2.22 on 2025-07-30 11:31 + +import django.contrib.auth.models +import django.db.models.deletion +from django.conf import settings +from django.db import migrations, models + + +class Migration(migrations.Migration): + dependencies = [ + ("example", "0004_actionuser_sectionuser"), + ] + + operations = [ + migrations.CreateModel( + name="NotableUser", + fields=[], + options={ + "proxy": True, + "indexes": [], + "constraints": [], + }, + bases=("example.user",), + managers=[ + ("objects", django.contrib.auth.models.UserManager()), + ], + ), + migrations.CreateModel( + name="UserNote", + fields=[ + ( + "id", + models.BigAutoField( + auto_created=True, + primary_key=True, + serialize=False, + verbose_name="ID", + ), + ), + ( + "type", + models.CharField( + choices=[("note", "Note"), ("tag", "Tag")], max_length=16 + ), + ), + ("note", models.CharField(blank=True, max_length=255)), + ("tag", models.CharField(blank=True, max_length=255)), + ( + "user", + models.ForeignKey( + on_delete=django.db.models.deletion.CASCADE, + to=settings.AUTH_USER_MODEL, + ), + ), + ], + ), + ] diff --git a/tests/server/example/models.py b/tests/server/example/models.py index ac43dc16c..a119cc194 100644 --- a/tests/server/example/models.py +++ b/tests/server/example/models.py @@ -24,3 +24,15 @@ class Tag(models.Model): def __str__(self): return self.name + + +class UserNote(models.Model): + user = models.ForeignKey(User, on_delete=models.CASCADE) + type = models.CharField(choices=[("note", "Note"), ("tag", "Tag")], max_length=16) + note = models.CharField(max_length=255, blank=True) + tag = models.CharField(max_length=255, blank=True) + + +class NotableUser(User): + class Meta: + proxy = True