Can't Get Photos to Work in Rails - ruby-on-rails

I am having all kinds of trouble doing a simple task, displaying a photo as part of a result set for each one. I'm pretty new to Rails in general, coming from another language where I can do this in seconds.
The #photo query finds 0 records even though multiple records for photos are in the DB matching the property ID.
I'm not too sure what I'm doing wrong here.
Here are my relevant files:
app/controllers/properties_controller.rb:
class PropertiesController < ApplicationController
......
def all
# gets all of the properties and uses will_paginate
#properties = Property.paginate(page: params[:page])
# should get the first positioned photo matching the results in the #properties query
#photos = Photo.where(:property_id => #properties.map(&:id)).order("position").first
end
# ......
end
app/models/property.rb:
class Property < ActiveRecord::Base
belongs_to :listing_type
belongs_to :property_type
belongs_to :status
has_many :photos
# ......
end
app/models/photo.rb:
class Photo < ActiveRecord::Base
mount_uploader :photoname, PhotoUploader
belongs_to :property
acts_as_list scope: :property_id
validates :photoname, presence: true
validates :property_id, presence: true
end
details.html.erb:
<% #properties.reverse_each do |property| %>
<div class="item col-md-4">
<div class="image">
<%= link_to property_path(property) do %>
<span class="btn btn-default"><i class="fa fa-file-o"></i> Details</span>
<% end %>
<%= image_tag(property) %>
</div>
# ......
<% end %>

Since you have a has_many realtion in the Property, you have to just access to the relation of the property to read all teh photoes:
photo = #property.photos.order("position").first
Well if you need to grab all the photoes of properties, use include to properties grab:
#properties = Property.includes(:photos).paginate(page: params[:page]).reverse
the include is needed to avoid N + 1 problem, then try replacing it with a first photo:
#photos = #properties.map { |pr| pr.photos.order("position").first }
Mpve reverse to controller, and to use #photos along with #properties use index:
<% #properties.each.with_index do |property, i| %>
#...
<%= image_tag(#photos[i]) %>
<- end >
NOTE that the code selecting an image pr.photos... be better moved to a decorator (see gem draper).

After a TON of help from #МалъСкрылевъ, which also had me learning a few new ways of "thinking" this in general, his answer led me to rethink what I was doing and go back to simply starting over and reconstructing what ended up being VERY basic. All I needed to do was make the photo query for the first photo in my loop over the properties. DUH! Here's what I did in case it helps some other poor new Rails developer!
properties_controller.rb
class PropertiesController < ApplicationController
......
def all
# gets all of the properties and uses will_paginate
#properties = Property.paginate(page: params[:page])
......
end
details.html.erb
<% #properties.each do |property| %>
<div class="item col-md-4">
<% #photo = Photo.where(property_id: property.id).order("position").first %>
<div class="image">
<%= link_to property_path(property) do %>
<span class="btn btn-default"><i class="fa fa-file-o"></i> Details</span>
<% end %>
<%= image_tag(#photo.photoname.medium) %>
</div>
......

Related

Narrow elements in a join table by a dropdown menu. (Rails 4)

for one of my join tables, I'd like to filter results based on a dropdown menu selection. Before I explain further, I will provide the relevant code.
I have 3 models in question:
class Scoreboard
has_many :teams, has_many :team_matches
end
class Team
belongs_to Scoreboard
#self reference with the teams to create team_matches.
has_many :first_team, class_name: "TeamMatch", foreign_key: "team_a_id", dependent: :destroy
has_many :team_a, through: :first_team, source: :team_a
has_many :second_team, class_name: "TeamMatch", foreign_key: "team_b_id", dependent: :destroy
has_many :team_b, through: :second_teams, source: :team_b
end
And the final Model which is a join table of teams:
class Team_Match
belongs_to :team_a, class_name: "Team"
belongs_to :team_b, class_name: "Team"
belongs_to :scoreboard
end
Team Matches Controller Code for the Index View:
class TeamMatchesController < ApplicationController
require 'will_paginate/array'
def index
#selected = true
#scoreboard = Scoreboard.find(params[:scoreboard_id])
#teams = #scoreboard.teams
#matches = #scoreboard.team_matches.order("match_date DESC").paginate(page: params[:page], per_page: 5)
end
end
The Index Page View:
<h2 class="team-matches-header">TEAM MATCHES</h2>
<% if manager_or_owner?(#scoreboard, current_user) %>
<%= link_to "Add Matches", new_scoreboard_team_match_path(#scoreboard), class: "btn btn-primary" %>
<%= link_to "Clear Matches", deletematches_scoreboard_path(#scoreboard),method: :delete, class: "btn btn-danger", :data => {:confirm => "Are you absolutely sure you want to delete all matches?"} %>
<% end %>
<%= form_tag(scoreboard_team_matches_path, :method => "get", id: "match-search-form", autocomplete: "off") do %>
<div class="row new-member-field">
<div class="col-xs-12 col-sm-6">
<%= select_tag "teams", options_from_collection_for_select(#teams, "id", "name"), prompt: "Select something", class:"form-control" %>
<%= submit_tag 'Filter' %>
</div>
</div>
<% end %>
<div class="match-list">
<%= render 'match_list' %>
</div>
The Match List Partial
<% if #matches.present? %>
<% #matches.each do |game| %>
<div class="row match-div clearfix" id="match_<%= game.id %>">
<div class="col-xs-12 match-column">
<div class="facing-teams">
<div class="first-team">
<%= game.team_a.name %>
</div>
<div class="second-team">
<%= game.team_b.name %>
</div>
</div>
</div>
</div>
<% end %>
<% end %>
<div class="row">
<div class="col-xs-12 new-member-pages">
<%= will_paginate #matches %>
</div>
</div>
So far on the team matches index page, I have been able to put a dropdown menu selection with a list of teams. The trouble I'm having is how to make it functional. Upon submission of a team from the dropdown selection, I would like to display a list of Match objects(#matches) whose :team_a_id or :team_b_id matches the selected Team's id. How would I go about doing that?
I would use scopes to achieve what you want. Scopes are reusable filters that you can apply to an association to find exactly the records that you want.
This is how you would create a scope that finds the matches for a particular team:
class Team_Match
scope :has_team, ->(team_id) {
team_a = arel_table[:team_a_id].eq(team_id)
team_b = arel_table[:team_b_id].eq(team_id)
team_a_or_b = team_a.or(team_b)
where(team_a_or_b)
}
end
This uses arel (which ActiveRecord uses under the hood to create SQL queries) in order to find a match in either the team_a_id or team_b_id columns. If you have no idea what's going on with the above code, take a quick trip to Mastering AR and Arel to get up to speed.
You use the scope like this:
matches = Team_Match.has_team(team_id)
You can also use the scope on any relationship that joins to Team_Match:
matches = #scoreboard.team_matches.has_team(team_id)
Scopes can also be chained together:
matches = #scoreboard.team_matches.has_team(team_id).where(match_date: today)
The scope allows you to pass in the team id from the select box to find the matches:
def index
#selected = true
#scoreboard = Scoreboard.find(params[:scoreboard_id])
#teams = #scoreboard.teams
#matches = #scoreboard.team_matches.order("match_date DESC")
#matches = #matches.paginate(page: params[:page], per_page: 5)
# we apply the scope (if present) to only show the matches for the selected team
if (team_id = params[:selected_team_id])
#matches = #matches.has_team(team_id)
end
end
The only change you would need to make to the above code is working out what the actual name is of the param variable that holds the selected team id.

Favouriting a picture in a Rails app

I am trying to figure out how to execute a "favourite a picture" method in a Rails app, which I am very new to, going from a JS/Meteor background.
The point is I have a User, FavPic, Pic classes:
class User < ActiveRecord::Base
#some user oauth stuff would be here
has_many :fav_pics
has_many :pics_favorited,
class_name: 'Pic',
through: :fav_pics
end
class FavPic < ActiveRecord::Base
belongs_to :user
belongs_to :pic
end
class Pic < ActiveRecord::Base
has_many :fav_pics
has_many :fav_users,
class_name: 'User',
through: :fav_pics
end
and here's my template:
<% if current_user %>
<%= form_tag(root_path, :method => "get") do %>
<p>
<%= text_field_tag :username, params[:username] %>
<%= submit_tag "Search", :name => nil %>
</p>
<% end %>
<ul>
<% #mentions.each do |mention| %>
<li>
<%= mention.text %>
<div class="row">
<% mention.media.each do |media| %>
<div class="col-xs-3">
<%=image_tag(media.media_url, class:"img-responsive")%>
<i class="fa fa-star fa-2x"></i>
</div>
<% end %>
</div>
</li>
<% end %>
</ul>
<% else %>
<p>
Sign in to be able to use the app.
</p>
<% end %>
current_user is a user signed in through Twitter and #mentions is a list of tweets that has the username inputed in the form mentioned. media.media_url is a picture url that is associated with that tweet.
I am trying to get that link (or whatever way it's done in Rails) to add that media URL into the DB so I can list all the images at a separate URL.
You've got a choice: change the <a href="#"> into a form, or have your action link include the url encoded url as a parameter. Either way, you can still use AJAX to invoke the call to the server.
Your server would have an action on a controller that accepted the appropriate call to mark something as a favorite. Since you don't have your Controller code listed let's call it your PicController I'm also assuming your Pic class has a url property on it.
I highly recommend this primer: http://guides.rubyonrails.org/association_basics.html and the very basic http://guides.rubyonrails.org/getting_started.html which covers how to do action links and forms.
class PicController < ApplicationController
def favorite
pic = Pic.find(params[:url])
favorite = FavPic.new
favorite.pic = pic
favorite.user = current_user # however you get at this...
favorite.save
pic.fav_users.add(favorite)
pic.save
current_user.fav_pics.add(favorite)
current_user.save
end
end
The above code covers how to create the new FavPic instance and save it appropriately.
Putting together that
have a current_user, that want's to add a picture to her/his favourites
pictures are identified by the url that is also the media_url in your template
You need
an action to add the picture as a favourite, i.e. in the pics_controler
a route to this action, since it's a data changeing action, it should be a POST route
so in your config/routes.rb, to define a named route add
put 'pics/:url/avourite' => 'pics#favourite', as: 'add_favourite'
and in your template change the link to
<%= link_to add_favourite_url(url: media.media_url), class: 'fav-img', method: :put do %>
<i class="fa fa-star fa-2x"></i>
<% end %>
Then in the controller:
class PicController < ApplicationController
def favorite
if current_user.present?
pic = Pic.find(params[:url])
FavPic.create pic: pic, user: current_user
# user and pic automaically have this `FavPic` assigned
end
end
end

Rails 4 problems with updating/deleting fields in a model with nested fields

I've been working on introducing a nested form in my app following Railscast episode 196 http://railscasts.com/episodes/196-nested-model-form-revised and the remastered version for rails 4 https://github.com/dnewkerk/nested-model-form.
Let's say we have a 1-to-many association between receipts and articles.
Here's how their models look like:
receipt.rb:
class Receipt < ActiveRecord::Base
has_many :articles, dependent: :destroy
accepts_nested_attributes_for :articles, allow_destroy: true, reject_if: :all_blank
belongs_to :shop
belongs_to :user
def display_name
self.name
end
end
article.rb:
class Article < ActiveRecord::Base
belongs_to :receipt
def name_with_brand
"#{name} #{brand}"
end
end
Here's how the receipts_controller.rb looks like:
class ReceiptsController < ApplicationController
before_action :set_shop, only: [:show, :edit, :update, :destroy]
respond_to :html, :xml, :json
def index
#receipts = current_user.receipts
respond_with(#receipts)
end
def show
respond_with(#receipt)
end
def new
#receipt = Receipt.new
2.times do
#receipt.articles.build
end
respond_with(#receipt)
end
def edit
end
def create
#receipt = Receipt.new(receipt_params)
user_id = current_user.id
#receipt.articles.each do |article|
warranty_time = article.warranty_time
article.warranty_expires = #receipt.shopping_date.advance(months: warranty_time)
end
#receipt.user_id = user_id
#receipt.save
respond_with(#receipt)
end
def update
if #receipt.update(receipt_params)
redirect_to #receipt, notice: "Successfully updated receipt."
else
render :edit
end
end
def destroy
#receipt.destroy
respond_with(#receipt)
end
private
def set_shop
#receipt = Receipt.find(params[:id])
end
def receipt_params
params.require(:receipt).permit(:name, :shopping_date, :shop_id, :file,
articles_attributes: [:id, :name, :brand, :warranty_time, :warranty_expires,
:receipt_id, :_destroy])
end
end
Here's how my receipts.js.coffee looks like:
jQuery ->
$('#receipt_shopping_date').datepicker(dateFormat: 'yy-mm-dd')
$.datepicker.setDefaults($.datepicker.regional['PL']);
$('form').on 'click', '.remove_fields', (event) ->
$(this).prev('input[type=hidden]').val('1')
$(this).closest('fieldset').hide()
event.preventDefault()
$('form').on 'click', '.add_fields', (event) ->
time = new Date().getTime()
regexp = new RegExp($(this).data('id'), 'g')
$(this).before($(this).data('fields').replace(regexp, time))
event.preventDefault()
$(document).ready(jQuery)
$(document).on('page:load', jQuery)
And finally here's my view for adding a new receipt and adding articles to it:
(other fields...)
<div class="large-12 columns">
<p>Add articles on the receipt:</p>
</div>
<div class="field">
<div class="large-12 columns">
<%= f.fields_for :articles do |builder| %>
<div class="article_fields">
<%= render "article_fields", :f => builder %>
</div>
<% end %>
<%= link_to_add_fields "Add another article", f, :articles %>
</div>
</div>
<div class="actions">
<div class="large-12 columns">
<%= f.submit "Sumbit Receipt" %>
</div>
</div>
<% end %>
As you can see I'm using a link_to_add_fields helper method, here's how it looks like:
def link_to_add_fields(name, f, association)
new_object = f.object.send(association).klass.new
id = new_object.object_id
fields = f.fields_for(association, new_object, child_index: id) do |builder|
render(association.to_s.singularize + "_fields", f: builder)
end
link_to(name, '#', class: "add_fields small button", data: {id: id, fields: fields.gsub("\n", "")})
end
And finally as you can see I'm generating a partial called _article_fields.html.erb, here's how it looks like:
<fieldset style="width:1400px">
<legend>new article</legend>
<div class="large-2 columns">
<%= f.text_field :name%>
</div>
<div class="large-2 columns">
<%= f.text_field :brand%>
</div>
<div class="large-2 columns">
<%= f.text_field :warranty_time, class: "warranty" %>
</div>
<div class="large-12 columns">
<%= link_to "delete article", '#', class: "remove_fields button small alert" %>
</div>
</fieldset>
Now let's get down to my problem. When creating a receipt for the first time everything is fine - I see the number of articles in a receipt in my show view and the warranty_expires in every article.
Things get messed up when I'm updating or deleting article_fields through receipts/edit:
1) When I edit a receipt and want to remove any of the articles (although visually in my edit view they disappear - the JS seems to work), the fields are not removed from my DB, thus the show view remains exactly the same like it was before.
Simple example:
before edit: my receipt has 6 articles
during edit: pressed 3 times the 'delete article' button, so the receipt should have 3 articles
after edit: the receipt has still 6 articles
2) When I edit a receipt and want to add an another article field, the value warranty_expires is always nil - how can I make it work with the update action in my receipts controller? I tried using the same code as in my create action:
#receipt.articles.each do |article|
warranty_time = article.warranty_time
article.warranty_expires = #receipt.shopping_date.advance(months: warranty_time)
end
but it won't work. Any idea why?
Simple example:
A receipt has 2 articles already. When I add the 3rd one I get the following result:
3 articles - all of them have names and warranty_time fields, but only 2 of them have a warranty_expires value.
All of your help would be deeply appreciated. Thank you in advance.
I think you can use some callbacks in your Article model for solve your 2nd problem,
Start deleting this, try to keep your controller as simple as possible and handle the operations in your models.
#receipt.articles.each do |article|
warranty_time = article.warranty_time
article.warranty_expires = #receipt.shopping_date.advance(months: warranty_time)
end
In your Article Model add some callbacks
class Article < ActiveRecord::Base
belongs_to :receipt
def name_with_brand
"#{name} #{brand}"
end
before_update :set_warranty_expires
before_create :set_warranty_expires
def set_warranty_expires
self.warranty_expires = self.receipt.shopping_date.advance(months: self.warranty_time)
end
end
Code its not tested, but its the idea. Hope it helps.
Check this two gems simple_form and nested_form this helps a lot when writing large forms and they play well with each other.
Update: I managed to fix the 1st issue.
The fix for the 1st solution is the following:
the hidden field :_destroy was missing for removing articles.
So I needed to changed the following code:
<div class="large-12 columns">
<%= link_to "delete article", '#', class: "remove_fields button small alert" %>
</div>
to:
<div class="large-12 columns">
<%= f.hidden_field :_destroy %>
<%= link_to "delete article", '#', class: "remove_fields button small alert" %>
</div>
Still have no idea how to fix the 2nd issue though.
First of all i noticed that, you have a loop in your reciepts_controller new action
2.times do
#receipt.articles.build
end
This means that, article will be created for only 2 times for that reciept.
Better remove the loop so that you can add as many as article you want.
For Issue number two add bellow line to edit action to your controller
#receipt.articles.build
I guess that would help you.
Also nested_form is a great gem for manage this kind of tasks.
https://github.com/ryanb/nested_form
Check it out.
This is an issue where .hide() is called in receipts.js.coffee. The simplest way to fix this I can think of is to simply replace .hide() with .remove()

How to correctly call models in controller

My Chefs Model is linked to my Meals Model through chef_id. I am displaying information about the chef, and would also like to show information about his meals. I have not been able to figure out the correct way to call it in the controller. Any ideas?
chef controller:
def index
#chefs=Chef.paginate(page: params[:page])
#meals = ????? Cant figure out how to call this
end
Index.html.erb
<div class = 'chef'>
<div class = 'row'>
<% #chefs.each do |chef| %>
<%= render chef %>
<% end %>
</div>
</div>
<%= will_paginate %>
_chef partial
<div class = 'span4'>
<div class = 'chef-box'>
<h2><center><%= link_to chef.name, chef %></center></h2>
<h2><center>"<%= chef.description %>"</center></h2>
<h2><center><%= THIS IS WHERE I WANT TO DISPLAY INFORMATION ON MEALS %></center></h2>
</div>
</div>
If you just want to access the #meals, than you can get it just by for an instance of #chef:
#meals = #chef.meals
given that you have defined the association in the Chef class like following:
class Chef < ActiveRecord::Base
has_many :meals
#rest of the code
end
Have a look at Active Record Associations here.
So, Here I am just giving an example of how to use it in views, you may have to correct it:
in _chef partial
<div class = 'span4'>
<div class = 'chef-box'>
<h2><center><%= link_to chef.name, chef %></center></h2>
<h2><center>"<%= chef.description %>"</center></h2>
<% chef.meals.each do |meal| %>
<h2><center><%= meal.information %></center></h2>
<% end %>
</div>
</div>
You will need to call correct attribute of meal in above sample code.
As previously mentioned – first and foremost make sure the relation is set up in your chef model:
class Chef < ActiveRecord::Base
has_many :meals
end
Then you can call any Chef.meals, whether that's in your controller as:
def index
#chef = Chef.paginate(page: params[:page])
#meals = #chef.meals
end
Or just in your view:
<div class = 'span4'>
<div class = 'chef-box'>
<h2><center><%= link_to chef.name, chef %></center></h2>
<h2><center>"<%= chef.description %>"</center></h2>
<h2>Has <%= pluralize(chef.meals.size, 'meal') %></h2>
<% chef.meals.each do |meal| %>
<% meal.name %>
<% end %>
</div>
</div>
Of note – since Chef is a model name, you should be able to render your _chef.html.erb more cleanly with simply:
<div class = 'chef'>
<div class = 'row'>
<%= render #chef %>
</div>
</div>
<%= will_paginate %>
Note that #chef is singular to match your controller, you could also pass it a collection <%= render #chefs %> if that's what's set up in your controller, and it will do render each chef separately.

how to render this partial in rails

i am trying to render this partial of the association belongs_to and has_many i.e
my model is as thus
activity Model
class Activity < ActiveRecord::Base
has_many :talks
end
talk model
class Talk < ActiveRecord::Base
belongs :activity
end
in my talk controller i have
#activities = Activity.where(user_id: [current_user.friend_ids, current_user])
#talks = Talk.find(:all, :order => "created_at DESC")
and in view i have
<% activity.talks.each do |c| %>
<div class="media">
<a class="pull-left" href="#">
<%= image_tag(c.user.image.url(:tiny),:style=> "width: 100%;")%>
</a>
<div class="media-body">
<strong><%= c.user.username %></strong> <%= c.details %><br>
<small><span class="muted"><%="#{time_ago_in_words(c.created_at)} ago "%></span></small>
</div>
</div>
<% end %>
this displays all the talk for each activity
how do i create a partial of <% activity.talks.each do |c| %>
Create a partial in
app/views/talks/_talk.html.erb
and call
<%= render activity.talks %>
This will render one _talk.html.erb partial for each talk in the activity.talks collection, which will have access to a talk variable.
Furthermore, you can optimize the code by preventing N + 1 queries. In your controller,
#activities = Activity.includes(:talks).where(user_id: [current_user.friend_ids, current_user])
Notice the includes(:talks). Read more about eager loading in the docs linked above.

Resources