Empty value for string in model functiondjango - inlineformset_factory with more than one ForeignKeyShow information of subclass in list_display djangoDjango south migration error with unique field in postgresql databaseDjango ImageField overwrites existing path when emptyCreate a new model which have all fields of currently existing modelfilter json data from Django model'NoneType' object is not subscriptable in using django smart selectsHow to expose some specific fields of model_b based on a field of model_a?f string interpolation syntax error python3.6
How did the Axis intend to hold the Caucasus?
What is "aligned sequences" and "consensus sequence" in the context of sequence logo? How to compute these?
Exploiting the delay when a festival ticket is scanned
Desktop app status bar: Notification vs error message
Why did Windows 95 crash the whole system but newer Windows only crashed programs?
Why would a pilot use ailerons for countering asymmetric thrust in mid-flight?
8086 stack segment and avoiding overflow in interrupts
Why did I lose on time with 3 pawns vs Knight. Shouldn't it be a draw?
How likely is fragmentation on a table with 40000 products likely to affect performance
To find islands of 1 and 0 in matrix
Does dual boot harms laptop battery or reduces it's life?
Struggling with cyclical dependancies in unit tests
How can I say in Russian "I am not afraid to write anything"?
Why is the number of local variables used in a Java bytecode method not the most economical?
Are there any unpublished Iain M. Banks short stories?
Must a song using the A minor scale begin or end with an Am chord? If not, how can I tell what the scale is?
Move the outer key inward in an association
What steps would an amateur scientist have to take in order to get a scientific breakthrough published?
How do you pronounce "Hain"?
Is there an antonym for "spicy" or "hot" regarding food (NOT "seasoned" but "spicy")?
Comparisons of a general truth
Telling manager project isn't worth the effort?
Why would anyone ever invest in a cash-only etf?
How can religions be structured in ways that allow inter-faith councils to work?
Empty value for string in model function
django - inlineformset_factory with more than one ForeignKeyShow information of subclass in list_display djangoDjango south migration error with unique field in postgresql databaseDjango ImageField overwrites existing path when emptyCreate a new model which have all fields of currently existing modelfilter json data from Django model'NoneType' object is not subscriptable in using django smart selectsHow to expose some specific fields of model_b based on a field of model_a?f string interpolation syntax error python3.6
.everyoneloves__top-leaderboard:empty,.everyoneloves__mid-leaderboard:empty,.everyoneloves__bot-mid-leaderboard:empty margin-bottom:0;
First project newbie here - In this model I am trying to create a field 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, I want it to display as "first_name1 & first_name2 last_name1". It works except that when last_name1 is empty, which will be the case a lot of the time, it displays something like "John & Jane None". I had to specify last_name1 as a string or else I got an error: must be str, not NoneType. How do I do this properly? Also what do I call this type of function in a model...is it a manager? I wasn't sure how to title this post.
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)
django django-models
add a comment |
First project newbie here - In this model I am trying to create a field 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, I want it to display as "first_name1 & first_name2 last_name1". It works except that when last_name1 is empty, which will be the case a lot of the time, it displays something like "John & Jane None". I had to specify last_name1 as a string or else I got an error: must be str, not NoneType. How do I do this properly? Also what do I call this type of function in a model...is it a manager? I wasn't sure how to title this post.
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)
django django-models
add a comment |
First project newbie here - In this model I am trying to create a field 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, I want it to display as "first_name1 & first_name2 last_name1". It works except that when last_name1 is empty, which will be the case a lot of the time, it displays something like "John & Jane None". I had to specify last_name1 as a string or else I got an error: must be str, not NoneType. How do I do this properly? Also what do I call this type of function in a model...is it a manager? I wasn't sure how to title this post.
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)
django django-models
First project newbie here - In this model I am trying to create a field 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, I want it to display as "first_name1 & first_name2 last_name1". It works except that when last_name1 is empty, which will be the case a lot of the time, it displays something like "John & Jane None". I had to specify last_name1 as a string or else I got an error: must be str, not NoneType. How do I do this properly? Also what do I call this type of function in a model...is it a manager? I wasn't sure how to title this post.
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)
django django-models
django django-models
edited Mar 26 at 19:35
DickyS
asked Mar 26 at 19:29
DickySDickyS
317 bronze badges
317 bronze badges
add a comment |
add a comment |
3 Answers
3
active
oldest
votes
The reason on why you're getting an error that last_name1 must be a String, not a NoneType is due to the fact you've set null to True in your field declaration for the said field.
So what's wrong with doing that? When you are defining null=True for fields like CharField or TextField you'll end up having None. The Django convention is to use EMPTY STRING.
Here's a link that talks about how you would use blank or null on field declarations.
That's it... I removed the null=True on those fields and had to delete all the database entries and re-add them. Now it works perfectly. Thanks!
– DickyS
Mar 26 at 20:59
1
Not sure if it matters, but you can still end up with'Jane & John '
. Note the trailing whitespace too.
– schillingt
Mar 26 at 21:18
add a comment |
You can check if those values are "Truth-y" before doing the comparison check. However, you'll need to decide how to handle the other cases.
@property
def get_full_name(self):
combined_name = ''
if self.last_name1 and self.last_name2:
if self.last_name1 == self.last_name2:
combined_name = self.first_name1 + ' & ' + self.first_name2 + ' ' + str(self.last_name1)
elif self.last_name1: # Only last_name1 is set
pass
elif self.last_name2: # Only last_name2 is set
pass
else: # Both last_name1 and last_name2 are None or ''
pass
return combined_name
why not simplyreturn combined_name
?
– thebjorn
Mar 26 at 20:14
That's a reasonable question, but likely requires context from the actual code. If it was simply a case/switch statement like it appears, I would personally return. I know other developers that wouldn't because they like to try to limit the returns of a function to a single spot. It's a matter of preference and I don't think either case provides a significant improvement so I left it the same.
– schillingt
Mar 26 at 20:21
1
I meantreturn combined_name
instead ofreturn f'combined_name'
-- the second version is just copying a string into another string before returning it...?
– thebjorn
Mar 26 at 21:17
D'oh. Thank you.
– schillingt
Mar 26 at 21:17
add a comment |
The way you've defined the names, any and all of them can be None
, if you change them to the empty string you'll have similar problems. To illustrate, lets start by writing a unit test (substitute the empty string for None if you want):
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"
Then it's just a question of dividing the problem into smaller pieces, I've put everything into a regular class just to make it easier to test. First some helper methods:
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
then we can define the full_name
property:
@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)
if we put everything in a file named fullname.py
we can use the pytest
tool (pip install pytest
) to run the tests:
c:srvtmp> 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:srvvenvfinautfakturascriptspython.exe
cachedir: .cache
rootdir: c:srvtmp, 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 ===========================
All is well... or is it?
Let's write another test:
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"
running pytest again reveals the (first) error:
c:srvtmp> 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:srvvenvfinautfakturascriptspython.exe
cachedir: .cache
rootdir: c:srvtmp, 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 ======================
i.e. the property returned " & Simpson"
instead of the expected "Simpson"
for the first assert.
To fix this we can make the full_name
property handle this added complexity as well, or.., we can solve the problem somewhere else, e.g. in the __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
running pytest again shows that this fixed the second test.
You can of course not provide your own __init__
in a Django model to solve this problem, but you can do something similar in if you override the save(..)
method:
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)
add a comment |
Your Answer
StackExchange.ifUsing("editor", function ()
StackExchange.using("externalEditor", function ()
StackExchange.using("snippets", function ()
StackExchange.snippets.init();
);
);
, "code-snippets");
StackExchange.ready(function()
var channelOptions =
tags: "".split(" "),
id: "1"
;
initTagRenderer("".split(" "), "".split(" "), channelOptions);
StackExchange.using("externalEditor", function()
// Have to fire editor after snippets, if snippets enabled
if (StackExchange.settings.snippets.snippetsEnabled)
StackExchange.using("snippets", function()
createEditor();
);
else
createEditor();
);
function createEditor()
StackExchange.prepareEditor(
heartbeatType: 'answer',
autoActivateHeartbeat: false,
convertImagesToLinks: true,
noModals: true,
showLowRepImageUploadWarning: true,
reputationToPostImages: 10,
bindNavPrevention: true,
postfix: "",
imageUploader:
brandingHtml: "Powered by u003ca class="icon-imgur-white" href="https://imgur.com/"u003eu003c/au003e",
contentPolicyHtml: "User contributions licensed under u003ca href="https://creativecommons.org/licenses/by-sa/3.0/"u003ecc by-sa 3.0 with attribution requiredu003c/au003e u003ca href="https://stackoverflow.com/legal/content-policy"u003e(content policy)u003c/au003e",
allowUrls: true
,
onDemand: true,
discardSelector: ".discard-answer"
,immediatelyShowMarkdownHelp:true
);
);
Sign up or log in
StackExchange.ready(function ()
StackExchange.helpers.onClickDraftSave('#login-link');
);
Sign up using Google
Sign up using Facebook
Sign up using Email and Password
Post as a guest
Required, but never shown
StackExchange.ready(
function ()
StackExchange.openid.initPostLogin('.new-post-login', 'https%3a%2f%2fstackoverflow.com%2fquestions%2f55364924%2fempty-value-for-string-in-model-function%23new-answer', 'question_page');
);
Post as a guest
Required, but never shown
3 Answers
3
active
oldest
votes
3 Answers
3
active
oldest
votes
active
oldest
votes
active
oldest
votes
The reason on why you're getting an error that last_name1 must be a String, not a NoneType is due to the fact you've set null to True in your field declaration for the said field.
So what's wrong with doing that? When you are defining null=True for fields like CharField or TextField you'll end up having None. The Django convention is to use EMPTY STRING.
Here's a link that talks about how you would use blank or null on field declarations.
That's it... I removed the null=True on those fields and had to delete all the database entries and re-add them. Now it works perfectly. Thanks!
– DickyS
Mar 26 at 20:59
1
Not sure if it matters, but you can still end up with'Jane & John '
. Note the trailing whitespace too.
– schillingt
Mar 26 at 21:18
add a comment |
The reason on why you're getting an error that last_name1 must be a String, not a NoneType is due to the fact you've set null to True in your field declaration for the said field.
So what's wrong with doing that? When you are defining null=True for fields like CharField or TextField you'll end up having None. The Django convention is to use EMPTY STRING.
Here's a link that talks about how you would use blank or null on field declarations.
That's it... I removed the null=True on those fields and had to delete all the database entries and re-add them. Now it works perfectly. Thanks!
– DickyS
Mar 26 at 20:59
1
Not sure if it matters, but you can still end up with'Jane & John '
. Note the trailing whitespace too.
– schillingt
Mar 26 at 21:18
add a comment |
The reason on why you're getting an error that last_name1 must be a String, not a NoneType is due to the fact you've set null to True in your field declaration for the said field.
So what's wrong with doing that? When you are defining null=True for fields like CharField or TextField you'll end up having None. The Django convention is to use EMPTY STRING.
Here's a link that talks about how you would use blank or null on field declarations.
The reason on why you're getting an error that last_name1 must be a String, not a NoneType is due to the fact you've set null to True in your field declaration for the said field.
So what's wrong with doing that? When you are defining null=True for fields like CharField or TextField you'll end up having None. The Django convention is to use EMPTY STRING.
Here's a link that talks about how you would use blank or null on field declarations.
answered Mar 26 at 20:09
PaoloPaolo
3131 silver badge12 bronze badges
3131 silver badge12 bronze badges
That's it... I removed the null=True on those fields and had to delete all the database entries and re-add them. Now it works perfectly. Thanks!
– DickyS
Mar 26 at 20:59
1
Not sure if it matters, but you can still end up with'Jane & John '
. Note the trailing whitespace too.
– schillingt
Mar 26 at 21:18
add a comment |
That's it... I removed the null=True on those fields and had to delete all the database entries and re-add them. Now it works perfectly. Thanks!
– DickyS
Mar 26 at 20:59
1
Not sure if it matters, but you can still end up with'Jane & John '
. Note the trailing whitespace too.
– schillingt
Mar 26 at 21:18
That's it... I removed the null=True on those fields and had to delete all the database entries and re-add them. Now it works perfectly. Thanks!
– DickyS
Mar 26 at 20:59
That's it... I removed the null=True on those fields and had to delete all the database entries and re-add them. Now it works perfectly. Thanks!
– DickyS
Mar 26 at 20:59
1
1
Not sure if it matters, but you can still end up with
'Jane & John '
. Note the trailing whitespace too.– schillingt
Mar 26 at 21:18
Not sure if it matters, but you can still end up with
'Jane & John '
. Note the trailing whitespace too.– schillingt
Mar 26 at 21:18
add a comment |
You can check if those values are "Truth-y" before doing the comparison check. However, you'll need to decide how to handle the other cases.
@property
def get_full_name(self):
combined_name = ''
if self.last_name1 and self.last_name2:
if self.last_name1 == self.last_name2:
combined_name = self.first_name1 + ' & ' + self.first_name2 + ' ' + str(self.last_name1)
elif self.last_name1: # Only last_name1 is set
pass
elif self.last_name2: # Only last_name2 is set
pass
else: # Both last_name1 and last_name2 are None or ''
pass
return combined_name
why not simplyreturn combined_name
?
– thebjorn
Mar 26 at 20:14
That's a reasonable question, but likely requires context from the actual code. If it was simply a case/switch statement like it appears, I would personally return. I know other developers that wouldn't because they like to try to limit the returns of a function to a single spot. It's a matter of preference and I don't think either case provides a significant improvement so I left it the same.
– schillingt
Mar 26 at 20:21
1
I meantreturn combined_name
instead ofreturn f'combined_name'
-- the second version is just copying a string into another string before returning it...?
– thebjorn
Mar 26 at 21:17
D'oh. Thank you.
– schillingt
Mar 26 at 21:17
add a comment |
You can check if those values are "Truth-y" before doing the comparison check. However, you'll need to decide how to handle the other cases.
@property
def get_full_name(self):
combined_name = ''
if self.last_name1 and self.last_name2:
if self.last_name1 == self.last_name2:
combined_name = self.first_name1 + ' & ' + self.first_name2 + ' ' + str(self.last_name1)
elif self.last_name1: # Only last_name1 is set
pass
elif self.last_name2: # Only last_name2 is set
pass
else: # Both last_name1 and last_name2 are None or ''
pass
return combined_name
why not simplyreturn combined_name
?
– thebjorn
Mar 26 at 20:14
That's a reasonable question, but likely requires context from the actual code. If it was simply a case/switch statement like it appears, I would personally return. I know other developers that wouldn't because they like to try to limit the returns of a function to a single spot. It's a matter of preference and I don't think either case provides a significant improvement so I left it the same.
– schillingt
Mar 26 at 20:21
1
I meantreturn combined_name
instead ofreturn f'combined_name'
-- the second version is just copying a string into another string before returning it...?
– thebjorn
Mar 26 at 21:17
D'oh. Thank you.
– schillingt
Mar 26 at 21:17
add a comment |
You can check if those values are "Truth-y" before doing the comparison check. However, you'll need to decide how to handle the other cases.
@property
def get_full_name(self):
combined_name = ''
if self.last_name1 and self.last_name2:
if self.last_name1 == self.last_name2:
combined_name = self.first_name1 + ' & ' + self.first_name2 + ' ' + str(self.last_name1)
elif self.last_name1: # Only last_name1 is set
pass
elif self.last_name2: # Only last_name2 is set
pass
else: # Both last_name1 and last_name2 are None or ''
pass
return combined_name
You can check if those values are "Truth-y" before doing the comparison check. However, you'll need to decide how to handle the other cases.
@property
def get_full_name(self):
combined_name = ''
if self.last_name1 and self.last_name2:
if self.last_name1 == self.last_name2:
combined_name = self.first_name1 + ' & ' + self.first_name2 + ' ' + str(self.last_name1)
elif self.last_name1: # Only last_name1 is set
pass
elif self.last_name2: # Only last_name2 is set
pass
else: # Both last_name1 and last_name2 are None or ''
pass
return combined_name
edited Mar 26 at 21:17
answered Mar 26 at 20:07
schillingtschillingt
6,7651 gold badge18 silver badges24 bronze badges
6,7651 gold badge18 silver badges24 bronze badges
why not simplyreturn combined_name
?
– thebjorn
Mar 26 at 20:14
That's a reasonable question, but likely requires context from the actual code. If it was simply a case/switch statement like it appears, I would personally return. I know other developers that wouldn't because they like to try to limit the returns of a function to a single spot. It's a matter of preference and I don't think either case provides a significant improvement so I left it the same.
– schillingt
Mar 26 at 20:21
1
I meantreturn combined_name
instead ofreturn f'combined_name'
-- the second version is just copying a string into another string before returning it...?
– thebjorn
Mar 26 at 21:17
D'oh. Thank you.
– schillingt
Mar 26 at 21:17
add a comment |
why not simplyreturn combined_name
?
– thebjorn
Mar 26 at 20:14
That's a reasonable question, but likely requires context from the actual code. If it was simply a case/switch statement like it appears, I would personally return. I know other developers that wouldn't because they like to try to limit the returns of a function to a single spot. It's a matter of preference and I don't think either case provides a significant improvement so I left it the same.
– schillingt
Mar 26 at 20:21
1
I meantreturn combined_name
instead ofreturn f'combined_name'
-- the second version is just copying a string into another string before returning it...?
– thebjorn
Mar 26 at 21:17
D'oh. Thank you.
– schillingt
Mar 26 at 21:17
why not simply
return combined_name
?– thebjorn
Mar 26 at 20:14
why not simply
return combined_name
?– thebjorn
Mar 26 at 20:14
That's a reasonable question, but likely requires context from the actual code. If it was simply a case/switch statement like it appears, I would personally return. I know other developers that wouldn't because they like to try to limit the returns of a function to a single spot. It's a matter of preference and I don't think either case provides a significant improvement so I left it the same.
– schillingt
Mar 26 at 20:21
That's a reasonable question, but likely requires context from the actual code. If it was simply a case/switch statement like it appears, I would personally return. I know other developers that wouldn't because they like to try to limit the returns of a function to a single spot. It's a matter of preference and I don't think either case provides a significant improvement so I left it the same.
– schillingt
Mar 26 at 20:21
1
1
I meant
return combined_name
instead of return f'combined_name'
-- the second version is just copying a string into another string before returning it...?– thebjorn
Mar 26 at 21:17
I meant
return combined_name
instead of return f'combined_name'
-- the second version is just copying a string into another string before returning it...?– thebjorn
Mar 26 at 21:17
D'oh. Thank you.
– schillingt
Mar 26 at 21:17
D'oh. Thank you.
– schillingt
Mar 26 at 21:17
add a comment |
The way you've defined the names, any and all of them can be None
, if you change them to the empty string you'll have similar problems. To illustrate, lets start by writing a unit test (substitute the empty string for None if you want):
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"
Then it's just a question of dividing the problem into smaller pieces, I've put everything into a regular class just to make it easier to test. First some helper methods:
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
then we can define the full_name
property:
@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)
if we put everything in a file named fullname.py
we can use the pytest
tool (pip install pytest
) to run the tests:
c:srvtmp> 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:srvvenvfinautfakturascriptspython.exe
cachedir: .cache
rootdir: c:srvtmp, 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 ===========================
All is well... or is it?
Let's write another test:
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"
running pytest again reveals the (first) error:
c:srvtmp> 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:srvvenvfinautfakturascriptspython.exe
cachedir: .cache
rootdir: c:srvtmp, 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 ======================
i.e. the property returned " & Simpson"
instead of the expected "Simpson"
for the first assert.
To fix this we can make the full_name
property handle this added complexity as well, or.., we can solve the problem somewhere else, e.g. in the __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
running pytest again shows that this fixed the second test.
You can of course not provide your own __init__
in a Django model to solve this problem, but you can do something similar in if you override the save(..)
method:
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)
add a comment |
The way you've defined the names, any and all of them can be None
, if you change them to the empty string you'll have similar problems. To illustrate, lets start by writing a unit test (substitute the empty string for None if you want):
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"
Then it's just a question of dividing the problem into smaller pieces, I've put everything into a regular class just to make it easier to test. First some helper methods:
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
then we can define the full_name
property:
@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)
if we put everything in a file named fullname.py
we can use the pytest
tool (pip install pytest
) to run the tests:
c:srvtmp> 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:srvvenvfinautfakturascriptspython.exe
cachedir: .cache
rootdir: c:srvtmp, 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 ===========================
All is well... or is it?
Let's write another test:
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"
running pytest again reveals the (first) error:
c:srvtmp> 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:srvvenvfinautfakturascriptspython.exe
cachedir: .cache
rootdir: c:srvtmp, 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 ======================
i.e. the property returned " & Simpson"
instead of the expected "Simpson"
for the first assert.
To fix this we can make the full_name
property handle this added complexity as well, or.., we can solve the problem somewhere else, e.g. in the __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
running pytest again shows that this fixed the second test.
You can of course not provide your own __init__
in a Django model to solve this problem, but you can do something similar in if you override the save(..)
method:
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)
add a comment |
The way you've defined the names, any and all of them can be None
, if you change them to the empty string you'll have similar problems. To illustrate, lets start by writing a unit test (substitute the empty string for None if you want):
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"
Then it's just a question of dividing the problem into smaller pieces, I've put everything into a regular class just to make it easier to test. First some helper methods:
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
then we can define the full_name
property:
@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)
if we put everything in a file named fullname.py
we can use the pytest
tool (pip install pytest
) to run the tests:
c:srvtmp> 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:srvvenvfinautfakturascriptspython.exe
cachedir: .cache
rootdir: c:srvtmp, 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 ===========================
All is well... or is it?
Let's write another test:
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"
running pytest again reveals the (first) error:
c:srvtmp> 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:srvvenvfinautfakturascriptspython.exe
cachedir: .cache
rootdir: c:srvtmp, 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 ======================
i.e. the property returned " & Simpson"
instead of the expected "Simpson"
for the first assert.
To fix this we can make the full_name
property handle this added complexity as well, or.., we can solve the problem somewhere else, e.g. in the __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
running pytest again shows that this fixed the second test.
You can of course not provide your own __init__
in a Django model to solve this problem, but you can do something similar in if you override the save(..)
method:
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)
The way you've defined the names, any and all of them can be None
, if you change them to the empty string you'll have similar problems. To illustrate, lets start by writing a unit test (substitute the empty string for None if you want):
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"
Then it's just a question of dividing the problem into smaller pieces, I've put everything into a regular class just to make it easier to test. First some helper methods:
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
then we can define the full_name
property:
@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)
if we put everything in a file named fullname.py
we can use the pytest
tool (pip install pytest
) to run the tests:
c:srvtmp> 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:srvvenvfinautfakturascriptspython.exe
cachedir: .cache
rootdir: c:srvtmp, 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 ===========================
All is well... or is it?
Let's write another test:
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"
running pytest again reveals the (first) error:
c:srvtmp> 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:srvvenvfinautfakturascriptspython.exe
cachedir: .cache
rootdir: c:srvtmp, 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 ======================
i.e. the property returned " & Simpson"
instead of the expected "Simpson"
for the first assert.
To fix this we can make the full_name
property handle this added complexity as well, or.., we can solve the problem somewhere else, e.g. in the __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
running pytest again shows that this fixed the second test.
You can of course not provide your own __init__
in a Django model to solve this problem, but you can do something similar in if you override the save(..)
method:
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)
answered Mar 26 at 21:13
thebjornthebjorn
14.8k6 gold badges53 silver badges90 bronze badges
14.8k6 gold badges53 silver badges90 bronze badges
add a comment |
add a comment |
Thanks for contributing an answer to Stack Overflow!
- Please be sure to answer the question. Provide details and share your research!
But avoid …
- Asking for help, clarification, or responding to other answers.
- Making statements based on opinion; back them up with references or personal experience.
To learn more, see our tips on writing great answers.
Sign up or log in
StackExchange.ready(function ()
StackExchange.helpers.onClickDraftSave('#login-link');
);
Sign up using Google
Sign up using Facebook
Sign up using Email and Password
Post as a guest
Required, but never shown
StackExchange.ready(
function ()
StackExchange.openid.initPostLogin('.new-post-login', 'https%3a%2f%2fstackoverflow.com%2fquestions%2f55364924%2fempty-value-for-string-in-model-function%23new-answer', 'question_page');
);
Post as a guest
Required, but never shown
Sign up or log in
StackExchange.ready(function ()
StackExchange.helpers.onClickDraftSave('#login-link');
);
Sign up using Google
Sign up using Facebook
Sign up using Email and Password
Post as a guest
Required, but never shown
Sign up or log in
StackExchange.ready(function ()
StackExchange.helpers.onClickDraftSave('#login-link');
);
Sign up using Google
Sign up using Facebook
Sign up using Email and Password
Post as a guest
Required, but never shown
Sign up or log in
StackExchange.ready(function ()
StackExchange.helpers.onClickDraftSave('#login-link');
);
Sign up using Google
Sign up using Facebook
Sign up using Email and Password
Sign up using Google
Sign up using Facebook
Sign up using Email and Password
Post as a guest
Required, but never shown
Required, but never shown
Required, but never shown
Required, but never shown
Required, but never shown
Required, but never shown
Required, but never shown
Required, but never shown
Required, but never shown