Home > Blockchain >  How to add a default value to uniqueness constraint
How to add a default value to uniqueness constraint

Time:06-09

I have a best practice question when it comes to default values, uniqueness constraints and API design. For this exercise, I am creating a pokedex api with the following database schema in postgresql.

 create_table :pokemon, id: :uuid do |t|
      t.string :name, null: false, unique: true
      t.string :national_index, null: false, unique: true
      t.text :description, default: 'unknown', null: false
      t.string :hp, default: '0', null: false
      t.string :attack, default: '0', null: false
      t.string :defense, default: '0', null: false
      t.string :special_attack, default: '0', null: false
      t.string :special_defense, default: '0', null: false
      t.string :speed, default: '0', null: false
      t.string :height, default: '0', null: false
      t.string :weight, default: '0', null: false
      t.boolean :male, default: false, null: false
      t.boolean :female, default: false, null: false
      t.string :category, default: 'unknown', null: false
      t.timestamps
    end

As you can see from this rails migration, I have presence constraints on all my fields with the addition of a unique constraint for the name of the pokemon and its national index. Additionally, you can see in the database migration, a pokemon's battle statistics are represented with a default value of 0 in the event that one cannot determine a pokemon's battle statistic and its description and category fall under 'unknown' as its default value.

Here is my situation and I think it can be best explained with a user use case.

Let's say that a pokemon researcher encounters a new undiscovered pokemon and uploads this pokemon to the pokedex database though the api that I am creating. I imagine in this case, the pokemon researcher will have to give this pokmeon a name (which can later be changed) and the rest of this pokemon's field will all be filled with default values that can also be changed later once more research has been conducted on the pokemon. However, the pokemon researcher can't assign it a national index number because it is an "unknown" pokemon. Only an officially recognized pokemon can have a national index number and no two pokemon official or undiscovered can have a conflicting number. In other words its not officially recognized. So my question, is what can I use to add a default value to a field that Im defining as present and unique? I would love to give it a N/A, unkown, or other type of value but I'm unable to because they aren't unique. The next undiscovered pokemon can't also have a value of 'N/A' for its national index number. Is there a best practice for a situation like this?

You can argue that perhaps it would be best to just leave those fields empty but from this excellent book I'm reading about API design, I specifically refer to this section under default values for strings.

Similarly, to number and Boolean fields, many serialization formats don't necessarily permit a distinct null value (null) from a zero value(""). As a result, it can be difficult to determine the difference between a user specifying that a string should be the empty string rather than a user specifically asking the API to 'do what's best' for the field given the rest of the context.

Luckily though, there are quite a few options available. In many cases, an empty string is simply not a valid value for a field. As a result, the empty string can indeed be used as a flag indicating that a default value should be injected and saved. In other cases, the string value might have a specific set of appropriate values, with the empty string being on of the choices. In this scenario, it's perfectly reasonable to allow a choice of 'default' to act as a flag indicating that a default value should be stored instead.

the book seems to be advocating for a default value, but is it even possible in the situation I described? Basically, I want my api to be predictable and reliable and I imagine the scenario I described to be something that happens quite often.

CodePudding user response:

You can set default values at the application level for more complex scenarios

class Pokemon < ActiveRecord::Base
  before_validation :set_national_index, on: :create

  private

  def set_national_index
    self.national_index = if official?
      # ...
    else
      "unknown_#{Time.zone.now.to_i}_#{rand(99999)}"
    end
  end
end
  • Related