模型函数中字符串的空值

时间:2019-03-26 19:29:43

标签: django django-models

这里的第一个新手项目-在此模型中,我试图创建一个字段,该字段将两个人的姓氏和名字结合在一起,这取决于姓氏是否相同。如果姓氏相同,我希望它显示为“ first_name1&first_name2 last_name1”。它的工作原理是,当last_name1为空时(在许多情况下会如此),它显示类似“ John&Jane None”的内容。我必须将last_name1指定为字符串,否则会出现错误:必须为str,而不是NoneType。如何正确执行此操作?另外,我在模型中怎么称这种类型的功能……是经理吗?我不确定如何为这篇文章加上标题。

class Contact(models.Model):
    first_name1 = models.CharField(max_length=100, verbose_name='First Name', null=True)
    last_name1 = models.CharField(max_length=100, verbose_name='Last Name', null=True, blank=True)
    first_name2 = models.CharField(max_length=100, verbose_name='First Name (Second Person)', null=True, blank=True)
    last_name2 = models.CharField(max_length=100, verbose_name='Last Name (Second Person)', null=True, blank=True)

    def get_full_name(self):
        combined_name = ''

        if self.last_name1 == self.last_name2:
            combined_name = self.first_name1 + ' & ' + self.first_name2 + ' ' + str(self.last_name1)

        return '%s' % (combined_name)

    full_name = property(get_full_name)

3 个答案:

答案 0 :(得分:1)

您可以在进行比较检查之前检查那些值是否为“ Truth-y”。但是,您需要确定如何处理其他情况。

find "$1" -type f '(' -name "*.html" -o -name "*.htm" ')' \
    -exec ./stripHtml {} {}_nobody \;

答案 1 :(得分:1)

为什么收到错误消息last_name1必须是String而不是NoneType的原因是由于您在所述字段的字段声明中将null设置为True。

那么这样做有什么问题呢?当为CharField或TextField之类的字段定义null = True时,您最终将具有 None 。 Django惯例是使用 EMPTY STRING

这里有一个link,它讨论了如何在字段声明中使用空白

答案 2 :(得分:0)

您定义名称的方式,所有名称都可以为None,如果将其更改为空字符串,您将遇到类似的问题。为了说明,让我们先编写一个单元测试(如果需要,用空字符串替换None):

def test_contact_full_name():
    # correct.
    assert Contact('Jane', None, 'John', None).full_name == "Jane & John"
    assert Contact('Bart', 'Simpson', 'Lisa', 'Simpson').full_name  == "Bart & Lisa Simpson"
    assert Contact('Bart', 'Simpson', 'Frodo', 'Baggins').full_name == "Bart Simpson & Frodo Baggins"
    assert Contact('Bart', None, None, None).full_name == "Bart"
    assert Contact('Bart', 'Simpson', None, None).full_name == "Bart Simpson"
    assert Contact(None, 'Simpson', None, None).full_name == "Simpson"

    assert Contact(None, None, None, None).full_name == ""

    # correct?
    assert Contact('Bart', 'Simpson', 'Lisa', None).full_name == "Bart Simpson & Lisa"

    # correct??
    assert Contact('Bart', None, 'Lisa', 'Simpson').full_name == "Bart & Lisa Simpson"

这只是将问题分成小块的问题,我将所有内容放入常规类中只是为了使其易于测试。首先介绍一些辅助方法:

class Contact(object):
    def __init__(self, a, b, c, d):
        self.first_name1 = a
        self.last_name1 = b
        self.first_name2 = c
        self.last_name2 = d

    def combined_last_name(self, a, b):
        "Return ``a`` if ``a`` and ``b`` are equal, otherwise returns None."
        return a if a and b and a == b else None

    def normalize_name(self, n):
        "Returns the name or the empty string if ``n`` is None."
        return n if n else ""

    def get_name(self, f, l):
        """Returns a string containing firstname lastname and omits any of them
           if they're None.
        """         
        if f and l:
            return "%s %s" % (f, l)
        if f:
            return f
        elif l:
            return l
        return ""

    def has_second_name(self):
        "Returns true if there is a second name."
        return self.first_name2 or self.last_name2

然后我们可以定义full_name属性:

    @property
    def full_name(self):
        """Returns a string that combines first and last names of two people
           depending if the last names are the same or not. If the last name
           is the same, it displays as::

               first_name1 & first_name2 last_name1

        """
        cln = self.combined_last_name(self.last_name1, self.last_name2)
        if cln:  # have a common last name..
            return "%s & %s %s" % (
                self.first_name1,
                self.first_name2,
                cln
            )
        elif self.has_second_name():
            return "%s & %s" % (
                self.get_name(self.first_name1, self.last_name1),
                self.get_name(self.first_name2, self.last_name2)
            )
        else:
            return self.get_name(self.first_name1, self.last_name1)

如果我们将所有内容都放入名为fullname.py的文件中,则可以使用pytest工具(pip install pytest)来运行测试:

c:\srv\tmp> pytest --verbose fullname.py
============================= test session starts =============================
platform win32 -- Python 2.7.16, pytest-3.3.1, py-1.5.2, pluggy-0.6.0 -- c:\srv\venv\finautfaktura\scripts\python.exe
cachedir: .cache
rootdir: c:\srv\tmp, inifile:
plugins: xdist-1.20.1, forked-0.2, django-3.1.2, cov-2.5.1
collected 1 item

fullname.py::test_contact_full_name PASSED                               [100%]

========================== 1 passed in 0.20 seconds ===========================

一切都很好...是吗?

我们再写一个测试:

def test_only_second_name():
    assert Contact(None, None, None, "Simpson").full_name == "Simpson"
    assert Contact(None, None, "Lisa", "Simpson").full_name == "Lisa Simpson"
    assert Contact(None, None, "Lisa", None).full_name == "Lisa"

再次运行pytest会显示(第一个)错误:

c:\srv\tmp> pytest --verbose fullname.py
============================= test session starts =============================
platform win32 -- Python 2.7.16, pytest-3.3.1, py-1.5.2, pluggy-0.6.0 -- c:\srv\venv\finautfaktura\scripts\python.exe
cachedir: .cache
rootdir: c:\srv\tmp, inifile:
plugins: xdist-1.20.1, forked-0.2, django-3.1.2, cov-2.5.1
collected 2 items

fullname.py::test_contact_full_name PASSED                               [ 50%]
fullname.py::test_only_second_name FAILED                                [100%]

================================== FAILURES ===================================
____________________________ test_only_second_name ____________________________

    def test_only_second_name():
>       assert Contact(None, None, None, "Simpson").full_name == "Simpson"
E       AssertionError: assert ' & Simpson' == 'Simpson'
E         -  & Simpson
E         ? ---
E         + Simpson

fullname.py:83: AssertionError
===================== 1 failed, 1 passed in 0.37 seconds ======================

即该属性返回" & Simpson",而不是第一个断言的预期"Simpson"

要解决此问题,我们可以使full_name属性也处理这种增加的复杂性,或者..我们可以在其他地方解决该问题,例如在__init__中:

class Contact(object):
    def __init__(self, a, b, c, d):
        self.first_name1 = a
        self.last_name1 = b
        self.first_name2 = c
        self.last_name2 = d
        if not a and not b:
            # if no name1, then put name2 into name1 and set name2 to None
            self.first_name1 = self.first_name2
            self.last_name1 = self.last_name2
            self.first_name2 = self.last_name2 = None

再次运行pytest表明这已修复了第二个测试。

您当然不能在Django模型中提供自己的__init__来解决此问题,但是如果您覆盖save(..)方法,则可以执行类似的操作:

def save(self, *args, **kwargs):
    if not self.first_name1 and not self.last_name1:
        self.first_name1 = self.first_name2
        self.last_name1 = self.last_name2
        self.first_name2 = self.last_name2 = None
    super(Contact, self).save(*args, **kwargs)