django-widget-tweaks using field.name in render_field - django

I am using django-widget-tweaks and its render_field. I have an odd situation and I wonder if there is a workaround or a different approach that I should use?
I am using django and allauth (I am not using the social auth part yet). I have the default django user model and I have a "Profile" model that extends the user model. That's all working.
This is a specialized "Membership App". Only adults over 18 should be able to login. Once logged in a person can create "member" one or more "member" records. The logged in person is responsible for payng for memberships but might not be a member themselves; for example, a parent might log in and create member records for youth under 18 while not being a member themselves.
When creating a new member record I need to give the logged in person the opportunity to create a member record for themselves (if they don't already have one). If they chose this option then the logged in person's "Profile" info should auto-fill the new member form. To facilitate this I am capturing and serializing the logged in user's Profile info and am passing it to the CBV MemberCreate method. This method renders the Member Create form using django-widget-tweaks and its render_field method, a la:
{% with field=newMemberForm.first_name %}
{% render_field field class="form-control form-control-sm" %}
{% endwith %}
I want to create a data-loggedindata= attribute that uses the field.name value as the "member name" of the logged in person's profile. SOmething like:
{% with field=newMemberForm.first_name %}
{% render_field field class="form-control form-control-sm" data-loggedindata=LoggedInProfile.{{field.name}} %}
{% endwith %}
But django-widget-tweaks doesn't seem to understand that and I am not wise enough in the world of django to know a workaround.
I can hear someone typing: "but you know the field name from the {% with ... %} construct". Yes, that is true but the Member form has a lot of fields so I wrapped the render_field part in a small html file that I include. So the reall segment above might look like:
{% with field=profileForm.first_name %}
<div class="form-group col col-3">
{% include "Members/template_includes/field_render.html" %}
</div>
{% endwith %}
And the included file has the render_field in it. Inside that included html file I have no idea of the name of the field.
ideas?
Edit
I have tried Pankaj's suggestion below but
{% render_field field|attr:"data-loggedindata=LoggedInProfile.{{field.name}}" %}
does not appear to interpolate the object LoggedInProfile or the {{field.name}} value. What I get is the un-interpolated string "LoggedInProfile.{{field.name}}".
I have tried:
{% render_field field|attr:"data-loggedindata:getattr(LoggedInProfile, field.name" %}
but that is not interpolated either. Is there a way in a Django template and/or widget-tweaks to programatically get the value of an object attribute?
Edit 2
I decided to go another way. I am still using widget-tweaks but I am not trying to use render_field in this way anymore. I am serializing the data that would be placed in the data- elements and placing the JSON version in a hidden form field. I am letting javascript deal with it now.

If you want to add attribute in field with django-widget-tweaks you can simply use attr for that, in your case it would be like -
{% with field=newMemberForm.first_name %}
{% render_field field|add_class:"form-control form-control-sm"|attr:"data-loggedindata=LoggedInProfile.{{field.name}}" %}
{% endwith %}
Still have any doubts let me know

Related

Get formset name variables in django templates

I am sending a formset to my template. I want to add some style to it, so I need to access his variables.
I took the following code from documentation, where they print the form and it has some attributes, amongside them the name="form-0-title".
for form in formset:
print(form.as_table())
<tr><th><label for="id_form-0-title">Title:</label></th><td><input type="text" name="form-0-title" value="Django is now open source" id="id_form-0-title"></td></tr>
...
As I want to style my forms, I need to give the inputs a name to know how to treat them in the backend. When I do the following in the templates
{{ proficiency_form.management_form }}
{% for form in proficiency_form %}
{{form.title.name}}
{% endfor %}
I get title, title, ... instead of id_form-0-title, id_form-1-title, ...
How can I get the correct name in the templates?
You can easily get the input's name of your fields like so:
{{ proficiency_form.management_form }}
{% for form in proficiency_form %}
{{form.title.html_name}}
{% endfor %}
If you want to get the label's id, you can consider this approach:
{{ proficiency_form.management_form }}
{% for form in proficiency_form %}
{{form.title.id_for_label}}
{% endfor %}
Read more about it in the official Docs.
You can find a similar(almost duplicate) question here.

How can I create a custom template for a TabularInline admin Formset in Django?

In my Django app (research database), when changing a person object in the admin, I'd like all of the sources for that person to be listed as hyperlinks to the file for that source. I'm trying to do this by creating a custom template for a stacked inline. Here is the custom template so far:
<p>Testing</p>
{% for form in inline_admin_formset %}
{% for fieldset in form %}
<h5>Fieldset</h5>
{% if fieldset.name %} <h2>{{ fieldset.name }}</h2>{% endif %}
{% for line in fieldset %}
<h6>Line</h6>
{% for field in line %}
<h6>Field</h6>
{{ field.field }}
{% endfor %}
{% endfor %}
{% endfor %}
{% endfor %}
A lot of this is just for me to see what's going on. I used the links here and here as sort of a guide. What renders from the {{ field.field }} is what you'd expect from an inline element - a dropdown menu with the source names as choices and some icons for adding/changing.
What I really want, however, is just the source name rendered as a hyperlink. How do I get the source name (the actual name of the attribute is source_name) from what I have in the Django template language (i.e. the "field" object)?
In that context, {{ field.field }} is a BoundField object, and the value method is probably what you would want to use, as is in {{ field.field.value }}.
A more Django-ish approach (and more complicated) might involve creating a custom widget (start by subclassing one of their built-ins) that only displays text, and then hook that into the form being used in the ModelAdmin for your model. I think there's a bit of a rabbit hole there, in terms of needing to subclass the BaseInlineFormset and possibly a few others down that chain... I'm seeing that the BaseFormSet class has a .form attribute referenced in its construct_form method, but things are little murky from there.
Might also be useful to check out this past thread: Override a form in Django admin

How to detect type of form element in Django template?

I'm working on rendering a form template. The relevant code is something like this:
{% for field in filter.form %}
{% if field.is_hidden %}
{{ field }}
{% else %}
<div class="field">
{{ field }}
</div>
{% endif %}
{% endfor %}
So far, so good. If it's a hidden field, just display the field. If not, put a div wrapper with the class field to activate some CSS from the framework I'm using.
However, I need that class in the div wrapper to be picker if the field is a select box. It needs to be picker-multiple if it's a select multiple box. And so on.
Is this possible to do in the template view? We're working with a framework (which is why I don't want to just target the form fields differently with CSS), but we'd like the core code to work without the framework (which, I think, is why we wouldn't want to do this sort of thing in the separate Python file).
As for what I've tried, I noticed that {{ field.field.widget }} renders something like <django.forms.widgets.Select object at 0x10d822a50>. I would have then expected {{ field.field.widget.Select }} to render something (True came to mind), but it does nothing.
django-widget-tweaks includes field_type and widget_type template filters for you.
I believe you have to use a custom template tag as detailed here. This answer explains a similar issue with a solution using custom template tag.

Django tabular inline display

Is it possible to change the display of inlines in order to change it to this? I was thinking of changing the admin template file "tabular.html", is it going to be possible or should I change something else?
Update
Ok, I've been trying to edit the tabular.html but my experience with Django isn't enough to understand how/where to make the necessary changes... Any clue of where I should start?
Shouldn't I be changing the CSS also?
I guess that {{ field.field }} automatically renders the dropdown menu (Django admin default) if I'm understand this correctly...
Update 2
I was able to change the second column to the functionality that I wanted but I think that for the first one it's going to be trickier... Current status
Update 3
One hack that I think would work is to display on each of the inlines only one of the options of the first field and then deactivate the "add another option". How can I iterate on the options in "tabular.html" ?
Update 4
I guess the trick should be done here... How can I iterate on the field choices in order to display only one choice per line?
{% for fieldset in inline_admin_form %}
{% for line in fieldset %}
{% for field in line %}
{{ field.get_choices_display }}
<td class="{{ field.field.name }}">
{% if field.is_readonly %}
<p>{{ field.contents }}</p>
{% else %}
{{ field.field.errors.as_ul }}
{{ field.field }}
{% endif %}
</td>
{% endfor %}
{% endfor %}
{% endfor %}
Yes, you could change template of your InlineModelAdmin instance to your customized template, for example customized_inline.html. Simply copy django/django/contrib/admin/templates/admin/edit_inline/tabular.html to customized_inline.html in your template path as starting.
edit
Perhaps I was misunderstanding. If you want to change the rendering style of a form field, the normal way is to change its widget. In Django ModelAdmin and InlineModelAdmin, the main ways of customizing a field widget goes around BaseModelAdmin.formfield_for_dbfield method inside django/contrib/admin/options.py, reading the code and the doc when you want to change the widget of a form field.
For field having choices, you could simply set radio_fields in ModelAdmin/InlineModelAdmin instance to render the field as radio select instead of dropdown.
Furthermore, use OneToOneField instead of ForeignKey, or set extra and max_num in your InlineModelAdmin instance to prevent admin from rendering multiple rows of inline, like:
class SomeInlineAdmin(admin.TabularInline):
model = Foo
extra = 1
max_num = 1
I cannot open your second link, you could post things in the question instead of using a external link.

Control the form errors display while using {{ form.as_ul }} in Django templates.

I like the convenient output form method {{ form.as_ul }} but is there a way I can still continue to use it but capture all the errors upfront instead of displaying the error just above each field.
I understand that there are ways to loop through each form element and so on as mentioned in django docs but I want to continue to utilize the capability of form.as_ul() except get control over error display.
Solved this problem by using Reusable Form Templates.
Its simple...
Create a reusable template with the following code snippets based on your need.
If you want to display all errors right at the top of the form...
{% if form %}
{% if form.errors %}
{% for field in form %}
{{field.errors}}
{% endfor %}
{% endif %}
{% for field in form %}
<li>{{ field.label_tag }}: {{ field }}</li>
{% endfor %}
{% endif %}
If you want to display all errors right after each form field without the default html elements around error use...
{% for field in form %}
{{ field.label_tag }}: {{ field }}
{% for error in field.errors %}{{ error }}{% endfor %}
{% endfor %}
Used the second template and created a Inclusion Tag
The only way I see is to inherit the new form class from forms.Form and alter as_ul method as you like. Which isn't very good if you are going to use third-party forms like login form and so on (they won't have this method).
I think the best solution is to create your own inclusion tag and render form with it. It will be as short as as_ul ({% render_form form %}) but very flexible, it will work with all forms and won't mix HTML and Python code.
I still think the customization for rendering form in templates is quite flexible. I usually do this for my webapps. You may work with a bit javascript and css but not a big problem. Moreover, I think we should try to make the app simple.