简体   繁体   中英

Ruby on Rails batch updating records in database after running gsub

This is a 2-part question. Part 1 asks if my structure is correct (it is working, I just want to know if this is the Rails way to do things). Part 2 asks how to actually accomplish the question in the title of this post, here we go:

I have the following structure, in my Dvd model:

def self.remove_parens 
    # we will remove the beginning parentheses from the entries sent to us by the parens method
    @dvds = self.parens # get the entries we need to edit
    @dvds.each do |dvd|
        @newDvds = dvd.title.gsub!(/^\([0-9]*\)/, '')
    end
end

In the DvdsController file:

  def fixer
    @newDvds = Dvd.remove_parens
  end

In the fixer View file:

<% 
  @newDvds.each do |dvd| 
    fullText = "#{dvd.title}"
%>

This works great, I can see the result of the gsub is working and removing entries like (245) from the title.

  1. Is this the right way of doing things in Rails? Putting most of the code in the Model, then having the controller simply call that function?
  2. I realize that this is only printing out the changes, not writing them back to the database. I want to write them back into the database, how would I accomplish this? Maybe by calling the update action on @newDvds in the Controller (since the Model doesn't know about the update method)?

Socjopata: I've ammended my model thusly based on your suggestions:

dvds = self.parens # get the entries we need to edit
dvds.each do |dvd|
    fixedTitle = dvd.title.gsub!(/^\([0-9]*\)/, '') # this prints it out but doesn't change the entries in the table
    dvd.update_attribute(:title, fixedTitle) # this is supposed to update the attribute in the table
end

But it doesn't update the data, data in the table is still the same.

What I finally did was this that seemed to do the trick:

Dvd.update(dvd.dogTag, { :title => fixedTitle } )

Now I need to trim that title so I figure I'd put something like:

fixedTitle = dvd.title.gsub!(/^\([0-9]*\)/, '').strip!

fixedTitle = dvd.title.gsub!(/^\\([0-9]*\\)/, '').strip! has a couple of subtle problems, the first of which is why your database doesn't get updated.

gsub! modifies the string in place and this bypasses (as of 3.2.7) ActiveRecord's way of knowing if an instance has changed. It believes the instance of your DVD is still unchanged and so skips updating the database.

Compare

dvd.title.gsub!(/^\\([0-9]*\\)/, '')
dvd.changed? # => always false if no other changes were made

dvd.title = dvd.title.gsub(/^\\([0-9]*\\)/, '')
dvd.changed? # => true if title was changed

Also, calling strip! on the return of gsub! could be dangerous. If gsub! makes no substitutions then it will return nil and you will be trying to call strip! on nil. In this case I think you want to use gsub (without !) instead.

  1. Yes, the logic should be placed in a Model.
  2. So what stops you from enhancing your remove_parens with updating your @dvds? Like

    _instance_of_a_dvd_model.update_attribute(:title, _gsubbed_title)

Also, you don't need to "@" your local variables in a model. Also why are you setting fullText variable in a view? are you using it somewhere? If yes, then you are aware that your view should be rather logic free?

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM