2

The General Problem

Suppose you have a certain model

class MyModel(models.Model):
    Name = models.CharField(max_length=64)

And an accompaniying form you use for creation

class CreateMyModelForm(forms.ModelForm):
    class Meta:
        model = MyModel
        fields = ['name']

In a normal situation, when the form is rendered in a template I usually loop over the fields in the template

{%for field in form%}
{{field}}
{%endfor%}

When custom styling is needed, I usually have to resort to placing my <div>s and <span>s in that loop, and specify the base widget and its attrs in the Meta subclass.

Looking through the Django Documentation on the topic, I understand that it seems like I have to play with the Media subclass to make all the manipulation happen in the widgets property in the Meta, but I just can't wrap my head around it.

Specific Problem

Suppose I have a template where TextInput forms need to render as in this template:

<div class="form-group input-group">
    <span class="input-group-addon">Name</span>
        <input type="text" name="name" class="form-control" placeholder="MyModel Name">
</div>

How do I go about creating a widget so that I can get the same result by using:

class CreateMyModelForm(forms.ModelForm):
    class Meta:
        model = MyModel
        fields = ['name']
        widgets={'name':MyWidget(addon="Name",placeholder="MyModel Name")}

I understand that the placeholder is something I can put in the attrs initialization of TextInput, but the addon (or parent div, in this case) is not or at least not to my knowledge

I apologize for a long question, I just felt that I need to thoroughly explain this problem because it's been bothering me for a while...

Community
  • 1
  • 1
robotHamster
  • 609
  • 1
  • 7
  • 24
  • in `addon="Name"`, Name is supposed to go inside the span right? – Vaibhav Vishal Sep 24 '18 at 07:25
  • @VaibhavVishal Nope, you can find the rendered form [here](https://blackrockdigital.github.io/startbootstrap-sb-admin-2/pages/forms.html), I'm trying to make something to render fields in the "Input Groups" section – robotHamster Sep 24 '18 at 07:31
  • you will have to create div and span inside the for loop manually, you can use `{{ field.label }}` to put text dynamically inside span – Vaibhav Vishal Sep 24 '18 at 07:34
  • @VaibhavVishal That's what I have been doing so far, and it gets frustrating having to keep track with lots of HTML in an enterprise application. I'm hoping someone out here has a better solution! – robotHamster Sep 24 '18 at 07:40
  • there are ways to do what you trying to do, but that will require hacking into django and they will just complicate your code even more. – Vaibhav Vishal Sep 24 '18 at 07:44

1 Answers1

5

We can do like below.

Widget

from django import forms
from django.template import loader
from django.utils.safestring import mark_safe


class MyWidget(forms.Widget):
    template_name = 'widget_template.html'

    def get_context(self, name, value, attrs=None):
        return {'widget': {
            'name': name,
            'value': value,
        }}

    def render(self, name, value, attrs=None):
        context = self.get_context(name, value, attrs)
        template = loader.get_template(self.template_name).render(context)
        return mark_safe(template)

widget_template.html

<div class="form-group input-group">
    <span class="input-group-addon">Name</span>
        <input type="text" class="form-control" id="mywidget-{{ widget.name }}" name="{{ widget.name }}" />
</div>
anjaneyulubatta505
  • 10,713
  • 1
  • 52
  • 62
  • 1
    Note that widgets' HTML MUST have a name attribute, or else the Django admin will not be able to read their values, and they will just be silently excluded from the form submission. – Alan Porter Aug 05 '20 at 16:40