2013-03-27 14 views
5

Występuje problem podczas próby zaktualizowania modelu, który ma powiązanie has_and_belongs_to_many.Edycja obiektu ze skojarzeniem HABTM Modyfikuj bazę danych przed sprawdzaniem poprawności za pomocą ActiveAdmin

Powiedzmy, że Posthas_and_belongs_to_manyTag i Post sprawdza obecność tytuł i Tags. Jeśli zaktualizuję Post, usuwając jego tytuł i znaczniki, pojawi się błąd sprawdzania poprawności w title i tags, ok. Ale ActiveAdmin już usunął zapisy, które łączą między Post i Tag, więc jeśli opuścisz stronę edycji Post, post pozostanie nieważne w bazie danych, bez tags.

Oto moje modele:

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 

I usign chosen-rails gem i pozwala użytkownikowi odznacz wszystkie tagi postu.

Podsumowując, moim problemem jest: ActiveAdmin aktualizuje relacje w bazie danych przed przeprowadzeniem walidacji modelu.

Istnieje rozwiązanie tego zachowania lub robię coś nie tak?

Edit:

Oto dziennik prośba kiedy próbuje zaktualizować wpis bez tytułu i tagi:

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, mam pewność, że ActiveAdmin ma ten błąd.

Patrząc na zachowanie ActiveRecord, myślę, że przepływ sprawdzania poprawności jest uszkodzony przy użyciu tylko klasy modelu. Zobacz ten przykład:

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 

Czy można zaktualizować atrybuty postu (w tym znaczniki) i zweryfikować model przed aktualizacją bazy danych?

+0

Nie powinno mieć tag_ids w poście Model. Czy posiadasz tabelę pośrednią w bazie danych, która będzie pasować do relacji habtm? – Zippie

+0

@Zippie, nie mam "tag_ids" w moim modelu. ActiveAdmin przypisuje identyfikatory tagów w poście za pomocą tej metody generowanej przez Rails. – Rodrigo

+0

brzmi dziwnie, skopiuj załącznik log kwerendy SQL z żądaniem – Fivell

Odpowiedz

-1

jeśli chcesz, możesz przedefiniować zaktualizować działania w active_admin aby zapobiec oszczędność pustych tagów, w coś takiego stylu

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 

i myślę, że ten materiał od modelu mogą zostać usunięte

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

Nie chcę tego robić dla wszystkich moich modeli. Powinien działać bez żadnej pracy. – Rodrigo

+0

W każdym razie potrzebujesz pracy, twój problem w atrybucie tag_ids, ten atrybut jest autogenerowany przez szyny has_many association. Przechodzisz do parametrów tag_ids i kiedy ustawienie atrybutów zapisuje asocjację. post.tags = [] usunie wszystkie tagi, więc musisz zmienić nazwy tag_ids w swoim widoku i odpowiednio się z nimi obchodzić –

+0

Więc, mówisz mi, że nie możesz przypisać tagów do Post i sprawdzić ich przed zapisaniem nowych skojarzeń? – Rodrigo

1

@Rodrigo Udało mi się odtworzyć problem lokalnie bez aktywnego administratora. Problem jest jedną z domyślnych operacji podczas używania relacji HABTM, jeśli widzisz [tutaj] [1]

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

collection = objects

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

Więc widocznie trzeba będzie zastąpić tę operację

Oto przykład:

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

Daj mi znać, w czym mogę Ci pomóc:

0

Proponuję dodać zmienną tmp i zapisać w niej wartości. Następnie powinieneś przenieść je do bazy danych, jeśli sprawdzanie poprawności minęło.

Oto przykład:

Twój 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 

Twój admin/article.rb, blok form:

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

To wszystko

Powiązane problemy