Update all data of model in rails - ruby-on-rails

For example we have:
class PublicLibrary < ActiveRecord::Base
has_many :books
end
class Book < ActiveRecord::Base
belongs_to :public_library
end
If we want do update all books in PublicLibrary, we can add to PublicLibrary model
accepts_nested_attributes_for :books, :allow_destroy => true, :reject_if=>:all_blank
And now we can do something like this
library=PublicLibrary.find(ID)
library.update_attributes(:books_attributes=>{<bunch of books here>})
And all related books will be updated, some books'll be removed and some new books will be inserted in table books
Now I have some model Book, that doesn't have relation with PublicLibrary:
class Book < ActiveRecord::Base
end
I have an admin panel that show all books in one big-big table and want to update/delete/insert new books just by one click, so I want something like
Book.bulk_update({...books...})
Or working with subset (don't sure is I really need it, but if we can do that...why not to know how?)
books_to_update=Book.where(...).bulk_update({...books...})
Of course Book may have some nested models.
Do you have any ideas?
P.S. Currently I have only idea of having some parent and do update for it...

your {...books...} hash either has multiple books in it for which you want
books.each { |id, book| Book.find(id).update_attributes(book) }
or you want to do a real bulk update (meaning same update to all books in action)
then you can use http://apidock.com/rails/ActiveRecord/Relation/update_all

Related

Applying conditions on has_many relationship

I'm having trouble retrieving some data from a Postgre database with rails 4.1.8
Let's considere two models with a has_many relationship
class Post < ActiveRecord::Base
has_many :comments
end
and
class Comment < ActiveRecord::Base
belongs_to :post
end
Comments have a state with approved or censured
I want to write a method in Post model self.all_comments_approved
I cannot figure out how to get only posts with all comments approved.
I would like to write something like this (not working example) :
def sef.all_comments_approved
joins(:comments).where("ALL(comments.state = 'approved')").references(:comments)
end
Thanks in advance for any help :)
You might try using joins with group and having statement, but relation you would get would be very fragile (you wouldn't be able to query it any further as easily as you wish - pluck would destroy it completely etc).
Way around this issue is to split it into two db calls:
def self.all_comments_approved
non_approved_ids = joins(:comments).where.not(comments: {state: 'approved'}).uniq.pluck(:id)
where.not(id: non_approved_ids)
end

Many to Many relation issue in Rails 4

I am a rails beginner and encountered the following issue
Models are setup as follows (many to many relation):
class Activity < ActiveRecord::Base
belongs_to :user
has_many :joinings
has_many :attendees, through: :joinings
end
class Joining < ActiveRecord::Base
belongs_to :activity
belongs_to :attendee
end
class Attendee < ActiveRecord::Base
has_many :joinings
has_many :activities, through: :joinings
end
This is one page test application for some users posting some activities, and other users to join the activities.
It is organized as single page format (activities index), and after each activity, there is a "Join" button users can click.
I am stuck at the point when a user needs to join a specific activity.
in the index.html.erb (of the activities), with the Join button code.
This will point to the attendee controller, to Create method, but I got no information regarding the Activity that I want to follow (eg. activity_id, or id)
Without this I cannot use the many to many relation to create the attendee.
What would be the correct button code, or any other suggestion to to get the corresponding activity ID in the attendees controller?
I tried a lot of alternatives, including even session[current_activity] , but is pointing (of course) always to the last activity.
Thanks so much !
If you have existing activities, and existing attendees, and you want to change the relationship between them, then you are editing the join table records. Therefore, you should have a controller for these. Like i said in my comment i'd strongly recomnmend renaming "joining" to "AttendeeActivity" so your schema looks like this:
class Activity < ActiveRecord::Base
belongs_to :user
has_many :attendee_activities
has_many :attendees, through: :attendee_activities
end
class AttendeeActivity < ActiveRecord::Base
belongs_to :activity
belongs_to :attendee
end
class Attendee < ActiveRecord::Base
has_many :attendee_activities
has_many :activities, through: :attendee_activities
end
Now, make an AttendeeActivitiesController, with the standard scaffoldy create/update/destroy methods.
Now, when you want to add an attendee to an activity, you're just creating an AttendeeActivity record. if you want to remove an attendee from an activity, you're destroying an AttendeeActivity record. Super simple.
EDIT
If you want to create an Attendee, and add them to an activity at the same time, then add a hidden field to the form triggered by the button:
<%= hidden_field_tag "attendee[activity_ids][]", activity.id %>
This will effectively call, when creating the attendee,
#attendee.activity_ids = [123]
thus adding them to activity 123 for example.
You have several options here. I'm assuming that the Join button will simply submit a hidden form to the attendees controller's create action. So the simplest solution would be to include the activity_id as a hidden form tag that gets submitted along with the rest of the form. This activity_id will be available in the params hash in the controller.
The other option is to setup Nested routing so that the activity_id is exposed via the path.
Thanks for all the details. I will change the naming of the join table for the future.
My problem was that I could not find the specific activity for attendee create method. Finally I found something like this for the JOIN button:
<%= button_to 'Join', attendees_path(:id => activity) %>
This will store the Activity ID, so I am able to find it in the Attendees controller:
def create
activity = Activity.find(params[:id])
#attendee = activity.attendees.create user_id: current_user.id
redirect_to activities_path
end
this updates also the Joinings (AttendeeActivity) table.
I will study about the hidden_field_tag solution, as is not clear to me yet.

how to add has_many association?

This is my first question, ive looking for an question similar but hadnt found it, i'm starting learning ruby on rails and i followed a course that said for creating foreing keys with has_many
i create my class Band by.
rails g model Band name:string website:string email:string
and i also have created my class Genres in the same way
this orders generate classes like
class Band < ActiveRecord:Base
end
so i edit my file Band.rb like this
class Band < ActiveRecord:Base
has_many :Genres
end
save changes (Ctrl+S) and when i try to create a new object o class Band
a = Band.new
a hasnt the genres atribut
Edit your band.rb file to:
class Band < ActiveRecord:Base
has_many :genres
end
reload, and everything should be fine since then.
First make sure that in Your Genre model you have
belongs_to :band
Also in you Band model lowercase you has_many relationship like so
has_many :genres
since a genre belongs to a band you need to have a band_id in you genres table which will point to the band that genre belongs to.
then you can easily find the band the genres for a band.In you controller you can find a band
#band = Band.find(params[:id])
and to check the genre that was set before you can do
#genre = #band.genres
this will give you the correct genres for that band .
However this seems more like a situation where a band has_many :genres and a genre has_many :bands.In this case you may want to use a has_and_belongs_to_many relationship.You can find more details for associations in the rails guides here: http://guides.rubyonrails.org/association_basics.html#the-has-and-belongs-to-many-association
If you go with the has_and_belongs_to_many relationships you need to create a separate table where each genre and band will point to each other so you can associate them easily
the new method will not create the actual record in the database, you have to call save on the instance. To create associate genre, you can call create_genre on the already exists Band instance.

Adding an associated Table to an Existing Table (Book > Revision)

I have the following models:
class Instance < ActiveRecord::Base
has_many :users
has_many :books
end
class User < ActiveRecord::Base
belongs_to :instance
end
class Book < ActiveRecord::Base
belongs_to :instance
end
I now want to add a BookRevision table, that has the following columns
(id, user_id, version # (auto increment), diff (old book copy),
timestamps)
Logic:
When a book is Created, a record is
added to the BookRevision table, so
we know who created the book in the
first place
When a book is Updated, a record is added with the user_id (could be
a different user), and a new version
, and the old book text, to serve as an archive.
Given that I have the Instance, User, Book table implement in my rails
add, are these the correct steps to make the above come to life?
- Add a migration for the BookRevision table....
rails generate migration AddTableBookRevision user_id:integer
version:integer diff:text
- Then update the models as follows:
class Instance < ActiveRecord::Base
has_many :users
has_many :books
end
class User < ActiveRecord::Base
belongs_to :instance
end
class Book < ActiveRecord::Base
belongs_to :instance
has_many :BookRevisions
end
class BookRevision < ActiveRecord::Base
belongs_to :Book
end
Then in my controller, when adding a new book? Right now I have:
#book = Book.create(params[:book].merge(:instance_id =>
current_user.instance_id))
How do I update that to account for the BookRevision association?
Thanks for helping me out!
You might want to check out something like acts_as_versioned instead of rolling your own. This works in the fashion you've described here, where modifications are saved into a separate but related table.
Keep in mind you will have to apply migrations to your Book and BookRevision table in parallel from that point forward. They must be schema compatible for revisioning to work.
I have built a version tracking system of this sort that used serialized models to avoid having to maintain migrations, as the intent was to preserve the exact state of the model regardless of future modifications via migrations. This has the disadvantage of not being able to roll back to an arbitrary older version because there may be a schema mis-match.

What is the best-practice, don't-work-yourself-into-a-corner way to add comments to many models?

Hi Stack Overflowers: I'm building a Ruby on Rails application that has several different models (e.g. Movie, Song, Photo) that I am storing movie clips, mp3s and photos. I'd like for users to be able to comment on any of those Models and have control over which comments are published.
Is the best practice to create a Comment model with:
belongs_to :movie
belongs_to :song
belongs_to :photo
And then tie each Model with:
has_many :comments
Then, I'm guessing in the Comment table, I'll need a foreign key for each Model:
comment, movie_id, song_id, photo_id
Is this the correct way to build something like this, or is there a better way? Thanks in advance for your help.
Use acts_as_commentable. It creates a comment table with a commentable_type (model name of the commented-upon item) and commentable_id (the model's ID). Then all you need to do in your models:
class Photo < ActiveRecord::Base
acts_as_commentable
end
Create a table to hold the relationships for each type of comment:
movie_comments, song_comments, photo_comments
and then use:
class Movie < ActiveRecord::Base
has_many :movie_comments
has_many :comments, :through => :movie_comments
end
class MovieComment < ActiveRecord::Base
include CommentRelationship
belongs_to :comment
belongs_to :movie
end
You can use a module (CommentRelationship) to hold all of the common functionality between your relationship tables (movie_comments)
This approach allows for the flexibility to be able to treat your comments differently depending on the type, while allowing for similar functionality between each. Also, you don't end up with tons of NULL entries in each column:
comment | movie_id | photo_id | song_id
----------------------------------------------------
Some comment 10 null null
Some other comment null 23 null
Those nulls are definitely a sign you should structure your database differently.
Personally I would model it this way:
Media table (media_id, type_id, content, ...)
.
MediaType table (type_id, description, ... )
.
MediaComments table ( comment_id, media_id, comment_text, ...)
After all, there is no difference to the database between a Song, Movie, or Photo. It's all just binary data. With this model you can add new "media types" without having to re-code. Add a new "MediaType" record, toss the data in the Media table.
Much more flexible that way.
I have no RoR Experience, but in this case you'd probably better off using inheritance on the database level, assuming your dbms supports this:
CREATE TABLE item (int id, ...);
CREATE TABLE movie (...) INHERITS (item);
CREATE TABLE song (...) INHERITS (item);
[...]
CREATE TABLE comments (int id, int item_id REFERENCES item(id));
Another approach could be a single table with a type column:
CREATE TABLE item (int id, int type...);
CREATE TABLE comments (int id, int item_id REFERENCES item(id));
As expressed before, I can't tell you how to exactly implement this using RoR.
The best idea is probably to do what Sarah suggests and use one of the existing plugins that handle commenting.
If you wish to roll your own, or just understand what happens under the covers, you need to read about Single Table Inheritance, the way Rails handles inheritance. Basically, you need a single comments table ala:
# db/migrate/xxx_create_comments
create_table :comments do |t|
t.string :type, :null => false
t.references :movies, :songs, :photos
end
Now you can define your comment types as
class Comment < ActiveRecord::Base
validates_presence_of :body, :author
# shared validations go here
end
class SongComment < Comment
belongs_to :song
end
class MovieComment < Comment
belongs_to :movie
end
class PhotoComment < Comment
belongs_to :photo
end
All your comments will be stored in a single table, comments, but PhotoComment.all only returns comments for which type == "Photo".

Resources