I have member associated with two kind of prefectures.
Both prefectures is not required in database.
I have following code.
schema "members" do
field :name, :string
belongs_to :prefecture1, Sample.Prefecture
belongs_to :prefecture2, Sample.Prefecture
end
When I try to get members and get JSON in view, it cause errors.
Because prefectures is not alway associated with members.
def list_members(member_id) do
query = from m in Member,
where: m.member_id == ^member_id,
preload: [:prefecture1, :prefecture2]
Repo.all(query)
end
def render("member.json", %{member: member}) do
%{id: member.id,
prefecture1_name: member.prefecture1.prefecture_name,
prefecture2_name: member.prefecture2.prefecture_name}
end
UndefinedFunctionError at GET /api/members/2281
function nil.prefecture_name/0 is undefined
Please give me some advice how to avoid error and to handle this case.
It depends what You want to do, a simple if can return āā if nil, but if You donāt want the key, You might do it like this.
def render("member.json", %{member: member}) do
Enum.reduce([:prefecture1, :prefecture2], %{id: member.id}, fn x, acc ->
prefecture = Map.get(member, x)
key = String.to_atom("#{x}_name")
if prefecture, do: Map.put(acc, key, prefecture.prefecture_name), else: acc
end)
end
But itās not tested⦠and I donāt like to generate atoms dynamically.
You mean I need to put some logic which return map?
You cannot call prefecture_name on an empty prefecture, so You should test itās presence first.
Yes, I understand. However I did not know how to know presence and to fill with āā value.
I have to understand your sample code first.
prefecture1_name: if member.prefecture1, do: member.prefecture1.prefecture_name, else: ""
# or if nil is ok
prefecture1_name: if member.prefecture1, do: member.prefecture1.prefecture_name
1 Like
I think you are right. However itās ashamed I canāt write right code. I got syntax error.
If you really need to fall back to the empty string when having a nil
input then you can just have a convenience function somewhere in your views, controller or the helpers:
def prefecture_name(%{name: name}) when is_binary(name), do: name
def prefecture_name(%{name: nil}), do: ""
And just call it like this:
def render("member.json", %{member: member}) do
%{
id: member.id,
prefecture1_name: prefecture_name(member.prefecture1),
prefecture2_name: prefecture_name(member.prefecture2)
}
end
This should fail, I think⦠Itās not the name (prefecture_name), but the prefecture which can be nil.
Isnāt it like this?
def prefecture_name(%{prefecture_name: name}) when is_binary(name), do: name
def prefecture_name(nil), do: ""
1 Like
Ah, oops. Oversight. You are right.
But in any case I never do this: if something, do: :yes, else: :no
ā it reads like a PHP-ism and is not explicit. Iād always check for nil
specifically and I never cared what is ātruthyā or āfalsyā. Code readability > everything else!
1 Like
I agree, i prefer not to use if, but I do exception for one liner command
I miss ternary operator
I would just do thisā¦
def render("member.json", %{member: member}) do
%{
id: member.id,
prefecture1_name: member.prefecture1[:prefecture_name] || "",
prefecture2_name: member.prefecture2[:prefecture_name] || "",
}
end
By accessing the prefecture#
's fields via [:fieldname]
instead of .fieldname
it will pass the existing nil
on, then the || ""
will convert any false
or nil
(nil
in this case) to ""
.
Yeah but as @kokolegorille pointed out, the object itself might be nil
, not the attribute. I overlooked this as well!
@dimitarvp Correct, thatās precisely why I used [...]
for that, it handles when prefecture1
or prefecture2
is nill and just passes it on without looking up :prefecture_name
.
1 Like
Donāt mind me, I caught the flu again and Iām doing more damage to the forum instead of contributing. 
Sorry!
2 Likes
Heh, happens to us all, some of my work is out for the ācommon coldā. ^.^