I need to render a radio button group without UL. In Django 1.9, I used this answer, and I use this in many places. I'm upgrading to Django 1.11, and RadioFieldRenderer is no longer supported. How can I accomplish what I'm doing right now in Django 1.11?
This is what I ended up doing in my template as per dirkgroten's answer
{% with id=widget.attrs.id %}
{% for group, options, index in widget.optgroups %}
{% for option in options %}
{% include option.template_name with widget=option %}
{% endfor %}
{% endfor %}
{% endwith %}
In Django 1.11, the way widgets are defined has changed a lot. But it makes it easier to customise. The RadioSelect widget uses the multiple_input.html template. Your best option to remove the <ul> is to do the following:
Subclass RadioSelect to use your own template:
class MyRadioSelect(RadioSelect):
template_name = 'my_widgets/radio.html'
Copy the multiple_input.html (that's the standard django template) code into your own template ("my_widgets/radio.html") and adjust everything the way you want it. You can use <div>s instead of <ul> and <li> or whatever you want.
In your form, use your own widget:
options = ChoiceField(widget=MyRadioSelect)
I guess you already have a form.py, where you store your forms.
In the respective form and field that you want to render as radio buttons, make it like this:
choices_field = forms.ChoiceField(
widget=forms.RadioSelect(attrs={'class': 'some-class-here'})
)
The important thing is to use the RadioSelect widget, either in a ChoiceField, ModelChoiceField etc.
Related
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
We have a Django project that has many (100s) of forms across it. We would like to apply Kendo UI styling (http://demos.telerik.com/kendo-ui/styling/index) to all of our forms. This pretty much entails adding classes to the appropriate elements in order to get the styling.
What's the best technique to achieve this result? I've found two main approaches, each with their drawbacks:
1 - Create a "Kendo Form" Django template. This template would have tons of logic in it for each type of possible field. However, this would probably be the easier of the two options for application authors to use (just include the template on their pages). Would we also lose functionality like .as_p() and .as_table()?
{% for hidden in form.hidden_fields %}
{{ hidden }}
{% endfor %}
{% for field in form.visible_fields %}
{% if field|is_choice %}
<stuff class="niftyKendoClass"></stuff>
{% elif field|is_radio %}
<stuff class="niftyKendoClass"></stuff>
......
{% endif %}
{% endfor %}
2 - Create custom "Kendo Widgets" by subclassing Django's widgets (https://docs.djangoproject.com/en/1.9/ref/forms/widgets). From what I can tell, implementation of this technique would be easier than the template since I just need CSS classes and can reuse Django's markup in most cases, but it becomes harder on the caller as their form declaration becomes heavier.
class SomeForm(forms.Form):
field = forms.CharField(widget=forms.KendoTextInput())
Of course, both of these options are to avoid scattered classes across all of the Django applications, which are very painful if we ever change styling for forms.
class SomeForm(forms.Form):
field = forms.CharField(
widget=forms.TextInput(
attrs={'class': 'niftyKendoClass'}
)
)
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.
If I have a model that contains a ChoiceField with a RadioSelect widget, how can I render the radio buttons separately in a template?
Let's say I'm building a web app that allows new employees at a company to choose what kind of computer they want on their desktop. This is the relevant model:
class ComputerOrder(forms.Form):
name = forms.CharField(max_length=50)
office_address = forms.Charfield(max_length=75)
pc_type = forms.ChoiceField(widget=RadioSelect(), choices=[(1, 'Mac'), (2, 'PC')])
On the template, how do I render just the Mac choice button? If I do this, it renders all the choices:
{{ form.pc_type }}
Somewhat naively I tried this, but it produced no output:
{{ form.pc_type.0 }}
(I found a few similar questions here on SO:
In a Django form, how do I render a radio button so that the choices are separated on the page?
Django Forms: How to iterate over a Choices of a field in Django form
But I didn't feel like they had good answers. Is there a way to resurrect old questions?)
Django 1.4+ allows you to iterate over the choices in a RadioSelect, along with the lines of
{% for choice in form.pc_type %}
{{ choice.choice_label }}
<span class="radio">{{ choice.tag }}</span>
{% endfor %}
I'm not sure if this change allows you to use the syntax you describe ({{ form.pc_type.0 }}) — if not, you could work around this limitation with the for loop above and a tag like {% if forloop.counter0 == 0 %}.
If you're tied to Django < 1.4, you can either override the render() method as suggested or go with the slightly-more-verbose-but-less-complicated option of building up the form field yourself in the template:
{% for choice in form.pc_type.field.choices %}
<input name='{{ form.pc_type.name }}'
id='{{ form.pc_type.auto_id }}_{{ forloop.counter0 }}' type='radio' value='{{ choice.0 }}'
{% if not form.is_bound %}{% ifequal form.pc_type.field.initial choice.0 %} checked='checked' {% endifequal %}
{% else %}{% ifequal form.pc_type.data choice.0 %} checked='checked' {% endifequal %}{% endif %}/>
<label for='{{ form.pc_type.auto_id }}_{{ forloop.counter0 }}'>{{ choice.1 }}</label>
{% endfor %}
(choice.0 and choice.1 are the first and second items in your choices two-tuple)
The rendering of the individual radio inputs is handled by the RadioSelect widget's render method. If you want a different rendering, subclass RadioSelect, change the render method accordingly, and then use your subclass as the field's widget.
I think the simply looking at what's available inside the for loop of a choice field will tell one what they need to know. For example, I needed the value to set a class surrounding the span of the option (for colors and such):
<div>
{% for radio_input in form.role %}
{# Skip the empty value #}
{% if radio_input.choice_value %}
<span class="user-level {{ radio_input.choice_value }}">{{ radio_input }}</span>
{% endif %}
{% endfor %}
</div>
There are several attributes as you can see that keep you from having to use the ordinal.
In Django 2.0+ you can subclass forms.RadioSelect and "simply" specify a template for rendering the radio fields:
class SlimRadioSelect(forms.RadioSelect):
template_name = 'includes/slim_radio.html'
where slim_radio.html contains a revised version of the combined template_name and option_template_name used by the default RadioSelect widget.
Note, the default RadioSelect widget template is low-level rendering and consists of heavily layered templates: include, conditional and loop logic tags abound.
You'll know you've arrived when you're digging around in packages/django/forms/templates/django/forms/widgets/input.html to get what you need.
One other oddity for overriding the default widget's template is that you must invoke the TemplatesSetting renderer or your subclass won't be able to find slim_radio.html in your project's normally accessible template paths.
To override RadioSelect's local-only template path lookup:
Add 'django.forms' to your INSTALLED_APPS;
Add FORM_RENDERER = 'django.forms.renderers.TemplatesSetting' to your settings.py.
This all seems harder than it should be, but that's frameworks. Good luck.
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.