Django - Render CheckboxSelectMultiple() widget individually in template (manually)

后端 未结 3 588
佛祖请我去吃肉
佛祖请我去吃肉 2020-12-13 02:39

I have those two models:

models.py

class App(models.Model):
    app_name = models.SlugField(max_length=50)
    options_loaded = models.ManyToManyFiel         


        
相关标签:
3条回答
  • 2020-12-13 02:53

    This is my simple solution: render CheckboxSelectMultiple() manually in template

    <table>
    <thead>
      <tr>
        <td>&nbsp;</td>
        <td>V</td>
        <td>S</td>
      </tr>
    </thead>    
    {% for pk, choice in form.options.field.widget.choices %}
    <tr>
      <td><a href="/link/{{ choice }}">{{ choice }}</a></td>
      <td>
        <label for="id_options_{{ forloop.counter0 }}">
          <input {% for m2moption in model.m2moptions.all %}{% if option.pk == pk %}checked="checked"{% endif %}{% endfor %} type="checkbox" id="id_options_{{ forloop.counter0 }}" value="{{ pk }}" name="options" />
        </label>
      </td>
    </tr>
    {% endfor %}                
    </table>
    
    0 讨论(0)
  • 2020-12-13 02:58

    http://dev.yaconiello.com/playground/example/one/

    Firstly, I'd restructure your models like so. The way you are currently set up, the option/app checkbox relationship would behave poorly. Each Option would only be able to have a single boolean checked value that it shared with ALL App objects.

    models

    from django.db import models
    from django.utils.translation import ugettext as _
    
    class Option(models.Model):
        condition = models.CharField(
            verbose_name = _(u'Condition Text'),
            max_length = 255,
        )
        option = models.CharField(
            verbose_name = _(u'Option Text'),
            max_length = 255,
        )
    
        def __unicode__(self):
            return self.condition
    
    
    class App(models.Model):
        title = models.CharField(
            verbose_name = _(u'App Name'), 
            max_length = 255
        )
        slug = models.SlugField(
            max_length = 50,
            unique = True
        )
        activated = models.BooleanField(
            verbose_name = _(u'Activated'),
            default = False,
        )
        options = models.ManyToManyField(
            Option,
            through="AppOption"
        )
    
        def __unicode__(self):
            return self.title
    
    
    class AppOption(models.Model):
        app = models.ForeignKey(
            App,
            verbose_name = _(u'App'), 
        )
        option = models.ForeignKey(
            Option,
            verbose_name = _(u'Option'), 
        )
        condition_activated = models.BooleanField(
            verbose_name = _(u'Condition Activated'),
            default = False,
        )
        option_activated = models.BooleanField(
            verbose_name = _(u'Option Activated'),
            default = False,
        )
    
        class Meta:
            unique_together = (("app", "option"),)
    
        def __unicode__(self):
            return "%s %s (%s | %s | %s)" % (self.app, self.option, self.app.activated, self.option_activated, self.condition_activated)
    

    secondly, you should use model formsets and model forms with custom logics inside...

    forms

    from django.forms.models import modelformset_factory
    from django import forms
    
    class AppOptionForm(forms.ModelForm):
        class Meta:
            model = AppOption
            fields = ("app", "option", "condition_activated", "option_activated")
    
    AppOptionFormSet = modelformset_factory(AppOption, form=AppOptionForm)
    
    class AppForm(forms.ModelForm):
        def __init__(self, *args, **kwargs):
            super(AppForm, self).__init__(*args, **kwargs)
    
            if self.instance:
                self.appoptions_prefix = "appoptions-%s"%self.instance.pk
                self.appoptions_formset = AppOptionFormSet(prefix=self.appoptions_prefix, 
                    queryset=AppOption.objects.filter(app=self.instance).order_by('option'))
    
        class Meta:
            model = App
            fields = ("id", "activated",)
    
    AppFormSet = modelformset_factory(App, form=AppForm)
    

    Ok so what just happened is we created a modelform for AppOption and then turned it into a modelformset.

    THEN, we created a modelform for App that has an overridden init method that instantiates an AppOption formset for the App model form's instance.

    Lastly, we created a modelformset using the App modelform.

    this is a view that saves all of the apps and appoptions

    def one(request):
        if request.method == 'POST':
            formset = AppFormSet(request.POST, prefix="apps") # do some magic to ALSO apply POST to inner formsets
            if formset.is_valid(): # do some magic to ALSO validate inner formsets
                for form in formset.forms:
                    # saved App Instances
                    form.save()
                    for innerform in form.appoptions_formset:
                        # saved AppOption instances
                        innerform.save()
        else:
            formset = AppFormSet(prefix="apps")
    
        options = Option.objects.all()
    
        return render(
            request,
            "playground/example/one.html",
            {
                'formset' : formset,
                'options' : options,
            }
        )
    

    template

    this is a test
    <style>
    thead td {
        width: 50px;
        height: 100px;
    }
    .vertical {
        -webkit-transform: rotate(-90deg);
        -moz-transform: rotate(-90deg);
        -ms-transform: rotate(-90deg);
        -o-transform: rotate(-90deg);
        filter: progid:DXImageTransform.Microsoft.BasicImage(rotation=3);
    }
    </style>
    <form>
    <table>
    <thead>
        <tr>
            <td>&nbsp;</td>
            <td><p class="vertical">Activate App</p></td>
            {% for option in options %}
            <td><p class="vertical">{{ option.condition }}</p></td>
            <td><p class="vertical">{{ option.option }}</p></td>
            {% endfor %}
        </tr>
    </thead>
    {% for form in formset.forms %}
        {% if form.instance.pk %}
        <tr>
            <td align="center">{{ form.instance.title }}{{ form.id.as_hidden }}</td>
            <td align="center">{{ form.activated }}</td>
            {% for optionform in form.appoptions_formset.forms %}
            {% if optionform.instance.pk %}
            <td align="center">
                {{ optionform.app.as_hidden }}
                {{ optionform.app.as_hidden }}
                {{ optionform.condition_activated }}
            </td>
            <td align="center">{{ optionform.option_activated }}</td>
            {% endif %}
            {% endfor %}
        </tr>
        {% endif %}
    {% endfor %}
    </table>
    </form>
    
    0 讨论(0)
  • 2020-12-13 03:10

    For those who came here looking to render CheckBoxMultipleSelect manually but in the standard way (the way Django does, using HTML lists), the following is what I came up with (@below-the-radar's solution helped me achieve it)

    <ul id="id_{{field.name}}">
      {% for pk, choice in field.field.widget.choices %}
        <li>
          <label for="id_{{field.name}}_{{ forloop.counter0 }}">
          <input id="id_{{field.name}}_{{ forloop.counter0 }}" name="{{field.name}}" type="checkbox" value="{{pk}}" />
          {{ choice }}
          </label>
        </li>
      {% endfor %}
    </ul>
    
    0 讨论(0)
提交回复
热议问题