ashkan117

ashkan117

Preloaded associations and views patterns

I’m wondering how do people structure their JSON Api’s with Phoenix. Using the blogs example, let’s say I have a blogs view like the following

  def render("show.json", %{blog: blog}) do
    %{
      id: blog.id,
      title: blog.title,
      posts: render_many(blog.posts, PostView, "post.json")
    }

This runs into things like what if posts has its own associations like comments, etc.

In my controller maybe sometimes I don’t care about the associations and other times I do. This leads me to two main problems?

  1. Are there any standards people follow to handle when a view should try and render associations?
  2. Should there be some helper function in Phoenix.View that handles conditionally rendering associations?

For the first problem I’m just curious how people do something like the following

  def show(conn, %{"id" => id}) do
    blog = Blogs.get_blog_with_all(id)
    # assume no associations are preloaded
    render(conn, "show.json", blog: blog)
  end

  def some_action(conn, %{"id" => id}) do
    blog = Blogs.get_blog_with_all(id, preload: [:association_one, :association_two])
    # here I want to include the two associations in my view
    render(conn, "show_with_association_one_and_two.json", blog: blog)
  end

Having a specific view for each combination of association seems tedious.

For the second problem I mean something like having a helper function maybe_render_association that will add the field to the view if the association is loaded or it will not include the field at all. My thinking is we don’t want to return nil in the case that the association is not loaded since it’s not actually nil. That pattern would look like


def render() do
  def render("post.json", %{post: post}) do
    %{
      id: post.id,
      title: post.title,
      body: post.body,
      comments: render_many(post.comments, CommentView, "comment.json")
    }
    # it would be nice if we can have it inside the above map but 
    # since it's a conditional field not sure how
    |> maybe_render_association(post.updated_by, :updated_by, User, "basic.json")
  end
end

Most Liked

LostKobrakai

LostKobrakai

I don’t think phoenix should drive such helpers. I’d argue a none preloaded association reaching a view template expecting that association to be preloaded is an error. There’s no definitive answer to if that association is missing because of a bug, because of the intention to skip the key of the association in the json or because of the intention to mask the data not being loaded with an empty value ([] or nil). I’ve seen people argue in favor of all of these, so a generalize solution is likely not the answer.

You can however quite build such a helper for your project and your intentions.

Personally I’d push this concern to other places though. E.g. have PostUser – like a user as shown in relation to posts – vs a UserDetails – user as rendered by e.g. /users/:id. That requires more upfront modeling, but makes code a lot more explicit. You’re not passing around data, which could fit 10+ branches of results – and you hope you get the one you intend – but you pass around data that matches the one or few intented usecases. It also makes change easier, because the approach of a single very dynamic view template works great if all “branches” are kind of supersets of the base case. It gets hairy though once they have conflicting keys. Say eventually you want to mask part of the users as returned with posts, but not mask that for user details. It might feel like duplication at first, but going that route is more maintainable in the long run.

Where Next?

Popular in Discussions Top

New
christopheradams
I was recently asked to step up and become the maintainer for the Elixir Style Guide. It was, I believe, the first, and is now the most p...
New
NKTgLaw
Hello fellow BEAM enthusiasts, I’d like to introduce a conceptual idea I’ve been developing, which I call NKTg Law. It’s a physics-inspi...
New
markmark206
Every time I build a web app, I worry about a bunch of basic things (persisting data, knowing when to compute which things, keeping thing...
New
bartblast
Hey there! :slight_smile: I’m working on updating the Hologram website home page and would love to get your input. Current situation: ...
New
bartblast
This thread is dedicated to announcing updates to the Hologram documentation, as well as discussing any ideas for improvements and sugges...
New
axelson
Hi there! :wave: @frigidcode and I (but mostly him) have been running an Elixir Book club, we’re almost done with Designing Elixir Syste...
New
New
adam
I wonder if anyone in the community has started to use Github’s Codespaces for their team’s development? If so, what is there to learn? ...
New
AstonJ
Posting this as a separate thread as I’m sure lots of people will be curious - what will the (or the intended) differences be and when mi...
New

Other popular topics Top

srinivasu
How to handle excepions in elixir? Suppose i have A, B, C ,D, E modules. and each module has get() function. A.get() method will call th...
New
dotdotdotPaul
Okay, I'm having a heck of a time trying to figure out how to best handle the validation of belongs_to associations in Ecto. I'm sure I'...
New
bsollish-terakeet
Credo is smart enough to check for (something like) this: assert length(the_list) == 0 with this response: Checking if an enum is empt...
New
ycv005
I have followed this StackOverflow post to install the specific version of Erlang. And When I am running mix ecto.setup then getting fol...
New
jononomo
I am trying to figure out how Mix knows whether the environment is test, dev, or prod -- where is this set? Thanks.
New
chrismccord
This release brings a number of exciting features, including integration with the new Phoenix LiveDashboard and Phoenix LiveView. There h...
New
lk-geimfari
What is most correct way to open, read and parse JSON file with poison? For example if we have example.json file in root of some projec...
New
lastday4you
I wanted to check elixir version in phoenix because i found that my elixir is 1.5 but when i use Enum.chunk_by it said the function is un...
New
belgoros
I’m not a pro in using Regex and can’t figure out why the following behaviour happens, especially if we take into account the difference ...
New
vrod
I am using the Starship cross-shell prompt – it seems pretty nice, but I get some errors: [WARN] - (starship::utils): Executing command ...
New

We're in Beta

About us Mission Statement