0

Ich verwende Rails 5 und alles in seinen neuesten stabilen Versionen. So bekomme ich folgendes:Warum Rails 5 verschachtelte Attribute nicht speichert, weil Elternmodell nicht zuerst speichert

Sie haben Ihre Zuordnung auf erforderlich, aber es fehlt. Verbände werden auf standardmäßig erforderlich in Schienen 5 so, wenn Sie wollen einen leeren Sie optional festlegen müssen halten: auf Ihrem Verband in Modus

Das ist großartig wahr und ich verstehe, was aber vor sich geht Für das Leben von mir kann ich nicht herausfinden, wie das Elternmodell zuerst gespeichert wird, damit die user_id den verschachtelten Modellsatz übersetzt. Ich sehe die gleiche Antwort oben überall, aber niemand erklärt eine andere Arbeit, als den Standard im Initialisierer von "wahr" auf "falsch" zu setzen. DIES LÖST DAS PROBLEM NICHT, da der Datensatz sicher speichert, aber die user_id nicht enthält.

Unten ist, was ich für meine Code-Basis habe, würde ich eher als mit dem oben genannten Zitat zu antworten bitten, könnte mir jemand aufklären, wie das Feld USER_ID in die geschachtelten Attribute während des Speicherns erhalten. Ich weigere mich, die Validierung zu deaktivieren und manuell die Einfügung zu handhaben, da dies nicht der rubinrote Weg ist und von Standards bricht! Vielen Dank im Voraus für jeden, der diese Frage direkt und ohne vage Erklärungen beantworten kann, die von der rubinroten Art der Dinge abweichen!

###Models 
#Users 
class User < ApplicationRecord 
    has_one :profile, inverse_of: :user 
    accepts_nested_attributes_for :profile, allow_destroy: true 
end 

#Profiles 
class Profile < ApplicationRecord 
    belongs_to :user, inverse_of: :profile 
end 

###Controller 
class UsersController < ApplicationController 
    before_action :set_user, only: [:show, :edit, :update, :destroy] 

    # GET /users 
    # GET /users.json 
    def index 
    @users = User.all 
    end 

    # GET /users/1 
    # GET /users/1.json 
    def show 
    end 

    # GET /users/new 
    def new 
    @user = User.new 
    @user.build_profile 
    end 

    # GET /users/1/edit 
    def edit 
    @user.build_profile 
    end 

    # POST /users 
    # POST /users.json 
    def create 
    @user = User.new(user_params) 

    respond_to do |format| 
     if @user.save 
     format.html { redirect_to @user, notice: 'User was successfully created.' } 
     format.json { render :show, status: :created, location: @user } 
     else 
     format.html { render :new } 
     format.json { render json: @user.errors, status: :unprocessable_entity } 
     end 
    end 
    end 

    # PATCH/PUT /users/1 
    # PATCH/PUT /users/1.json 
    def update 
    respond_to do |format| 
     if @user.update(user_params) 
     format.html { redirect_to @user, notice: 'User was successfully updated.' } 
     format.json { render :show, status: :ok, location: @user } 
     else 
     format.html { render :edit } 
     format.json { render json: @user.errors, status: :unprocessable_entity } 
     end 
    end 
    end 

    # DELETE /users/1 
    # DELETE /users/1.json 
    def destroy 
    @user.destroy 
    respond_to do |format| 
     format.html { redirect_to users_url, notice: 'User was successfully destroyed.' } 
     format.json { head :no_content } 
    end 
    end 

    private 
    # Use callbacks to share common setup or constraints between actions. 
    def set_user 
     @user = User.find(params[:id]) 
    end 

    # Never trust parameters from the scary internet, only allow the white list through. 
    def user_params 
     params.require(:user).permit(:username, :password, :user_type_id, profile_attributes: [:user_id, :first_name, :middle_name, :last_name, :phone_number, :cell_number, :email]) 
    end 
end 

##View 
<%= form_for(@user) do |f| %> 
    <% if user.errors.any? %> 
    <div id="error_explanation"> 
     <h2><%= pluralize(user.errors.count, "error") %> prohibited this user from being saved:</h2> 

     <ul> 
     <% user.errors.full_messages.each do |message| %> 
     <li><%= message %></li> 
     <% end %> 
     <!--<li><%= debug f %></li>--> 
     </ul> 
    </div> 
    <% end %> 

    <div class="field"> 
    <%= f.label :username %> 
    <%= f.text_field :username %> 
    </div> 

    <div class="field"> 
    <%= f.label :password %> 
    <%= f.text_field :password %> 
    </div> 

    <div class="field"> 
    <% if params[:trainer] == "true" %> 
     <%= f.label :user_type_id %> 
     <%= f.text_field :user_type_id, :readonly => true, :value => '2' %> 
    <% else %> 
     <%= f.label :user_type_id %> 
     <%= f.text_field :user_type_id, :readonly => true, :value => '1' %> 
    <% end %> 
    </div> 
    <h2>Account Profile</h2> 
    <%= f.fields_for :profile do |profile| %> 
     <%#= profile.inspect %> 
     <div> 
      <%= profile.label :first_name %> 
      <%= profile.text_field :first_name %> 
     </div> 
     <div> 
      <%= profile.label :middle_name %> 
      <%= profile.text_field :middle_name %> 
     </div> 
     <div> 
      <%= profile.label :last_name %> 
      <%= profile.text_field :last_name %> 
     </div> 
     <div> 
      <%= profile.label :email %> 
      <%= profile.text_field :email %> 
     </div> 
     <div> 
      <%= profile.label :phone_number %> 
      <%= profile.telephone_field :phone_number %> 
     </div> 
     <div> 
      <%= profile.label :cell_phone %> 
      <%= profile.telephone_field :cell_number %> 
     </div> 
    <% end %> 
    <div class="actions"> 
    <%= f.submit %> 
    </div> 
    <%= debug params %> 
    <%= debug user %> 
    <%= debug user.profile %> 
<% end %> 

UPDATE Für den Anfang ich herausgefunden habe, dass Sie automatische Speicherung einschließen müssen: true die Beziehung wie so

class User < ApplicationRecord 
    has_one :profile, inverse_of: :user, autosave: true 
    accepts_nested_attributes_for :profile, allow_destroy: true 
end 

Dann wird der übergeordnete Datensatz wird, bevor das Kind gerettet. Jetzt kommt ein weiterer Gotcha, die ich bin nur nicht sicher und ist ungerade, wenn das Formular abgeschickt wird Sie in der Konsolenausgabe bemerken werde ich unter dem klebte das INSERT INTO profiles Anweisung enthält die User_id Spalte und den Wert von . Es übergibt die Validierung und sieht so aus, als ob es ordnungsgemäß von der Ausgabe ausgeführt wird. Die Spalte user_id in der Tabelle ist jedoch immer noch null. Ich werde weiter graben, hoffentlich wird einer meiner anderen Rubyies da draußen das sehen und ein paar Ideen haben, wie man das fertigstellen kann. Ich liebe Rails 5 Verbesserungen bisher, aber es wäre nicht ROR ohne kleine interessante Probleme! Nochmals vielen Dank im Voraus!

Started POST "/users" for 192.168.0.31 at 2017-03-12 22:28:14 -0400 
Cannot render console from 192.168.0.31! Allowed networks: 127.0.0.1, ::1, 127.0.0.0/127.255.255.255 
Processing by UsersController#create as HTML 
    Parameters: {"utf8"=>"✓", "authenticity_token"=>"YA7kQnScvlIBy5OiT+BmOQ2bR7J00ANXId38FqNwX37Cejd+6faUyD3rMF4y0qJNKBUYGaxrRZqcLrXonL6ymA==", "user"=>{"username"=>"john", "password"=>"[FILTERED]", "user_type_id"=>"1", "profile_attributes"=>{"first_name"=>"john", "middle_name"=>"r", "last_name"=>"tung", "email"=>"[email protected]", "phone_number"=>"8033207677", "cell_number"=>"8033207677"}}, "commit"=>"Create User"} 
    (0.1ms) BEGIN 
    SQL (0.3ms) INSERT INTO `users` (`username`, `password`, `user_type_id`, `created_at`, `updated_at`) VALUES ('john', '0000', 1, '2017-03-13 02:28:14', '2017-03-13 02:28:14') 
    SQL (0.4ms) INSERT INTO `profiles` (`user_id`, `email`, `first_name`, `middle_name`, `last_name`, `phone_number`, `cell_number`, `created_at`, `updated_at`) VALUES (1, '[email protected]', 'john', 'r', 'tung', '8033207677', '8033207677', '2017-03-13 02:28:14', '2017-03-13 02:28:14') 
    (10.8ms) COMMIT 
Redirected to http://192.168.0.51:3000/users/1 
Completed 302 Found in 24ms (ActiveRecord: 11.5ms) 
+0

Die 'Autosave'-Option wird ausführlich in den offiziellen Dokumenten von Rails erklärt. http://api.rubyonrails.org/classes/ActiveRecord/AutosaveAssociation.html – jvnill

Antwort

2

Ok, ich meine eigene Frage zu beantworten bin, weil ich viele Leute wissen, mit diesem zu kämpfen haben, und ich habe tatsächlich die Antwort und keine vage Antwort auf die Dokumentation.

Zuerst werden wir nur eine Eins-zu-eins-Beziehung für dieses Beispiel verwenden.Wenn Sie Ihre Beziehungen erstellen müssen Sie sicherstellen, dass das übergeordnete Modell den folgenden

  1. inverse_of hat:
  2. automatische Speicherung: true
  3. accepts_nested_attributes_for: Modell, allow_destroy: true

Hier ist die Benutzer Modell dann werde ich erklären,

class User < ApplicationRecord 
    has_one :profile, inverse_of: :user, autosave: true 
    accepts_nested_attributes_for :profile, allow_destroy: true 
end 

in Rails 5 Sie benötigen inverse_of: weil dies Rails mitteilt, dass eine Beziehung über einen Fremdschlüssel besteht und dass es beim Speichern der Formulardaten im verschachtelten Modell festgelegt werden muss. Jetzt, wenn Sie autosave verlassen: true von der Beziehungslinie weg sind Sie mit der user_id nicht speichern in der Profiltabelle und nur die anderen Spalten, es sei denn, Sie haben Validierungen aus und dann wird es kein Fehler heraus wird es nur ohne die user_id speichern. Was ist hier los ist Autosave: True stellt sicher, dass der Benutzer Datensatz zuerst gespeichert wird, so dass es die user_id in den verschachtelten Attributen für das Profil Modell gespeichert hat. Das ist es auf den Punkt gebracht, warum die user_id nicht auf das Kind durchquert und es Rollback statt Commit war. Auch eine letzte gotcha ist, gibt es einige Beiträge da draußen sagen Sie in Ihrem Controller für die Bearbeitungsroute sollten Sie hinzufügen @ user.build_profile wie ich in meinem Beitrag haben. NICHT TUN ES sie tot sind falsch, nachdem die Konsolenausgabe der Beurteilung in

Ergebnisse
Started GET "https://stackoverflow.com/users/1/edit" for 192.168.0.31 at 2017-03-12 22:38:17 -0400 
Cannot render console from 192.168.0.31! Allowed networks: 127.0.0.1, ::1, 127.0.0.0/127.255.255.255 
Processing by UsersController#edit as HTML 
    Parameters: {"id"=>"1"} 
    User Load (0.4ms) SELECT `users`.* FROM `users` WHERE `users`.`id` = 1 LIMIT 1 
    Profile Load (0.5ms) SELECT `profiles`.* FROM `profiles` WHERE `profiles`.`user_id` = 1 LIMIT 1 
    (0.1ms) BEGIN 
    SQL (0.5ms) UPDATE `profiles` SET `user_id` = NULL, `updated_at` = '2017-03-13 02:38:17' WHERE `profiles`.`id` = 1 
    (59.5ms) COMMIT 
    Rendering users/edit.html.erb within layouts/application 
    Rendered users/_form.html.erb (44.8ms) 
    Rendered users/edit.html.erb within layouts/application (50.2ms) 
Completed 200 OK in 174ms (Views: 98.6ms | ActiveRecord: 61.1ms) 

Wenn Sie es betrachten das Profil von Grund auf neu erstellt und das Zurücksetzen des user_id für die Aufzeichnung auf null, der den aktuellen Benutzer übereinstimmt Sie sind Bearbeitung. Sei also sehr vorsichtig, denn ich habe Tonnen von Beiträgen gesehen, die diesen Vorschlag machen, und es kostete mich TAGE der Forschung, um eine Lösung zu finden!

+0

Der Grund, warum Sie das '@ user.build_profile' in Ihrer Editieraktion nicht benötigen, ist, weil Sie bereits einen' Profil'-Datensatz speichern, der damit verknüpft ist Ihr Benutzer. Wenn ein Benutzer ohne Profileintrag auf die Seite zum Bearbeiten von Benutzern wechselt, wird das Formular für das Profil nicht angezeigt. Überprüfen Sie also, ob das Profil existiert oder nicht, bevor Sie '@ user.build_profile' aufrufen. – jvnill

+0

Wie ich in meiner Antwort sagte, merke ich das und ich erklärte es hier, weil es scheint, dass die Leute im Netz gerne falsch beraten und ein erfahrener Profi bin. Ich habe ein großes Problem mit Fehlinformationen. –

+0

Aber Sie haben nicht erklärt, warum Sie das überhaupt nicht brauchen. Du hast gerade gesagt, dass es falsch ist, das zu tun. Wie ich schon sagte, es ist von Fall zu Fall. Wenn es in Ihrer App nicht funktioniert, heißt das nicht, dass es bei anderen nicht funktioniert. – jvnill