2013-02-04 64 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

相关问题