1

Bei den folgenden Modellen:Prevent doppelte has_many Datensätze in Rails 5

class Client < ApplicationRecord 
    has_many :preferences 
    validates_associated :preferences 
    accepts_nested_attributes_for :preferences 
end 

class Preference < ApplicationRecord 
    belongs_to :client 
    validates_uniqueness_of :block, scope: [:day, :client_id] 
end 

Ich bin noch in der Lage Vorlieben mit doppelten Tagen zu erstellen * wenn eine Charge von Präferenzen während der Client-Erstellung zu schaffen. Dies liegt (scheinbar) daran, dass der Fremdschlüssel client_id nicht verfügbar ist, wenn die Validierung validates_uniqueness_of ausgeführt wird. (* Ich habe einen Index, der verhindert, dass das Duplikat gespeichert wird, aber ich möchte den Fehler abfangen und eine benutzerfreundliche Fehlermeldung zurückgeben, bevor er die Datenbank erreicht.)

Gibt es einen Weg zu verhindern dies durch ActiveRecord Validierungen?

EDIT: This appears to be a known issue.

+0

ist 'client_id' gesetzt, wenn Sie eine benutzerdefinierte Validierung erstellen (dh' validate' off) – Kris

+0

@Kris Nr. Related, wenn ich 'client_id' Präsenz Validierung hinzufügen,' client.save' wird fehlschlagen, weil (natürlich) 'client_id' existiert noch nicht. – pdoherty926

Antwort

1

Es gibt keine sehr saubere Möglichkeit, dies mit AR-Validierungen beim Batch-Einfügen zu tun, aber Sie können es manuell mit den folgenden Schritten tun.

  1. Erstellen Sie mit einer Postgresql VALUES-Liste eine einzige Abfrage an die Datenbank, um möglicherweise doppelte Datensätze zu laden.
  2. Vergleichen Sie die Datensätze, die Sie über zu Charge erstellen und Duplikate herausziehen
  3. manuell generieren und senden Sie Ihre Fehlermeldung

Schritt 1 ein wenig wie diese dann

# Build array of uniq attribute pairs we want to check for 
uniq_attrs = new_collection.map do |record| 
    [ 
    record.day, 
    record.client_id, 
    ] 
end 

# santize the values and create a tuple like ('Monday', 5) 
values = uniq_attrs.map do |attrs| 
    safe = attrs.map {|v| ActiveRecord::Base.connection.quote(v)} 
    "(#{safe.join(",")})" 
end 

existing = Preference.where(%{ 
    (day, client_id) in 
    (#{values.join(",")}) 
}) 

# SQL Looks like 
# select * from preferences where (day, client_id) in (('Monday',5), ('Tuesday', 3) ....) 

sieht können Sie Nehmen Sie die Sammlung existing und verwenden Sie sie in den Schritten 2 und 3, um Ihre Duplikate herauszuziehen und Ihre Fehlermeldungen zu generieren.

Wenn ich diese Funktionalität benötigt habe, habe ich gemacht es in der Regel eine Selbst Methode meiner Klasse, so etwas wie

class Preference < ApplicationRecord 

    def self.filter_duplicates(collection) 
    # blah blah blah from above 

    non_duplicates = collection.reject do |record| 
     existing.find do |exist| 
     exist.duplicate?(record) 
     end 
    end 

    [non_duplicates, existing] 

    end 

    def duplicate?(record) 
    record.day == self.day && 
    record.client_id = self.client_id 
    end 
end 
Verwandte Themen