Handbook contents

Going further 05~7 min

Project configuration

Why does a staging model become a view in the right schema with the right grants, when your file contains nothing but a SELECT? Because dbt_project.yml decided it already.

One file, all the defaults

dbt_project.yml at the repo root configures every model by folder path. An abridged view of ours:

dbt_project.yml (abridged)
name: 'wnl_analytics'
profile: 'wnl_analytics'

models:
  wnl_analytics:
    +materialized: table          # project default
    +persist_docs:
      columns: true               # YAML descriptions -> Snowflake comments
    +post-hook: ["{{ grant_ownership_with_copy() }}"]

    raw:
      +materialized: view
      +database: "STAGING"
      +schema: "DBT_RAW"

    staging:
      +materialized: view         # staging overrides the default
      +database: "STAGING"
      +tags: ["staging", "daily"]

    reporting:
      +database: "REPORTING"
      +tags: ["reporting"]
      commissioning:
        +schema: "COMMISSIONING_REPORTING"

    published:
      +tags: ["published_reporting"]
      direct_care:
        +database: "PUBLISHED_REPORTING__DIRECT_CARE"
      secondary_use:
        +database: "PUBLISHED_REPORTING__SECONDARY_USE"

Read it top-down: settings cascade from project → folder → subfolder, with deeper levels winning. The + prefix marks a config property (as opposed to a folder name).

Raw is the exception with one fixed schema: STAGING.DBT_RAW. Staging models use source or domain schemas within the STAGING database, including CSDS, OLIDS andREFERENCE. The project's schema configuration derives or assigns the appropriate schema rather than putting every staging model into one shared schema.

The hierarchy

When the same setting is defined in more than one place, the most specific wins:

LevelExampleWins over
config() in the model file{{ config(materialized='view') }}everything below
Folder in dbt_project.ymlstaging: +materialized: viewproject default
Project default+materialized: tabledbt's built-in default

This is why your staging model file contains only a SELECT: location does the configuring. It is also why putting a file in the right folder is a real decision — it sets materialisation, database, schema, tags and post-hooks in one move.

Tags drive the schedules

Those +tags are not decoration. The nightly build selects models by tag — daily-tagged layers rebuild every night; heavier marts run on their own cadence. You can use them too:

dbt build -s tag:daily          # everything in the nightly selection
dbt ls -s tag:reporting         # list models carrying a tag

Variables: one value, overridable per run

Some values need to be consistent everywhere but changeable for a single run — reference dates are the project's main example. The QOF reference date is defined once and read through a macro; every register model calls {{ qof_reference_date() }} rather than hardcoding a date. To rebuild a register as of a different date, override it at the command line:

dbt build -s fct_person_diabetes_register --vars '{"qof_reference_date": "2025-03-31"}'

One definition in normal use, an explicit override when you need to rewind — and the override is visible in the command, not hidden in an edited file.

Post-hooks: governance on autopilot

The project-level +post-hook runs after every model build — transferring ownership so the right roles can manage the object. Published models add hooks for Snowflake governance tags. You get all of this without writing a line: it is the layer doing the work.

Quiz

0/2 answered
  1. 1A model file has config(materialized='view') but its folder sets +materialized: table. What builds?

  2. 2Moving a model file between layer folders can change…