Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add Examples to Readme #229

Closed
triptec opened this issue Jan 27, 2016 · 14 comments
Closed

Add Examples to Readme #229

triptec opened this issue Jan 27, 2016 · 14 comments
Labels

Comments

@triptec
Copy link

triptec commented Jan 27, 2016

It would be nice with some code to go with the example of books, publishers and reviews in the readme.

@allenwlee
Copy link

agreed; it isn't clear how we are supposed to implement an import with more than one associated model

@jkowens
Copy link
Collaborator

jkowens commented Feb 16, 2016

The wiki has some examples including one that imports an associated model. I believe the :recursive option (import associations) is only available for Postgres, but that isn't specified in the wiki doc.

@allenwlee
Copy link

yes i added a 'multi-tier' example to the wiki last week and neglected to update this issues thread. thanks. i discuss it in issue #231

@JHFirestarter
Copy link

@allenwlee what might the 'multi-tier' example syntax be if both books and reviews tables have multiple attributes? Say the starting data is a csv of reviews with book, author, and commentary columns...and you had Book and Review models (Book has_many reviews, Review belongs_to book)? The books table may have title and author attributes, while the reviews may have commentary and book_id (foreign key) attributes.

@allenwlee
Copy link

@JHFirestarter is commentary a separate model or just a column?

if i understand your question correctly, an example would be as follows:

books = []
10.times do |i| 
  book = Book.new(:name => "book #{i}", :author=>"John Smith")
  book.reviews.build(:title => "Excellent", :commentary => "Ipsum lorem...")
  books << book
end
Book.import books, recursive: true

@JHFirestarter
Copy link

@allenwlee correct re: just a column. Apologies, on "line 3" you have the book attributes (name and author), while in "line 4" you have the review attributes (title and commentary)? One of my issues was understanding which model's attributes go where.

@allenwlee
Copy link

yes that is intentional. line three instantiates a Book model. Line four 'builds' a review model that belongs_to that newly instantiated book object. Here's a good explanation of the ActiveRecord build method for associated objects.

@JHFirestarter
Copy link

Thx. I'll update my question to match the attributes you have in the example.

@JHFirestarter
Copy link

@allenwlee Btw, +1 for @triptec 's OP. I think (maybe wrong) a good example would be a gist with models and similar example .rb using books, publishers & reviews, where there's need for a bulk action with associations in an app that relies on those associations in its views. Instead of values like "John Smith," one would have to work with a variable of some sort before import.

If source data is commonly in some type of excel file, would the best example include converting that to an array of arrays (or array of hashes)? How does one refactor for an array of arrays (or array of hashes) instead of "John Smith?"

The reason for the question is reading other issues, it seems there's no solid example that includes the things you found (like the need for autosave: true? or inverse_of?)

Here's my attempt, which fails to accomplish the goal, as (while it does import to 3 tables quickly) it does not populate the foreign keys publisher_id or book_id in the reviews table, assuming that there are has_many and belongs_to associations. The below also touches on how to combine roo with ar-import. Starting with a 4-column xlsx file:

require 'roo'
require 'activerecord-import'

xlsx = Roo::Excelx.new(relative_file_path)
xlsx.default_sheet = xlsx.sheets[1] # sets second sheet
first = 5 # in the event headers are further down the file on say row 4 (so values are 4+1)
last = xlsx.last_row # if unknowable number of rows bc starting file may change

# AR table columns
bk_col = [:title, :author]
rev_col = [:review]
pub_col = [:publisher]

# values to import
bks = []
revs = []
pubs = []

first.upto(last) do |row|
  title = xlsx.cell(row, 1)
  auth = xlsx.cell(row, 2)
  pub = xlsx.cell(row, 3)
  rev = xlsx.cell(row, 4)

  bks << [title, auth] # can be in any order in xlsx file
  revs << [rev]
  pubs << [pub]
end

Book.import bk_col, bks, :validate => false # for speed
Review.import rev_col, revs, :validate => false
Publisher.import pub_col, pubs, :validate => false

It seems that perhaps the reviews and publishers importing above may be redundant (and counterproductive) if associations are setup well and .build is used--but I do not know how to extrapolate from your example. I'm sure there's a much better way than I'm attempting...but after days (weeks) on stack overflow and reading ar-import lib/ etc, I would think this kind of example is very common and useful to others if done correctly. Simply adding :recursive => true as an import option above does not do the trick (each error).

@allenwlee
Copy link

OK, i don't know what the association is between books and publishers. unless it is Book has_one or has_many Publisher (which I doubt), then it is true in this example that you can't import books and publishers all in one import function. you can however import books and reviews in one go:

e.g.:

books=[]
first.upto(last) do |row|
  title = xlsx.cell(row, 1)
  auth = xlsx.cell(row, 2)
  pub = xlsx.cell(row, 3)
  rev = xlsx.cell(row, 4)
  book = Book.new(title: title, auth: auth)
  book.reviews.build(comment: rev)
  books << book
end

Book.import books

@JHFirestarter
Copy link

@allenwlee True (was trying to keep with README). Better example would be critics instead of publishers. Both Books and Critics would each have has_many .. through reviews associations (just like a cops, criminals, arrests example). Still, this is quite helpful--can import Books and Reviews as long as using version 0.12.0 to avoid "private..select" error.

@JHFirestarter
Copy link

Update: tried importing Critics a few ways to no avail (e.g.

books = []
...
  book = Book.new(title: title, author: auth)
  book.critics.build(name: critic) # `has_many .. through .. reviews` association
  book.reviews.build(review: rev)
  books << book
end

starts to create books in one big motion, but then it yields NoMethodError: undefined method critic_id=' for #<Critic:0x007fd244263158>)

@jkowens jkowens mentioned this issue Apr 4, 2017
10 tasks
@jkowens jkowens changed the title Examples? Add Examples to Readme Jun 22, 2017
@dillonwelch
Copy link
Contributor

With what's in the README here now, is this still needed @jkowens ?

@jkowens
Copy link
Collaborator

jkowens commented Nov 15, 2018

I think we can go ahead and close this issue.

@jkowens jkowens closed this as completed Nov 15, 2018
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
Projects
None yet
Development

No branches or pull requests

6 participants