Django:从charField中删除空格

22

如何从Django的charField字段中去除末尾的空格(trim)?

以下是我的模型,您可以看到我尝试了添加清理方法,但这些方法从未运行。

我也尝试了name.strip()models.charField().strip(),但这些方法也不起作用。

有没有一种方法可以强制charField自动为我修剪空格?

谢谢。

from django.db import models
from django.forms import ModelForm
from django.core.exceptions import ValidationError
import datetime

class Employee(models.Model):
    """(Workers, Staff, etc)"""
    name                = models.CharField(blank=True, null=True, max_length=100)

    def save(self, *args, **kwargs):
        try:
            # This line doesn't do anything??
            #self.full_clean()
            Employee.clean(self)
        except ValidationError, e:
            print e.message_dict

        super(Employee, self).save(*args, **kwargs) # Real save

    # If I uncomment this, I get an TypeError: unsubscriptable object
    #def clean(self):
    #   return self.clean['name'].strip()

    def __unicode__(self):
        return self.name

    class Meta:
        verbose_name_plural = 'Employees'

    class Admin:pass


class EmployeeForm(ModelForm):
    class Meta:
        model = Employee

    # I have no idea if this method is being called or not  
    def full_clean(self):       
        return super(Employee), self.clean().strip()
        #return self.clean['name'].strip()

编辑:更新了代码到最新版本。我不确定我做错了什么,因为它仍然没有去除姓名字段的空格(修剪)。

6个回答

27
当你使用ModelForm实例来创建/编辑模型时,保证会调用模型的clean()方法。因此,如果你想从字段中去除空格,只需在你的模型中添加一个clean()方法即可(无需编辑ModelForm类):
class Employee(models.Model):
    """(Workers, Staff, etc)"""
    name = models.CharField(blank=True, null=True, max_length=100)

    def clean(self):
        if self.name:
            self.name = self.name.strip()

我发现以下代码片段很有用-它可以修剪模型中所有子类为CharField或TextField(因此也包括URLField字段)的字段的空格,而无需逐个指定这些字段:

def clean(self):
    for field in self._meta.fields:
        if isinstance(field, (models.CharField, models.TextField)):
            value = getattr(self, field.name)
            if value:
                setattr(self, field.name, value.strip())

有人正确指出在名称声明中不应该使用null=True。最佳实践是避免在字符串字段中使用null=True,这种情况下可以简化为:

def clean(self):
    for field in self._meta.fields:
        if isinstance(field, (models.CharField, models.TextField)):
            setattr(self, field.name, getattr(self, field.name).strip())

3
好的解决方案。但是,我想改变的一件事是:if value and hasattr(value, 'strip'):因为某些自定义字段不包含值的strip方法(至少在我的情况下是这样)。这会防止出现这种情况。 - Troy Grosfield

16

模型清理需要手动调用,因此在保存方法中添加 self.full_clean()
http://docs.djangoproject.com/en/dev/ref/models/instances/#django.db.models.Model.full_clean

至于您的表单,您需要返回经过剥离并清理的数据。

return self.cleaned_data['name'].strip()

我觉得你刚刚尝试了一些不起作用的操作。请记住,表单和模型是两个非常不同的东西。

查看有关如何验证表单的文档: http://docs.djangoproject.com/en/dev/ref/forms/validation/

super(Employee), self.clean().strip() 完全没有意义!

这里是修复后的代码:

class Employee(models.Model):
    """(Workers, Staff, etc)"""
    name = models.CharField(blank=True, null=True, max_length=100)

    def save(self, *args, **kwargs):
        self.full_clean() # performs regular validation then clean()
        super(Employee, self).save(*args, **kwargs)


    def clean(self):
        """
        Custom validation (read docs)
        PS: why do you have null=True on charfield? 
        we could avoid the check for name
        """
        if self.name: 
            self.name = self.name.strip()


class EmployeeForm(ModelForm):
    class Meta:
        model = Employee


    def clean_name(self):
        """
        If somebody enters into this form ' hello ', 
        the extra whitespace will be stripped.
        """
        return self.cleaned_data.get('name', '').strip()

我仍然遇到问题——它仍然没有去除空格(trim)。我尝试了你发布的教程,并尝试在保存方法中调用full_clean()的想法。我很难理解a)是否正在调用EmployeeForm?b)是否正在调用full_clean()方法?我已经在我的问题中更新了代码示例。我希望能得到正确的指引。 - zardon
感谢您的帮助,我对Django/Python还比较新,因此对于我应该如何实现各种功能存在各种错误和误解,请多多包涵。 - zardon
没问题。只要查阅文档,而不是猜测,你就能理解一切!Django的文档非常清晰,并提供了很多示例。 - Yuji 'Tomita' Tomita
那很好用。关于您对名称字段为什么接受null=True的评论,这是因为我刚开始使用Django并不太熟悉所有内容。无论如何,还是谢谢您,因为这解决了我的问题。希望将来我能在所有文本字段中都使用它。 - zardon
null=True 表示数据库可以存储 null 值,这对于像可选的 Integer 字段这样的字段非常重要,因为 '' 是无效的。对于字符字段,'' 通常完全可以作为空白值,这也确保该字段始终返回字符串,因此您可以安全地对其执行任何 Python 字符串操作,例如 strip() - Yuji 'Tomita' Tomita

7
如果您有很多需要修剪的数据字段,为什么不尝试扩展CharField?
from django.db import models
from django.utils.translation import ugettext_lazy as _

class TrimCharField(models.CharField):
   description = _(
       "CharField that ignores leading" 
       " and trailing spaces in data")

   def get_prep_value(self, value)
       return trim(super(TrimCharField, self
           ).get_prep_value(value))

   def pre_save(self, model_instance, add):
       return trim(super(TrimCharField, self
           ).pre_save(model_instance, add))

更新: 对于Django版本小于等于1.7的情况,如果您想要扩展字段,您需要使用models.SubfieldBase元类。因此,在这里它将是这样的:

class TrimCharField(six.with_metaclass(
    models.SubfieldBase, models.CharField)):

7
Django 1.9提供了一种简单的方法来实现此功能。通过使用 strip 参数,默认为True,您可以确保去掉前导和尾随空格。但是只能对表单字段进行操作,以确保用户输入被修剪。但这仍然无法保护模型本身。如果您仍然想要这样做,可以使用上述任何方法。
有关更多信息,请访问https://docs.djangoproject.com/en/1.9/ref/forms/fields/#charfield

1
我需要加强对于未来可能遇到此问题的人的评论:在Django 1.9之后,即使您使用ModelForm,底层的charField默认情况下也会将strip设置为True。这意味着您不需要手动去除任何内容。但是,请注意,根据您实现视图的方式,您仍然可能会看到未修剪的字段,因为在返回给视图的form对象上它仍然是未修剪的。 - lapin
在1.9版本之后,默认启用了strip功能。如果您想禁用此功能,请查看我的答案:https://dev59.com/VlkT5IYBdhLWcg3wKsWq#68112671 - Hunger

3

我正在视图中使用装饰器处理这个问题。如果字段的值超过了CharField的max_length值,我也会对其进行截断。

from django import forms
from django import models
from django.db.models.fields import FieldDoesNotExist
from django.utils.encoding import smart_str

class CleanCharField(forms.CharField):
        """Django's default form handling drives me nuts wrt trailing
        spaces.  http://code.djangoproject.com/attachment/ticket/6362
        """
        def clean(self, value):
            if value is None:
                value = u''
            value = smart_str(value).strip()
            value = super(forms.CharField, self).clean(value)
            return value

def truncate_charfield(model):
    """decorator to truncate CharField data to model field max_length.
    Apply to the clean method in views Form:

    @truncate_charfield(MyModel)
    def clean(self):
        ...
    """
    def wrap(f):
        def wrapped_f(*args):
            f(*args)
            d = args[0].cleaned_data
            for field in model._meta.fields:
                try:
                    mf = model._meta.get_field(field.name)
                    if isinstance(mf, models.CharField) and field.name in d:
                        d[field.name] = d[field.name][:mf.max_length]
                except FieldDoesNotExist:
                    pass
            return d
        return wrapped_f
    return wrap

谢谢,但对我来说似乎有点过于高级了 - 我不确定应该把这个文件放在哪里,如何调用它等等; 但是随着我学习 Django/Python,我相信我会逐渐理解的。再次感谢。 - zardon
zardon,你可以将它放在一个模块中,比如maxtruncate.py,然后在views.py中导入:from maxtruncate import CleanCharField, truncate_charfield。然后用CleanCharField替换CharField,并在clean方法的顶部放置truncate_charfield装饰器。主要优点是您不会让输入字段超过max_length...这将导致数据库运行时错误。 - Jeff Bauer

0

如果你还没有使用Django 1.9+,那么你(和我)应该感到羞愧,将此代码放入你的表单中。这与@jeremy-lewis的答案类似,但我遇到了几个问题。

def clean_text_fields(self):
    # TODO: Django 1.9, use on the model strip=True
    # https://docs.djangoproject.com/en/1.9/ref/forms/fields/#charfield
    from django.forms.fields import CharField
    cd = self.cleaned_data
    for field_name, field in self.fields.items():
        if isinstance(field, CharField):
            cd[field_name] = cd[field_name].strip()
            if self.fields[field_name].required and not cd[field_name]:
                self.add_error(field_name, "This is a required field.")

def clean(self):
    self.clean_text_fields()

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接