Rails: Automatically add username to new post - ruby-on-rails

In my posts controller, I am using the following to see the user ID's of different posts made by students:
def new
#post = current_user.posts.build
end
This is very useful. However, it would be more useful if I can see their names and usernames too. Right now I am making students manually type in their name.
How can you make new posts automatically grab the logged in user's username and name?

To see the user's username and name associated with the #post, you can do:
username = #post.user.username
name = #post.user.name
You should always go and ask the user of #post for the attributes that belong to the user.

You could also use the delegate method/Delegation pattern described more here. API description is here.
class Post < ActiveRecord::Base
belongs_to :user
delegate :username, :name, :to => :user
end
Then you can call: #post.username and that will return the username of the user.

I guess that you are using Devise. So in the _form.html.erbyou can show the username like this...First, let's say that each User has_one :Profile (:username, :first_name, :last_name)
so that
user.rb
has_one :profile
profile.rb
belongs_to :user
In your Post form you could do
_form.html.erb
<div class="field">
<%= f.label :username %> : <%= current_user.profile.username %>
or (if you define a method in your Model that will return your first_name and last_name)
<%= f.label :full_name %> : <%= current_user.profile.first_and_last_name %>
or
<%= f.label :full_name %> : <%= current_user.profile.first_name %> <%= current_user.profile.last_name %>
</div>

A post created with the code you've posted will automatically have the user's name available through the .user associative method:
#post = Post.find x
#post.user.name #-> "name" of associated "user" model
Since you've only posted a new method, and have asked the proceeding question, I'll write some code for you:
Right now I am making students manually type in their name
Why do students have to type their name anyway?
The whole point of ActiveRecord (and relational databases) is to give access to associative data; storing user details (name etc) in the users table, and having it accessible through posts.
This is what you'd do with your posts controller:
#app/controllers/posts_controller.rb
class PostsController < ApplicationController
def new
#post = current_user.posts.new
end
def create
#post = current_user.posts.new post_params
#post.save
end
def show
#post = Post.find params[:id]
#username = #post.user.name
end
end
This will automatically set the user_id foreign key for your new post, which should allow you to call the user's name using #post.user.name
--
If you wanted to refactor this, to avoid the law of demeter, you'll want to use the delegate method as Dewyze recommended:
#app/models/post.rb
class Post < ActiveRecord::Base
belongs_to :user
delegate :name, to: :user, prefix: true #-> post.user_name
end
Dewyze's answer is slightly wrong in that his would yield #post.name - you need the prefix if you wanted to identify the record by its model.

Related

Form to create relationship between objects

In my app I have User and Language models.
User can have multiple languages and a language can have multiple users.
class Language < ApplicationRecord
has_and_belongs_to_many :users
end
class User < ApplicationRecord
has_and_belongs_to_many :languages
end
I want to create a form that will allow user to add a new language to the profile.
Since both User and Language models already exist, I'm wondering how to create a form that will not create any new model, but just create a relation between existing models.
Both the has_many and HABTM macros create _ids and _ids= setters and getters that make it trivial to associate different records:
<%= form_with(model: #user) do |form| %>
<div class="field">
<%= form.label :language_ids, "Languages" %>
<%= form.collection_select(:language_ids, Language.all, :id, :name, multiple: true) %>
# or if you prefer checkboxes
<%= form.collection_checkboxes(:language_ids, Language.all, :id, :name) %>
</div>
# ...
<% end %>
The form collection helpers are smart enough to iterate accross the collection and will select/check depending on if an assocation already exists.
You whitelist an array parameter by passing a hash key to permit with an empty array as its value:
class UsersController < ApplicationController
def create
#user = User.new(user_params)
# ...
end
private
def user_params
params.require(:user)
.permit(
:foo, :bar, :baz,
langauge_ids: []
)
end
end
I would also seriously consider if you want to use has_and_belongs_to_many in the first place. Since there is no model you can't access any additional columns on the join table like for example how proficient a user is or if its their primary language. There is also no straight forward way to query the join table directly. has_many through: is a actually better solution in most cases.

Rails 4 Tagging users on a model

Whats the best method on tagging users? If you have a team model, and when you create a team, you want to add the members, how would this architecture work?
I was thinking of just using acts as tanggble and use it on the users, but not sure if this would be the best method? Is there another gem out there that would do something like this?
It sounds like you're looking for a has many through relationship. This would require you to have a joining table called team_members to record which users are members of each team, having user_id and team_id columns. So for example your Team model would have a relationship that looks like this:
has_many :users, through: :team_members
This then defines the appropriate method on Team for adding, querying and removing users.
More information is here
To add to #tpbowden's answer, if you just want to "tag" users, you may wish to use has_and_belongs_to_many:
# app/models/user.rb
class User < ActiveRecord::Base
has_and_belongs_to_many :teams
end
# join table "teams_users" - team_id | user_id
# app/models/team.rb
class Team < ActiveRecord::Base
has_and_belongs_to_many :users
end
This will allow you to use the singular_collection_ids method, with which you'll be able to define which user is in a "team":
#app/controllers/teams_controller.rb
class TeamsController < ApplicationController
def edit
#team = Team.find params[:id]
end
def update
#team = Team.find params[:id]
#team.update team_params
end
private
def team_params
params.require(:team).permit(user_ids: [])
end
end
#app/views/teams/edit.html.erb
<%= form_for #team do |f| %>
<%= f.collection_select :user_ids, User.all, :id, :name %>
<%= f.submit %>
<% end %>
This is as close to "tagging" as you're going to get without any extra dependencies.

How do I create multiple new records in a single controller action?

I have a student profile page. There's a form on that page which allows you to create new Note record with for that student. Works well.
I want to add a new field above the "Note" text field labeled "Who?" which allows you to enter additional students, thereby logging notes in bulk:
I'm not quite sure how to structure my form and controller action to achieve this bulk creation of new Note records associated with each student listed.
I'm thinking of taking this approach:
POST to the same action (/notes#create) and detect the presence of the array field student_ids in the params and then do something like this:
params[:student_ids].each do |s|
note_params[:event_attributes][:student_id] = s
note = Note.new(note_params)
note.save
end
But I'd have to modify note_params so that it contains the proper student_id reference on each iteration.
Currently, on the singular form, note_params looks like this:
=> {
"content" => "test",
"event_attributes" => {
"student_id" => "2",
"user_id" => "1",
"is_milestone" => "0",
"happened_at_string" => ""
}
}
Is there a better/cleaner/easier way to do this without having to loop through each id and manually modify the params?
You don't need to modify params that way.
params.fetch(:student_ids, []).each do |student_id|
student = Student.find(student_id)
note = student.notes.new(note_params)
note.save
end
I think you'd be better creating some sort of join table for this functionality.
This way, you can create one note, and then "copy" it by simply duplicating the join records. The downside is that one note may be accessible to multiple people, but that's okay:
#app/models/student.rb
class Student < ActiveRecord::Base
has_many :created_notes, class_name: "Note", foreign_key: :user_id
has_and_belongs_to_many :notes
end
#app/models/note.rb
class Note < ActiveRecord::Base
belongs_to :user #-> to know who made it
has_and_belongs_to :users
end
Then you can use your notes#create method as follows:
#app/controllers/notes_controller.rb
class NotesController < ApplicationController
def new
#note = current_user.created_notes.new
#users = User.where.not(id: current_user.id)
end
def create
#note = current_user.created_notes.new note_params
#note.save
end
private
def note_params
params.require(:note).permit(:body, :user_ids)
end
end
Thus, you'll be able to use the collection_select (or similar) functionality to define user_ids for your note:
#app/views/notes/new.html.erb
<%= form_for #note do |f| %>
<%= f.collection_select :user_ids, #users, :id, :name %>
<%= f.submit %>
<% end %>

How to dispaly a login student written message in ruby on rails

I want to display login student name and message.
after login student can write messages and send related to courses.the messages he send is displayed above in same page with his/her name and message
I got name, but message field fetches all messages that are in database. How to display a particular student name and message?
Here is my code
controller.erb
class CourseQueriesController <ApplicationController
def index
#course_queries = CourseQuery.all
#course_query = CourseQuery.new
end
def create
#course_query = CourseQuery.new(student_id: current_student.id, coach_id: "2", message: params[:course_query][:message])
if #course_query.save
redirect_to course_queries_path, notice: 'Query was successfully send.'
else
render :new
end
end
end
course_queries/index.html.erb
<% #course_queries.each do |queries| %>
<p><b><%= current_student.name %></b></p>
<%= queries.message %>
<% end %>
<%= simple_form_for (#course_query) do |f| %>
<%= f.input :message %>
<%= f.button :submit , "Send or press enter"%>
<% end %>
how to display a particular student name and message
You need to have the relevant associations established in your models, like what Pavan wrote.
I'll give you some more information on why this is important...
ActiveRecord
One of the main reasons Rails works so well is the way it helps you create & manage objects. In OOP, objects form everything from your init commands to your user input responses, Ruby being a prime exponent of this structure.
Rails is built on Ruby, and therefore is object orientated too. It uses ActiveRecord, the MVC structure & classes to give you a platform from which you can populate and manipulate objects:
Thus, you shouldn't be treating your application's interactions as a way to edit a database, or "display a login message" - it should be a way to invoke & manipulate objects.
Objects - in the case of Rails - are built in the models. The model data can then be used in the controllers and views.
This seems to be lacking in your code. If you can remedy it, your code will become a lot simpler and more powerful...
Associations
I'd do something like this:
#app/models/student.rb
class Student < ActiveRecord::Base
has_many :queries
has_many :coarse_queries, through: :queries
end
#app/models/course.rb
class Course < ActiveRecord::Base
has_and_belongs_to_many :coaches
has_many :queries
has_many :student_queries, through: :queries
end
#app/models/coach.rb
class Coach < ActiveRecord::Base
has_and_belongs_to_many :courses
has_many :queries
end
#app/models/query.rb
class Message < ActiveRecord::Base
belongs_to :course
belongs_to :student
belongs_to :coach (maybe)
end
This structure will allow a student to send queries to specific courses, selecting the coach as necessary. Importantly, this sets up your associations so that you don't have to invoke multiple classes each time you want to populate the various objects.
#app/controllers/course_queries_controller.rb
class CourseQueriesController <ApplicationController
def index
#queries = Query.all
#query = current_student.queries.new
end
def create
#query = current_student.queries.new query_params
if #query.save
redirect_to course_queries_path, notice: 'Query was successfully send.'
else
render :new
end
end
private
def query_params
params.require(:query).permit(:message).merge(coach_id: "2")
end
end
#app/views/queries/index.html.erb
<% #queries.each do |query| %>
<p><b><%= query.student.name %></b></p>
<%= query.message %>
<% end %>
<%= simple_form_for #query do |f| %>
<%= f.input :message %>
<%= f.button :submit , "Send or press enter"%>
<% end %>
You should add has_many :course_queries to the Student model
#student.rb
class Student < ActiveRecord::Base
has_many :course_queries
...
end
And in the controller in index method change #course_queries = CourseQuery.all to #course_queries = current_student.course_queries
Now <%= queries.message %> will only display the course_query's message of the current_student

Not able to access related objects fields

So I'm new to rails and I'm not entirely sure what I'm doing wrong. Everything I've read says that I'm doing this right.
I have a relationships between two models.
class Photo < ActiveRecord::Base
has_many :votes
belongs_to :user
end
And
class User < ActiveRecord::Base
attr_accessible :username, :email, :password, :password_confirmation, :remember_me
has_many :votes
has_many :photos
end
Here are my Controller methods
def index
#photos = Photo.order("created_at desc").to_a
end
def create
#photo = Photo.new(params[:photo])
#photo.user_id = current_user.id
if !#photo.save
#error = #photo.errors.full_messages.join('. ')
render view_for_new
return
end
end
I know the relationship works because in my view when I do this: <%= photo.user %> I get a user object back, and when I do <%= photo.user.inspect %> it shows all the expected fields with the correct keys and values.
However I want to access fields such as username, email, etc and display those on the page. How do I do this? I've tried doing <%= photo.user.email %> and some other fields that are available but it doesn't seem to be working
Alright figured this out, or at least partially.
Instead of <%= photo.user.email %> I did <%= photo.user.try(:email) %> and that brought the correct attribute back that I was looking for. It seems the association is done correctly. I don't know why <%= photo.user.email %> doesn't work, everywhere I look on line seems to use that sort of syntax.

Resources