2016-04-14 15 views
2

Ich habe drei Modelle:Wie stellen Sie sicher, dass die Tabelle keine Duplikate enthält (basierend auf zwei Fremdschlüsseln)?

class Ingredient < ActiveRecord::Base 
    has_one :component 
end 

class Size < ActiveRecord::Base 
    has_one :component 
end 

class Component < ActiveRecord::Base 
    belongs_to :ingredient 
    belongs_to :size 
end 

Mein schema.rb wie folgt aussieht:

ActiveRecord::Schema.define(version: 20160414202240) do 

    create_table "components", force: :cascade do |t| 
    t.boolean "active" 
    t.decimal "cost" 
    t.decimal "price" 
    t.datetime "created_at", null: false 
    t.datetime "updated_at", null: false 
    t.integer "size_id" 
    t.integer "ingredient_id" 
    end 

    add_index "components", ["ingredient_id"], name: "index_components_on_ingredient_id" 
    add_index "components", ["size_id"], name: "index_components_on_size_id" 

    create_table "ingredients", force: :cascade do |t| 
    t.string "name" 
    t.boolean "active" 
    t.datetime "created_at", null: false 
    t.datetime "updated_at", null: false 
    end 

    create_table "sizes", force: :cascade do |t| 
    t.string "name" 
    t.boolean "active" 
    t.datetime "created_at", null: false 
    t.datetime "updated_at", null: false 
    end 

end 

Nun bin Impfen ich die Datenbank mit:

size_small = Size.create(name: 'Small', active: true) 
size_medium = Size.create(name: 'Medium', active: true) 
size_large = Size.create(name: 'Large', active: true) 

ingredient_tomato = Ingredient.create(name: 'Tomato', active: true) 
ingredient_onion = Ingredient.create(name: 'Onion', active: true) 
ingredient_red_onion = Ingredient.create(name: 'Red onion', active: true) 
ingredient_champignons = Ingredient.create(name: 'Champignons', active: true) 
ingredient_shrimps = Ingredient.create(name: 'Shrimps', active: true) 

Component.create(cost: 0.20, price: 1.00, active: true, size: size_small, ingredient: ingredient_tomato) 
Component.create(cost: 0.20, price: 1.00, active: true, size: size_small, ingredient: ingredient_onion) 
Component.create(cost: 0.20, price: 1.00, active: true, size: size_small, ingredient: ingredient_red_onion) 
Component.create(cost: 0.30, price: 1.00, active: true, size: size_small, ingredient: ingredient_champignons) 
Component.create(cost: 0.50, price: 1.50, active: true, size: size_small, ingredient: ingredient_shrimps) 

Component.create(cost: 0.30, price: 1.50, active: true, size: size_medium, ingredient: ingredient_tomato) 
Component.create(cost: 0.30, price: 1.50, active: true, size: size_medium, ingredient: ingredient_onion) 
Component.create(cost: 0.30, price: 1.50, active: true, size: size_medium, ingredient: ingredient_red_onion) 
Component.create(cost: 0.45, price: 1.50, active: true, size: size_medium, ingredient: ingredient_champignons) 
Component.create(cost: 0.75, price: 2.25, active: true, size: size_medium, ingredient: ingredient_shrimps) 

Component.create(cost: 0.40, price: 2.00, active: true, size: size_large, ingredient: ingredient_tomato) 
Component.create(cost: 0.40, price: 2.00, active: true, size: size_large, ingredient: ingredient_onion) 
Component.create(cost: 0.40, price: 2.00, active: true, size: size_large, ingredient: ingredient_red_onion) 
Component.create(cost: 0.60, price: 2.00, active: true, size: size_large, ingredient: ingredient_champignons) 
Component.create(cost: 1.00, price: 3.00, active: true, size: size_large, ingredient: ingredient_shrimps) 

# This one uses again `size_small` and `ingredient_tomato` and shouldn't be allowed. 
Component.create(cost: 2.99, price: 7.99, active: true, size: size_small, ingredient: ingredient_tomato) 

Was die meisten rails-api Weg validieren Sie, dass die size und ingredient kombiniert zusammen sollten ineinzigartig seinTisch?

Sollte ich eine Logik in Component 's Controller implementieren oder ist es möglich, einige Regeln/Bereiche/was auch immer?

Bitte verzeihen Sie meine Unwissenheit, ich habe gerade begonnen, Ruby (und Rails) zu lernen.

Ich verwende Rails 4.2.6 mit Ruby 2.3.0p0 (2015-12-25 revision 53290).

Vielen Dank im Voraus.

Antwort

2

Sie müssen dem Modell eine Gültigkeitsbereichs-Eindeutigkeitsprüfung und einen eindeutigen Index für die Komponenten-Tabelle hinzufügen.

# app/models/component.rb 
class Component < ActiveRecord::Base 
    ... 
    validates :size_id, uniqueness: { scope: :ingredient_id } 
    ... 
end 

# app/db/migrate/20160412134948_add_uniqueness_index_to_components_table.rb 
class AddUniquenessIndexToComponentsTable 
    def change 
    add_index :components, [:size_id, :ingredient_id], unique: true, name: 'components_uniqueness_validation' 
    end 
end 

Der Grund fügen Sie den eindeutigen Index mit einem Multi-Threaded-Server, um sicherzustellen, Einzigartigkeit ist. Hier ist ein Artikel, wo Sie mehr über Threadsafe Eindeutigkeit Validierungen mit Rails lesen können, http://www.kpheasey.com/2016/02/09/thread-safe-model-uniqueness-validations/

+0

Danke für einen tollen Hinweis. Zumindest habe ich ein gutes Gefühl für Bereiche. :-) Noch eine Frage - sollte ich etwas in einem Controller eines Modells tun, um API-Benutzer zu informieren (versuchen, solche Art von wiederholten Informationen mit POST-Methode hinzufügen) oder Rails (mit Hilfe der oben genannten Bereich Konfiguration) wird einen gültigen HTTP-Return-Code zurückgeben (und vielleicht ein Fehler) an sich? – tommus

+0

@tommus Sie müssen nichts in der Steuerung tun. Die Modellvalidierungen verhindern, dass der Datensatz gespeichert wird, und Sie sollten bereits Modellfehler in den Ansichten anzeigen. Werfen Sie einen Blick auf die Schienen Guides auf dieser, http://guides.rubyonrails.org/active_record_validations.html – KPheasey