No route matches error: Difference between #model.association vs querying? - ruby-on-rails

Technically, I could get this working, but why my current code doesn't work confuses me. I have a many-to-many relationship in my Events <> Users. This is where my view farts out, saying No route matches: missing required key [:id] ...
<% #event_users.each do |event_user| %>
<%= link_to event_user.user.try(:full_name), user_path(event_user.user) %>
<% end %>
This is my code in my controller. One way that works, one way that doesn't work.
#event = Event.find(params[:id])
#event_users = #event.event_users # This does NOT work
# #event_users = EventUser.where(event_id: 14) # This does work
This is my controller relationships
class User < ActiveRecord::Base
has_many :event_users, dependent: :destroy
has_many :events, through: :event_users
end
class EventUser < ActiveRecord::Base
belongs_to :user
belongs_to :event
validates :user, presence: true
validates :event, presence: true
end
class Event < ActiveRecord::Base
has_many :event_users
has_many :users, through: :event_users
end
What exactly is going on here that I have to query and can't use the association to create a link? When I print out the text, I get the relevant data (the ID), so it should work. I've also tried this below and it still doesn't work.
<%= link_to event_user.user.try(:full_name), user_path(event_user.user_id) %>

You don't need to query #event.event_users, thats what the has_many :through association is for, you can simply use the #event object to get its users:
<% #event.users.each do |user| %>
<%= link_to user.full_name, user_path(user) %>
<% end %>
Note, you can also just pass an object to link_to that rails will automatically use a helper to create a route to users#show, like this:
<%= link_to user.full_name, user %>

Related

Database query - Rails

I'm trying to write a database query and i've been scratching my head at what i'm doing wrong.
Here's the relevant model
class User
has_one :store
has_many :products, through: :store
enum gender: %i[menswear womenswear unisex]
def menswear
self.gender == 'menswear'
end
def womenswear
self.gender == 'womenswear'
end
end
class Product
belongs_to :store
end
and controller is
class UsersController
def index
#male = User.menswear
#female = User.womenswear
#products = Product.all.order('created_at DESC')
end
end
View
<% #male.products.in_groups_of(3, false).each do |group| %>
<% for product in group %>
<%= link_to product_path(product) do %>
<%= image_tag product.secimage_url(:index).to_s, class: "image hide" %>
<%= image_tag product.image_url(:index).to_s, class: "image" %>
<% end %>
<% end %>
<% end %>
Did the same for womenswear aswell.
But i'm getting a NoMethodError
undefined method 'products' for #<User::ActiveRecord_Relation:0x007fbd25e42ac0>
EDIT: I might have been unclear initially, i want the view to show the products sorted by menswear and womenswear, Please help! Thanks
Any pointers on what i'm doing wrong would be appreciated.
Thanks in advance!
Migration
I added gender to Users later and not in the original migration
def up
add_column :users, :gender, :integer
add_index :users, :gender
end
def down
remove_column :users, :gender
end
UPDATE: I changed the users controller to this
def index
#partners = User.partner
#male = User.find_by(gender: 0)
#female = User.find_by(gender: 1)
end
It works but only returns the first instance in both the male and female. I need it to return all of it!
You are mixing has_one :through association with has_many :through association here. You can check proper documentation here. You are having has_one :store and trying to make association has_many :products, through: :store, which is not possible.
Either make it has_one through like:
has_one :store
has_one :product, through: :store
Or for your case make it has_many :through like:
has_many :stores
has_many :product, through: :stores
Or you can remove through from has_many :products, through: :store and make it has_many :products
Choosing solution is upto your requirement.
Try replacing:
def index
#partners = User.partner
#male = User.find_by(gender: 0)
#female = User.find_by(gender: 1)
end
with
def index
#partners = User.partner
#male = User.where(gender: 0)
#female = User.where(gender: 1)
end

How to retrieve a value of a "belongs to" element attribute on an index page in rails?

I try to make a web application where I have 4 models :
User, Recipe, Step, Steps_advancement.
Globally, a user can like a recipe composed by steps. When the user opens and reads a step of the recipe, it puts a "is_read=true" on the table steps_advancement which belongs to user and step.
For the moment I have this code :
Models :
class Recipe < ApplicationRecord
belongs_to :user
has_many :steps
has_many :favorites
has_many :favorited_by_users, :through => :favorites, :source => 'user'
class Step < ApplicationRecord
belongs_to :recipe
has_one :steps_advancement
end
class StepsAdvancement < ApplicationRecord
belongs_to :step
belongs_to :user
end
class Favorite < ApplicationRecord
belongs_to :recipe
belongs_to :user
end
class User < ApplicationRecord
has_many :recipes
has_many :favorites
has_many :favorite_recipes, :through => :favorites, :source => 'recipe’
has_many :steps_advancements
devise :database_authenticatable, :registerable,
:recoverable, :rememberable, :trackable, :validatable
end
end
In the database, i have a table Steps_advancement with a boolean "is_read", default: false, null: false (and recipe_id and step_id on index)
In the views, the steps index page lists all the steps of the recipe.
I want to be able to retrieve, in the steps index view, the value of the steps_advancement ‘is_read’. In order to display it if the previous is read, or hide it, else.
But I can't, I have an error message.
StepsController
def index
#recipe = Recipe.find(params[:recipe_id])
#steps = #recipe.steps
in the steps index view (index.html.erb)
<% if current_user %>
<% #steps.each do |st|%>
<% if st.steps_advancement.is_read?%> # <<< error here
<%= link_to([#recipe, steps]) do %>
<strong><%=st.name %></strong> --
<%else%>
<%="not avalaible for the moment!"%>
<% end %>
<% end %>
In the third line I have a problem. I cannot access the steps_advancement. I guess rails does not recognize the step but how can i resolve it ? :)
In advance thanks!
So step_advancement is not created on step until it is read, correct?
Add a try before is_read? like such:
steps/index.html.erb
<% if current_user %>
<% #steps.each do |st|%>
<% if st.steps_advancement.try(:is_read?)%> # <<< error here
<%= link_to([#recipe, steps]) do %>
use this:
seems your code is fine here i'm skipping <% if st.steps_advancement.is_read?%> block if st.steps_advancement is blank.
<% if current_user %>
<% #steps.each do |st|%>
<%next if st.steps_advancement.blank?%>
<% if st.steps_advancement.is_read?%> # <<< error here
<%= link_to([#recipe, steps]) do %>
<strong><%=st.name %></strong> --
<%else%>
<%="not avalaible for the moment!"%>
<% end %>
<%end%>
<% end %>
I don't know if it will work, but you can try to add nested attributes for
class Step < ApplicationRecord
belongs_to :recipe
has_one :steps_advancement
accepts_nested_attributes_for :steps_advancement
def steps_advancement
self.steps_advancement
end
end
EDIT
I don't know if could work but maybe that could be a workaround solution

polymorphic_path not generating correct path

I am trying to user a rails method called polymorphic_path but I am getting the wrong url.
My polymorphic association is with Students and Landlords who are both Users through userable.
Here are my models:
class User < ActiveRecord::Base
belongs_to :userable, polymorphic: true
end
class Student < ActiveRecord::Base
has_one :user, :as => :userable
end
class Landlord < ActiveRecord::Base
has_one :user, :as => :userable
end
I have a variable called current_user holding the User object. The following line:
<%= link_to "Profile", polymorphic_path(current_user) %>
gives me the url "users/22" instead of returning the Student/Landlord url.
Here is my routes.rb file if that helps..
resources :users
resources :students
resources :landlords
Where am I going wrong?
Thanks!
Ok, I got it! And the solution was painfully obvious...
<%= link_to "Profile", polymorphic_path(current_user.userable) %>
<%= link_to "Edit", edit_polymorphic_path(current_user.userable) %>
Hmm, not sure if polymorphic_path should work they way You using it, home brewed alternative
# application_controller.rb
helper_method :current_profile, :path_to_profile
def current_profile
#current_profile ||= current_user.userable
end
def path_to_profile
send("#{current_profile.class.downcase}_path", current_profile)
end
With few additional lines You can extend it to work with other methods too, not only show.

Has_many through and path helper - accessing resources through the application

I have an app in which users can follow law firms
I have 3 models
- User
- Firm
- Follow
class Firm < ActiveRecord::Base
has_many :follows, :dependent => :destroy
has_many :users, :through => :follows
class User < ActiveRecord::Base
has_many :follows, :dependent => :destroy
has_many :firms, :through => :follows
class Follow < ActiveRecord::Base
belongs_to :firm
belongs_to :user
In a table in my firms index view, I would like to take the current signed and create an association between that user and the law firm - through the follow table.
In effect doing this -
firm.users << User(current)
This is the code that I have at present, how would you suggest that I structure the path, and the corresponding controller?
<% #firms.each do |firm| %>
<tr id = "firm_<%= firm.id %>">
<td><%= link_to image_tag(firm.logo_url, :size => "80x120"), firm.url %></td>
<td><%= link_to firm.name, firm_path(firm) %></td>
<% if user_signed_in? %><td>
<%= button_to 'Follow', ? , method: :post %>
</td>
<% end %>
I am using devise for the User authentication and have put the following helpers into application helper to allow my login partial to function in a different models view.
def resource_name
:user
end
def resource_id
:user_id
end
def resource
#resource ||= User.new
end
The simplest way would be to have a follow action on a FirmsController.
In config/routes.rb:
resources :firms do
post :follow, on: :member
end
In your FirmsController:
def follow
#firm.users << current_user
end
In your view:
<%= link_to "Follow", follow_firm_path(#firm), method: :post %>
Another way would be to represent a follow relationship as a singular resource. You'd follow a firm by POSTing to /firms/1234/follow and you'd unfollow a firm by sending a DELETE request to /firms/1234/follow.
If you wanted to take that approach, you'd stick this in your config/routes.rb:
resources :firms do
resource :follow, on: :member
end
And you'd create a FollowsController like this:
class FollowsController < ApplicationController
def create
#firm = Firm.find params[:firm_id]
#firm.users << current_user
# respond with success
end
def destroy
#firm = Firm.find params[:firm_id]
#firm.users.delete current_user
# respond with success
end
end

Rails: validate presence of parent_id in has_many association

I have a projects resource that has many tasks. I want to ensure that every task has a project_id by adding validates_presence_of :project_id to the tasks model.
However, when creating a new project with tasks, the project_id won't be available until the record saves, therefore I can't use validates_presence_of :project_id.
So my question is, how do I validate presence of project_id in the task model? I want to ensure every task has a parent.
...
class Project < ActiveRecord::Base
has_many :tasks, :dependent => :destroy
accepts_nested_attributes_for :tasks, :allow_destroy => true
...
class Task < ActiveRecord::Base
belongs_to :project
validates_presence_of :project_id
Your code works:
If you validates_presence_of :project, then as long as the project is there, it will validate. But if your project is unsaved, you could still save the task.
If you validates_presence_of :project_id, then the integer must be there, indicating a saved value.
Here's rSpec that proves the point. If you validate :project_id, you can't save a task without saving the Project.
class Task < ActiveRecord::Base
belongs_to :project
end
/specs/model_specs/task_spec.rb
require File.dirname(__FILE__) + '/../spec_helper'
describe Task do
before(:each) do
#project = Project.new
end
it "should require a project_id, not just a project object" do
task = Task.new
task.project = #project
Task.instance_eval("validates_presence_of :project_id")
task.valid?.should == false
end
it "should not be valid without a project" do
task = Task.new
task.project = #project
Task.instance_eval("validates_presence_of :project")
task.valid?.should == false
task.save.should == false
end
end
See here for the definitive answer :
class Project < ActiveRecord::Base
has_many :tasks, :dependent => :destroy, :inverse_of => :project
accepts_nested_attributes_for :tasks, :allow_destroy => true
class Task < ActiveRecord::Base
belongs_to :project
validates_presence_of :project
Not so elegant if you ask me... It should transparently validate.
Maybe I don't understand something, but it looks like you are trying to cheat rails. Why don't you just do like this:
class Task < ActiveRecord::Base
belongs_to :project
validate_presence_of :project
end
Take a look at this:
https://rails.lighthouseapp.com/projects/8994/tickets/2815-nested-models-build-should-directly-assign-the-parent
One thing I have done in the past is add: validates_presence_of :parent_id, :on => :update. Not great but it helps tighten the net a little.
I think you're having the same issue I dealt with. I have two models, Account and User, and when the account is created the first user is created through a #account.users.build. The User model has a validates_presence_of :account validation.
To make the first user pass validation, I added the following code to my Account model:
before_validation_on_create :initialize_users
def initialize_users
users.each { |u| u.account = self }
end
In reality you need both:
validates_presence_of project
validates_presence_of project_id
That way the task will not be saved in either of the following cases assuming that you have only 2 valid projects in the database, i.e. project id 99 is invalid:
task.project_id = 99
task.save
task.project = Project.new
task.save
I hope this is of help to someone.
Your Project class must define
accepts_nested_attributes_for :tasks
See Nested Model Form on Railscasts for more details on how to make the form.
EDIT:
In your form you should have something like this:
_form.html.erb
<% form_for #project do |f| %>
# project fields...
<% f.fields_for :tasks do |builder| %>
<%= render 'task_fields', :f => builder %>
<% end %>
<p><%= link_to_add_fields "Add task", f, :tasks %></p>
<%= f.submit %>
<% end %>
_task_fields.html.erb
<%= f.label :name, "Task name:" %>
<%= f.text_field :name %>
# task fields...
<%= link_to_remove_fields "Delete task", f, :tasks %>
link_to_add_fields and link_to_remove_fields are methods defined in application_helper to add/delete fields dynamically.

Resources