Транзакция Rails со стрекозой

В настоящее время я работаю над проектом Rails. У меня есть вложенная форма, в которой я разрешаю пользователю загружать несколько изображений. Я использую стрекозу для загрузки. Я также установил некоторые ограничения на размер и форматы изображений, которые пользователь может загружать, и они работают нормально. Моя проблема в том, что когда я пытаюсь загрузить недопустимые изображения, даже если они не будут загружены, остальная часть формы все равно сохраняется. Чтобы смягчить эту проблему, я изначально написал метод create следующим образом:

def create
  @announcements = Announcement.new(announcement_params)
  images = []

  respond_to do |format|
    params[:photos]['image'].each do |pic|
    img = Photo.new(image: pic)
    if img.valid?
      images << img
    else
      @photos = @announcement.photos.build
      format.html { render: 'new' }
    end

    if @announcement.save
      params[:photos]['image'].each do |pic|
        @photos = @announcement.photos.create(image: pic)
      end
      format.html { redirect_to @announcement }
    else
      format.html { render: 'new' }
    end
  end

end

Это прекрасно работает, но я уверен, что многие из вас согласятся с тем, что в коде много повторений, а код уродлив. Я написал код просто как быстрый обходной путь, пока не найду лучшее решение. Я пытался использовать транзакции, как показано ниже:

def create
  @announcement = Announcement.new(announcement_params)

  respond_to do |format|
    ActiveRecord::Base.transaction do
      begin
        @announcement.save
        params[:photos]["image"].each do |pic|
          @photos = @announcement.photos.create!(image: pic)
        end
        format.html { redirect_to @announcement }
      rescue
        format.html { render action: 'new' }
      end
    end
end

конец

Но это не работает, может ли кто-нибудь сказать мне, что я делаю неправильно, или предложить лучший способ сделать это. Спасибо

Изменить: вот выдержка из моих журналов:

Started POST "/announcements" for 127.0.0.1 at 2015-09-20 15:07:31 +0100

Обработка AnnouncementsController#create as HTML Параметры: {"utf8"=>"✓",

"authenticity_token"=>"fjU1kjnxqSdDwTqieOpTTCH56//p65AynqNyQQX6yiu84zwO0bJeQ3ZKr8tEBvGSZJphclxKkoys2bFp771hWg==", "announcement"=>{"title"=>"Testing wrong image size", "content"=>"Testing wrong image format with transaction code "}, "photos"=>{"image"=>[#<ActionDispatch::Http::UploadedFile:0x007fe9a83f9b18 @tempfile=#<Tempfile:/tmp/RackMultipart20150920-10097-muom0i.jpg>, @original_filename="AhiiZFK2.jpg", @content_type="image/jpeg", @headers="Content-Disposition: form-data; name=\"photos[image][]\"; filename=\"AhiiZFK2.jpg\"\r\nContent-Type: image/jpeg\r\n">]}, "commit"=>"Créer l'announce"}
  User Load (0.6ms)  SELECT  "users".* FROM "users" WHERE "users"."id" = $1  ORDER BY "users"."id" ASC LIMIT 1  [["id", 1]]
   (0.3ms)  BEGIN
  SQL (0.9ms)  INSERT INTO "announcements" ("title", "content", "created_at", "updated_at") VALUES ($1, $2, $3, $4) RETURNING "id"  [["title", "Testing wrong image size"], ["content", "Testing wrong image format with transaction code "], ["created_at", "2015-09-20 14:07:31.247186"], ["updated_at", "2015-09-20 14:07:31.247186"]]
DRAGONFLY: shell command: 'identify' '-ping' '-format' '%m %w %h' '/tmp/RackMultipart20150920-10097-muom0i.jpg'
   (41.4ms)  COMMIT
  Photo Load (0.8ms)  SELECT "photos".* FROM "photos" WHERE "photos"."announcement_id" = $1  [["announcement_id", 1]]
  Rendered announcements/new.html.erb within layouts/application (24.1ms)
  Rendered layouts/_header.html.erb (1.9ms)
  Rendered layouts/_sidebar.html.erb (0.5ms)
Completed 200 OK in 3287ms (Views: 3154.3ms | ActiveRecord: 44.1ms)

person Codejunky    schedule 20.09.2015    source источник
comment
Что именно не работает? Зачем вам блок транзакций здесь?   -  person Зелёный    schedule 20.09.2015
comment
Я правильно понимаю, что запросы внутри блока транзакций должны быть отброшены, если не все из них выполнены успешно, но в моем случае, когда я пытаюсь загрузить недопустимые изображения, даже если Rails не позволяет их загружать, остальная часть формы все еще сохраняется к БД   -  person Codejunky    schedule 20.09.2015
comment
Не могли бы вы показать кусок файла журнала с соответствующим запросом?   -  person Зелёный    schedule 20.09.2015
comment
Я добавил журналы к вопросу.   -  person Codejunky    schedule 20.09.2015
comment
похоже, что все изображения прошли проверку. Вы пытаетесь проверить это в консоли именно методом create!?   -  person Зелёный    schedule 20.09.2015
comment
Да, я знаю, что я хочу сделать, это отменить оставшуюся часть транзакции, если изображение недействительно, как при сохранении всего или ничего.   -  person Codejunky    schedule 20.09.2015
comment
я предполагаю, что вы можете добавить что-то вроде break unless Photo.new(image: pic).valid? перед create!, это должно разорвать цикл с другими изображениями, если я правильно понимаю, что вы хотите.   -  person Зелёный    schedule 20.09.2015


Ответы (1)


Если вы не хотите, чтобы данные хранились в вашей базе данных, когда запись недействительна, вам необходимо прервать транзакцию. Это делается, когда возникает исключение. Но то, как вы вложили свой код, спасает исключение из create!, и блок транзакции может завершиться нормально.

Вам нужно вложить это так:

begin
  ActiveRecord::Base.transaction do
    @announcement.save!
    params[:photos]["image"].each do |pic|
      @announcement.photos.create!(image: pic)
    end
  end # transaction
  format.html { redirect_to @announcement }
rescue
  logger.warn "transaction aborted"
  format.html { render action: 'new' }
end

Для дальнейшего улучшения вы помещаете материал базы данных в собственный метод, чтобы вызовы базы данных и вызовы рендеринга не смешивались.

Чтобы явно прервать (или в терминах базы данных «откатить») транзакцию, вы можете создать исключение ActiveRecord::Rollback. Это прерывает транзакцию и не выбрасывается повторно за пределы блока транзакции. http://api.rubyonrails.org/classes/ActiveRecord/Rollback.html

person Meier    schedule 20.09.2015
comment
Большое спасибо, чувак, ты официально избавил меня от головной боли. - person Codejunky; 21.09.2015