-
-
Notifications
You must be signed in to change notification settings - Fork 4k
Creating Objects
When collecting user input, we often define the kind of input we expect using schemas. These schemas can use certain Python base types, but they can also use custom types, called objects, that we define. For example, consider this schema:
{
"type": "dict",
"properties": [
{
"name": "length",
"schema": {
"type": "custom"
"obj_type": "Real",
}
}
]
}
The schema would match a dictionary like this:
{"length": 34.5}
This schema describes a dictionary with a length
key whose value is of custom type Real
, and the Real
type is one of our objects. We define these objects as classes in extensions/objects/models/objects.py
and components in extensions/objects/templates
.
The Python classes for objects are defined in objects.py
. For example, here's the class for the int
type:
class Int(BaseObject):
"""Integer class."""
description = 'An integer.'
default_value = 0
@classmethod
def get_schema(cls):
"""Returns the object schema.
Returns:
dict. The object schema.
"""
return {
'type': 'int'
}
Object classes all meet the following criteria:
-
They inherit from either
BaseObject
or another object class. -
Their
description
class variables are set to a string describing the object. -
Their
default_value
class variables are set to the value that should be the default whenever this type is used in a rule. Note that this variable is technically not required if the object will never be used in a rule. -
They provide a
normalize()
class method that accepts a raw Python object. The method should first validate the raw object to check whether it is malformed. If the validation checks pass, then the method should return a normalized form of the object. Note that in this context, "normalized" means a primitive Python type or multiple primitive types combined with lists and/or dicts.- The
BaseObject
class provides anormalize
class method that can normalize any object so long as the object's class provides aget_schema
class method that returns a schema describing the object.BaseObject.normalize
passes the schema toschema_utils.normalize_against_schema
, so make sure thatnormalize_against_schema
won't try to normalize the object using the object class. Otherwise, there will be an endless loop betweennormalize_against_schema
andBaseObject.normalize
.
- The
Every object is accompanied by a component defined in extensions/objects/templates
and imported in extensions/objects/object-components.module.ts
. Each component provides the HTML and frontend code needed to create a form for the user to provide input in the form of the object. These components rely heavily on schema-based forms. For example, the int-editor
component's HTML is just this:
<schema-based-editor [schema]="getSchema.bind(this)"
[localValue]="value"
(localValueChange)="updateValue($event)">
</schema-based-editor>
The getSchema
function is defined in int-editor.component.ts
and returns this schema:
{
type: "int",
validators: [{
id: "is_integer"
}]
}
The schema-based-editor
component already knows how to construct a form for this schema.
Let's suppose you're creating a new object called MyObject
(in practice you should use a more descriptive name). You'll need to follow these steps:
- Create a new object class named
MyObject
. It must satisfy the criteria documented above. - Add tests for your class in
extensions/objects/models/objects_test.py
. You should add normalization tests to theObjectNormalizationUnitTests
class. You can also create a new test class to hold additional test cases. - Add a new Angular component to accept input in the form of your object. Your component files should be named
my-object-editor.component.*
, and your component class should be namedMyObjectEditorComponent
. - Import your component in
extensions/objects/object-components.module.ts
and add it to the module there.
You will usually use objects by referencing them as you create interactions, define rules, or otherwise use schemas.
Have an idea for how to improve the wiki? Please help make our documentation better by following our instructions for contributing to the wiki.
Core documentation
- Oppia's mission
- Code of Conduct
- Get involved!
- How to report a bug
- Google Summer of Code 2024
- Hacktoberfest 2024
Developing Oppia
- FAQs
- How to get help
- Getting started with the project
- How the codebase is organized
- Making your first PR
- Debugging
- Testing
- Codebase policies and processes
- Guidelines for launching new features
- Guidelines for making an urgent fix (hotfix)
- Testing jobs and other features on production
- Guidelines for Developers with Write Access to the Oppia Repository
- Release schedule and other information
- Revert and Regression Policy
- Privacy aware programming
- Code review:
- Project organization:
- QA Testing:
- Design docs:
- Team-Specific Guides
- LaCE/CD:
- Developer Workflow:
Developer Reference
- Oppiabot
- Git cheat sheet
- Frontend
- Backend
- Backend Type Annotations
- Writing state migrations
- Calculating statistics
- Storage models
- Coding for speed in GAE
- Adding a new page
- Adding static assets
- Wipeout Implementation
- Notes on NDB Datastore transactions
- How to handle merging of change lists for exploration properties
- Instructions for editing roles or actions
- Protocol buffers
- Webpack
- Third-party libraries
- Extension frameworks
- Oppia-ml Extension
- Mobile development
- Performance testing
- Build process
- Best practices for leading Oppia teams
- Past Events