Rails 3: бесконечный SQL-запрос? - PullRequest
1 голос
/ 19 апреля 2011

Я изменил свою модель пользователя на профиль accept_nested_attributes_for, и я пытаюсь создать пользователя и профиль одновременно. Я использую Devise для аутентификации.

Кажется, что это работает - за исключением одного гигантского хача ...

Каждый раз, когда я создаю нового пользователя, происходит сбой приложения с «Illegal Instruction», и когда я проверяю журнал, это выглядит так ...

Started POST "/users" for 127.0.0.1 at 2011-04-18 21:01:54 -0500
  Processing by UsersController#create as HTML
  Parameters: {"utf8"=>"‚úì", "authenticity_token"=>"Rua6PUxnE4a4TvaFcVMfmycw8Y9AFRjEsXVrqwWC2EM=", "user"=>{"email"=>"_______________________@gmail.com", "password"=>"[FILTERED]", "password_confirmation"=>"[FILTERED]", "profile_attributes"=>{"first_name"=>"Name", "last_name"=>"Tester"}, "student_claimed"=>"false", "school"=>"", "invite_code"=>"Texas!", "terms_of_service"=>"1"}, "commit"=>"Create Account!"}
  [1m[35mSQL (0.3ms)[0m   SELECT name
 FROM sqlite_master
 WHERE type = 'table' AND NOT name = 'sqlite_sequence'
  [1m[36mSQL (0.3ms)[0m  [1m SELECT name
 FROM sqlite_master
 WHERE type = 'table' AND NOT name = 'sqlite_sequence'
[0m
  [1m[35mUser Load (0.2ms)[0m  SELECT "users"."id" FROM "users" WHERE (LOWER("users"."email") = LOWER('_______________________@gmail.com')) LIMIT 1
  [1m[36mInvitation Load (0.1ms)[0m  [1mSELECT "invitations".* FROM "invitations" WHERE "invitations"."code" = 'Texas!' LIMIT 1[0m
  [1m[35mUser Load (0.1ms)[0m  SELECT "users".* FROM "users" WHERE "users"."confirmation_token" = 'duALIT6yCL5ShpMvbw79' LIMIT 1
  [1m[36mRole Load (0.3ms)[0m  [1mSELECT "roles".* FROM "roles" WHERE "roles"."name" = 'member' LIMIT 1[0m
  [1m[35mAREL (0.3ms)[0m  UPDATE "invitations" SET "remaining_uses" = 9993, "updated_at" = '2011-04-19 02:01:54.506243' WHERE "invitations"."id" = 1
  [1m[36mAREL (0.2ms)[0m  [1mINSERT INTO "users" ("email", "encrypted_password", "reset_password_token", "remember_token", "remember_created_at", "sign_in_count", "current_sign_in_at", "last_sign_in_at", "current_sign_in_ip", "last_sign_in_ip", "created_at", "updated_at", "plan_code", "confirmation_token", "confirmed_at", "confirmation_sent_at", "student_claimed", "student_confirmed", "school", "invitation_id") VALUES ('_______________________@gmail.com', '$2a$10$7qzC7T6b1kLiXvPSkMRkduCFClBznDWnnOu7I1ssU8blB9NMJznn2', NULL, NULL, NULL, 0, NULL, NULL, NULL, NULL, '2011-04-19 02:01:54.509656', '2011-04-19 02:01:54.509656', NULL, 'duALIT6yCL5ShpMvbw79', NULL, '2011-04-19 02:01:54.437796', 'f', 'f', '', 1)[0m
  [1m[35mSQL (0.1ms)[0m  INSERT INTO "roles_users" ("role_id", "user_id") VALUES (3, 6)
Rendered devise/mailer/confirmation_instructions.html.erb (0.9ms)

Sent mail to _________@gmail.com (1966ms)
Date: Mon, 18 Apr 2011 21:01:55 -0500
From: __________
Reply-To: ___________
To: _____________
Message-ID: <4daced1352c84_1ff5817d6b04978cd@Titan.local.mail>
Subject: Please confirm your email address
Mime-Version: 1.0
Content-Type: text/html;
 charset=UTF-8
Content-Transfer-Encoding: 7bit

<p>Name,</p>

<p>You registered with the email address: _________@gmail.com. You can confirm your account through the link below:</p>

<p><a href="http://localhost:3000/users/confirmation?confirmation_token=duALIT6yCL5ShpMvbw79">Confirm my account</a></p>

<p>Thanks for signing up!</p>

  [1m[36mAREL (0.2ms)[0m  [1mINSERT INTO "profiles" ("first_name", "last_name", "created_at", "updated_at", "user_id", "avatar_file_name", "avatar_content_type", "avatar_file_size", "avatar_updated_at", "address1", "city", "state", "country", "zip") VALUES ('Name', 'Tester', '2011-04-19 02:01:57.266502', '2011-04-19 02:01:57.266502', 6, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL)[0m
[paperclip] Saving attachments.
  [1m[35mUser Load (0.1ms)[0m  SELECT "users"."id" FROM "users" WHERE (LOWER("users"."email") = LOWER('_______________________@gmail.com')) AND ("users".id <> 6) LIMIT 1
  [1m[36mUser Load (1.6ms)[0m  [1mSELECT "users".* FROM "users" WHERE "users"."id" = 6 LIMIT 1[0m
  [1m[35mProfile Load (1.6ms)[0m  SELECT "profiles".* FROM "profiles" WHERE ("profiles".user_id = 6)
  [1m[36mCACHE (0.0ms)[0m  [1mSELECT "users"."id" FROM "users" WHERE (LOWER("users"."email") = LOWER('_______________________@gmail.com')) AND ("users".id <> 6) LIMIT 1[0m
  [1m[35mCACHE (0.0ms)[0m  SELECT "users".* FROM "users" WHERE "users"."id" = 6 LIMIT 1
  [1m[36mCACHE (0.0ms)[0m  [1mSELECT "profiles".* FROM "profiles" WHERE ("profiles".user_id = 6)[0m
  [1m[35mCACHE (0.0ms)[0m  SELECT "users"."id" FROM "users" WHERE (LOWER("users"."email") = LOWER('_______________________@gmail.com')) AND ("users".id <> 6) LIMIT 1
  [1m[36mCACHE (0.0ms)[0m  [1mSELECT "users".* FROM "users" WHERE "users"."id" = 6 LIMIT 1[0m
  [1m[35mCACHE (0.0ms)[0m  SELECT "profiles".* FROM "profiles" WHERE ("profiles".user_id = 6)
  [1m[36mCACHE (0.0ms)[0m  [1mSELECT "users"."id" FROM "users" WHERE (LOWER("users"."email") = LOWER('_______________________@gmail.com')) AND ("users".id <> 6) LIMIT 1[0m
  [1m[35mCACHE (0.0ms)[0m  SELECT "users".* FROM "users" WHERE "users"."id" = 6 LIMIT 1
  [1m[36mCACHE (0.0ms)[0m  [1mSELECT "profiles".* FROM "profiles" WHERE ("profiles".user_id = 6)[0m
  [1m[35mCACHE (0.0ms)[0m  SELECT "users"."id" FROM "users" WHERE (LOWER("users"."email") = LOWER('_______________________@gmail.com')) AND ("users".id <> 6) LIMIT 1
  [1m[36mCACHE (0.0ms)[0m  [1mSELECT "users".* FROM "users" WHERE "users"."id" = 6 LIMIT 1[0m
  [1m[35mCACHE (0.0ms)[0m  SELECT "profiles".* FROM "profiles" WHERE ("profiles".user_id = 6)

  ... and so on for about 100 more lines ...

  [1m[35mCACHE (0.0ms)[0m  SELECT "users".* FROM "users" WHERE "users"."id" = 6 LIMIT 1
  [1m[36mCACHE (0.0ms)[0m  [1mSELECT "profiles".* FROM "profiles" WHERE ("profiles".user_id = 6)[0m
  [1m[36mSQL (0.3ms)[0m  [1m SELECT name

Итак, этого не было до того, как я начал принимать вложенные атрибуты ... и я довольно озадачен тем, почему это происходит сейчас. У кого-нибудь есть понимание того, как отладить это и исправить проблему?

Спасибо!

- EDIT -

Модель пользователя:

class User < ActiveRecord::Base
  # RELATIONSHIPS
  has_one :profile, :dependent => :destroy
  has_many :photos
  has_many :votes
  has_many :voted_photos, :through => :votes, :source => :photo
  has_many :ratings
  has_many :rated_photos, :through => :ratings, :source => :photo
  has_many :comments
  has_and_belongs_to_many :roles
  has_many :assignments
  has_many :collections, :through => :assignments
  belongs_to :invitation

  accepts_nested_attributes_for :profile

  # VIRTUAL ATTRIBUTES
  attr_accessor :invite_code

  # AUTHENTICATION
  devise :database_authenticatable, :recoverable, :rememberable, :trackable, :validatable, :confirmable

  # SECURITY
  attr_accessible :email, :password, :password_confirmation, :remember_me, :confirmed_at, :invite_code, :student_claimed, :school, :terms_of_service, :profile_attributes

  # FILTERS
  before_create :set_role_to_member, :set_invitation
  after_save :update_recurly_account, :unless => Proc.new { Rails.env.test? }

  # VALIDATIONS
  validates_acceptance_of :terms_of_service, :message => "You must agree to the terms of service in order to create an account."
  validate :invitation_status, :on => :create
  validates_presence_of :profile
  validates_associated :profile

  # DELEGATES
  delegate  :first_name, :last_name, :full_name,
            :to => :profile,
            :allow_nil => true

  # ROLES
  def set_role_to_member
    self.roles << Role.find_by_name('member')
  end

  def has_role?( r )
    !roles.find_by_name( r ).nil?
  end

  def list_roles
    list = []
    roles.all.each do |r|
      list << r.name
    end
    list.join(', ')
  end

  # DEVISE RELATED
  # Hook up recurly account after confirmation
  def confirm!
    self.setup_recurly_account unless Rails.env.test?

    if student_claimed && validate_student_email
      self.student_confirmed = true
      self.save
    end

    super
  end

  protected
  # Don't require password on update
  def password_required?
    !persisted? || password.present? || password_confirmation.present?
  end

  public

  # RECURLY RELATED

  def setup_recurly_account
    ...
  end

  private

    def update_recurly_account
      ...
    end

    def validate_student_email
      self.email =~ /\.edu$/ ? true : false
    end

    def invitation_status
      ...
    end

    def set_invitation
      ...
    end

end

Модель профиля

class Profile < ActiveRecord::Base
  include Helpers::AssetStorage

  # RELATIONSHIPS
  belongs_to :user

  stores_file_as :avatar,
                    :styles =>  { :tenth => "87x87#", :eighth => "106x106#" },
                    :filename_interpolation => "avatars/:user_id/:id_:style.:extension",
                    :default_url => '/images/no_avatar_:style.png'

  # VALIDATIONS
  validates_presence_of :first_name, :last_name

  # CALLBACKS
  after_update :save_user

  def full_name
    [first_name,last_name].join(" ")
  end

  private

    def save_user
      self.user.save!
    end

end

1 Ответ

1 голос
/ 19 апреля 2011

Вам не нужен обратный вызов save_user для модели Profile.

При выполнении user.save автоматически сохраняется user.profile.Из-за обратного вызова, user.profile сохранен, и он вызывает пользователя, чтобы сохранить снова.И пользователь сохраняет, он также сохраняет свой профиль ......

Это цикл.

Таким образом, самая простая модификация будет удалить обратный вызов after_update в модели профиля.1013 * Если вы хотите сохранить только профиль, используйте profile.save.Если у объекта пользователя тоже есть обновления, используйте user.save или profile.user.save.

...