2016-06-09 6 views
1

Hier sind meine Models:Einmaligkeit Validierung auf has_many Verein Aufzeichnungen über accepts_nested_attributes_for

class User < ApplicationRecord 
    has_many :cities_users, inverse_of: :user 
    has_many :cities, through: :cities_users 

    accepts_nested_attributes_for :cities_users 
end 


class CitiesUser < ApplicationRecord 
    belongs_to :user 
    belongs_to :city 

    validates :user_id, uniqueness: { scope: :city_id, message: "already specified that they have lived in this city"} 
end 


class City < ApplicationRecord 
    has_many :cities_users 
    has_many :users, through: :cities_users 
end 

Während eine neue user Datensatz zu erstellen: Der Benutzer kann dynamisch Städte hinzufügen, die sie in (über die nested_form_fields gem) gelebt haben. Dies erstellt schließlich Datensätze in der Join-Tabelle cities_users.

clicking button

Nach einem Klick auf die 'eine Stadt hinzufügen Sie in gelebt haben' Button:

enter image description here

Benutzer dann geht und klickt auf die Schaltfläche wieder andere Stadt hinzuzufügen:

click button again

In dieser Situation oben möchte ich einen Validierungsfehler auslösen, wenn der Benutzer auf die Schaltfläche Create User klickt. Beim Erstellen eines neuen Benutzerdatensatzes: Ein Benutzer sollte NICHT angeben, dass er mehr als einmal in derselben Stadt gelebt hat.

So oben: ohne Validierung: zweicities_users Aufzeichnungen würden mit der gleichen user_id und city_id erstellt werden. Das ist nicht gut, also möchte ich das Formular erneut rendern, die zwei verschuldeten cities_users verschachtelten Felder markieren und eine Fehlermeldung wie "Sie können nicht angeben, dass der Benutzer mehr als einmal in derselben Stadt gelebt hat" ausgeben lassen.

Dies erfordert eindeutig eine Validierung entweder auf dem user Modell oder dem cities_user Modell. Ich weiß nicht, wohin die Validierung gehen soll, und ich weiß nicht, wie ich die Validierung so programmieren soll, dass sie diesen Fehler erkennt. Die aktuelle uniqueness Validierung, die ich auf dem CitiesUser Modell habe, fängt diese Situation nicht ein.

zusätzliche Setup-Informationen nur für den Fall jemand will das Szenario neu

Das war, wie ich die nested_fields innerhalb des Benutzers einrichten _form.html.erb:

<%= f.nested_fields_for :cities_users do |ff| %> 

<div> 
    <%= ff.label :city_id %> 
    <%= ff.collection_select(:city_id, City.all, :id, :name) %> 
</div> 

<% end %> 

<br> 

<div><%= f.add_nested_fields_link :cities_users, "Add a City You have lived in" %></div> 

Die users_controller#create Aktion ist Standard, erzeugt aus der scaffold Befehl. Ich habe den starken params hinzufügen, um für die verschachtelten Attribute zu ermöglichen:

def user_params 
    params.require(:user).permit(:name, cities_users_attributes: [:id, :city_id, :user_id]) 
end 

Antwort

1

Ich habe endlich das den Job zu erledigen:

#models/user.rb 
class User < ActiveRecord::Base 

    has_many :cities_users 
    has_many :cities, through: :cities_users 

    accepts_nested_attributes_for :cities_users, allow_destroy: true 

    validate :no_duplicate_cities 

    private 

    def no_duplicate_cities 
    errors.add(:duplicate_cities, "are present") if self.cities_users.group_by(&:city_id).values.detect{|arr| arr.size > 1} 
    end 
end 
1

Das Beste, was meiner Meinung nach tun, da Sie das dem User-Modell in Ihrer user_params Methode sind vorbei ist, eine benutzerdefinierte Validierung hinzufügen in Ihrem Benutzermodell. Erzwinge, dass die ID der Stadt, die sie hinzufügen, in der Anzahl der Städte, die über 'id' eingereicht werden, eindeutig ist. Tun Sie etwas wie:

class User < ActiveRecord::Base 

    validate :no_duplicate_cities 

    #I'm assuming user_cities is an array of city id's so we only want one id of each different city at most. 
    def no_duplicate_cities 
    self.user_cities = self.user_cities.uniq 
    end 

Dies wird nur die eindeutige IDs in diesem Array entfernen alle doppelten Städte.

+0

Dank für Ihre Antwort danken. Diese Antwort ist nah, aber wie es nicht funktioniert. 'self.users_cities' gibt ein Collection-Proxy-Objekt zurück. Was ich getan habe, ist, dass ich die 'no_duplicate_cities' private Methode erstellt habe, einen Haltepunkt dort,' self.users_cities' genannt, um zu sehen, was es mir zurückgeben würde. Hier ein Beispiel: '# , # , # ]> ' – Neil

+0

vielleicht muss ich irgendwie die Items durchlaufen Suchen Sie in collection_proxy, ob bereits ein Element mit dieser 'city_id' in der Liste vorhanden ist, und lehnen Sie es gegebenenfalls ab? – Neil

+0

Ich hatte Hoffnungen, dass dies das tun würde, aber es ist nicht. irgendwelche Vorschläge? 'self.users_cities = self.users_cities.uniq {| user_city | user_city.city_id} ' – Neil

0

Ich habe etwas ähnliches in einer App von mir gemacht, versuchen Sie den folgenden Code in Ihrem Benutzermodell.

validate :unique_cities 

def unique_cities 
    unless self.user_cities.map(&:city_id).uniq 
    errors.add(:cites, "- can not be 2 of the same cities") 
    end 
end 
+0

Danke für die Antwort, Dave. Leider macht die 'uniq'-Methode hier nicht das, was wir dafür wollen. – Neil

+0

okay, lass es mich in einem env ill testen einige Dinge aus – Dave

+0

oh ich sehe deine Antwort! gute Arbeit, sieht aus, als ob du auf dem richtigen Weg bist – Dave

Verwandte Themen