Pulling a data file based on filename

I have data files with filenames set to the same as the content files they relate to, for example:

content / post / content-item.md
data / content-item.toml

I need to have this set-up as the data files are auto-generated from an API (usually I’d put this info into the page param).

What I’ve been doing is having a param in the data file such as
post = "content-item"
and looping through the data files, comparing the post param to the slug on the content item in order to render out the correct file contents. This works fine, but I’d like to get rid of the requirement of the additional param.

Is there a way of filtering {{ range .Site.Data }} based on the slug directly?

(In existing example terms as on http://gohugo.io/extras/datafiles/ I’d like get a specific discography based on a filename set in a variable).


@tatsushid is the expert on this, but you can use the where filter (see doc) with params (like post in your case) with all its operators.

I guess you can also use it with Page with all its fields and methods. But you ask for several different things, so it’s hard to give a simple answer.

Test it.

I have this problem too, and I thought I could solve it with code like:

{{ range where $.Site.Data.places "name" .Params.data_name }}
   {{ . }}
{{ end }}

…based on the documentation at http://gohugo.io/templates/functions/

Here I would have for instance:

# data/places/first.toml
name = "foo"

…and in the Front Matter for content/places/first.md I would specify

data_name = "foo"

However it doesn’t work, and gives me this error:

ERROR: (...) error calling where: can't iterate over map[string]interface {}

What I really want to do, like @adamliptrot, is get the struct for a data file by name (sans extension), so I don’t have to bother with the “data_name” business and can do it based on the path. That is, access the value of the map by its key. But I am perfectly willing to have a param/property pair if that’s what it takes.

Any suggestions? Is this a bug?

By the way, my use case is a multilingual site in which each “place” has several different content items (one per language) but they all share the same core data (address, url, etc).

You are linking to the doc; please read it:

Works on lists, taxonomies, terms, groups

It should maybe say that the list quantity is Pages, but this does not include some ad hoc string map.

The error says it’s a map, so that is a hint.

The index func may help.

Here’s another example that works nicely for creating entirely data-driven hugo sites.

Inside the data/person/demo.toml file we have a basic example person:

first = "Demo"
last = "Person"
pubname = "Demo"
github = "youraccount"

Then in layouts/student/block.html view template, (which I use to include in the _default/single.html template):

{{ with (index .Site.Data.person (substr $.File.LogicalName 0 -3)) }}
<div class="student">
  <div class="student__pubname">{{ .pubname }}</div>
{{ end }}

Notice that the date is a logical person and the template is for a student type. This is because in the larger application I only have person logical data types and cast them to roles and views using this method of type views and content, which brings us to the content/student/demo.md file. It’s empty. Just add it. This is because as far as I know, Hugo doesn’t actually create any single page unless there is a corresponding content file of some kind. At first this seems annoying, but turns out it is not that bad and allows you to turn on or off a page simple by removing or touching the content file.

Thanks for this solution @robmuh. I don’t quite grasp data files fully and this is as close as got to getting mine to work how I’d like them too. Using this method how would one then range through data file?

I was running into this same issue and wanted to post my full solution. Hopefully it will help someone.

I’m porting a blog from Jekyll and have this structure for my comments:

├── some-post/
│   ├── entry202010233322.json
│   └── entry202009014483.json
└── another-post/
    └── entry201910233322.json

The some-post and another-post names correspond to blog post slugs like this:

├── 2020-01-01-some-post/
│   └── index.md
└── 2021-01-02-another-post/
    └── index.md

In my single.html template I eventually got it to iterate over comments corresponding to the current post with the following (relying on a slug attribute being defined in each post):

{{ with (index .Site.Data.blog.comments .Params.slug ) }}
  {{ range . }}
  {{ end }}
{{ end }}

And to base it on the filename of the blog post rather than .Params.slug I did this to chop off the date portion of the parent directory’s name (the first 11 characters):

{{ $slug := substr (index (split .File.Dir "/") 1) 11 }}
{{ with (index .Site.Data.blog.comments $slug) }}
  {{ range . }}
  {{ end }}
{{ end }}

With either of the above, that inner {{ range . }} lets me access comment data like {{ .name }} and {{ .comment }}.