Django - 将两个模型之间的关系渲染为单选按钮

时间:2012-03-17 10:58:08

标签: django django-models django-forms

我有以下型号:

class Profile(models.Model):
    verified = models.BooleanField(default=False)
    primary_phone = models.OneToOneField('Phone', related_name='is_primary', null=True, blank=True)

class Phone(models.Model):
    profile = models.ForeignKey(Profile)
    type = models.CharField(choices=PHONE_TYPES, max_length=16)
    number = models.CharField(max_length=32)

    @property
    def is_primary(self):
        return profile.primary_phone == self

以下形式:

class PhoneForm(ModelForm):
    class Meta:
        from accounts.models import Phone
        model = Phone
        fields = ('type', 'number', )

用于modelformset_factory

我正在渲染这样的formset:

<div class="span-13 last">
    {{ formset.management_form }}
    {% for form in Phones %}
        <div class="span-2">{{ form.type|add_class:'dropdown' }}</div>
        <div class="span-11 last">{{ form.number|add_class:'phone-number' }}</div>
        <div class="clearfix"></div>
    {% endfor %}
</div>

现在我要做的是在模板中渲染一个单选按钮,以反映is_primary模型的Phone属性。有两种方法可以通过Phone模型本身或Profile.primary_phone来确定这种关系。但后来我将Phone模型渲染为formset,因此循环遍历其实例,因此我尝试在'is_primary'字段中包含PhoneForm,但由于它是属性,因此无效。

知道怎么做吗?

更新#1:

我使用了jpic方法并尝试将primary渲染为单选按钮:

class PhoneForm(ModelForm):
    primary = forms.BooleanField(widget=forms.RadioSelect( choices=((0, 'False'), (1, 'True')) ))

    class Meta:
        from accounts.models import Phone
        model = Phone
        fields = ('primary', 'type', 'number', )

但是,它为每个Phone实例显示两个单选按钮,而我需要它每个实例只显示一个单选按钮。我打算玩一会儿,看看我能不能正确显示它。

1 个答案:

答案 0 :(得分:2)

而不是:

class Profile(models.Model):
    verified = models.BooleanField(default=False)
    primary_phone = models.OneToOneField('Phone', related_name='is_primary', null=True, blank=True)

class Phone(models.Model):
    profile = models.ForeignKey(Profile)
    type = models.CharField(max_length=16)
    number = models.CharField(max_length=32)

你应该:

class Profile(models.Model):
    verified = models.BooleanField(default=False)

    def primary_phone(self):
        return self.phone_set.get(primary=True)

class Phone(models.Model):
    profile = models.ForeignKey(Profile)
    type = models.CharField(max_length=16)
    number = models.CharField(max_length=32)
    primary = models.BooleanField(default=False)

    def save(self, force_insert=False, force_update=False, using=None):
        if self.primary:
            # clear the primary attribute of other phones of the related profile
            self.profile.phone_set.update(primary=False)
        self.save(force_insert, force_update, using)

这会让你的生活更轻松。

如果您无法进行此更改:Phone formset实际上是许多Phone表单的包装器。但是你所追求的领域允许编辑Profile.primary_phone。

所以一种方法是手动完成:

{% for form in Phones %}
    <input type="radio" name="primary_phone" checked="{% if form.instance == profile.primary_phone %}checked{% endif %}" value="{{ form.instance.pk }}" />
    <!-- snip ... ->

但问题是收音机没有空电话表单的值,因为值为{{ form.instance.pk }}

另一种方法是在PhoneForm中添加一个复选框:

from django import forms

from accounts.models import Phone

class PhoneForm(forms.ModelForm):
    primary = forms.BooleanField(required=False, default=False)

    class Meta:
        model = Phone
        fields = ('type', 'number', )

我们在这里使用的是BooleanField,因为对于每个Phone表单,都要设置primary。但是,你仍然必须自己渲染它:

{% for form in Phones %}
    <input type="checkbox" name="{{ form.prefix }}-primary" checked="{% if form.instance == profile.primary_phone %}checked{% endif %}" value="true" />
    <!-- snip ... ->

但是,你需要javascript来确保当时只检查一个无线电,例如用jQuery:

$('input[type=checkbox]').change(function() {
    $('input[type=checkbox][checked=checked]').attr('checked', '');
    $(this).attr('checked', 'checked');
});

当然,您应该更新上面示例中的选择器,以确保只有“主要电话”复选框受到影响。

最后,要连接复选框,这样的内容可能会起作用:

class PhoneForm(forms.ModelForm):
    primary = forms.BooleanField(required=False)

    def __init__(self, *args, **kwargs):
        super(PhoneForm, self).__init__(*args, **kwargs)

        if self.instance.is_primary:
            self.data['primary'] = True

    def save(self, *args, **kwargs):
        super(PhoneForm, self).save(*args, **kwargs)

        if self.cleaned_data['primary']:
            self.profile.primary_phone = self
            self.profile.save()