|
| 1 | +.. _mongoid-field-behaviors: |
| 2 | + |
| 3 | +========================= |
| 4 | +Customize Field Behaviors |
| 5 | +========================= |
| 6 | + |
| 7 | +.. facet:: |
| 8 | + :name: genre |
| 9 | + :values: reference |
| 10 | + |
| 11 | +.. meta:: |
| 12 | + :keywords: customize, attributes, optimize, model, configure, code example |
| 13 | + |
| 14 | +.. contents:: On this page |
| 15 | + :local: |
| 16 | + :backlinks: none |
| 17 | + :depth: 1 |
| 18 | + :class: singlecol |
| 19 | + |
| 20 | +Overview |
| 21 | +-------- |
| 22 | + |
| 23 | +In this guide, you can learn how to customize the behavior of fields in {+odm+} models. |
| 24 | + |
| 25 | +Specify Default Values |
| 26 | +---------------------- |
| 27 | + |
| 28 | +You can configure fields to have default values by using the ``default`` option. |
| 29 | +Default field values can be either fixed or ``Proc`` values. |
| 30 | + |
| 31 | +The following example specifies a fixed default value for the ``state`` |
| 32 | +field: |
| 33 | + |
| 34 | +.. literalinclude:: /includes/data-modeling/field-behaviors.rb |
| 35 | + :language: ruby |
| 36 | + :start-after: # start-field-default |
| 37 | + :end-before: # end-field-default |
| 38 | + |
| 39 | +The following example specifies a ``Proc`` default value for the ``fulfill_by`` |
| 40 | +field: |
| 41 | + |
| 42 | +.. literalinclude:: /includes/data-modeling/field-behaviors.rb |
| 43 | + :language: ruby |
| 44 | + :start-after: # start-field-default-processed |
| 45 | + :end-before: # end-field-default-processed |
| 46 | + |
| 47 | +.. note:: |
| 48 | + |
| 49 | + The driver evaluates default values that are not ``Proc`` instances when the |
| 50 | + class *loads*. The driver evaluates ``Proc`` values when the document is |
| 51 | + *instantiated*. The following default field values do not produce equivalent outcomes: |
| 52 | + |
| 53 | + .. code-block:: ruby |
| 54 | + |
| 55 | + # Time.now is set to the time the class is loaded |
| 56 | + field :submitted_at, type: Time, default: Time.now |
| 57 | + |
| 58 | + # Time.now is set to the time the document is instantiated |
| 59 | + field :submitted_at, type: Time, default: ->{ Time.now } |
| 60 | + |
| 61 | +You can set a default value that depends on the document's state by using the |
| 62 | +``self`` keyword in a ``Proc`` instance. The following example sets the |
| 63 | +``fulfill_by`` default value to depend on the state of the ``submitted_at`` |
| 64 | +field: |
| 65 | + |
| 66 | +.. literalinclude:: /includes/data-modeling/field-behaviors.rb |
| 67 | + :language: ruby |
| 68 | + :start-after: # start-field-default-self |
| 69 | + :end-before: # end-field-default-self |
| 70 | + |
| 71 | +By default, {+odm+} applies ``Proc`` default values after setting and |
| 72 | +initializing all other attributes. To apply the default before setting the other |
| 73 | +attributes, set the ``pre_processed`` option to ``true``, as shown in the |
| 74 | +following example: |
| 75 | + |
| 76 | +.. literalinclude:: /includes/data-modeling/field-behaviors.rb |
| 77 | + :language: ruby |
| 78 | + :start-after: # start-field-default-pre-processed |
| 79 | + :end-before: # end-field-default-pre-processed |
| 80 | + |
| 81 | +.. tip:: |
| 82 | + |
| 83 | + Always set the ``pre-processed`` option to ``true`` to set a |
| 84 | + default ``Proc`` value for the ``_id`` field. |
| 85 | + |
| 86 | +Specify Storage Names |
| 87 | +--------------------- |
| 88 | + |
| 89 | +You can specify a separate field name to store in the database, while still |
| 90 | +referring to the field by its original name in your application. This can save |
| 91 | +storage space, because MongoDB stores all field information along |
| 92 | +with every document. |
| 93 | + |
| 94 | +You can set an alternate storage name by using the ``as:`` keyword. The |
| 95 | +following example creates a field called ``name`` that {+odm+} stores in the database as |
| 96 | +``n``: |
| 97 | + |
| 98 | +.. literalinclude:: /includes/data-modeling/field-behaviors.rb |
| 99 | + :language: ruby |
| 100 | + :start-after: # start-field-as |
| 101 | + :end-before: # end-field-as |
| 102 | + |
| 103 | +{+odm+} stores the ``name`` field as ``"n"``, but you can still access the field as |
| 104 | +``name`` in your application. |
| 105 | + |
| 106 | +Field Aliases |
| 107 | +------------- |
| 108 | + |
| 109 | +You can create an alias for your field by using the ``alias_attribute`` option. |
| 110 | +Specifying an alias does not change how {+odm+} stores the field in the |
| 111 | +database, but it allows you to access the field by a different name in your |
| 112 | +application. |
| 113 | + |
| 114 | +The following example specifies an alias for the ``name`` field: |
| 115 | + |
| 116 | +.. literalinclude:: /includes/data-modeling/field-behaviors.rb |
| 117 | + :language: ruby |
| 118 | + :start-after: # start-field-alias |
| 119 | + :end-before: # end-field-alias |
| 120 | + |
| 121 | +To remove a field alias, you can use the ``unalias_attribute`` option. The |
| 122 | +following example removes the alias for the ``name`` field: |
| 123 | + |
| 124 | +.. literalinclude:: /includes/data-modeling/field-behaviors.rb |
| 125 | + :language: ruby |
| 126 | + :start-after: # start-field-unalias |
| 127 | + :end-before: # end-field-unalias |
| 128 | + |
| 129 | +You can also use ``unalias_attribute`` to remove the predefined ``id`` alias from the |
| 130 | +``_id`` field. This can be used to store different values in the ``_id`` field |
| 131 | +and an ``id`` field. |
| 132 | + |
| 133 | +Field Redefinition |
| 134 | +------------------ |
| 135 | + |
| 136 | +By default, {+odm+} allows you to redefine fields on a model. To raise an error |
| 137 | +when a field is redefined, set the ``duplicate_fields_exception`` configuration |
| 138 | +option in your ``mongoid.yml`` file to ``true``. |
| 139 | + |
| 140 | +If the ``duplicate_fields_exception`` option is set to ``true``, you can still |
| 141 | +redefine a specific field by setting the ``overwrite`` option to ``true`` when |
| 142 | +you define the field. The following example defines the ``name`` field, and then |
| 143 | +redefines the field by using the ``overwrite`` option: |
| 144 | + |
| 145 | +.. literalinclude:: /includes/data-modeling/field-behaviors.rb |
| 146 | + :language: ruby |
| 147 | + :start-after: # start-field-overwrite |
| 148 | + :end-before: # end-field-overwrite |
| 149 | + |
| 150 | +Custom ID Field |
| 151 | +--------------- |
| 152 | + |
| 153 | +By default, {+odm+} defines the ``_id`` field on documents to contain a |
| 154 | +``BSON::ObjectId`` value that {+odm+} generates automatically. You can customize |
| 155 | +the type or specify the default value of the ``_id`` field by specifying it in your |
| 156 | +model. |
| 157 | + |
| 158 | +The following example creates a ``Band`` class with a custom ``_id`` field: |
| 159 | + |
| 160 | +.. literalinclude:: /includes/data-modeling/field-behaviors.rb |
| 161 | + :language: ruby |
| 162 | + :start-after: # start-custom-id |
| 163 | + :end-before: # end-custom-id |
| 164 | + |
| 165 | +You can omit the default value for the ``_id`` field. If you don't specify a |
| 166 | +default value for the field, {+odm+} persists the document without an ``_id`` |
| 167 | +value. For top-level documents, the MongoDB server automatically |
| 168 | +assigns an ``_id`` value. However, for embedded documents, the server does not |
| 169 | +assign an ``_id`` value. |
| 170 | + |
| 171 | +When you don't specify a value for the ``_id`` field, {+odm+} does not retrieve |
| 172 | +the automatically assigned value from the server. Because of this, you cannot |
| 173 | +retrieve the document from the database by using the ``_id`` value. |
| 174 | + |
| 175 | +Uncastable Values |
| 176 | +----------------- |
| 177 | + |
| 178 | +A value is considered **uncastable** if it cannot be converted to the specified |
| 179 | +field type. For example, an array is considered uncastable when assigned to an |
| 180 | +``Integer`` field. |
| 181 | + |
| 182 | +In v8.0 and later, {+odm+} assigns ``nil`` to values that are uncastable. The |
| 183 | +original uncastable value is stored in the ``attributes_before_type_cast`` hash |
| 184 | +with their field names. |
| 185 | + |
| 186 | +Custom Getters and Setters |
| 187 | +-------------------------- |
| 188 | + |
| 189 | +You can override the default getter and setter methods for a field by specifying |
| 190 | +a method with the same name as the field and calling the ``read_attribute`` or |
| 191 | +``write_attribute`` method to operate on the raw attribute value. |
| 192 | + |
| 193 | +The following example creates a custom getter and setter for the ``name`` field |
| 194 | +of a ``Person`` class: |
| 195 | + |
| 196 | +.. literalinclude:: /includes/data-modeling/field-behaviors.rb |
| 197 | + :language: ruby |
| 198 | + :start-after: # start-custom-getter-setter |
| 199 | + :end-before: # end-custom-getter-setter |
| 200 | + |
| 201 | +Read-Only Attributes |
| 202 | +-------------------- |
| 203 | + |
| 204 | +You can specify a field to be read-only by specifying the ``attr_readonly`` option. |
| 205 | +This allows you to create documents with the attributes, but not update them. |
| 206 | + |
| 207 | +The following example creates a ``Band`` class and specifies the ``name`` field |
| 208 | +as read-only: |
| 209 | + |
| 210 | +.. literalinclude:: /includes/data-modeling/field-behaviors.rb |
| 211 | + :language: ruby |
| 212 | + :start-after: # start-read-only |
| 213 | + :end-before: # end-read-only |
| 214 | + |
| 215 | +If you call a mass-update method, such as ``update_attributes``, and pass in a |
| 216 | +read-only field, {+odm+} ignores the read-only field and updates all others. If |
| 217 | +you attempt to explicitly update a read-only field, {+odm+} raises a |
| 218 | +``ReadonlyAttribute`` exception. |
| 219 | + |
| 220 | +.. note:: |
| 221 | + |
| 222 | + Calls to atomic persistence operators, such as ``bit`` and ``inc``, still |
| 223 | + persist changes to the read-only field. |
| 224 | + |
| 225 | +Localize Fields |
| 226 | +--------------- |
| 227 | + |
| 228 | +{+odm+} supports localized fields by using the `i18n gem |
| 229 | +<https://github.com/ruby-i18n/i18n>`__. When you localize a field, {+odm+} |
| 230 | +stores the field as a hash of locale keys and values. Accessing the fields |
| 231 | +behaves in the same way as a string value. You can localize fields of any field |
| 232 | +type. |
| 233 | + |
| 234 | +The following example creates a ``Product`` class with a localized ``review`` |
| 235 | +field: |
| 236 | + |
| 237 | +.. literalinclude:: /includes/data-modeling/field-behaviors.rb |
| 238 | + :language: ruby |
| 239 | + :start-after: # start-localized-field |
| 240 | + :end-before: # end-localized-field |
| 241 | + |
| 242 | +You can get and set all translations at once by calling the ``_translations`` |
| 243 | +method: |
| 244 | + |
| 245 | +.. literalinclude:: /includes/data-modeling/field-behaviors.rb |
| 246 | + :language: ruby |
| 247 | + :start-after: # start-localized-translations |
| 248 | + :end-before: # end-localized-translations |
| 249 | + |
| 250 | +You can specify fallbacks for localized fields by enabling the `i18n fallbacks |
| 251 | +<https://github.com/ruby-i18n/i18n/wiki/Fallbacks>`__ feature. |
| 252 | + |
| 253 | +Enable fallbacks in a Rails application by setting the ``config.i18n.fallbacks`` |
| 254 | +configuration setting in your environment and setting the fallback languages: |
| 255 | + |
| 256 | +.. literalinclude:: /includes/data-modeling/field-behaviors.rb |
| 257 | + :language: ruby |
| 258 | + :start-after: # start-localized-fallbacks |
| 259 | + :end-before: # end-localized-fallbacks |
| 260 | + |
| 261 | +Enable fallbacks in non-Rails applications by including the module into the i18n |
| 262 | +backend and setting the fallback languages: |
| 263 | + |
| 264 | +.. literalinclude:: /includes/data-modeling/field-behaviors.rb |
| 265 | + :language: ruby |
| 266 | + :start-after: # start-localized-fallbacks-non-rails |
| 267 | + :end-before: # end-localized-fallbacks-non-rails |
| 268 | + |
| 269 | +After enabling fallbacks, if an active language does not have have a |
| 270 | +translation, it is looked up in the specified fallback language. |
| 271 | + |
| 272 | +You can disable fallback languages for a specified field by setting the |
| 273 | +``fallbacks`` option to false when defining the field: |
| 274 | + |
| 275 | +.. literalinclude:: /includes/data-modeling/field-behaviors.rb |
| 276 | + :language: ruby |
| 277 | + :start-after: # start-localized-no-fallbacks |
| 278 | + :end-before: # end-localized-no-fallbacks |
| 279 | + |
| 280 | +When querying localized fields, {+odm+} automatically alters the query criteria |
| 281 | +to match the current locale. The following example queries the ``Product`` class |
| 282 | +for a review in the ``en`` locale: |
| 283 | + |
| 284 | +.. literalinclude:: /includes/data-modeling/field-behaviors.rb |
| 285 | + :language: ruby |
| 286 | + :start-after: # start-localized-query |
| 287 | + :end-before: # end-localized-query |
| 288 | + |
| 289 | +.. note:: |
| 290 | + |
| 291 | + If you want to query extensively on localized fields, we recommend indexing |
| 292 | + each locale that you want to query on. |
0 commit comments