2015-05-29 50 views
0

我有以下型號及形式:如何才能在ModelForm中顯示ForeignKey,而不是使其可編輯?

#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> 

現在,這將顯示我的ForeignKey的使用HTML 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}}不工作。我可以在模板中以某種方式訪問​​嗎?希望我清楚自己想做什麼,這是可能的。

+0

用戶在'NetworkDevice'創建時預定義了用戶,還是從中獲取請求? – zymud

+0

嘗試只讀= '只讀' 看到這一點:http://stackoverflow.com/questions/16109358/what-is-the-correct-readonly-attribute-syntax-for-input-text-elements –

+0

用戶是在模型中預定義 – user3917718

回答

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'] 
+0

我認爲這仍然會爲選擇標籤生成不同的選項。我不想那樣。我嘗試只讀,但它似乎不影響選擇標記。當我使用diabled驗證失敗。 – user3917718

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 

不得不只讀這裏,而不是殘疾人使用,否則驗證不起作用,即使我不使用該字段。並在視圖中創建我的modelformset:

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

這似乎是做我想做的。沒有觸摸模板或模型定義。

+0

ModelForm不是這樣嗎?如果我沒有在字段中指定'用戶',那麼當表單被保存時,如果我正確地閱讀了文檔,則不應該修改它。我只是嘗試修改它客戶端,它並沒有保存。 – user3917718

+0

絕對正確。 –

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',) 
相關問題