How to gain specific field value manually in django admin fieldsets

Viewed 47

I'm overriding admin/includes/app-name/fieldset.html to change the behavior of some fields using the JavaScript.

In the original fieldset.html by django theres a loop to display the fields, But I want to pick the value of first field for some usecase. How can I do that

 # I removed some code to make it simpler to read
    {% for line in fieldset %}
                {% for field in line %}
                    {{ field.field }}
                {% endfor %}
            {% endfor %}

I'm trying

 {{ fieldset[0].field }}

But this gives the error:

Could not parse the remainder: '[1].field' from 'fieldset[1].field'

If i do that

{{ fieldset }} 

This gives

<django.contrib.admin.helpers.Fieldset object at 0x000002873F9B1940>

How I can Pick the data of first field instead of running a loop, means I do not need iteration here, Just wants to pick values manually. I'm not fully aware of django template tags. So your help would be needed here.

1 Answers

See Edit, this is wrong

I believe it would be this: {{fieldset.0.0.field}}


Thought Process:

{% for line in fieldset %} means fieldset is an List
{% for field in line %} means line is an List

So we're looking at something like:

    fieldset = [
        [fieldObj0, fieldObj1],     # Line 0
        [fieldObj2, fieldObj3],     # Line 1
    ]

Get Line 0 (first line):
{{fieldset.0}} = [fieldObj0, fieldObj1]

Get first fieldObj in Line 0:
{{fieldset.0.0}} = fieldObj0

Get field attribute of the first fieldObj in the first line
{{fieldset.0.0.field}} = fieldObj0.field

Hopefully that's correct (it was not)


Edit

Two Solutions

Manually Fetching field by Name: {{fieldset.form.actual_field_name_here}}

  • Lose the dynamic part of first field

Using Counter in Loop

    {% for line in fieldset %}
        {% for field in line %}

            {% if forloop.parentloop.first and forloop.first %}
                first line + first field: {{ field.field }}
            {% else %}
                {{ field.field }}
            {% endif %}

        {% endfor %}
    {% endfor %}

Things I've learned

After looking at the source code, fieldset actually uses an __iter__ method and that's why my original stuff didn't work.
And What is extra annoying is they create the Fieldline Object in the __iter__ method instead of just generating them beforehand and throwing it into an attribute. This means the only way you can access those objects is during the loop. This extends to the Fieldline object which does the same thing with AdminField

contrib\admin\helpers.py
class Fieldset:
    def __init__(self, form, name=None, readonly_fields=(), fields=(), classes=(),
                 description=None, model_admin=None):

        self.form = form                        # Is Django Form
        self.name, self.fields = name, fields   # Fields is an array of strings

        ## more stuff - chopped out
        

    def __iter__(self):
        for field in self.fields:
            yield Fieldline(self.form, field, self.readonly_fields, model_admin=self.model_admin)


class Fieldline:
    def __init__(self, form, field, readonly_fields=None, model_admin=None):
        self.form = form  # A django.forms.Form instance
        if not hasattr(field, "__iter__") or isinstance(field, str):
            self.fields = [field]
        else:
            self.fields = field

        ## more stuff - chopped out

    def __iter__(self):
        for i, field in enumerate(self.fields):
            if field in self.readonly_fields:
                yield AdminReadonlyField(self.form, field, is_first=(i == 0), model_admin=self.model_admin)
            else:
                yield AdminField(self.form, field, is_first=(i == 0))
Related