If I used this code to set up a tagging system for my app, how would I then render the posts with the tags I assigned to them. For instance if I assigned a sports tag to a couple posts, how would I then render all posts with the sports tag assigned to it
rails g model tag name:string
rails g model tagging article_id:integer tag_id:integer
rake db:migrate
class Tagging < ActiveRecord::Base
belongs_to :article
belongs_to :tag
end
class Tag < ActiveRecord::Base
has_many :taggings, :dependent => :destroy
has_many :articles, :through => :taggings
end
class Article < ActiveRecord::Base
has_many :comments, :dependent => :destroy
has_many :taggings, :dependent => :destroy
has_many :tags, :through => :taggings
validates_presence_of :name, :content
attr_writer :tag_names
after_save :assign_tags
def tag_names
#tag_names || tags.map(&:name).join(' ')
end
private
def assign_tags
if #tag_names
self.tags = #tag_names.split(/\s+/).map do |name|
Tag.find_or_create_by_name(name)
end
end
end
end
<p>
<%= f.label :tag_names %><br />
<%= f.text_field :tag_names %>
</p>
Maybe something like this:
# TagsController
def show
#articles = Tag.find_by_name('sports').articles
end
# tags/show.html.haml
- #articles.each do |article|
= render :partial => :article
Related
I have three objects: Contact, Sector, and Contact_sector.
Contact contains an id and some other irrelevant (non-reference) columns
Sector contains an id and sector column with ~10 editable entries
Contact_sector has a contact_id reference and a sector_id reference. In my mind I imagine that every sector that applies to some contact can be found here, and if a sector is un-applied it is removed from here.
I want to have a collection of checkboxes in the contact _form formed from list of entries in :sectors, but updating the form with certain boxes ticked adds/removes entries from :contact_sectors.
Where am I going wrong?
UPDATED: Fixed strong_params to permit sectors, now I am unable to find the sectors by id ActiveRecord::RecordNotFound (Couldn't find Sector with ID=["1", ""] for Contact with ID=1)
Models:
class Contact < ActiveRecord::Base
has_many :contact_sectors
has_many :sectors, through: :contact_sectors
accepts_nested_attributes_for :contact_sectors, :reject_if => :all_blank, :allow_destroy => true
accepts_nested_attributes_for :sectors, :reject_if => :all_blank, :allow_destroy => true
end
class Sector < ActiveRecord::Base
has_many :contact_sectors
has_many :contacts, through: :contact_sectors
def name_with_initial
"#{sector}"
end
end
class ContactSector < ActiveRecord::Base
belongs_to :contact
belongs_to :sector
end
View:
<%= f.fields_for(:sectors) do |s| %>
<%= s.collection_check_boxes :id, Sector.all,
:id, :name_with_initial,
{ prompt: true }, { class: 'form-control' } %>
<% end %>
Controller
def edit
#contact.sectors.build
end
def contact_params
#Not sure if I need something like this or not
params['contact']['sectors'] = params['contact']['sectors']['id'].split(',')
params.require(:contact).permit(:firstname, :lastname,
contact_sectors_attributes: [:id],
sectors_attributes: [:_destroy, {:id => []}])
end
Instead of creating the join model explicitly you can just declare the relationship as has_many through: and let ActiveRecord handle the join model:
class Contact < ActiveRecord::Base
has_many :contact_sectors
has_many :sectors, through: :contact_sectors
accepts_nested_attributes_for :sector,
reject_if: :all_blank, allow_destroy: true
end
class Sector < ActiveRecord::Base
has_many :contact_sectors
has_many :contacts, through: :contact_sectors
end
class ContactSector < ActiveRecord::Base
belongs_to :contact
belongs_to :sector
end
<%= form_for(#contact) do |f| %>
<%= f.fields_for(:sectors) do |s| %>
<%= s.collection_check_boxes :id, Sector.all,
:id, :name_with_initial,
{ prompt: true }, { class: 'form-control' } %>
<% end %>
<% end %>
models
class Contact < ActiveRecord::Base
has_many :sectors, through: :contact_sectors
has_many :contact_sectors
accepts_nested_attributes_for :sectors
end
class Sector < ActiveRecord::Base
has_many :contacts, :through => :contact_sectors
has_many :contact_sectors
end
class ContactSector < ActiveRecord::Base
belongs_to :contact
belongs_to :sector
end
view
<%= form_for(#contact) do |f| %>
<% Sector.all.each do |sector| %>
<%= check_box_tag "contact[sector_ids][]", sector.id, f.object.sectors.include?(sector) %>
<%= sector.sector %>
<% end %>
<% end %>
controller
def update
#To make sure it updates when no boxes are ticked
#contact.attributes = {'sector_ids' => []}.merge(params[:contact] || {})
end
def contact_params
params.require(:contact).permit(:firstname, :lastname, sector_ids: [])
end
Recommended reading:
http://millarian.com/rails/quick-tip-has_many-through-checkboxes/
Rails 4 Form: has_many through: checkboxes
I am trying to display the number of UserLesson.where(user_id: current_user_id) in the provided view (profiles_controller.rb). At the moment it displays the total UserLesson and not to individual user.
Here is my view:
<% #user.lectures.each do |lecture| %>
<div class="col-md-4 margin-s mb-60">
<div class="lecture-box">
<%= image_tag lecture.picture.url(:medium) %>
<%= lecture.lessons.size %>
<%= lecture.lessons.total_lesson %> #SHOULD BE HERE
<p class="pad5"><%= truncate(lecture.description, length: 80) %> </p>
<%= link_to "Start Now", lecture, class: "btn btn-primary" %>
</div> <!-- .lecture-box -->
</div> <!-- .col-md-4 -->
<% end %>
Here is my lesson.rb:
class Lesson < ActiveRecord::Base
has_one :lecture, through: :chapter
belongs_to :chapter
has_many :user_lessons
def self.total_lesson
Lesson.joins(:user_lessons).size
end
end
Other Models:
class UserLesson < ActiveRecord::Base
belongs_to :user
belongs_to :lesson
# validates_uniqueness_of :user_lesson, :scope => [:user, :lesson]
end
class Lesson < ActiveRecord::Base
has_one :lecture, through: :chapter
belongs_to :chapter
end
class User < ActiveRecord::Base
has_many :enrollments
has_many :user_lessons
has_many :lectures, through: :enrollments
accepts_nested_attributes_for :enrollments
end
class Enrollment < ActiveRecord::Base
belongs_to :user
belongs_to :lecture
validates :lecture, uniqueness: { scope: :user, message: "should happen once per user" }
end
class Lecture < ActiveRecord::Base
belongs_to :category
has_many :lessons, through: :chapters, dependent: :destroy
has_many :chapters
belongs_to :teacher
# For course user relationship
has_many :enrollments
has_many :users, through: :enrollments
accepts_nested_attributes_for :enrollments
accepts_nested_attributes_for :chapters
end
class Chapter < ActiveRecord::Base
has_many :lessons
belongs_to :lecture
accepts_nested_attributes_for :lessons
end
To find the number of lessons belonging to a lecture in which the current user is related you can do this
<%= lecture.lessons.select{ |l| l.users.inculde? current_user }.count %>
but you have to add this to Lesson.rb:
has_many :users, through: :user_lessons
As you have #user in your view (which is the current_user), so you can just call the following in your view to get the total number of lessons for this user:
Lesson.joins(:user_lessons).where('user_lessons.user_id = ?', #user.id).count
To get the total number of lessons in this lecture for this user:
Lesson.joins(:user_lessons).joins(:lectures).where('user_lessons.user_id = ? AND lectures.id = ?', #user.id, lecture.id).count
where lecture is your local variable in your view file.
You should be able to call this directly from your view to get the desired count.
I'm trying to build a form for a model with a join-table containing references to itself.
I'll give you a quick example of what i'm trying to achive:
Lets say i have the subject physics. To learn physics you will need to know basic math, i.e physics is dependent on math. Any subject should be able to have multiple dependencies both ways.
What i'm having problems with is submitting this relationship through a form.
My code:
Model:
class Subject < ActiveRecord::Base
has_many :needs, foreign_key: :target_id
has_many :subjects, through: :needs
accepts_nested_attributes_for :needs,:subjects,allow_destroy: true
end
class Need < ActiveRecord::Base
belongs_to :target, class_name: :subject
belongs_to :prerequisite, class_name: :subject
end
Controller:
class SubjectsController < ApplicationController
def create
#subject = Subject.new secure_params
if #subject.save
redirect_to root_path, success: 'Subject created'
else
render :new
end
end
def new
#subject = Subject.new
end
private
def secure_params
params.require(:subject).permit(:name, :content, needs_attributes: [:target,:prerequisite])
end
end
Form:
<div class="row">
<div class="col-md-8">
<%= simple_nested_form_for #subject do |f| %>
<%= f.input :name %>
<%= f.input :content %>
<%= f.fields_for :needs do |d| %>
<%=d.association :prerequisite %>
<% end %>
<%= f.link_to_add "Add a prerequisite", :needs %>
<%= f.submit class: 'btn btn-primary btn-lg' %>
<% end %>
</div>
</div>
Using this approach i get "uninitialized constant Need::subject" on entering the "new" action.
Any ideas on mistakes in my approach or code will be greatly appreciated.
Edit: adding join table
class CreateNeeds < ActiveRecord::Migration
def change
create_table :needs do |t|
t.references :target
t.references :prerequisite
end
end
end
Edit2: working code (only changed parts)
Model
class Subject < ActiveRecord::Base
has_many :needs, foreign_key: :target_id
has_many :prerequisites, class_name: "Subject",through: :needs, source: :prerequisites
accepts_nested_attributes_for :needs,:prerequisites,allow_destroy: true
end
class Need < ActiveRecord::Base
belongs_to :target, class_name: "Subject"
belongs_to :prerequisite, class_name: "Subject"
end
Controller
def secure_params
params.require(:subject).permit(:name, :content, needs_attributes: [:prerequisite_id])
end
end
Like i said in my comment, focus on making sure all the associations are hooked up properly before you worry about forms. The console, or automated tests, are both a good place to test this.
I think it maybe should be like so:
class Subject < ActiveRecord::Base
has_many :needs, as: :target
has_many :subjects, :class_name: "Subject", through: :needs, source: :prerequisite
accepts_nested_attributes_for :needs,:subjects,allow_destroy: true
end
#assuming you have fields target_id and prerequisite_id in this table
class Need < ActiveRecord::Base
belongs_to :target, class_name: :subject
belongs_to :prerequisite, class_name: :subject
end
I think the association name "subjects" is potentially confusing as it will return the subjects that this subject requires. This isn't immediately obvious to the reader, which could include you in the future.
At some point you may want to list the subjects that require this subject. It may be worth differentiating between these now, so you can clearly tell the difference. You could do this like so:
class Subject < ActiveRecord::Base
has_many :required_needs, as: :target
has_many :required_by_needs, as: :prerequisite
has_many :required_subjects, :class_name: "Subject", through: :required_needs, source: :prerequisite
has_many :required_by_subjects, :class_name: "Subject", through: :required_by_needs, source: :target
accepts_nested_attributes_for :required_needs, :required_by_needs, :required_subjects, :required_by_subjects, allow_destroy: true
end
I'm having issue creating a nested polymorphic form. I am following the solution from this problem:
Rails: has_many through with polymorphic association - will this work?
The description was: A Person can have many Events and each Event can have one polymorphic Eventable record
Here are the relevant models:
class Event < ActiveRecord::Base
belongs_to :person
belongs_to :eventable, :polymorphic => true
end
class Meal < ActiveRecord::Base
has_one :event, :as => eventable
end
class Workout < ActiveRecord::Base
has_one :event, :as => eventable
end
class Person < ActiveRecord::Base
has_many :events
has_many :meals, :through => :events, :source => :eventable,
:source_type => "Meal"
has_many :workouts, :through => :events, :source => :eventable,
:source_type => "Workout"
end
My controller looks like this:
def
#person = Person.new
#person.meals.new
#person.workouts.new
new
My view looks like this:
<%= form_for #person do |person| %>
<%= person.fields_for :meals, #person.meals.build do |meals| %>
<%= meals.text_field :food %>
<% end %>
<% end %>
The error I am currently getting is:
unknown attribute: person_id
I would think that the polymorphic association is hindering the creation of the object because meals can't be create until person has been created? Did I set up the form correctly? Thanks!
You'll need to include a person_id attribute in the events table (as per the belongs_to association)
There are also some other issues you should resolve:
Controller
def new
#person = Person.build
end
def create
#person = Person.new(person_attributes)
#person.save
end
private
def person_attributes
params.require(:person).permit(:your, :attributes, events_attributes: [:eventable_type, :eventable_id, meal_attributes: [:params], workout_attributes: [:params]])
end
Models
#app/models/person.rb
Class Person < ActiveRecord::Base
has_many :events
has_many :meals, :through => :events, :source => :eventable,
:source_type => "Meal"
has_many :workouts, :through => :events, :source => :eventable,
:source_type => "Workout"
def self.build
person = Person.new
person.events.build.build_meal
person.events.build.build_workout
end
end
Views
#app/views/people/new.html.erb
<%= form_for #person do |person| %>
<%= person.fields_for :events do |e| %>
<%= e.fields_for :meal %>
<%= e.text_field :food %>
<% end %>
<% end %>
<% end %>
models/user.rb
class User < ActiveRecord::Base
has_many :clubs, :dependent => :destroy
has_many :announcements, :dependent => :destroy
end
models/announcement.rb
class Announcement < ActiveRecord::Base
belongs_to :user
belongs_to :club
end
models/club.rb
class Club < ActiveRecord::Base
belongs_to :user
has_many :announcements, :dependent => :destroy
end
controllers/announcements/announcements_controller.rb
def index
#announcements = Announcement.find(:all, :include => [:user, :club])
end
Problem:
When i type this code,
views/announcements/index.html.erb
<% #announcements.each do |announcement| %>
<%= announcement.user.username %>
<% end %>
I get this error:
NoMethodError in Announcements#index
undefined method `username' for nil:NilClass
when i change code to this, it works.
<% #announcements.each do |announcement| %>
<%= announcement.club.user.username %>
<% end %>
Why is the first code is not working? What is the difference between these codes. Thanks.
It looks like you've got an announcement which has no user set, but does have a club which in turn does have a user. Perhaps consider adding a validation to require that the :user_id column on announcement is present.