title: Equivalent of Marshmallow dump_only fields for Pydantic/FastAPI without multiple schemas
body: <h3>Question</h3>
  <p>Is it possible to replicate Marshmallow's <code>dump_only</code> feature using pydantic for FastAPI, so that certain fields are &quot;read-only&quot;, <em><strong>without</strong></em> defining separate schemas for serialization and deserialization?</p>
  <h3>Context</h3>
  <p>At times, a subset of the attributes (e.g. <code>id</code> and <code>created_date</code>) for a given API resource are meant to be read-only and should be ignored from the request payload during deserialization (e.g. when POSTing to a collection or PUTting to an existing resource) but need to be returned with that schema in the response body for those same requests.</p>
  <p>Marshmallow provides a convenient <a href="https://marshmallow.readthedocs.io/en/stable/quickstart.html#read-only-and-write-only-fields" rel="nofollow noreferrer"><code>dump_only</code></a> parameter that requires only one schema to be defined for both serialization and deserialization, with the option to exclude certain fields from either operation.</p>
  <h3>Existing Solution</h3>
  <p>Most attempts I've seen to replicate this functionality within FastAPI (i.e. <a href="https://fastapi.tiangolo.com/tutorial/extra-models/" rel="nofollow noreferrer">FastAPI docs</a>, <a href="https://github.com/tiangolo/fastapi/issues/2981" rel="nofollow noreferrer">GitHub Issue</a>, <a href="https://stackoverflow.com/questions/59920126/rest-api-in-python-with-fastapi-and-pydantic-read-only-property-in-model">Related SO Question</a>) tend to define separate schemas for input (deserialization) and output (serialization) and define a common base schema for the shared fields between the two.</p>
  <p>Based on my current understanding of this approach, it seems a tad inconvenient for a few reasons:</p>
  <ol>
  <li>It requires the API developer to reserve separate namespaces for each schema, a problem that is exacerbated by following the practice of abstracting the common fields to a third &quot;base&quot; schema class.</li>
  <li>It results in the proliferation of schema classes in APIs that have nested resources, since each level of nesting requires a separate input and output schema.</li>
  <li>The the OAS-compliant documentation displays the input/output schemas as separate definitions, when the consumer of that API only ever needs to be aware of a single schema since the (de)serialization of those read-only fields should be handled properly by the API.</li>
  </ol>
  <h3>Example</h3>
  <p>Say we're developing a simple API for a survey with the following models:</p>
  <pre class="lang-py prettyprint-override"><code>from sqlalchemy.orm import declarative_base, relationship
  from sqlalchemy import (
      func,
      Column,
      Integer,
      String,
      DateTime,
      ForeignKey,
  )
  
  Base = declarative_base()
  
  
  class SurveyModel(db.Base):
      &quot;&quot;&quot;Table that represents a collection of questions&quot;&quot;&quot;
  
      __tablename__ = &quot;survey&quot;
  
      # columns
      id = Column(Integer, primary_key=True, index=True)
      name = Column(String, nullable=False)
      created_date = Column(DateTime, default=func.now())
  
      # relationships
      questions = relationship(&quot;Question&quot;, backref=&quot;survey&quot;)
  
  
  class QuestionModel(Base):
      &quot;&quot;&quot;Table that contains the questions that comprise a given survey&quot;&quot;&quot;
  
      __tablename__ = &quot;question&quot;
  
      # columns
      id = Column(Integer, primary_key=True, index=True)
      survey_id = Column(Integer, ForeignKey(&quot;survey.id&quot;))
      text = Column(String)
      created_date = Column(DateTime, default=func.now())
  </code></pre>
  <p>And we wanted a <code>POST /surveys</code> endpoint to accept the following payload in the request body:</p>
  <pre><code>{
      &quot;name&quot;: &quot;First Survey&quot;,
      &quot;questions&quot;: [
          {&quot;text&quot;: &quot;Question 1&quot;},
          {&quot;text&quot;: &quot;Question 2&quot;}
      ]
  }
  </code></pre>
  <p>And return the following in the response body:</p>
  <pre><code>{
      &quot;id&quot;: 1,
      &quot;name&quot;: &quot;First Survey&quot;,
      &quot;created_date&quot;: &quot;2021-12-12T00:00:30&quot;,
      &quot;questions&quot;: [
          {
               &quot;id&quot;: 1,
               &quot;text&quot;: &quot;Question 1&quot;,
               &quot;created_date&quot;: &quot;2021-12-12T00:00:30&quot;
          },
          {
               &quot;id&quot;: 2,
               &quot;text&quot;: &quot;Question 2&quot;,
               &quot;created_date&quot;: &quot;2021-12-12T00:00:30&quot;
          },
      ]
  }
  </code></pre>
  <p>Is there an alternative way to make <code>id</code> and <code>created_date</code> read-only for both <code>QuestionModel</code> and <code>SurveyModel</code> other than defining the schemas like this?</p>
  <pre class="lang-py prettyprint-override"><code>from datetime import datetime
  from typing import List
  
  from pydantic import BaseModel
  
  
  class QuestionIn(BaseModel):
      text: str
  
      class Config:
          extra = &quot;ignore&quot;  # ignores extra fields passed to schema
  
  
  class QuestionOut(QuestionIn):
      id: int
      created_date: datetime
  
  
  class SurveyBase(BaseModel):
      name: str
  
      class Config:
          extra = &quot;ignore&quot;  # ignores extra fields passed to schema
  
          
  class SurveyOut(SurveyBase):
      id: int
      created_date: datetime
  
  
  class SurveyQuestionsIn(SurveyBase):
      questions: List[QuestionIn]
  
  
  class SurveyQuestionsOut(SurveyOut):
      questions: List[QuestionOut]
  </code></pre>
  <p>Just for comparison, here would be the equivalent schema using marshmallow:</p>
  <pre><code>from marshmallow import Schema, fields
  
  
  class Question(Schema):
      id = fields.Integer(dump_only=True)
      created_date = fields.DateTime(dump_only=True)
      text = fields.String(required=True)
  
  class Survey(Schema):
      id = fields.Integer(dump_only=True)
      created_date = fields.DateTime(dump_only=True)
      name = fields.String(required=True)
      questions = fields.List(fields.Nested(Question))
  </code></pre>
  <h3>References</h3>
  <ol>
  <li><a href="https://marshmallow.readthedocs.io/en/stable/quickstart.html#read-only-and-write-only-fields" rel="nofollow noreferrer">Marshmallow read-only/load-only fields</a></li>
  <li><a href="https://stackoverflow.com/questions/59920126/rest-api-in-python-with-fastapi-and-pydantic-read-only-property-in-model">Existing Stack Exchange Question</a></li>
  <li><a href="https://github.com/tiangolo/fastapi/issues/2981" rel="nofollow noreferrer">Read-only fields issue on FastAPI repo</a></li>
  <li><a href="https://fastapi.tiangolo.com/tutorial/extra-models/" rel="nofollow noreferrer">FastAPI documentation on Schemas</a></li>
  </ol>