Django 如何将中间表内联*呈现为*多回音ICE

Django 如何将中间表内联*呈现为*多回音ICE,django,django-forms,many-to-many,m2m,Django,Django Forms,Many To Many,M2m,我有以下几种型号 ### models.py class Foo(models.Model): name = models.CharField(max_length=200) class Bar(models.Model): foo = models.ForeignKey(Foo) baz = models.ManyToManyField(Baz, through='Between') class Baz(models.Model): name = mode

我有以下几种型号

### models.py

class Foo(models.Model):
    name = models.CharField(max_length=200)

class Bar(models.Model):
    foo = models.ForeignKey(Foo)
    baz = models.ManyToManyField(Baz, through='Between')

class Baz(models.Model):
    name = models.CharField(max_length=200)

class Between(models.Model):
    foo = models.ForeignKey(Foo)
    bar = models.ForeignKey(Bar)
    CHOICES = (
        ('A', 'A'),
        ('B', 'B'),
        ('C', 'C'),
        )
    value = models.CharField(max_length=1, choices=CHOICES)
我有以下表格

### forms.py
class FooForm(forms.ModelForm):
    class Meta:
        model = Foo

class BarForm(forms.ModelForm):
    baz = forms.ModelMultipleChoiceField(widget=forms.CheckboxSelectMultiple(),
                                         queryset=Baz.objects.all())
    class Meta:
        model = Bar
        exclude = ('foo',)


BarFormSet = inlineformset_factory(Foo, Bar, form=BarForm, can_delete=False)
现在,这非常有效,因为我可以渲染单个
Foo
,并且我可以得到许多
Bar
的内联表单。这是因为内联
条形图
Baz
的所有选项呈现为复选框

我希望将
Baz
的每条记录呈现为一组单选按钮,代表
value
的可能选项以及一个“N/a”选项,这样,如果选择了a、B或C,则暗示与
Baz
的关系。但默认情况下,完全重新实现RadioSelect或实现一个全新的小部件似乎不是一个很好的方法,但我希望遵循阻力最小的路径


希望我能把事情弄清楚。

我的解决方案是创建允许子表单的
超级表单。总体解决方案对每个人来说都不太通用,但我在这里尽了最大的努力来使用我所使用的,并使其更通用一些

下面是
models.py

from django.db import models

class Enemy(models.Model):
    name = models.CharField(max_length=200)
    def __unicode__(self):
        return self.name

class Hero(models.Model):
    name = models.CharField(max_length=200)
    enemy = models.ManyToManyField(Enemy, through='Relationship', blank=False, null=False)
    def __unicode__(self):
        return self.name

class Relationship(models.Model):
    hero = models.ForeignKey(Hero)
    enemy = models.ForeignKey(Enemy)
    ENEMY_TYPE_CHOICES = (
        ('G', 'Good'),
        ('B', 'Bad'),
        ('U', 'Ugly'),
        )
    enemy_type = models.CharField(max_length=1, choices=ENEMY_TYPE_CHOICES)

    def __unicode__(self):
        return u"{0} {1} {2}".format(self.hero, self.strength, self.relationship)
from models import *
from django import forms
from django.forms.formsets import formset_factory, BaseFormSet
from django.forms.models import inlineformset_factory, BaseModelFormSet
from django.utils.safestring import mark_safe


class SuperForm(forms.ModelForm):
    def __init__(self, *args, **kwargs):
        self.formsets = []
        super(SuperForm, self).__init__(*args, **kwargs)

    @property
    def is_bound(self):
        return self._is_bound or \
            any([subformset.is_bound for subformset in self.formsets])

    @is_bound.setter
    def is_bound(self, value):
        self._is_bound = value

    def has_changed(self):
        return bool(self.changed_data) or \
            any([subformset.has_changed() for subformset in self.formsets])

    def update_empty_permitted(self, subformset):
        empty_permitted = not self.has_changed()
        for subformset in self.formsets:
            for form in subformset:
                form.empty_permitted = empty_permitted

    def is_valid(self):
        subforms_valid = True
        for subformset in self.formsets:
            self.update_empty_permitted(subformset)
            sfserrors = [err for err in subformset.errors if err]
            if bool(sfserrors):
                subforms_valid = False

        return subforms_valid and super(SuperForm, self).is_valid()

class RelationshipForm(forms.ModelForm):
    enemy = forms.ModelChoiceField(queryset=Enemy.objects.all(),
                                   widget=forms.HiddenInput(),
                                   required=True)
    enemy_type = forms.ChoiceField(label="", widget=forms.RadioSelect,
                                 choices=Relationship.ENEMY_TYPE_CHOICES,
                                 required=True)
    def name(self):
        pk = self['enemy'].value()
        return self.fields['enemy'].queryset.get(pk=pk)

    class Meta:
        model = Relationship
        exclude = ('hero', 'enemy_type',)


RelationshipFormSet = formset_factory(RelationshipForm, extra=0, can_delete=False)

class HeroForm(SuperForm):
    def __init__(self, *args, **kwargs):
        super(HeroForm, self).__init__(*args, **kwargs)

        initial=[dict(enemy=enemy.pk) for enemy in Enemy.objects.all()]
        self.relationship_formset = RelationshipFormSet(initial=initial, prefix=self.prefix, data=kwargs.get('data'))
        self.formsets.append(self.relationship_formset)

    class Meta:
        model = Hero
        exclude = ('enemy',)

HeroFormSet = formset_factory(HeroForm, extra=1, can_delete=False)
from django.views.generic import TemplateView
from django.views.generic.edit import FormMixin
from forms import *

class HeroView(FormMixin, TemplateView):
    template_name = "formfun/hero.html"
    form_class = HeroFormSet

    def get_context_data(self, **kwargs):
        context = super(HeroView, self).get_context_data(**kwargs)
        context['formset'] = HeroFormSet()
        return context

    def form_valid(self, form):
        return render_to_response({'form':form, 'valid': True})

    def form_invalid(self, form):
        return render_to_response({'form':form, 'valid': False})
这是我的
表单中的内容。py

from django.db import models

class Enemy(models.Model):
    name = models.CharField(max_length=200)
    def __unicode__(self):
        return self.name

class Hero(models.Model):
    name = models.CharField(max_length=200)
    enemy = models.ManyToManyField(Enemy, through='Relationship', blank=False, null=False)
    def __unicode__(self):
        return self.name

class Relationship(models.Model):
    hero = models.ForeignKey(Hero)
    enemy = models.ForeignKey(Enemy)
    ENEMY_TYPE_CHOICES = (
        ('G', 'Good'),
        ('B', 'Bad'),
        ('U', 'Ugly'),
        )
    enemy_type = models.CharField(max_length=1, choices=ENEMY_TYPE_CHOICES)

    def __unicode__(self):
        return u"{0} {1} {2}".format(self.hero, self.strength, self.relationship)
from models import *
from django import forms
from django.forms.formsets import formset_factory, BaseFormSet
from django.forms.models import inlineformset_factory, BaseModelFormSet
from django.utils.safestring import mark_safe


class SuperForm(forms.ModelForm):
    def __init__(self, *args, **kwargs):
        self.formsets = []
        super(SuperForm, self).__init__(*args, **kwargs)

    @property
    def is_bound(self):
        return self._is_bound or \
            any([subformset.is_bound for subformset in self.formsets])

    @is_bound.setter
    def is_bound(self, value):
        self._is_bound = value

    def has_changed(self):
        return bool(self.changed_data) or \
            any([subformset.has_changed() for subformset in self.formsets])

    def update_empty_permitted(self, subformset):
        empty_permitted = not self.has_changed()
        for subformset in self.formsets:
            for form in subformset:
                form.empty_permitted = empty_permitted

    def is_valid(self):
        subforms_valid = True
        for subformset in self.formsets:
            self.update_empty_permitted(subformset)
            sfserrors = [err for err in subformset.errors if err]
            if bool(sfserrors):
                subforms_valid = False

        return subforms_valid and super(SuperForm, self).is_valid()

class RelationshipForm(forms.ModelForm):
    enemy = forms.ModelChoiceField(queryset=Enemy.objects.all(),
                                   widget=forms.HiddenInput(),
                                   required=True)
    enemy_type = forms.ChoiceField(label="", widget=forms.RadioSelect,
                                 choices=Relationship.ENEMY_TYPE_CHOICES,
                                 required=True)
    def name(self):
        pk = self['enemy'].value()
        return self.fields['enemy'].queryset.get(pk=pk)

    class Meta:
        model = Relationship
        exclude = ('hero', 'enemy_type',)


RelationshipFormSet = formset_factory(RelationshipForm, extra=0, can_delete=False)

class HeroForm(SuperForm):
    def __init__(self, *args, **kwargs):
        super(HeroForm, self).__init__(*args, **kwargs)

        initial=[dict(enemy=enemy.pk) for enemy in Enemy.objects.all()]
        self.relationship_formset = RelationshipFormSet(initial=initial, prefix=self.prefix, data=kwargs.get('data'))
        self.formsets.append(self.relationship_formset)

    class Meta:
        model = Hero
        exclude = ('enemy',)

HeroFormSet = formset_factory(HeroForm, extra=1, can_delete=False)
from django.views.generic import TemplateView
from django.views.generic.edit import FormMixin
from forms import *

class HeroView(FormMixin, TemplateView):
    template_name = "formfun/hero.html"
    form_class = HeroFormSet

    def get_context_data(self, **kwargs):
        context = super(HeroView, self).get_context_data(**kwargs)
        context['formset'] = HeroFormSet()
        return context

    def form_valid(self, form):
        return render_to_response({'form':form, 'valid': True})

    def form_invalid(self, form):
        return render_to_response({'form':form, 'valid': False})
以下是
视图.py

from django.db import models

class Enemy(models.Model):
    name = models.CharField(max_length=200)
    def __unicode__(self):
        return self.name

class Hero(models.Model):
    name = models.CharField(max_length=200)
    enemy = models.ManyToManyField(Enemy, through='Relationship', blank=False, null=False)
    def __unicode__(self):
        return self.name

class Relationship(models.Model):
    hero = models.ForeignKey(Hero)
    enemy = models.ForeignKey(Enemy)
    ENEMY_TYPE_CHOICES = (
        ('G', 'Good'),
        ('B', 'Bad'),
        ('U', 'Ugly'),
        )
    enemy_type = models.CharField(max_length=1, choices=ENEMY_TYPE_CHOICES)

    def __unicode__(self):
        return u"{0} {1} {2}".format(self.hero, self.strength, self.relationship)
from models import *
from django import forms
from django.forms.formsets import formset_factory, BaseFormSet
from django.forms.models import inlineformset_factory, BaseModelFormSet
from django.utils.safestring import mark_safe


class SuperForm(forms.ModelForm):
    def __init__(self, *args, **kwargs):
        self.formsets = []
        super(SuperForm, self).__init__(*args, **kwargs)

    @property
    def is_bound(self):
        return self._is_bound or \
            any([subformset.is_bound for subformset in self.formsets])

    @is_bound.setter
    def is_bound(self, value):
        self._is_bound = value

    def has_changed(self):
        return bool(self.changed_data) or \
            any([subformset.has_changed() for subformset in self.formsets])

    def update_empty_permitted(self, subformset):
        empty_permitted = not self.has_changed()
        for subformset in self.formsets:
            for form in subformset:
                form.empty_permitted = empty_permitted

    def is_valid(self):
        subforms_valid = True
        for subformset in self.formsets:
            self.update_empty_permitted(subformset)
            sfserrors = [err for err in subformset.errors if err]
            if bool(sfserrors):
                subforms_valid = False

        return subforms_valid and super(SuperForm, self).is_valid()

class RelationshipForm(forms.ModelForm):
    enemy = forms.ModelChoiceField(queryset=Enemy.objects.all(),
                                   widget=forms.HiddenInput(),
                                   required=True)
    enemy_type = forms.ChoiceField(label="", widget=forms.RadioSelect,
                                 choices=Relationship.ENEMY_TYPE_CHOICES,
                                 required=True)
    def name(self):
        pk = self['enemy'].value()
        return self.fields['enemy'].queryset.get(pk=pk)

    class Meta:
        model = Relationship
        exclude = ('hero', 'enemy_type',)


RelationshipFormSet = formset_factory(RelationshipForm, extra=0, can_delete=False)

class HeroForm(SuperForm):
    def __init__(self, *args, **kwargs):
        super(HeroForm, self).__init__(*args, **kwargs)

        initial=[dict(enemy=enemy.pk) for enemy in Enemy.objects.all()]
        self.relationship_formset = RelationshipFormSet(initial=initial, prefix=self.prefix, data=kwargs.get('data'))
        self.formsets.append(self.relationship_formset)

    class Meta:
        model = Hero
        exclude = ('enemy',)

HeroFormSet = formset_factory(HeroForm, extra=1, can_delete=False)
from django.views.generic import TemplateView
from django.views.generic.edit import FormMixin
from forms import *

class HeroView(FormMixin, TemplateView):
    template_name = "formfun/hero.html"
    form_class = HeroFormSet

    def get_context_data(self, **kwargs):
        context = super(HeroView, self).get_context_data(**kwargs)
        context['formset'] = HeroFormSet()
        return context

    def form_valid(self, form):
        return render_to_response({'form':form, 'valid': True})

    def form_invalid(self, form):
        return render_to_response({'form':form, 'valid': False})
这是我渲染时的模板:

{% extends "base.html" %}
{% block title %}form fun{% endblock %}

{% block content %}
<form>
  {% for form in formset %}
  {{ form.as_p }}
  {% for subform in form.relationship_formset %}
  <label for="id_{{ subform.html_name }}_0" class="label50">{{ subform.name }}</label>
  {{ subform.as_p }}
  {% endfor %}
  {% endfor %}
</form>
{% endblock content %}
{%extends“base.html”%}
{%block title%}表单乐趣{%endblock%}
{%block content%}
{formset%中表单的%s}
{{form.as_p}}
{form.relationship_formset%}中的子窗体为%
{{subform.name}
{{subform.as_p}}
{%endfor%}
{%endfor%}
{%endblock内容%}

单选按钮不允许多选。它们可以与简单的
(模型)选项字段一起使用,是的,我理解。但必须有办法做到这一点。我相信SuperForms.py不久前就在尝试这样做?这是浏览器行为。你为什么要打破它?我想你误解了。请注意,我正在使用queryset执行一个
modelmultipechoicefield
w/queryset。这意味着查询集中的每个元素都将得到一个复选框。但我不想要复选框,因为我的M2M使用的是一个
表。我想要的是用一组单选按钮替换每个复选框,根据它们的选择,这些单选按钮将暗示一个复选标记,并通过
字段为
提供信息。好的,那么您需要在其中添加另一个表单集。您不能通过带有一个字段的
模型使用
(或者应该手动构造
多字段的祖先并手动处理它)。