I have two models: project and todo. Project has many todos.
So I wanna create a form, where I select project category from the combobox and then I add a todo to it.
For instance:
I have following categories: family, work, study.
In form in the combobox I select 'study', and then in textfield I spell a todo like 'make homework for monday' and press submit button.
project.rb
class Project < ActiveRecord::Base
has_many :todos
end
todo.rb
class Todo < ActiveRecord::Base
belongs_to :project
end
my data schema:
create_table "projects", force: :cascade do |t|
t.string "title"
t.datetime "created_at", null: false
t.datetime "updated_at", null: false
end
create_table "todos", force: :cascade do |t|
t.string "text"
t.boolean "isCompleted"
t.integer "project_id"
t.datetime "created_at", null: false
t.datetime "updated_at", null: false
end
_form.html.erb
<%= form_for #project do |f| %>
<div class="form_control">
<%= f.select :title, options_for_select([["Work", "w"],
["Family", "f"],
["Study", "f"],
["TheRest", "t"]]) %>
</div>
<div class="form_control">
*** HERE I NEED TO FIGURE OUT HOW TO ADD SOME DATA TO todo.text ***
</div>
<div class="form_control">
<%= f.submit 'Add' %>
</div>
<% end %>
this is how I show all the projects with their todos:
<% #projects.each do |project| %>
<h2> <%= project.title %> </h2>
<% project.todos.all.each do |todo| %>
<p><%= todo.text %> <%= check_box('tag', todo.__id__, {checked: todo.isCompleted}) %></p>
<% end %>
<% end %>
GitHub link : https://github.com/NanoBreaker/taskmanager
In your todo form, you could have a select box to choose the project the todo belongs to:
# todos/_todo_form.html.erb
<%= select_tag "project_id", options_for_select(Project.pluck(:title, :id)) %>
And in your todos_controller create action:
def create
#project = Project.find(params[:project_id])
#todo = #project.todos.new(todo_params)
if #todo.save
# success
else
# error
end
end
finally, permit the project_id in todo_params:
def todo_params
params.require(:todo).permit(:text, :project_id) # add any other attributes you want
end
Related
I have a User who has a Profile (2 models). Here is the relevant part of my schema:
create_table "profiles", force: :cascade do |t|
t.text "about"
t.datetime "created_at", null: false
t.datetime "updated_at", null: false
t.integer "user_id"
end
create_table "users", force: :cascade do |t|
t.string "email", default: "", null: false
t.string "encrypted_password", default: "", null: false
t.string "reset_password_token"
t.datetime "reset_password_sent_at"
t.datetime "remember_created_at"
t.datetime "created_at", null: false
t.datetime "updated_at", null: false
t.string "provider"
t.string "uid"
t.string "first_name"
t.string "last_name"
t.string "street"
t.integer "house_number"
t.string "city"
t.integer "zip_code"
t.string "image"
t.index ["email"], name: "index_users_on_email", unique: true
t.index ["reset_password_token"], name: "index_users_on_reset_password_token", unique: true
end
The reason I have a Profile as a separate model, as because I thought it was easier to assign roles later, for certain manipulations. So, now I am wondering, if it is possible to ask for
user.first_name , user.last_name, user.email and user.password
in the registration form and for
user.street, user.house_number, user.city and user.zip_code
in the Profile#new _form. Like this:
<%= form_for([#user, #profile], url: user_profiles_path, method: :post) do |form| %>
<div class="field">
<%= form.label :about %>
<%= form.text_area :about %>
</div>
<div class="field">
<%= form.file_field :avatar %>
<% form.label "Profile photo" %>
</div>
<div class="field">
<%= form.label :street %><br />
<%= form.text_field :street, class: 'form-control' %>
</div>
<div class="actions">
<%= form.submit %>
</div>
<% end %>
So here you can see, that avatar and about refer to a Profile, while street if from User table. But somehow this form, doesn't undertand this. I allow nested_attributes for :profile, but I guess, this doesn't matter for this form. I know, that maybe the easier way would be, to rearrange my table, so that all the adress attributes are stored in Profile. But as I am new to Rails and I really wish to learn more, I would love to know, if there is a way of saving to both #user and #profile in one form? Thank you!
You're touching on two somewhat different concepts here that most beginners get stumped on.
The first is nested resources. A nested resource has its path nested under another resource.
# config/routes.rb
resources :magazines do
resources :ads
end
So now instead of /ads we have /magazines/:magazine_id/ads. So the routes themselves describe the relation between the two resources in a RESTful way - awesome.
class AdsController < ApplicationController
before_action :set_magazine
# GET /magazines/:magazine_id/ads/new
def new
#ad = #magazine.ads.new
end
# POST /magazines/:magazine_id/ads/new
def create
#ad = #magazine.ads.new(ad_params)
if #ad.save
redirect_to #ad
else
render :new
end
end
def set_magazine
#magazine = Magazine.find(params[:magazine_id])
end
# ...
end
<%= form_for([#ad, #magazine]) do |f| >
# ...
<% end %>
This will let you create ads that belong to a magazine. It will not magically let you create a magazine at the same time as an add in the same form.
That's where nested attributes comes in. It creates a super-powered setter in the model which lets it accept attributes for an associated model and creates / updates the associated records in the same request as the parent.
This for example would let us create a user and a profile in the same form:
class User < ApplicationRecord
has_one :profile
accepts_nested_attributes_for :profile
end
class Profile < ApplicationRecord
belongs_to :user
end
<%= form_for(#user) do |f|>
<div class="field">
<%= f.label :email %>
<%= f.email_field :street, class: 'form-control' %>
</div>
# ...
<%= f.fields_for(:profile) do |profile_fields| %>
<div class="field">
<%= profile_fields.label :about %>
<%= profile_fields.text_area :about %>
</div>
<% end %>
# ...
<% end %>
class UsersController < ApplicationRecord
POST /users
def create
#user = User.new(user_params)
if #user.save
redirect_to :user
else
render :new
end
end
# ...
private
def user_params
params.require(:user)
.permit(:email, ..., profile_attributes: [:about])
end
end
accepts_nested_attributes_for is one of the most misused, misunderstood and hardest concepts to grasp in rails though. If you're just starting out you should consider bypassing this and circling back around once you have a better understanding of rails.
I was building a rails app. I want to all post on a page. I am able to show posts title and body but i am not able users name(to whom post belongs -owner of post).
Post-Model
class Post < ApplicationRecord
belongs_to :user
end
User-Model
class User < ApplicationRecord
has_many :posts
end
Posts-Controller
class PostsController < ApplicationController
def index
#posts = Post.all
end
end
Posts-View index.html.erb
<h1> All article </h1>
<% #posts.each do |post| %>
<ul>
<div class="row">
<div class="col-md-offset-3 col-md-5">
<h5><b><%= post.title %></b> by <%= %></h5>
<p><%= post.body %></p>
</div>
</div>
</ul>
<% end %>
Schema looks like
create_table "posts", force: :cascade do |t|
t.string "title"
t.text "body"
t.datetime "created_at", precision: 6, null: false
t.datetime "updated_at", precision: 6, null: false
t.integer "user_id"
end
create_table "users", force: :cascade do |t|
t.string "name"
t.string "email"
t.string "password_digest"
t.datetime "created_at", precision: 6, null: false
t.datetime "updated_at", precision: 6, null: false
end
Display name of user who posted that post
Since you have a relation to the User in the Post as belongs_to :user, So, you can simply use post.user.name to get the name of the user like.
<h1> All article </h1>
<% #posts.each do |post| %>
<ul>
<div class="row">
<div class="col-md-offset-3 col-md-5">
<h5><b><%= post.title %></b> by <%= post.user.name %></h5>
<p><%= post.body %></p>
</div>
</div>
</ul>
<% end %>
bonus
If you want to eager load the user in a single query you might wanna use. Post.includes(:user).all, its better if you always use the user, saves you extra query. Mind that it has some downsides to it as well.
Try post.user to get user object. Then you can print any attribute from the user object.
<p><%= post.user.name %></p>
And use includes method to avoid N + 1 queries problem.
def index
#posts = Post.includes(:user)
end
Reference: https://guides.rubyonrails.org/active_record_querying.html
It's quite easy to access. Try post.user.name and you will get name of associated user.
Read here about all standard methods available for belongs_to association
https://guides.rubyonrails.org/association_basics.html#methods-added-by-belongs-to
My aim is to organize uploaded posts by categories. These categories are shown on the nav bar and if you click one of the category's names, you will see the posts assigned with the category. When uploading a post, you can assign it with multiple categories too. So, I think this is like many posts can have many categories and many categories can have many posts.
This is how I want my posts organized by categories
However, I cannot set things right in my posts_controller.rb, posts/index.html.erb, posts/show.html.erb, and _navigation.html.erb
post.rb
class Post < ActiveRecord::Base
#This validates presence of title, and makes sure that the length is not more than 140 words
validates :title, presence: true, length: {maximum: 140}
#This validates presence of body
validates :body, presence: true
has_many :categorizations
has_many :categories, :through => :categorizations
end
category.rb
class Category < ApplicationRecord
has_many :categorizations
has_many :posts, :through => :categorizations
end
categorization.rb
class Categorization < ApplicationRecord
belongs_to :post
belongs_to :category
end
Then, here are these controller and views I am confused with:
posts_controller.rb
class PostsController < ApplicationController
before_action :find_post, only: [:edit, :update, :show, :delete]
before_action :authenticate_admin!, except: [:index, :show]
# Index action to render all posts
def index
if params.has_key?(:category)
#category = Category.find_by_name(params[:category])
#posts = Post.where(category: #category)
else
#posts = Post.all
end
end
# New action for creating post
def new
#post = Post.new
end
# Create action saves the post into database
def create
#post = Post.new(post_params)
if #post.save
flash[:notice] = "Successfully created post!"
redirect_to post_path(#post)
else
flash[:alert] = "Error creating new post!"
render :new
end
end
# Edit action retrives the post and renders the edit page
def edit
end
# Update action updates the post with the new information
def update
#post = Post.find(params[:id])
if #post.update_attributes(post_params)
flash[:notice] = "Successfully updated post!"
redirect_to posts_path(#posts)
else
flash[:alert] = "Error updating post!"
render :edit
end
end
# The show action renders the individual post after retrieving the the id
def show
end
# The destroy action removes the post permanently from the database
def destroy
#post = Post.find(params[:id])
if #post.present?
#post.destroy
flash[:notice] = "Successfully deleted post!"
redirect_to posts_path
else
flash[:alert] = "Error updating post!"
end
end
private
def post_params
params.require(:post).permit(:title, :body, category_ids: [])
end
def find_post
#post = Post.find(params[:id])
end
end
index.html.erb
<div class="container">
<div class="col-sm-10 col-sm-offset-1 col-xs-12">
<% #posts.each do |post| %>
<div class="col-xs-12 text-center">
<div class="text-center">
<h2><%= post.title %></h2>
<h6><%= post.created_at.strftime('%b %d, %Y') %></h6>
</div>
<div>
<%= raw post.body.truncate(358) %>
</div>
<div class="text-center">
<%= link_to "READ MORE", post_path(post) %>
</div>
<% if admin_signed_in? %>
<%= link_to "Show", post_path(post), class: "btn btn-primary" %>
<%= link_to "Edit", edit_post_path(post), class: "btn btn-default" %>
<%= link_to "Delete", post_path(post), class: "btn btn-danger", data: {:confirm => "Are you sure?"}, method: :delete %>
<% end %>
<hr />
</div>
<% end %>
</div>
</div>
show.html.erb
<div class="col-sm-11 col-xs-12 blog-content">
<h2 class="text-center"><%= #post.title %></h2>
<h1 class="text-center"><%= #category.name %></h1>
<h5 class="text-center"><%= #post.created_at.strftime('%b %d, %Y') %></h5>
<div class="text-center"><%= raw #post.body %></div>
</div>
_navigation.html.erb(part of it)
<ul class="nav navbar-nav navbar-left">
<% Category.all.each do |cat| %>
<li class="text-center"><%= link_to cat.name, posts_path(category: cat.name) %></li>
<% end %>
</ul>
Just in case, schema.rb
ActiveRecord::Schema.define(version: 2018_11_07_082317) do
create_table "admins", force: :cascade do |t|
t.string "email", default: "", null: false
t.string "encrypted_password", default: "", null: false
t.string "reset_password_token"
t.datetime "reset_password_sent_at"
t.datetime "remember_created_at"
t.datetime "created_at", null: false
t.datetime "updated_at", null: false
t.string "avatar"
t.index ["email"], name: "index_admins_on_email", unique: true
t.index ["reset_password_token"], name: "index_admins_on_reset_password_token", unique: true
end
create_table "categories", force: :cascade do |t|
t.string "name"
t.datetime "created_at", null: false
t.datetime "updated_at", null: false
end
create_table "categories_posts", id: false, force: :cascade do |t|
t.integer "category_id"
t.integer "post_id"
end
create_table "categorizations", force: :cascade do |t|
t.integer "post_id"
t.integer "category_id"
t.datetime "created_at", null: false
t.datetime "updated_at", null: false
end
create_table "ckeditor_assets", force: :cascade do |t|
t.string "data_file_name", null: false
t.string "data_content_type"
t.integer "data_file_size"
t.string "type", limit: 30
t.integer "width"
t.integer "height"
t.datetime "created_at", null: false
t.datetime "updated_at", null: false
t.index ["type"], name: "index_ckeditor_assets_on_type"
end
create_table "posts", force: :cascade do |t|
t.string "title"
t.text "body"
t.datetime "created_at", null: false
t.datetime "updated_at", null: false
end
end
In show view you have
<h1 class="text-center"><%= #category.name %></h1>
But you don't define #category in show action. If you want to list categories, it should be
<h1 class="text-center"><%= #post.categories.pluck(:name).join(', ') %></h1>
Btw, looks like you have useless table categories_posts in the schema.rb
Update:
About index action - you should change query for #posts, since the post doesn't have a category column, but he has categories association:
def index
if params.has_key?(:category)
# you can remove #category defining if you don't need it somewhere in view
#category = Category.find_by_name(params[:category])
#posts = Post.joins(:categories).where(categories: { name: params[:category] } )
else
#posts = Post.all
end
end
Note, it is better to use id for the query, not name, searching by id is faster. You need to change link in navbar to link_to cat.name, posts_path(category: cat.id) for it and replace name with id in the query. And it is better to move the whole query to named scope in the Post model.
I'm new to Rails.
I use devise for authentication in my Rails 5.0.1 project.
# Here is my Helper Module
module CategoriesHelper
def categories_for_select
Category.all.collect { |m| [m.name] }
end
end
in _form.html.erb page
<div class="field">
<%= f.label :category_name %>
<%= f.select(:category_name, categories_for_select, :prompt => 'Select') %>
<%= link_to 'Create New Category', new_category_path %>
How can I list all the items from the Category table to a drop-down list that is associated with the current_user.
UPDATE:
Schema
create_table "categories", force: :cascade do |t|
t.string "name"
t.datetime "created_at", null: false
t.datetime "updated_at", null: false
t.integer "user_id"
end
Assuming your User has_many categories, you can simply do:
current_user.categories.pluck(:name)
Hi can anyone point me to the right direction? I´m trying to show images on views/pages/index.html.erb the images are uploaded on views/products/new.html.erbthrough the _form.html.erbpartial. Each product/picture then belongs to a category which I can select in the _navbar.html.erb and is then directed to the views/categories/show.html.erbto see pictures of each product in that category and so on.
That is all working fine
But now I want to display the last added picture in each category on the views/pages/index.html.erb and I´m always getting this error : undefined method 'image' for #<Array:0x007f8d1fb19ff0>
I´m pretty lost at the moment, and hopefully someone can guide me to the right path.
My code id like this:
pages_controller.rb
class PagesController < ApplicationController
def index
#products = Product.all.order(created_at: :desc).group_by(&:category_id)
end
def about
end
def location
end
def stockists
end
end
views/pages/index.html.erb
<% #products.each do |product| %>
<div class="col-lg-3 col-sm-6 col-xs-12 center-block " >
<%= image_tag product.image.url(:medium) %>
<p><%= product.name %></p>
<p><%= product.category.name %></p>
<% end %>
</div>
And then I have, the products.rb and category.rb
product.rb
class Product < ActiveRecord::Base
mount_uploader :image, ImageUploader
validates_presence_of :name, :price
validates_numericality_of :price
belongs_to :category
end
category.rb
class Category < ActiveRecord::Base
has_many :products
end
this as part of the schema.rb
create_table "products", force: :cascade do |t|
t.string "name"
t.string "description"
t.float "price"
t.string "image"
t.datetime "created_at", null: false
t.datetime "updated_at", null: false
t.integer "category_id", default: 1
end
add_index "products", ["category_id"], name: "index_products_on_category_id", using: :btree
and in the end there is this part
add_foreign_key "order_items", "orders", on_delete: :cascade
add_foreign_key "order_items", "products"
add_foreign_key "orders", "users", on_delete: :cascade
add_foreign_key "products", "categories"
end
You are using group_by in the controller, an enumerable method that returns a hash of Product arrays keyed by category_id.
#product = {
:category1 => [#<Product category_id=1>, #<Product category_id=1>, ...],
:category2 => [#<Product category_id=2>, #<Product category_id=2>, ...]
}
When you loop through #products in the view, you are looping through a hash where each iteration is passing an array.
The product variable does not contain a product, but an array of products.
<% #products.each do |product| %> # product is type Array!
<%= image_tag product.image.url(:medium) %> # Array.image throws an error!
<% end %>
You must create an outer loop to step through the hash.
<% #products.each do |category, products| %>
<% products.each do |product| %>
# do stuff
<% end %>
<% end %>