2013-02-04 97 views
4

我有一個TextField,其中的文本來自admin中的txt文件。我的txt有換行符。問題在於TextField處於readonly_fields中時,所有換行符便宜且所有內容都分組。 如何在readonly_fields模式下使用此字段的格式? 當不在readonly_fields時,問題不會發生。 謝謝!使用admin中的readonly_fields保留TextField中的格式文本

回答

1

的文本中的換行符通常由字符\n\r或經常\r\n(看看這個article on wikipedia獲取更多信息)表示。

您遇到的問題是這些字符將用於在文本編輯字段中顯示新行,但它們不代表html中的新行(它們將被忽略)。

如果您希望它們顯示在只讀字段中,那麼您可以用<br/>元素替換它們。

如果您可以將您的字符串標記爲safe(即,如果您可以安全地添加html代碼而沒有任何人使用該字段添加惡意代碼的風險),那麼您可以覆蓋模型上的保存方法以換出文本行減免HTML換行符 -

from django.utils.safestring import mark_safe 

def save(self, *args, **kwargs): 
    self.text_field = mark_safe(self.text_field.replace("\n", "<br/>")) 
    super(YourModel, self).save(*args, **kwargs) 

另一種方法是使用一個插件添加全文格式化功能,例如django-tinymce

我最後的建議是用javascript來破解它。將admin文件夾添加到您的模板中,然後創建一個base_site.html文件,該文件擴展了原始文件並添加了一個簡單的javascript函數(如here所述)。喜歡的東西 -

{% extends "admin/base.html" %} 

{% block extrahead %} 
    <script type="text/javascript"> 
     window.onload = function() { 
      var p_elements = document.getElementById('content-main').getElementsByTagName('p'); 
      var unixNewLine = new RegExp("\n", "g"); 
      for (var i = p_elements.length - 1; i >= 0; i--) { 
       p_elements[i].innerHTML = p_elements[i].innerHTML.replace(unixNewLine, '<br/>'); 
      } 
     } 
    </script> 
{% endblock %} 

你需要添加一個replace對每種類型的新線在你的文字(例如\r\r\n)有。雖然這可能會做你所需要的,但它似乎是最糟糕的黑客。

+0

如果你保存br標籤(它改變了數據),標準textarea不再有換行符,並且像django-tinymce這樣的編輯器變得更加必要。如果你需要真正的換行符,你在另一個方向上面臨同樣的問題... –

+0

是的,這是一個很好的觀點。我認爲解決方案實際上取決於數據的用途。 –

+0

感謝您的答案,但txt文件的內容是由自定義命令編寫的。使用JavaScript也失敗了。任何其他想法?謝謝! – LinuxMan

2

當您查看頁面的源代碼時,會看到換行符。該空格在瀏覽器中顯示爲單個空格。您需要將所有換行符(\n)轉換爲HTML換行符(<br />)以使其看起來像您想要的樣子。

選項1:jQuery來拯救。

事情是這樣的:

<script type="text/javascript"> 
     (function($) { 

     $(document).ready(function() { 
     // Adjustments for read-only fields: 
     // a) Convert quoted HTML entities back to HTML 
      $('.readonly').each(function() { 
       // Ensure there isn't valid html in the field 
       // The RegEx checks for any valid html opening tag 
       {% comment %} 
       TODO: It would be better to check against a special class name 
       on the widget 
       {% endcomment %} 
       if ($(this).html().match(/<(\w+)((?:\s+\w+(?:\s*=\s*(?:(?:"[^"]*")|(?:'[^']*')|[^>\s]+))?)*)\s*(\/?)>/) == null) { 
        $(this).html($(this).text()); 
        $('ul', this).addClass('with_bullet'); 
        $('li', this).addClass('with_bullet'); 
       } 
      }); 

      // b) Insert &nbsp; into empty <p>'s (m2m fields) so they don't break layout 
      // (see comment on text nodes: http://api.jquery.com/empty-selector/) 
      $('p.readonly:empty').each(function() { $(this).html('&nbsp;') }) 
     }); 
     })(django.jQuery); 
    </script> 

(注:我們增加了「with_bullet」類,因爲我們使用格拉佩利和UL的李的get風格沒有子彈(列表樣式類型:無)等等這是使它們重新出現在我們自己的CSS中的一種方式...) 還要注意最後的佈局修復,我認爲這在後來的grappelli版本中不需要。

選項2:猴補丁django.contrib.admin.helpers.AdminReadonlyField:

from django.contrib.admin import helpers 
from django.contrib.admin.util import (lookup_field, 
    display_for_field, label_for_field, help_text_for_field) 
from django.core.exceptions import ObjectDoesNotExist 
from django.db.models.fields.related import ManyToManyRel 
from django.forms.util import flatatt 
from django.template.defaultfilters import capfirst 
from django.utils.encoding import force_unicode, smart_unicode 
from django.utils.html import escape, conditional_escape 
from django.utils.safestring import mark_safe 


class BetterAdminReadonlyField(object): 
    def __init__(self, form, field, is_first, model_admin=None): 
     label = label_for_field(field, form._meta.model, model_admin) 
     # Make self.field look a little bit like a field. This means that 
     # {{ field.name }} must be a useful class name to identify the field. 
     # For convenience, store other field-related data here too. 
     if callable(field): 
      class_name = field.__name__ != '<lambda>' and field.__name__ or '' 
     else: 
      class_name = field 
     self.field = { 
      'name': class_name, 
      'label': label, 
      'field': field, 
      'help_text': help_text_for_field(class_name, form._meta.model) 
     } 
     self.form = form 
     self.model_admin = model_admin 
     self.is_first = is_first 
     self.is_checkbox = False 
     self.is_readonly = True 

    def label_tag(self): 
     attrs = {} 
     if not self.is_first: 
      attrs["class"] = "inline" 
     label = self.field['label'] 
     contents = capfirst(force_unicode(escape(label))) + u":" 
     return mark_safe('<label%(attrs)s>%(contents)s</label>' % { 
      "attrs": flatatt(attrs), 
      "contents": contents, 
     }) 

    def contents(self): 
     from django.contrib.admin.templatetags.admin_list import _boolean_icon 
     from django.contrib.admin.views.main import EMPTY_CHANGELIST_VALUE 
     field, obj, model_admin = self.field['field'], self.form.instance, self.model_admin 
     try: 
      f, attr, value = lookup_field(field, obj, model_admin) 
     except (AttributeError, ValueError, ObjectDoesNotExist): 
      result_repr = EMPTY_CHANGELIST_VALUE 
     else: 
      if f is None: 
       boolean = getattr(attr, "boolean", False) 
       if boolean: 
        result_repr = _boolean_icon(value) 
       else: 
        result_repr = smart_unicode(value) 
        if getattr(attr, "allow_tags", False): 
         result_repr = mark_safe(result_repr) 
      else: 
       if value is None: 
        result_repr = EMPTY_CHANGELIST_VALUE 
       elif isinstance(f.rel, ManyToManyRel): 
        result_repr = ", ".join(map(unicode, value.all())) 
       else: 
        result_repr = display_for_field(value, f) 
     return conditional_escape(result_repr) 

helpers.AdminReadonlyField = BetterAdminReadonlyField 

你可以把這個文件夾中的 「monkeypatches」,並稱之爲 「admin_readonly_field.py」(不要忘了還添加一個空的__init__.py使該文件夾成爲一個模塊)。 然後在你的應用程序的__init__.py添加

from monkeypatches import admin_readonly_field 

和你離開。

上面的代碼只包含相關的導入和代碼monkeypatch AdminReadonlyField(在這種情況下從Django 1.3複製)。沒有什麼實際上改變了原來的課程。改變你在你的情況下最有用的東西。

你的具體情況,你可以,也許這兩行添加到第二個最後一個:

 result_repr = display_for_field(value, f) 

     if isinstance(field, models.TextField): 
      result_repr = result_repr.replace('\n', '<br />') 

(和from django.db import models在頂部)

我很抱歉,但該類附帶的Django如此糟糕,選項2是我推薦的方式。您的文本字段是不是唯一的領域,看起來以只讀模式不好......

+0

我喜歡重寫管理域來改變顯示的想法。從我+1。 –

+0

jQuery的第一個選擇提出了一個期望,即'ModelAdmin.readonly_fields'中列出的字段在呈現時自動獲得'readonly' css類。這似乎並不是這樣,至少在我使用的Django版本中(1.4.3)。 –

+0

只是意識到你正在使用grappelli。我希望有一種比覆蓋模板更簡單的方法,或者將整個野獸進行修飾。基本上我只想添加一個css類,但這似乎不可能,'attrs'是硬編碼的。我想知道爲什麼Django不使用(剝離)小部件。 –

3

我還在使用django 1.3,最後想出了一個解決方案。因此,如果其他人仍然在這艘船上:

覆蓋模板fieldset.html(從pythondir/djangodir/django/contrib/admin/templates/admin/includes/fieldset.html複製到djangoprojectdir/templates/admin /包括/ fieldset.html)

它包含行:

     {% if field.is_readonly %} 
          <p>{{ field.contents }}</p> 

他們更改爲:

     {% if field.is_readonly %} 
          <p>{{ field.contents|linebreaksbr }}</p> 

這是試圖丹尼的解決方案結束後翅因爲從內容函數返回的文本被轉義以替換具有轉義碼的標籤(「<」等),然後閱讀以下內容:https://code.djangoproject.com/ticket/19226

相關問題