Checkbox for removing images in a form with carrierwave - ruby-on-rails

Im creating a blog application in ruby on rails. I have "album" model and a "photo" model. In the album edit view I want to be able to delete the uploaded images which are associated to an album.
Unfortunately I'm getting an undefined method error for checkbox?!
Do you have any solution or hints to make this work?
How can I get to the bottom of why it does not work?
If you need further information just let me know.
albums/edit.html.erb
<%= form_for #album do |f| %>
<% if #album.errors.any? %>
<h2><%= pluralize(#album.errors.count, "error") %> prevent this post from saving:</h2>
<ul>
<% #album.errors.full_messages.each do |msg| %>
<li><%= msg %></li>
<% end %>k
</ul>
<% end %>
<div class="blog_edit_wrapper">
<%= f.label :title %><br>
<%= f.text_field :title, class: "blog_edit_title" %>
<br>
<br>
<%= f.submit 'Submit', class: "blog_edit_submit" %>
</div>
<% end %>
<hr>
<% #album.photos.each do |photo| %>
<%= image_tag(photo.gallery_image) %>
<%= photo.check_box :remove_gallery_image %>
<br>
<br>
<%= f.submit 'Submit', class: "blog_edit_submit" %>
<% end %>
photos_controller.rb
class PhotosController < ApplicationController
before_action :find_photo, only: [ :show, :edit, :update, :destroy]
before_action :set_album
def index
#photo = Photo.all
end
def new
#photo = #album.photos.new
end
def show
end
def create
#photo = #album.photos.new photo_params
#photo.album = #album
if #photo.save
redirect_to #album
else
render :new
end
end
def edit
end
def update
#photo = #album.photos.find params[:id]
if #photo.update photo_params
redirect_to #album, notice: "Your photo was successfully saved!"
else
render 'edit'
end
end
def destroy
#photo.destroy
redirect_to photos_path
end
private
def set_album
#album = Album.find params[:album_id]
end
def photo_params
params.require(:photo).permit(:gallery_image, :album_id, :title, :remove_gallery_image)
end
def find_photo
#photo = Photo.find(params[:id])
end
end
photo.rb
class Photo < ActiveRecord::Base
mount_uploader :gallery_image, ImageUploader
belongs_to :album
end
albums_controller.rb
class AlbumsController < ApplicationController
before_action :find_album, only: [:show, :edit, :update, :destroy]
before_action :authenticate_user!, except: [:index, :show]
def index
#albums = Album.all.order("created_at desc").paginate(page: params[:page], per_page: 12)
end
def new
#album = Album.new
end
def show
#photo = Photo.all
end
def create
#album = Album.new album_params
if #album.save
redirect_to #album
else
render :new
end
end
def edit
end
def update
if #album.update album_params
redirect_to #album, notice: "Your article was successfully saved!"
else
render 'edit'
end
end
def destroy
#album.destroy
redirect_to albums_path
end
private
def album_params
params.require(:album).permit(:title)
end
def find_album
#album = Album.find(params[:id])
end
end
album.rb
class Album < ActiveRecord::Base
has_many :photos
end
schema.rb
ActiveRecord::Schema.define(version: 20170424131600) do
create_table "albums", force: :cascade do |t|
t.string "title"
t.datetime "created_at", null: false
t.datetime "updated_at", null: false
end
create_table "friendly_id_slugs", force: :cascade do |t|
t.string "slug", null: false
t.integer "sluggable_id", null: false
t.string "sluggable_type", limit: 50
t.string "scope"
t.datetime "created_at"
end
add_index "friendly_id_slugs", ["slug", "sluggable_type", "scope"], name: "index_friendly_id_slugs_on_slug_and_sluggable_type_and_scope", unique: true
add_index "friendly_id_slugs", ["slug", "sluggable_type"], name: "index_friendly_id_slugs_on_slug_and_sluggable_type"
add_index "friendly_id_slugs", ["sluggable_id"], name: "index_friendly_id_slugs_on_sluggable_id"
add_index "friendly_id_slugs", ["sluggable_type"], name: "index_friendly_id_slugs_on_sluggable_type"
create_table "photos", force: :cascade do |t|
t.string "gallery_image"
t.datetime "created_at", null: false
t.datetime "updated_at", null: false
t.integer "album_id"
end
create_table "posts", force: :cascade do |t|
t.string "title"
t.text "content"
t.datetime "created_at", null: false
t.datetime "updated_at", null: false
t.string "slug"
t.string "post_main_image"
end
add_index "posts", ["slug"], name: "index_posts_on_slug", unique: true
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.integer "sign_in_count", default: 0, null: false
t.datetime "current_sign_in_at"
t.datetime "last_sign_in_at"
t.string "current_sign_in_ip"
t.string "last_sign_in_ip"
t.datetime "created_at", null: false
t.datetime "updated_at", null: false
end
add_index "users", ["email"], name: "index_users_on_email", unique: true
add_index "users", ["reset_password_token"], name: "index_users_on_reset_password_token", unique: true
end

I believe your issue is that you're not using a form but just iterating over the photos.
Also you can place the code for editing the photos inside the first form.
Place this code below the <br> tags and remove the <% #album.photos.each do |photo| %> interation.
<%= fields_for(#album, #album.photos.build) do |f| %>
<%= image_tag(f.gallery_image)%>
<%= f.check_box :remove_gallery_image %>
<% end %>
Edit:
To clarify the above; place the aforementioned code inside your original edit #album form and remove the iteration for the #album.photos.each

Related

Making a to-do webapp and got such an error SQLite3::SQLException: no such column: tasks.list_id

Showing /home/arthur/Desktop/application/todo/app/views/tasks/index.html.erb where line #7 raised:SQLite3::SQLException: no such column: tasks.list_id
This error appears when I am trying to open the created list to add new tasks. I think the problem is the app can't get the id of the list I am trying to open. But I can't understand what's wrong. (new with ruby and rails). Do I need to add views or something else?
Task Controller
class TasksController < ApplicationController
helper_method :task, :list, :tasks
def create
task.save
redirect_to list_tasks_path(list)
end
def update
task.update task_params
redirect_to list_tasks_path(list)
end
def complete
task.complete
redirect_to list_tasks_path(list)
end
def destroy
task.destroy
redirect_to list_tasks_path(list)
end
private
delegate :tasks, to: :list, private: true
def task
#task ||= params[:id] ? list.tasks.find(params[:id]) : list.tasks.new(task_params)
end
def list
#list ||= List.find params[:list_id]
end
def task_params
params.require(:task).permit(:title, :completed)
end
end
and ListsController
class ListsController < ApplicationController
#helper_method :list, :lists
def index
#lists = List.order('created_at')
end
def new
#list = List.new
end
def create
#list = List.new list_params
#list.save
redirect_to lists_path
end
def edit
#list = List.find params[:id]
redirect_to lists_path
end
def update
#list = List.find params[:id]
#list.update list_params
redirect_to lists_path
end
def destroy
#list = List.find params[:id]
#list.destroy
redirect_to lists_path
end
private
def list_params
params.require(:list).permit(:title)
end
end
View Index.html.erb
<div class="col-md-12">
<h2>Tasks</h2>
<div class="buffer-top"><%= link_to "New Task", new_list_task_path(list), class: 'btn btn-primary' %></div>
<div class="buffer-top">
<% tasks.each do |task| %> <----------- this line
<%= render partial: 'task', object: task %>
<% end %>
</div>
<div class="buffer-top"><%= link_to "Back to Lists", lists_path, class: 'btn btn-primary btn-sm' %></div>
</div>
class Task < ApplicationRecord
belongs_to :lists
def complete!
self.completed = true
save
end
end
class List < ApplicationRecord
has_many :tasks
end
schema
ActiveRecord::Schema.define(version: 2020_04_06_202846) do
create_table "lists", force: :cascade do |t|
t.string "title"
t.datetime "created_at", precision: 6, null: false
t.datetime "updated_at", precision: 6, null: false
end
create_table "tasks", force: :cascade do |t|
t.string "title"
t.boolean "completed"
t.datetime "created_at", precision: 6, null: false
t.datetime "updated_at", precision: 6, null: false
end
end
You have belongs_to :lists in your Task model but no list_id in your corresponding tasks table. Your tasks table needs a migration with t.integer "list_id", :null => false in it.
create_table "tasks", force: :cascade do |t|
t.integer "list_id", :null => false
t.string "title"
t.boolean "completed"
t.datetime "created_at", precision: 6, null: false
t.datetime "updated_at", precision: 6, null: false
end

How can I set my controller and views when I have two Many-to-Many association?

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.

Model validate method returns error from server rails 5

I have this method in my micropost.rb, the error is souly when I run this method. When I submit my form I do not get the errors from the model but the rendering of the else condition in the controller. Something seems to be wrong in the model but don't see why I get rollback transaction from server?
The method just checks that a user has not entered and sent more than one field.
Micropost.rb
validate :return_media_field_errors
private
def return_media_field_errors
if :img_url? && :video_url?
errors.add(:img_url, "Can only submit one field at a time")
end
end
MicropostController
def create
#micropost = current_user.microposts.build(micropost_params)
if #micropost.save
flash[:success] = "Micropost created!"
#micropost = current_user.microposts.build
#feed_items = current_user.feed.paginate(page: params[:page])
render 'static_pages/home'
else
#feed_items = []
render 'shared/error_messages'
end
end
private
def micropost_params
params.require(:micropost).permit(:content, :picture, :picture_cache, :image_url, :video_url, :gif_url)
end
micropost schema.rb
create_table "microposts", force: :cascade do |t|
t.text "content"
t.integer "user_id"
t.datetime "created_at", null: false
t.datetime "updated_at", null: false
t.string "picture"
t.string "image_url"
t.string "video_url"
t.string "gif_url"
t.index ["user_id", "created_at"], name: "index_microposts_on_user_id_and_created_at"
t.index ["user_id"], name: "index_microposts_on_user_id"
end
View params
<div class="media_field_1">
Image <%= f.text_field :image_url, class: 'form-control' %>
</div>
<div class="media_field_2">
Video <%= f.text_field :video_url, class: 'form-control' %>
</div>
I think the problem is this line:
if :img_url? && :video_url?
here :img_url? is a symbol, not an attribute
What you want is self.image_url? or just image_url, same for video_url

How can I make it so only users who are admins can post articles?

The user model has a attribute :admin which can be true or false. I want to make a validation so only users with the admin = true can post articles or see the "New article" button in the view.
I am using the devise gem
Controller (articles_controller.rb) :
class ArticlesController < ApplicationController
before_action :set_article, only: [:show, :edit, :update, :destroy]
before_action :authenticate_user!
# GET /articles
# GET /articles.json
def index
#articles = Article.paginate(page: params[:page], per_page: 4)
end
# GET /articles/1
# GET /articles/1.json
def show
end
# GET /articles/new
def new
#article = current_user.articles.build
end
# GET /articles/1/edit
def edit
end
# POST /articles
# POST /articles.json
def create
#article = current_user.articles.build(article_params)
respond_to do |format|
if #article.save
format.html { redirect_to #article, notice: 'Article was successfully created.' }
format.json { render :show, status: :created, location: #article }
else
format.html { render :new }
format.json { render json: #article.errors, status: :unprocessable_entity }
end
end
end
# PATCH/PUT /articles/1
# PATCH/PUT /articles/1.json
def update
respond_to do |format|
if #article.update(article_params)
format.html { redirect_to #article, notice: 'Article was successfully updated.' }
format.json { render :show, status: :ok, location: #article }
else
format.html { render :edit }
format.json { render json: #article.errors, status: :unprocessable_entity }
end
end
end
# DELETE /articles/1
# DELETE /articles/1.json
def destroy
#article.destroy
respond_to do |format|
format.html { redirect_to articles_url, notice: 'Article was successfully destroyed.' }
format.json { head :no_content }
end
end
private
# Use callbacks to share common setup or constraints between actions.
def set_article
#article = Article.find(params[:id])
end
# Never trust parameters from the scary internet, only allow the white list through.
def article_params
params.require(:article).permit(:title, :body)
end
end
Model (article.rb):
class Article < ActiveRecord::Base
belongs_to :user
has_many :comments
validates :title, length: { minimum: 5 }
validates :title, uniqueness: true, uniqueness: { message: "This article title has already been posted."}
validates :body, length: { minimum: 15 }
end
Model (user.rb):
class User < ActiveRecord::Base
# Include default devise modules. Others available are:
# :confirmable, :lockable, :timeoutable and :omniauthable
devise :database_authenticatable, :registerable,
:recoverable, :rememberable, :trackable, :validatable
validates_uniqueness_of :username
has_many :articles
has_many :comments
end
Schema :
ActiveRecord::Schema.define(version: 20160320222854) do
create_table "articles", force: :cascade do |t|
t.string "title"
t.text "body"
t.datetime "created_at", null: false
t.datetime "updated_at", null: false
t.integer "user_id"
end
create_table "comments", force: :cascade do |t|
t.text "body"
t.integer "user_id"
t.integer "article_id"
t.datetime "created_at", null: false
t.datetime "updated_at", null: false
end
add_index "comments", ["article_id"], name: "index_comments_on_article_id"
add_index "comments", ["user_id"], name: "index_comments_on_user_id"
create_table "contacts", force: :cascade do |t|
t.string "name"
t.string "email"
t.text "message"
t.datetime "created_at"
t.datetime "updated_at"
end
create_table "users", force: :cascade do |t|
t.string "username", default: "", null: false
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.integer "sign_in_count", default: 0, null: false
t.datetime "current_sign_in_at"
t.datetime "last_sign_in_at"
t.string "current_sign_in_ip"
t.string "last_sign_in_ip"
t.string "confirmation_token"
t.datetime "confirmed_at"
t.datetime "confirmation_sent_at"
t.string "unconfirmed_email"
t.datetime "created_at", null: false
t.datetime "updated_at", null: false
t.boolean "admin", default: false
t.string "firstname"
t.string "lastname"
end
add_index "users", ["confirmation_token"], name: "index_users_on_confirmation_token", unique: true
add_index "users", ["email"], name: "index_users_on_email", unique: true
add_index "users", ["reset_password_token"], name: "index_users_on_reset_password_token", unique: true
end
view /articles (index.html.erb):
<div class="row">
<!-- Blog article Content Column -->
<div class="col-lg-8">
<% #articles.each do |article| %>
<!-- Blog article -->
<!-- Title -->
<h4 style="font-size: 45.5px;"><%= link_to article.title, article %></h4>
<!-- Date/Time -->
<p><span class="glyphicon glyphicon-time"></span>
<%= time_ago_in_words(article.created_at) %>
</p>
<!-- Author -->
<p>
Article By:<strong> <%= article.user.username %></strong>
</p>
<hr>
<% end %>
<%= link_to "New article", new_article_path, class: "btn btn-default btn-xs" %>
</div>
<!-- paginate -->
<%= will_paginate #articles %>
<br />
Thanks in advance to all the wonderful people here willing to lend a helping hand.
You can add a filter method in Application Controller
def admin_access
render(text: 'Unauthorised') and return unless current_user.admin
end
And use this in Atricles controller or any other place where it is required
before_filter :admin_access, only: [:new, :create, :edit, :update, :destroy]
And in the views, check if user is admin
<% if current_user.admin %>
# new/edit/delete links
<% end %>
Even though, not showing the link in the view solves the problem, It is always better to have proper authorisation logic in the server side.
in the controller
def new
if current_user.admin
#article = current_user.articles.build
else
redirect_to root_path and return #somewhere you want to redirect
end
end
in the view
<% if current_user.admin %>
<%= link_to "New article", new_article_path, class: "btn btn-default btn-xs" %>
<% end %>
To Show the New Article link to the only Admin. Add condition to your link_to tag. This will only hide the link.
<%= link_to "New article", new_article_path, class: "btn btn-default btn-xs" if current_user.admin? %>
Suppose if you want Admin Users only create a New Article. Then you need to implement authorization in your application. Try cancancan gem
For the docs and video

Rails One-To-Many Nested Form - Error using controller show action to view posted form?

I'm trying to create a posting form to post a url & some text. The nested form submits without an error but I can't display the subsequent content of that form in my show action controller.
Post Model
class Post < ActiveRecord::Base
has_many :texts
has_many :urls
accepts_nested_attributes_for :texts, :urls
end
Text Model
class Text < ActiveRecord::Base
belongs_to :post
end
Url Model
class Url < ActiveRecord::Base
belongs_to :post
end
Post Controller
class PostsController < ApplicationController
def index
#posts = Post.all
end
def show
#post = Post.find(params[:id])
#texts = #post.texts
#urls = #post.urls
end
def new
#post = Post.new
end
def create
#post = Post.new(post_params)
if #post.save
redirect_to #post
else
render 'new'
end
end
private
def post_params
params.require(:post).permit(:texts_attributes => [:textattr], :urls_attributes => [:urlattr])
end
show.html.erb
<%= #text.textattr %>
<%= #url.urlattr %>
Database Schema
create_table "posts", force: :cascade do |t|
t.datetime "created_at", null: false
t.datetime "updated_at", null: false
end
create_table "texts", force: :cascade do |t|
t.text "textattr"
t.datetime "created_at", null: false
t.datetime "updated_at", null: false
t.integer "post_id"
end
add_index "texts", ["post_id"], name: "index_texts_on_post_id"
create_table "urls", force: :cascade do |t|
t.string "urlattr"
t.datetime "created_at", null: false
t.datetime "updated_at", null: false
t.integer "post_id"
end
add_index "urls", ["post_id"], name: "index_urls_on_post_id"
end
Error message after I press submit on the form
!(http://imgur.com/dzdss5z)
Your help would be amazing - thank you!
The #text and #url variables are never set in the controller's show action, so they are nil. Hence, there is an error when you try to call those attributes from them in the view.
You have set the #texts and #urls variables, so you can do something like this:
<% #texts.each do |text| %>
<%= text.textattr %>
<% end %>
<% #urls.each do |url| %>
<%= url.urlattr %>
<% end %>

Resources