我怎样才能在ModelForm中显示ForeignKey,而不是让它可编辑?

时间:2015-05-29 12:06:06

标签: python django modelform formset

我有以下模型和表格:

#models.py
class NetworkDevice(models.Model):
    user = models.ForeignKey(User)
    device_name = models.CharField(_('device name'), max_length=100)
    ...

#forms.py
class NetworkDevicesForm(ModelForm):
    class Meta:
        model = NetworkDevice
        fields=('user', 'device_name',...)

'...'是我遗漏的一些字段,因为它们对此并不重要。我想基于我的ModelForm创建一个formset:

#views.py
in some view:
    network_device_formset = modelformset_factory(models.NetworkDevice,  
         extra=0, form=NetworkDevicesForm, fields=(
         'user', 'device_name', ...))

我在模板中显示如下:

<form action="{% url 'some:action'  %}" method="post">
{% csrf_token %}
{{ devices_formset.management_form }}
<table>
{% for form in devices_formset %}
    {% if forloop.first %}
    <thead>
        <tr>
            {% for field in form.visible_fields %}
            <th>{{ field.label }}</th>
            {% endfor %}
        </tr>
    </thead>
    {% endif %}
{% endfor %}

    <tbody>
        {% for form in devices_formset %}
        <tr>
            {% for field in form %}
            <td>{{ field }}</td>
            {% endfor %}
        </tr>
        {% endfor %}
    </tbody>
</table>
<input type="submit" value='{% trans "Save" %}'/>
</form>

现在,这将显示带有HTML选择标记的ForeignKey。我甚至不想显示那里的所有选择。我只想显示相应实例的密钥。我可以禁用select标签:

class NetworkDevicesForm(ModelForm):
    class Meta:
        model = NetworkDevice
        fields=('user', 'device_name', ...more fields)
        widgets = {'user': widgets.Select(attrs={'readonly': True,
                                                          'disabled': True})}

但是我在验证用户字段时遇到错误。猜猜我可能会以某种方式覆盖验证。但是这仍然会在生成的html中显示外键的所有选项。我没有办法在模板中显示值而不在ModelForm中指定它,因为我不想编辑它。有些神奇的话:

    <tbody>
        {% for form in devices_formset %}
        <tr>
            <td>{{ form.user }}</td>
            {% for field in form %}
            <td>{{ field }}</td>
            {% endfor %}
        </tr>
        {% endfor %}
    </tbody>

除{{form.user}}不起作用。我可以在模板中以某种方式访问​​它吗?希望我清楚我想做什么,这是可能的。

3 个答案:

答案 0 :(得分:0)

您可以尝试这样的事情:

class NetworkDevicesForm(ModelForm):
    def __init__(self, *args, **kwargs):
        super(NetworkDevicesForm, self).__init__(*args, **kwargs)
        instance = getattr(self, 'instance', None)
        if instance and instance.pk:
            self.fields['pk'].widget.attrs['readonly'] = True

    def clean_pk(self):
        instance = getattr(self, 'instance', None)
        if instance and instance.pk:
            return instance.pk
        else:
            return self.cleaned_data['pk']

答案 1 :(得分:0)

好吧,我想我明白了。我将我的ModelForm改为:

class NetworkDevicesForm(ModelForm):

  readonlyUser = CharField(max_length=100 )

  class Meta:
      model = NetworkDevice
      fields=('readonlyUser', 'device_name')        

  def __init__(self, *args, **kwargs):
      super(NetworkDevicesForm, self).__init__(*args, **kwargs)
      if self.instance.user:
          self.initial['readonlyUser'] = self.instance.user.username
          self.fields['readonlyUser'].widget.attrs['readonly'] = True

不得不在这里使用readonly而不是禁用,否则验证不起作用,即使我不使用该字段。在视图中,我创建了我的modelformset:

network_device_formset = modelformset_factory(models.NetworkDevice, 
     extra=0, form=NetworkDevicesForm, fields=(
     'readonlyUser', 'device_name'))

这似乎做我想要的。没有触摸模板或模型定义。

答案 2 :(得分:0)

如果你经常使用它,你可以创建一个新的基类:

class SpanWidget(forms.Widget):
    '''Renders a value wrapped in a <span> tag.

    Requires use of specific form support. (see ReadonlyForm
    or ReadonlyModelForm)
    '''

    def render(self, name, value, attrs=None):
        final_attrs = self.build_attrs(attrs, name=name)
        return mark_safe(u'<span%s >%s</span>' % (
            forms.util.flatatt(final_attrs), self.display_value))

    def value_from_datadict(self, data, files, name):
        return self.original_value


class SpanField(forms.Field):
    '''A field which renders a value wrapped in a <span> tag.

    Requires use of specific form support. (see ReadonlyForm
    or ReadonlyModelForm)
    '''

    def __init__(self, *args, **kwargs):
        kwargs['widget'] = kwargs.get('widget', SpanWidget)
        super(SpanField, self).__init__(*args, **kwargs)
class Readonly(object):
    '''Base class for ReadonlyForm and ReadonlyModelForm which provides
    the meat of the features described in the docstings for those classes.
    '''

    class NewMeta:
        readonly = tuple()

    def __init__(self, *args, **kwargs):
        super(Readonly, self).__init__(*args, **kwargs)
        readonly = self.NewMeta.readonly
        if not readonly:
            return
        for name, field in self.fields.items():
            if name in readonly:
                field.widget = SpanWidget()
            elif not isinstance(field, SpanField):
                continue
            model_field = self.instance._meta.get_field_by_name(name)[0]
            field.widget.original_value = model_field.value_from_object(self.instance)
            field.widget.display_value = unicode(getattr(self.instance, name))


class ReadonlyForm(Readonly, forms.Form):
    '''A form which provides the ability to specify certain fields as
    readonly, meaning that they will display their value as text wrapped
    with a <span> tag. The user is unable to edit them, and they are
    protected from POST data insertion attacks.

    The recommended usage is to place a NewMeta inner class on the
    form, with a readonly attribute which is a list or tuple of fields,
    similar to the fields and exclude attributes on the Meta inner class.

        class MyForm(ReadonlyForm):
            foo = forms.TextField()
            class NewMeta:
                readonly = ('foo',)
    '''
    pass


class ReadonlyModelForm(Readonly, forms.ModelForm):
    '''A ModelForm which provides the ability to specify certain fields as
    readonly, meaning that they will display their value as text wrapped
    with a <span> tag. The user is unable to edit them, and they are
    protected from POST data insertion attacks.

    The recommended usage is to place a NewMeta inner class on the
    form, with a readonly attribute which is a list or tuple of fields,
    similar to the fields and exclude attributes on the Meta inner class.

        class Foo(models.Model):
            bar = models.CharField(max_length=24)

        class MyForm(ReadonlyModelForm):
            class Meta:
                model = Foo
            class NewMeta:
                readonly = ('bar',)
    '''
    pass

这是我在production中使用的代码:

class MembershipForm(ReadonlyModelForm):
    class Meta:
        model = Membership
        fields = ('user','board', 'privileged', 'alumni')

    class NewMeta:
        readonly = ('user')
    def email(self):
        return self.instance.user.email

回答你的问题:

#forms.py
class NetworkDevicesForm(ReadOnlyModelForm):
    class Meta:
        model = NetworkDevice
        fields=('user', 'device_name',...)
    class NewMeta:
        readonly=('user',)