I'm trying to turn my choices column from my Questionnaire table into separate strings as multiple choice.
Questionnaire Controller:
class QuestionnairesController < ApplicationController
def index
#questions = Questionnaire.find(params[:category_id])
#params[:category_id]= <%=category.id%>
#category = Category.find(params[:category_id])
#videos = VideoClue.find(params[:category_id])
###This finds all the questions from the question table by their category_id. Whenever I select a category, it matches the question related to the category
render :show
###render :show Renders Html page
end
def choose_answer
# binding.pry
#questions = Questionnaire.find(params[:id])
#params[:id] = /:id = /1
render :choose_answer
end
end
Questionnaire Model:
class Questionnaire < ActiveRecord::Base
belongs_to :categories
end
Chose_answer.html.erb
<h1>Congrats You Hit The Choices Page!</h1>
<%= semantic_form_for #questions.choices do |c| %>
<%= c.inputs do %>
<%= c.input :choices, :as => :check_boxes , :collection =>
[#questions.choices].map(&:inspect).join(', ') %>
<%end%>
<%end%>
Questionnaire table seed:
Questionnaire.create({question: "In that year did MTV (Music Television)
premiere and what was the first music video the channel aired?", choices:
["1982 Michael Jackson 'Bille Jean'", "1984 Madonna 'Like a virgn'", "1981
The Buggles 'Video Killed The Radio Star'"], correct_answer:"1981 The
Buggles 'Video Killed The Radio Star' ", category_id:1})
#question.choices returns
["1982 Michael Jackson 'Bille Jean'", "1984
Madonna 'Like a virgn'", "1981 The Buggles 'Video Killed The Radio Star'"],
How do I separate the strings from the choices column array and use formtastic to turn the strings into a mutiple choice format?
<%= c.input :choices, :as => :check_boxes , :collection => #questions.choices %> should do what you want here, no need to modify it if it's already an array IIRC, unless I'm misunderstanding the situation.
Related
I am learning Ruby on Rails and have a search form set up and its working. On the pins index view I can search for pins(posts) by their title. However if I wanted to search by Username which is not in the pins table and display the results on the Pins index page how would I do this? How do I access an attribute from a different table? (Sorry for the newbie attempt at explaining my issue)
Pins controller
def index
#pins = Pin.search(params[:term])
end
Pin Model
def self.search(term)
if term
where('description LIKE ?', "%#{term}%")
else
order('id DESC')
end
end
_search.html.erb
<%= form_tag(pins_path, method: :get) do %>
<%= text_field_tag :term, params[:term] %>
<%= submit_tag 'Search', description: nil %>
<% end %>
Assuming you have set up something like
class User < ApplicationRecord
has_many :pins
# the username is stored in the attribute 'username'
end
class Pin < ApplicationRecord
belongs_to :user
end
you may do the following
# PinsController
def index
terms = params[:term]
username = params[:username]
#pins = Pin
#pins = #pins.where("description LIKE '%?%'", term) if term
#pins = #pins.includes(:user).where("users.username LIKE '%?%'", username) if username
# you may want to sort by id anyway
#pins = #pins.order('id DESC')
end
Note that I put the code straight to the controller for brevity. You may refactor this to use your search method in pin model.
# _search.html.erb
<%= form_tag(pins_path, method: :get) do %>
<%= text_field_tag :term, params[:term] %>
<%= text_field_tag :username, params[:username] %>
<%= submit_tag 'Search', description: nil %>
<% end %>
In case you want to do some more searching and filtering you may have a look at the ransack gem although I think you're going the right path in trying to figure this out yourself.
Although those railscasts episodes are from the past I think they are applicable to the current rails versions. Anyway one can get the point from them
http://railscasts.com/episodes/37-simple-search-form
http://railscasts.com/episodes/111-advanced-search-form
http://railscasts.com/episodes/240-search-sort-paginate-with-ajax
Another good resource is gorails.com (not affiliate in any way!!). I can highly recommend them as a resource for learning
Provided you have an association between User and Pin
class User
has_many :pins
end
class Pin
belongs_to :user
end
You can join :user from Pin and set conditions on the association:
Pin.joins(:user).where('users.username ? AND awesome = ?', 'Max', true)
# or the preferred method
Pin.joins(:user).where(user: { username: 'Max', awesome: true })
Note that we use users.username and not user.username when writing a SQL string you're specifying the table name - not the association.
To search for pins based on the username you could do:
def self.by_username(term)
joins(:user).where('users.username LIKE ?', "%#{term}%")
end
I have a bit of a problem with a "has one" association on my app.
What I want to achieve is to be able to attach an optional quote to the topic. The quote can only be used once (in other words, if it's used for topic 1, it can't be used for any other topics).
I have a Topic model and a Quote model.
Topic has one quote.
Quote belongs to topic.
I also want to be able to attach a quote to other models (ex. Profile Model).
I'm really confused on what to do on my "edit topic" view as well as in the controller. I thought it would work like a "one to many" association, which I had no problem configuring. Somehow the "has one" is more complicated (for me!)
What I'd like is to have in the "edit topic" view a radio list of the available quotes which I can freely update. (Same for the "new topic" view).
My current controller:
def edit
#topic = Topic.find(params[:id])
#quote = #topic.quote
#packages = #topic.packages
#books = #topic.books
#tasklists = #topic.tasklists
#links = #topic.links
#terms = #topic.terms
end
def update
#topic = Topic.find(params[:id])
if #topic.update_attributes(topic_params)
flash[:success] = t('helpers.success-update', model: "topic")
redirect_to backend_topics_url
else
render partial: 'edit'
end
end
def topic_params
params.require(:topic).permit(:topic_id, :theme_id, :cover, :topic_status, :topic_access, :slug, *Topic.globalize_attribute_names, :quote_attributes => [:id, :topic_id], :package_ids => [], :book_ids => [], :link_ids => [], :tasklist_ids => [], :term_ids => [])
end
My current Topic model:
has_one :quote
accepts_nested_attributes_for :quote
My current Quote model:
belongs_to :topic
And my "Edit Topic" view:
<h4>Quote</h4>
<% if #quote %>
<h5>Current quote</h5>
<%= #quote.quote %> <%= link_to('[change]', '#') %>
<% end %>
<%= f.input :quote, :collection => Quote.all, :label_method => :quote, :label_value => :id, :checked => #quote.id, as: :radio_buttons %>
I'm sure there is something obvious that I'm missing but I can't figure out what.
Any ideas?
Thanks!
- Vincent
First off, if you want to have quote belong to multiple models, you will need a polymorphic association. Otherwise, you would need to add multiple foreign ids to quote like this: topic_id, profile_id etc and that will get messy fast. You can view a screencast on polymorphism here: http://railscasts.com/episodes/154-polymorphic-association-revised
has_one and belongs_to is basically the exact same as has_many and belongs_to except you are only dealing with 1 record instead of a collection of records.
For your current setup - in your edit action you need to fetch all the quotes that are not associated to any Topics. You can do that like this:
#available_quotes = Quote.where(topic_id: nil)
and then:
<%= f.input :quote, :collection => #available_quotes, :label_method => :quote, :label_value => :id, :checked => #quote.id, as: :radio_buttons %>
instead of
Quote.all in your form which is returning all quotes.
If you move to a polymorphic model, watch the screencast, and you would replace "commentable_id" in the screencast with something like "quotable_id" and then in your edit action to find the unassigned quotes you would do this:
#quotes = Quote.where(quotable_id: nil)
I'm on Rails 4. Let's say I have three models: House, Color, and HouseColoring.
class House < ActiveRecord::Base
has_many :house_colorings
has_many :colors, through: :house_colorings
accepts_nested_attributes_for :house_colorings, allow_destroy: true
end
class Color < ActiveRecord::Base
has_many :house_colorings
has_many :houses, through: :house_colorings
end
class HouseColoring < ActiveRecord::Base
belongs_to :house
belongs_to :color
end
houses_controller.rb:
class HousesController < ApplicationController
before_action :set_house
...
def new
#house = House.new
#house.house_colorings.build
end
def create
#house = House.create(house_params)
if #house.save
redirect_to #house
else
render 'new'
end
end
def edit
#Gets #house from set_house
end
def update
if #house.update(house_params)
redirect_to #house
else
render 'edit'
end
end
...
private
def set_house
#house = House.find(params[:id])
end
def house_params
params.require(:house).permit(:some_parameters, house_colorings_attributes: [:id, :color_id])
end
end
Here is my _form.html.erb partial for my house new and edit
<%= form_for #house do |f| %>
<div id="house_colorings">
<%= f.fields_for :house_colorings do |c| %>
<%= render "house_colorings", f: c %>
<% end %>
<%= link_to "Add color", add_color_path, remote: true %>
</div>
<% end %>
_house_colorings.html.erb:
<%= f.collection_select :color_id, Color.all, :id, :name, {include_blank: "Select color"} %>
In the houses_controller, I've added:
def add_color
respond_to do |format|
format.js
end
end
add_color.js.erb:
$("#house_colorings").append("<%= escape_javascript render 'house_colorings', f: c %>");
I added a route for my add_color method:
GET "/add_color" => "houses#add_color"
When I click my add color link, nothing happens on screen, but in my log I get a 500 internal server error.
Started GET "/add_color" for 127.0.0.1 at 2013-10-26 21:11:41 -0700
Processing by HousesController#add_color as JS
Rendered houses/add_color.js.erb (11.3ms)
Completed 500 Internal Server Error in 14ms
ActionView::Template::Error (undefined local variable or method `f' for #<#<Class:0x007fc317428538>:0x007fc31710d060>):
1: $("#house_colorings").append("<%= escape_javascript render 'house_colorings', f: c %>");
app/views/houses/add_color.js.erb:1:in `_app_views_houses_add_color_js_erb__1847085463095078116_70237941180700'
As of now, I only have one field to add a house_coloring to my house. I want to add some ajax, and have a link in my form that adds a new field after the one that is there, but I'm not sure how to do this.
I've gone through the "Nested model form" from Railscasts and used parts of them to get to the point I am now, but I would like to use the "data_remote" helpers provided by rails if I can. I've edited my question and included the log for the error I'm getting when I click my add color link. I'm pretty sure that I need to change either my add_color.js.erb or the add_color action in my houses controller.
Any suggestions?
Well, you do have couple alternatives here.
Use the information in the Nested model forms railscasts: Part 1 and Part 2
Use the FormObject Pattern to make thet nesting a bit easier. The pattern is described in a dozen places and also on railscasts (subscribtion needed).
Use a js framework like Angular.js to add new fields on the client side on the fly. Angular.js is also covered in a railscast (subscribtion needed) and has very rich documentation.
UPDATE
The error tells you pretty much all of it. You send a c object to the partial as a form-builder object. And seems like you don't instantiate it in the houses#add_color action.
Check out these two railscasts episode:
Nested Forms 1
Nested Forms 2
The 2nd one explains in depth what you're exactly looking for.
rails is so much fun until you need to find a solution and it takes three days with no luck! ;-(
How do I order by the latest entry in has_many association for each entry?
Explanation:
I do a ranking of a Model Video by an associated Model Score (with a field ranking_score). The database is a postgreSQL.
Because I will frequently add a score for a video I need to order the videos by the latest score entry for each video.
Video.all(:include => :scores, :order => "scores.ranking_score DESC")
...works as expected until I add a new score entry for a video (I don't update, because I will have statistics on the ranking).
i also tried with all varieties (DESC & ASC) and I reordered the priorities but without luck:
Video.all(:include => :scores, :order => "scores.created_at DESC, scores.ranking_score")
and
Video.all(:include => :scores, :order => "scores.ranking_score, scores.created_at")
and so on.
I searched the web, tried to figure out postgreSQL commands and so on. But beeing a total noob (on rails for 6 weeks now) I need help, please.
Here is my code:
Video Model:
class Video < ActiveRecord::Base
.
has_many :scores
.
end
Score Model:
class Score < ActiveRecord::Base
attr_accessible :ranking_score, :video_id
belongs_to :video, :foreign_key => :video_id
def position
self.class.count( :conditions => ['ranking_score >= ?', self.ranking_score])
end
end
Video Controller:
class VideosController < ApplicationController
def index
setup_videos
#video ||= Video.new
respond_to do |format|
format.html # show.html.erb
format.json { render json: #video }
end
end
.
private
def setup_videos
#videos = Video.all(:include => :scores, :order => "scores.ranking_score DESC")
#user = current_user
end
end
As you can see to verify the ranking I included a method position, because I need a user specific view where only user videos get listed, but with the overall ranking shown (different problem, because like this it does not work with multiple entries for each video)
_ranking.html.erb (rendered whithin "index") --- without HTML Tags:
<% #videos.each.with_index do |video, index| %>
<%= index.+1 %>
ranking <%= video.scores.last.ranking_score %>
postition <%= video.scores.last.position %>
<% end %>
<% #user.videos.count %>
Thank you for your time!
joh
I'm having troubles understanding your requirements. However - have you thought of caching the highest rank with each of the video records?
You could add another column in video called highest_ranking that gets updated in an Score after_save operation or similar (depending on the score update frequency).
This will save you the trouble of joining to scores to order the video.
Background:
I followed the tutorial here to setup a polymorphic User favorites data model in my application. This allows me to let a User make pretty much any Entity in the system which I add 'has_many :favorites, :as => :favorable' line to its model a favorite. I plan on using this to implement a Facebook style 'Like' system as well as several other similar systems.
To start off I added the favoritability to a Post model (each user can create status updates like on Facebook). I have it all done and unit tested so I know the data model is sound and functioning from either side of the relationship (User and Post).
Details:
I have a Home controller with a single index method and view.
on the index view I render out the posts for the user and the user's friends
I want the user to be able to like posts from their friends
The Posts controller has only a create and a destroy method with associated routes (not a full fledged resource) and through the Post method via AJAX posts are created and deleted without issue
Where I am stuck
How do I add the link or button to add the post to the user's Favorites?
According to the tutorial the way to create a new Favorite through the polymorphic association is to do it from the Post.favorites.build(:user_id => current_user.id). From this direction the build handles pulling out the Post's ID and TYPE and all I have to do is pass in the user's id
Do I use an AJAX form post to a Favorites controller with a Create and Destroy method similar to the Post controller?
I am still struggling to uncross the wires in my brain from ASP.Net N-Tier web application development over to Rails MVC. Hasn't been too bad until now ;)
I bet there are Gems out there that might do this but I need to learn and the best way is to suffer through it. Maybe a tutorial or sample code from someone who has implemented liking functionality within their application would be helpful.
Thanks in advance for the assistance!
Jaap, I appreciate your comment on my question. After writing the question I pretty much didn't want to wait because the real learning takes place through trial and error, so I errored it up ;)
It turns out that what you suggested was pretty much in line with exactly what I ended up doing myself (it's always nice to find out that what you decide to do is what others would do as well, I love the sanity check value of it all).
So here is what I did and it is all working through post-backs. Now I just need to implement AJAX and style it:
My favorite model because my Polymorphic Favorites model requires that an Entity can only be favorited once by a user I added to the validations 'Scopes' which indicate that for each attribute it has to be unique in the scope of the other 2 required attributes. This solves the issue of multiple favorites by the same user.
class Favorite < ActiveRecord::Base
before_save :associate_user
belongs_to :favorable
belongs_to :user
# Validations
validates :user_id, :presence => true,
:uniqueness => {:scope => [:favorable_id, :favorable_type], :message => "item is already in favorites list."}
validates :favorable_id, :presence => true,
:uniqueness => {:scope => [:user_id, :favorable_type], :message => "item is already in favorites list."}
validates :favorable_type, :presence => true,
:uniqueness => {:scope => [:favorable_id, :user_id], :message => "item is already in favorites list."}
# Callbacks
protected
def associate_user
unless self.user_id
return self.user_id = session[:user_id] if session[:user_id]
return false
end
end
end
My User Model (that which is relevant): I added 2 methods, the get_favorites which is the same as favorable one from the tutorial and a Favorite? method which checks to see if the Entity in question has already been added to the user's favorites.
class User < ActiveRecord::Base
# Relationships
has_many :microposts, :dependent => :destroy
has_many :favorites
# Methods
def favorite?(id, type)
if get_favorites({:id => id, :type => type}).length > 0
return true
end
return false
end
def get_favorites(opts={})
# Polymorphic Favoritability: allows any model in the
# application to be favorited by the user.
# favorable_type
type = opts[:type] ? opts[:type] : :topic
type = type.to_s.capitalize
# add favorable_id to condition if id is provided
con = ["user_id = ? AND favorable_type = ?", self.id, type]
# append favorable id to the query if an :id is passed as an option into the
# function, and then append that id as a string to the "con" Array
if opts[:id]
con[0] += " AND favorable_id = ?"
con << opts[:id].to_s
end
# Return all Favorite objects matching the above conditions
favs = Favorite.all(:conditions => con)
case opts[:delve]
when nil, false, :false
return favs
when true, :true
# get a list of all favorited object ids
fav_ids = favs.collect{|f| f.favorable_id.to_s}
if fav_ids.size > 0
# turn the Capitalized favorable_type into an actual class Constant
type_class = type.constantize
# build a query that only selects
query = []
fav_ids.size.times do
query << "id = ?"
end
type_conditions = [query.join(" AND ")] + fav_ids
return type_class.all(:conditions => type_conditions)
else
return []
end
end
end
end
My Micropost Model (that which is relevant): note the Polymorphic association in the has_many relationship titled :favorites.
class Micropost < ActiveRecord::Base
attr_accessible :content
# Scopes
default_scope :order => 'microposts.created_at DESC'
# Relationships
belongs_to :user
has_many :favorites, :as => :favorable # Polymorphic Association
# Validations
validates :content, :presence => true, :length => { :minimum => 1, :maximum => 140 }
validates :user_id, :presence => true
end
My Micropost Form: as you can see I am passing in the entity that will be mapped to the Favorite model as a local variable to the 2 Favorite forms as 'local_entity'. This way I can pull out the ID and the TYPE of the Entity for the Polymorphic association.
<div class="post">
<span class="value">
<%= micropost.content %>
</span>
<span>
<% if current_user.favorite?(micropost.id, micropost.class.to_s) %>
<%= render :partial => 'favorites/remove_favorite', :locals => {:local_entity => micropost} %>
<% else %>
<%= render :partial => 'favorites/make_favorite', :locals => {:local_entity => micropost} %>
<% end %>
</span>
<span class="timestamp">
Posted <%= time_ago_in_words(micropost.created_at) %> ago.
</span>
<div class="clear"></div>
</div>
My Make Favorite Form:
<%= form_for current_user.favorites.build do |f| %>
<div><%= f.hidden_field :favorable_id, :value => local_entity.id %></div>
<div><%= f.hidden_field :favorable_type, :value => local_entity.class.to_s %></div>
<div class="actions"><%= f.submit "make favorite" %></div>
<% end %>
My Remove Favorite Form:
<%= form_for current_user.get_favorites(
{:id => local_entity.id,
:type => local_entity.class.to_s}),
:html => { :method => :delete } do |f| %>
<div class="actions"><%= f.submit "remove favorite" %></div>
<% end %>
If you don't want to call this on the current_user, you would have to have these routes in your config/routes.rb to make nested routes for favorites on a user. I assume you have a Favorite model which belongs_to :user:
resources :users do
resources :favorites
end
Then make sure your favorites controller loads the user in some kind of before_filter:
def load_user
#user = User.load params[:user_id]
end
And then you can render a remote form to create a new favorite for any kind of object (it will only show a button):
<%= remote_form_for [#user, Favorite.new] do |f| -%>
<%= f.hidden_field :favorable_type, object.class.to_s %>
<%= f.hidden_field :favorable_id, object.id %>
<%= f.submit 'Like' %>
<%- end -%>
You would have to render that form as a partial sending along an object (e.g. a Post) and then it will create an AJAX POST call to /users/:id/favorites/ which will create the favorite object and render some kind of javascript response in a create.rjs file.
I hope this helps. The code itself is untested, but it might get you moving.