2013-03-27 11 views
5

I Si verifica un problema durante il tentativo di aggiornamento di un modello con associazione has_and_belongs_to_many.Modifica oggetto con associazione HABTM modifica database prima della convalida con ActiveAdmin

Diciamo che Posthas_and_belongs_to_manyTag, e Post convalida la presenza di titolo e Tags. Se aggiorno Post, rimuovendo il titolo e i tag, ricevo l'errore di convalida in title e tags, ok. Tuttavia, sono stati già rimossi i record che creano un'associazione tra Post e Tag, pertanto, se lascio la pagina di modifica Post, lo post non è valido sul database, senza tags.

Qui i miei modelli:

class Tag < ActiveRecord::Base 
    attr_accessible :label 
    has_and_belongs_to_many :posts 
end 

class Post < ActiveRecord::Base 
    attr_accessible :content, :title, :tag_ids 
    has_and_belongs_to_many :tags 
    validates_presence_of :content, :title, :tags 
end 

ActiveAdmin.register Post do 

    form do |f| 
    f.inputs do 
     f.input :title 
     f.input :content 
     f.input :image 
     f.input :tags 
    end 

    f.buttons 
    end 
end 

ho usign chosen-rails gemma e permette all'utente di deselezionare tutti i tag del post.

Riassumendo, il mio problema è: ActiveAdmin aggiorna le relazioni sul database prima di eseguire validazioni del modello.

C'è una soluzione per questo comportamento o faccio qualcosa di sbagliato?

Edit:

Qui il registro richiesta quando si cerca di aggiornare post senza titolo e tag:

Started PUT "/admin/posts/8" for 127.0.0.1 at 2013-04-01 10:32:07 -0300 
Processing by Admin::PostsController#update as HTML 
    Parameters: {"utf8"=>"✓", "authenticity_token"=>"amSbLlP/rgDrNn/N8lgq/KEaRXK1fMPShZDwpZ0QIJ4=", "post"=>{"title"=>"", "content"=>"content", "tag_ids"=>["", ""]}, "commit"=>"Update Post", "id"=>"8"} 
    AdminUser Load (0.2ms) SELECT `admin_users`.* FROM `admin_users` WHERE `admin_users`.`id` = 1 LIMIT 1 
    Post Load (0.2ms) SELECT `posts`.* FROM `posts` WHERE `posts`.`id` = 8 LIMIT 1 
    Tag Load (0.2ms) SELECT `tags`.* FROM `tags` INNER JOIN `posts_tags` ON `tags`.`id` = `posts_tags`.`tag_id` WHERE `posts_tags`.`post_id` = 8 
    (0.1ms) BEGIN 
    SQL (12.3ms) DELETE FROM `posts_tags` WHERE `posts_tags`.`post_id` = 8 AND `posts_tags`.`tag_id` IN (1, 2) 
    (49.6ms) COMMIT 
    (0.1ms) BEGIN 
    (0.2ms) ROLLBACK 
    Post Load (0.3ms) SELECT `posts`.* FROM `posts` WHERE `posts`.`id` = 8 LIMIT 1 
    Tag Load (0.2ms) SELECT `tags`.* FROM `tags` 
    Rendered /home/rodrigo/.rvm/gems/[email protected]/gems/activeadmin-0.5.1/app/views/active_admin/resource/edit.html.arb (192.3ms) 
Completed 200 OK in 276ms (Views: 194.8ms | ActiveRecord: 63.3ms) 

EDIT 2:

Ok, certo che ActiveAdmin ha questo bug

Osservando il comportamento di ActiveRecord, penso che il flusso di convalida sia interrotto utilizzando solo la classe del modello. Vedere questo esempio:

1.9.3p125 :064 > post = Post.find(8) 
    Post Load (0.3ms) SELECT `posts`.* FROM `posts` WHERE `posts`.`id` = 8 LIMIT 1 
=> #<Post id: 8, title: "title", content: "content", created_at: "2013-03-27 13:13:20", updated_at: "2013-03-27 13:13:20", image: "extrato.bmp"> 
1.9.3p125 :065 > post.tags 
    Tag Load (0.2ms) SELECT `tags`.* FROM `tags` INNER JOIN `posts_tags` ON `tags`.`id` = `posts_tags`.`tag_id` WHERE `posts_tags`.`post_id` = 8 
=> [#<Tag id: 1, label: "tag", created_at: "2013-02-25 18:32:45", updated_at: "2013-02-25 18:32:45">, #<Tag id: 2, label: "new", created_at: "2013-02-25 18:32:50", updated_at: "2013-02-25 18:32:50">] 
1.9.3p125 :066 > post.title = "" 
=> "" 
1.9.3p125 :067 > post.save #<<<<<<< It's invalid on title 
=> false 
1.9.3p125 :068 > post.tags = [] #<<<<<<< This shouldnt trigger database update 
    (0.3ms) BEGIN 
    SQL (0.5ms) DELETE FROM `posts_tags` WHERE `posts_tags`.`post_id` = 8 AND `posts_tags`.`tag_id` IN (1, 2) 
    (55.5ms) COMMIT 
=> [] 
1.9.3p125 :069 > post.save #<<<<<<< It's invalid on title AND TAGS 
    (0.2ms) BEGIN 
    (0.2ms) ROLLBACK 
=> false 
1.9.3p125 :070 > post.reload 
    Post Load (0.2ms) SELECT `posts`.* FROM `posts` WHERE `posts`.`id` = 8 LIMIT 1 
=> #<Post id: 8, title: "title", content: "content", created_at: "2013-03-27 13:13:20", updated_at: "2013-03-27 13:13:20", image: "extrato.bmp"> 
1.9.3p125 :071 > post.valid? #<<<<<<< Now, I have this model in invalid state 
    Tag Load (0.6ms) SELECT `tags`.* FROM `tags` INNER JOIN `posts_tags` ON `tags`.`id` = `posts_tags`.`tag_id` WHERE `posts_tags`.`post_id` = 8 
=> false 

ha alcun modo per aggiornare gli attributi posta (compresi i tag) e validare il modello prima di fare qualsiasi aggiornamento del database?

+0

Non dovrebbe avere tag_ids nel tuo post modello. Hai la tabella intermedia nel tuo database che corrisponderà alla relazione habtm? – Zippie

+0

@Zippie, non ho 'tag_ids' nel mio modello. ActiveAdmin assegna gli ID dei tag in post utilizzando questo metodo generato da Rails. – Rodrigo

+0

suono strano, si prega di copiare allegare log sql query della vostra richiesta – Fivell

risposta

-1

se si vuole, si può ridefinire si aggiorna azione active_admin per impedire il salvataggio tag vuoti, in qualcosa di simile a questo stile

ActiveAdmin.register Post do 

    controller do 
    def update 
     if params[:post][:tag_ids] == ["", ""] 
     flash.now[:alert] = "You can't remove all tags" 
     render :edit 
     else 
     super 
     end 
    end 
    end 
... 
end 

e credo che questa roba da modello può essere eliminato

attr_accessor :new_tag_ids 

validate :validate_new_tags_ids 

after_save :update_tags 

def update_tags 
    self.tag_ids = @new_tag_ids if defined?(@new_tag_ids) 
    @new_tag_ids = nil 
end 

private 
def validate_new_tags_ids 
    errors[:tags] << "can't be blank (2)" if @new_tag_ids.blank? 
end 
+0

Non voglio farlo per tutti i miei modelli. Dovrebbe funzionare senza problemi. – Rodrigo

+0

In ogni caso è necessario un intervento, il tuo problema nell'attributo tag_ids, questo attributo è generato automaticamente dalle rotaie has_many association. Passando a params tag_ids e quando si imposta su attributi salva l'associazione. post.tags = [] rimuoverà tutti i tag, quindi devi rinominare tags_ids nella tua vista e gestirla correttamente –

+0

Quindi, mi stai dicendo che non puoi assegnare tag a Post e convalidarlo prima di salvare queste nuove associazioni? – Rodrigo

1

@Rodrigo Sono riuscito a riprodurre localmente il problema senza amministratore attivo, il problema è in realtà una delle operazioni predefinite quando si utilizzano le relazioni HABTM se si vede [qui] [1]

[1]: http://api.rubyonrails.org/classes/ActiveRecord/Associations/ClassMethods.html#method-i-has_and_belongs_to_many si dice:

raccolta = oggetti

Replaces the collection’s content by deleting and adding objects as appropriate. 

Quindi a quanto pare è necessario sostituire questa operazione

Ecco un esempio:

Override ActiveRecord << operator on has_many :through relationship, to accept data for the join model

Fammi sapere come posso aiutarti

0

Mi piacerebbe aggiungere una variabile tmp e memorizzare i valori al suo interno. Quindi dovresti spostarli nel database se la convalida è stata superata.

Ecco un esempio:

tuo models/article.rb:

class Article < ActiveRecord::Base 
    validate :author_presence 
    has_and_belongs_to_many :authors 

    attr_writer :tmp_author_ids 

    def tmp_author_ids 
    @tmp_author_ids || author_ids 
    end 

    def author_presence 
    if tmp_author_ids.reject(&:blank?).empty? 
     errors.add(:tmp_author_ids, 'Author is missing') 
    else 
     self.author_ids = tmp_author_ids 
    end 
    end 
end 

tuo admin/article.rb, bloccare form:

f.input :tmp_author_ids, as: :select, multiple: true, collection: Author.all, label: 'Authors' 

Questo è tutto

Problemi correlati