Downloading CSV downloads all files, need it to download specific ones - ruby-on-rails

I'm having a little trouble downloading values to a CSV file. I have two models, downtowns and properties. Downtowns have many properties. Properties each have one downtown. Problem i'm having is that with what i've done i've been able to download all properties, which is cool! But what I'm struggling with is downloading only properties owned by the downtown.
To what I have initially to download all properties I have
In my properties controller:
def index
#properties = Property.all
respond_to do |format|
format.html
format.csv { send_data #properties.to_csv }
end
end
In my properties model
def self.to_csv
attributes = %w(id name owner_first_name created_at)
CSV.generate(headers: true) do |csv|
csv << attributes
all.each do |property|
csv << property.attributes.values_at(*attributes)
end
end
end
and then in my view I have.
= link_to "download CSV", downtown_properties_path(:format => :csv), class: "button"
So again this works, but because of my associations the view where i can add my link can only be in downtowns/1/properties which based on my putting the code in the index this actually makes sense
I tried something along the lines of
#download_properties = Property.where(downtown: #downtown_id)
respond_to do |format|
format.html
format.csv { send_data #download_properties.to_csv }
end
However #downtown_id is not defined at this point. So I think the solution is better in the downtown model?
If anyone has any idea looking through this and able to help me out, I would greatly appreciate it!

shouldn't it be:
#download_properties = Property.where(downtown: params[:id])
or
#download_properties = Downtown.find(params[:id]).properties

Related

How do I export a filtered table to CSV?

controller code
def index
#courses = Course.all
respond_to do |format|
format.html
format.csv { send_data #courses.to_csv }
end
end
Link code
<%= link_to "Export Course", courses_path(format: :csv) %>
model code
def self.to_csv
CSV.generatezz do |csv|
csv << column_names
all.each do |product|
csv << product.attributes.values_at(*column_names)
end
end
end
With that code, I can properly export a csv file that contains every course in the database. I want to set it up where I can export only the information of one course. If possible i'd like to use a variable(like a url parameter) as I have other tables I want to iterate over with the same id that would let me get one course to the csv.
The model code is a class method, which annoyingly enough won't work for an instance of that class, so I can't, in the controller, go #courses = Course.find(params[:course_id].to_i) and then send_data #courses.to_csv, as it acts as if its an entirely new thing.
Any help on this would be great, thanks.
What if you made an instance method:
def to_csv
CSV.generatezz do |csv|
csv << column_names
csv << attributes.values_at(*column_names)
end
end
I dont know what column_names is here, maybe another method you made? Anyhow, you should be able to call that in your controller after you set the #course variable with #course = Course.find(params[:id])

How to create multiple new records, checking for existing duplicates, and adding new or existing record to join table

I am writing a simple blogging app that creates posts and adds multiple tags to the post via
has_many :tags, :through => :tag_joins
Tags are pulled from a comma separated string typed into the view which is separated into individual tags by a regex in the post create action.
def create
#post = Post.new(post_params)
respond_to do |format|
if #post.save
#tags = #post.tagstring.split(/\s*,\s*/)
#tags.each do |i|
#tag = Tag.new(:name => i)
if #tag.save
#post.tags << #tag
end
end
format.html { redirect_to "/#{#post.permalink}", notice: "success" }
else
format.html { render action: 'new' }
end
end
end
This works fine except is creating duplicate tags in the tag table.
How would I check to see if a tag already exists and, if it does, add the existing one to the post rather than creating a new one? I have looked at using first_or_create but am having difficulty getting my head around how it would fit within the context of another create action.
Yes, first_or_initialize will help you..
#tags.each do |i|
tag = Tag.where(name: i).first_or_initialize
if tag.persisted? || tag.save
#post.tags << tag
end
end
Check docs here persisted?
If there's a possibility of deleting tags then, check !tag.new_record? instead.
Read new_record?
Use find_or_initialize_by. Something like this:
#tag = Tag.find_or_initialize_by(:name => i)
Because, if the #tag already exists, you won't create a new one.
Then you probably want to do some check on that, like:
if #tag.new_record?
#post.tags << #tag if #tag.save
else
#post.tags << #tag
end
That if statement is sort of jenk. Sorry about that. If I were you, I'd spend a few minutes making it cooler.

Why is Rails exporting all rows instead of only filtered results?

I'm running a Rails 4 application and am using the Ransack gem to filter results for employees. I've seen multiple examples of how to limit columns on the exported CSV file, but not on limiting rows. To my understanding, the following code should call the .to_csv method on the filtered employees, but currently all rows are being downloaded. Do I need to pass an array of the IDs of the filtered results to the .to_csv method?
View:
<h3>Download</h3>
<%= link_to "CSV", employees_path(format: "csv") %>
Controller:
def index
#q = Employee.ransack(params[:q])
#q.build_condition
#employees = #q.result(distinct: true)
respond_to do |format|
format.html
format.csv { render text: #employees.to_csv }
end
end
Model:
def self.to_csv(options = {})
CSV.generate(options) do |csv|
csv << column_names
all.each do |employee|
csv << employee.attributes.values_at(*column_names)
end
end
end
I know this is a bit delayed but this is how I fixed same problem.
Change the link to have params.merge to keep the filtered results.
<%= link_to "CSV", employees_path(params.merge(format: "csv")) %>
I also had to change my format.csv to the following to get it to work:
format.csv { send_data #employees.to_csv, filename: "employees.csv" }
Hope this helps.
In this line you're adding all of the employee models.
all.each do |employee|
Instead you should have something like
def self.to_csv(employees)
..
employees.each do |employee|
..
end
And call this class method like this:
format.csv { render text: Employee.to_csv(#employees) }
(Read more about class methods here)

Ransack export results to CSV

I'm trying to export a list of Ransack (Railscast) results to a CSV file (Railcast). However, it keeps exporting all of the objects, instead of the results returned by the Ransack search. Can anyone tell me where I'm going wrong?
In the Reports controller, I've tried passing both #bookings and #search.result:
def index
#search = current_user.bookings.search(params[:q])
#bookings = #search.result
#search.build_condition
respond_to do |format|
format.html
format.csv { render text: Booking.to_csv(#bookings) }\
end
end
And then the Booking to_csv method:
def self.to_csv list
CSV.generate do |csv|
csv << column_names
list.each do |booking|
csv << booking.attributes.values_at(*column_names)
end
end
end
Yet every time, I get the unfiltered list of current_user.bookings. Why?
To export only the filtered results as csv, you should make sure that the search parameters are included in the url you call to generate the csv.
Hence, if you want to export the results that you see on the html page you should call:
reports_path(params.merge(format: 'csv')
Try this:
def index
session[:q] = params[:q] if params[:q]
#search = current_user.bookings.search(session[:q])
#bookings = #search.result
#search.build_condition
respond_to do |format|
format.html
format.csv { render text: Booking.to_csv(#bookings) }\
end
end

Rails: overriding as_json for dynamic value -- is there a smarter way?

I want to output a list of affiliate links, each tagged to identify the current user. It would be simple in HTML, but we're writing an API, so the output is JSON.
I have it working, but it seems overly complicated. Is this the best approach?
My model, AffiliateLink contains a field (the raw HTML of the link) that I'll transform and output on the fly by adding a token. I have a model method that produces the replacement -- it is non-trivial because we use multiple affiliates and each has a special transformation rule that this method knows about:
def link_with_token(user_token)
# some gnarly code that depends on a lot of stuff the model knows
# that returns a proper link
end
To get my correct link html in JSON I have done these things:
add attr_accessor :link_html to model
add an instance method to set the new accessor
...
def set_link_html(token)
self.link_html = link_with_tracking_token(token)
end
override as_json in the model, replacing the original html_code with link_html
...
def as_json(options = {})
super(:methods => :link_html, :except => :html_code)
end
iterate over the collection returned in the controller method to do the transformation
...
def index
#links = Admin::AffiliateLink.all # TODO, pagination, etc.
respond_to do |format|
format.html # index.html.erb
format.json do
#links.each do |link|
link.set_link_html(account_tracking_token)
end
render json: #links
end
end
end
This seems like a lot of stuff to do just to get my teensy-weensy transformation done. Helpful suggestions (relating to this problem and not to other aspects of the code, which is in flux now) are welcome.
1) A quick solution to your problem (as demonstrated here):
affiliate_links_controller.rb
def index
#links = Admin::AffiliateLink.all # TODO, pagination, etc.
respond_to do |format|
format.html # index.html.erb
format.json do
render json: #links.to_json(:account_tracking_token => account_tracking_token)
end
end
end
AffiliateLink.rb
# I advocate reverse_merge so passed-in options overwrite defaults when option
# keys match.
def as_json(options = {})
json = super(options.reverse_merge(:except => :html_code))
json[:link_with_token] = link_with_token(options[:account_tracking_token])
json
end
2) A more hardcore solution, if you're really writing an API:
See this article describing your problem.
See the gem that the authors made as a solution.
See this railscast on using the gem.
3) And lastly, the convenient solution. If you have a convenient model relation, this is clean:
Pretending AffiliateLink belongs_to :user. And assuming user_token is an accessible attribute of User.
AffiliateLink.rb
# have access to user.user_token via relation
def link_with_token
# some gnarly code that depends on a lot of stuff the model knows
# that returns a proper link
end
def as_json(options = {})
super(options.reverse_merge(:methods => :link_with_token, :except => :html_code))
end
affiliate_links_controller.rb
def index
#links = Admin::AffiliateLink.all # TODO, pagination, etc.
respond_to do |format|
format.html # index.html.erb
format.json do
render json: #links
end
end
end

Resources