".save" only inserts null values in database - mysql

I'm trying to make a RoR application for a Hospital so it has patients, doctors, offices, etc.
The problem I'm having is that, at the patient "Sign-up", I'm not able to save the new patient in the database. In fact, despite I've checked that the attributes are ok (It's just a name and a personal ID), once the method is excecuted, in the database only appears a new row with "<null>" instead of the actual attribute values. Here's the method:
def pat_create
pName = params[:name].to_s
id = params[:pid].to_s.to_i
pat = Patient.where(:pID => id).first
if pat == nil
pat = Patient.new(:name => pName, :pID =>id)
pat.save
end
end
Also, This is the query that it constructs:
INSERT INTO `patients` (`created_at`, `name`, `pID`, `updated_at`) VALUES ('2013-05-20 02:04:28', NULL, NULL, '2013-05-20 02:04:28')
This method is called after some other view collects the :name and :pid information in this form:
<%= form_tag('/page/pat_create') do %>
<%= text_field_tag :name, nil%>
<%= text_field_tag :pid, nil%>
<%= button_tag(type: "submit", class: "btn btn-success") do %>
Register<i class="icon-white icon-plus"></i>
<%end%>
<%end%>
Needless to say, pat.errors.empty? is true, and so is pat.save.
Any idea why this happens?
Here's Patient Model:
class Patient < ActiveRecord::Base
attr_accessible :name, :pID
attr_accessor :name, :pID
has_many :appointments
validates_presence_of :name
validates :name, :format => {:with=> /^[a-zA-Z\s\D]+$/}
validates_presence_of :pID
validates_numericality_of :pID
validates_inclusion_of :pID, :in => 100000..9999999999
end

Remove the following line in class Patient:
attr_accessor :name, :pID
What happened was that attr_accessor replaced the two database column attributes :name and :pID (which were automatically generated) with its own, resulting in two virtual attributes, :name and :pID.
Thus, the virtual attributes were being set and validated instead of the corresponding database attributes, which resulted in no errors yet null values in the database.

Can you show us how this method is called? Also are you sure that params[:name] and params[:pid].
You have used the column :pid and :pID, as below
pat = Patient.where(:pID => id).first
if pat == nil
pat = Patient.new(:name => pName, :pID =>id) # should use pat = Patient.new(:name => pName, :pid =>id)
pat.save
end

So in your controller your params are nil but you call .to_s and .to_s.to_i which results in an empty string "" and 0 (zero). You then save them into your database. A couple recommendations:
def pat_create
pat = Patient.new(:name => params[:name], :pid =>params[:pid])
pat.save
end
In addition to the uniqueness validation I would make sure your db column has a unique index on it to insure no duplicate patients.
class Patient < ActiveRecord::Base
attr_accessible :name, :pid
attr_accessor :name, :pid
has_many :appointments
validates :name, presence: true,
length: { maximum: 50 },
format: {:with=> /^[a-zA-Z\s\D]+$/}
validates :pid, presence: true,
numericality: { only_integer: true,
greater_than_or_equal_to: 100000,
less_than_or_equal_to: 9999999999 },
uniqueness: true
end
If you are getting valid values this will work and if not you will see why it is not.

Related

rails - association params selected, but params "changing" on persist

I really am not sure of how to describe this problem and I've never had an issue like this before. I am trying to set up a "has_many :through" association that handles membership requests to a group. The other actions (destroy, update) seem to be working fine. Whenever I initiate the create action in my controller, the object shows that it is being passed the correct params and the association is created. However, it is creating an association only to the group with the "1" id. I have no idea how to clearly explain this problem or solve it. I'll post my log and code below.
To clarify my question: why is the data "changing" when it persists to the database (on create)?
Cliqs = Groups
Log:
Started POST "/cliqs/2/cliq_requests" for ::1 at 2016-03-31 20:35:32 -0500
Processing by CliqRequestsController#create as HTML
Parameters: {"authenticity_token"=>"uaVHFgB7digMywl2a/n2GKMtwi691WA/dw2F2mzdkSCK69C46TZICiSp90xldj3hosFwSOPEi3fSOvOSkIVMjA==", "cliq_id"=>"2"}
Cliq Load (0.0ms) SELECT `cliqs`.* FROM `cliqs` WHERE (2) LIMIT 1
User Load (0.0ms) SELECT `users`.* FROM `users` WHERE `users`.`id` = 1 ORDER BY `users`.`id` ASC LIMIT 1
(0.0ms) BEGIN
SQL (1.0ms) INSERT INTO `cliq_requests` (`cliq_id`, `user_id`, `created_at`, `updated_at`) VALUES (1, 1, '2016-04-01 01:35:32', '2016-04-01 01:35:32')
(198.0ms) COMMIT
Redirected to http://localhost:3000/cliqs
Completed 302 Found in 237ms (ActiveRecord: 199.0ms)
Controller Action:
def create
#cliq = Cliq.find_by(params[:cliq_id])
#cliq_request = current_user.cliq_requests.new(cliq: #cliq)
if #cliq_request.save
redirect_to cliqs_path
else
redirect_to current_user
end
end
Other Actions (just in case):
def update
#cliq = Cliq.find_by(params[:cliq_id])
#cliq_request = CliqRequest.find(params[:id])
#cliq_request.accept
end
def destroy
#cliq = Cliq.find_by(params[:cliq_id])
#cliq_request = CliqRequest.find(params[:id])
#cliq_request.destroy
if #cliq_request.destroy
redirect_to cliqs_path
else
redirect_to current_user
end
end
And the Models:
class User < ActiveRecord::Base
has_one :owned_cliq, foreign_key: 'owner_id', class_name: 'Cliq', dependent: :destroy
has_many :cliq_memberships, dependent: :destroy
has_many :cliqs, through: :cliq_memberships
has_many :cliq_requests, dependent: :destroy
end
class Cliq < ActiveRecord::Base
belongs_to :owner, class_name: 'User'
has_many :cliq_memberships, dependent: :destroy
has_many :members, through: :cliq_memberships, source: :user
has_many :cliq_requests, dependent: :destroy
has_many :pending_members, through: :cliq_requests, source: :user, foreign_key: 'user_id'
end
class CliqRequest < ActiveRecord::Base
#from
belongs_to :user
#to
belongs_to :cliq
def accept
cliq.members << cliq.pending_members.find(user_id)
destroy
end
end
Finally my View:
<h1><%= #cliq.name %></h1>
<%= link_to 'Request to join Cliq', cliq_cliq_requests_path(#cliq, #cliq_request), :method => :post %>
<% #cliq_members.each do |cliq_member| %>
<ul><%= link_to cliq_member.username, user_path(cliq_member) %></ul>
<% end %>
<h3>Cliq Requests:</h3>
<ul>
<% #cliq.pending_members.each do |pending_member| %>
<%= link_to pending_member.username, user_path(pending_member) %>
<% end %>
<% #cliq.cliq_requests.each do |cliq_request| %>
<%= link_to "Accept", cliq_cliq_request_path(#cliq, cliq_request), :method => :put %>
<%= link_to "Deny", cliq_cliq_request_path(#cliq, cliq_request), :method => :delete %>
</ul>
<% end %>
As you found out yourself, using #cliq = Cliq.find_by(id: params[:cliq_id]) works, and #cliq = Cliq.find_by(params[:cliq_id]) does not. So why is this the case?
The find_by method matches on conditions. Using find_by, you can match on any attribute. For example, this would also work:
#cliq = Cliq.find_by(some_attribute: "foo")
So using find_by, you must specify the id attribute in order for the query to return the correct record. Your find_by query was actually running SQL that looked something like this:
SELECT `cliqs`.* FROM `cliqs` WHERE (2) LIMIT 1
That select statement will return the whole table, and the LIMIT 1 just grabs the first record.
As a bonus, the preferred rails way to assign #cliq based on an params[:cliq_id] would be using just find, which searches for the record using its primary key:
#cliq = Cliq.find(params[:cliq_id])

Efficently querying multi language categories and category items

So I have a bit of a server response time issue - which I think is caused due to obsolete queries. One major query chain that I have takes up to 370ms, which is obviously causing an issue.
Here are the requirements:
5 Different languages
There are several Product Categories (i.e. Cat 1, Cat 2, Cat 3, etc.)
Categories displayed depend on language. For example whilst category 1 is displayed in all languages, category 2 is only displayed in Germany and France but not in the UK
Each category contains x number of items (has_many belongs_to relationship). Again some items are displayed in certain languages others are not. For example even category 2 is displayed in France and Germany, only in Germany you can buy Item 1 and hence Item 1 should not be displayed in France but Germany.
The categories and items do have boolean fields named after the locale. This way I can set via flag whether or not to display the category and item in a specific language.
My solution:
Building the solution is quiet easy. In controller I read out all the categories for the current locale:
application_controller.rb (since it is used on every single page)
#product_categories = ProductCategory.where("lang_" + I18n.locale.to_s + " = ?", true)
And in the view (the navigation) I do the following:
layouts/navs/productnav.html.haml
- #product_categories.each do |category|
...
- category.products.includes(:product_teasers).where("lang_" + I18n.locale.to_s + " = ? AND active = ?", true, true).in_groups_of(3).each do |group|
...
The issue with this solution is that each time I fire a lot of queries towards the database. Using "includes" does not solve it as I can not specify what items to pull. Furthermore I require the in_groups_of(3) in my loop to display the items correctly on the page.
I was also looking into memchached solutions to have the queries cached all together - i.e. Dalli however, this would require me to change a lot of code as I am guessing I would require to query all categories for each language and cache them. In addition to it I have to query each item for each langugage depending on language and store that somehow in an array ?!
My question:
How to approach this ? There must be a simpler and more efficient solution. How to efficiently query respectively cache this?
Thank you!
UPDATE:
As requested here are my two Models:
1.) ProductCategory
class ProductCategory < ActiveRecord::Base
translates :name, :description, :slug, :meta_keywords, :meta_description, :meta_title, :header, :teaser, :fallbacks_for_empty_translations => true
extend FriendlyId
friendly_id :name, :use => [:globalize, :slugged]
globalize_accessors :locales => [:at, :de, :ch_de, :ch_fr, :fr, :int_en, :int_fr], :attributes => [:slug]
has_paper_trail
has_many :products, :dependent => :destroy
validates :name, presence: true, uniqueness: { case_sensitive: false }
default_scope { includes(:translations) }
private
def slug_candidates
[
[:name]
]
end
end
2.) Product
And every product Category can have 0..n Products, and each Product must belongs to one category.
class Product < ActiveRecord::Base
translates :slug, :name, :meta_keywords, :meta_description, :meta_title, :teaser, :power_range, :product_page_teaser, :product_category_slider_teaser, :fallbacks_for_empty_translations => true
extend FriendlyId
friendly_id :name, :use => :globalize
before_save :change_file_name
searchable do
text :name, :teaser, :product_page_teaser, :product_category_slider_teaser
integer :product_category_id
end
belongs_to :product_category
has_many :product_teasers, :dependent => :destroy
has_many :product_videos, :dependent => :destroy
has_many :product_banners, :dependent => :destroy
has_many :product_documents, :dependent => :destroy
has_many :product_tabs, :dependent => :destroy
has_many :active_relationships, class_name: "Relationship",
foreign_key: "follower_id",
dependent: :destroy
has_many :passive_relationships, class_name: "Relationship",
foreign_key: "followed_id",
dependent: :destroy
has_many :following, through: :active_relationships, source: :followed
has_many :followers, through: :passive_relationships, source: :follower
has_many :references
has_and_belongs_to_many :contacts
accepts_nested_attributes_for :product_teasers, :reject_if => :all_blank, :allow_destroy => true
accepts_nested_attributes_for :product_tabs, :reject_if => :all_blank, :allow_destroy => true
accepts_nested_attributes_for :product_videos, :reject_if => :all_blank, :allow_destroy => true
accepts_nested_attributes_for :product_banners, :reject_if => :all_blank, :allow_destroy => true
accepts_nested_attributes_for :product_documents, :reject_if => :all_blank, :allow_destroy => true
has_paper_trail
validates :name, presence: true, uniqueness: { case_sensitive: false }
default_scope {includes(:translations)}
.... a lot more going on here ...
end
Please note: That category contains language flags (booleans), i.e lang_at, lang_de, lang_fr, etc. and if set then this category is displayed in that particualar language. SAME applies to products, as certain products are not displayed in all langauges altough the category might be.
Examples:
#product_categories = ProductCategory.where("product_categories.lang_" + I18n.locale.to_s + " = ?", true)
#products = Product.where("product_categories.lang_" + I18n.locale.to_s + " = ?", true)
I skipped any includes on purpose above - it is just to demonstrate the language logic.
UPDATE
The system have spent a lot of times to loop data in nested loop. Avoid to fetch data in nested loop. You have to use join or includes to catch your data more effective. For example:
Controller
#products = Product.includes(:product_category).where("product_categories.lang_" + I18n.locale.to_s + " = ? AND product_categories.active = ?", true, true).group_by(&:category)
View
- #products.each do |category, products|
<%= category.name %>
- products.each do |product|
<%= product.title %>
It needs to fix with your necessary code. I just help the main query. For example: active = ? is for products field or product_categories field. I hope It can help you.

Rails search a model based on multiple parameters using a form

So... I've been working on creating a search form for a rails application. I've gone through the railscast episodes 37, 111, and 112.
While the simple text search with a text input field works. I need to be able to define more parameters to refine the search.
I've found a few other methods, some using scopes...I keep running into issues getting any of these working in my application....
What I have right now is a simple form defined on my home index that points at my assets index:
<% form_tag assets_path, :method => 'get' do %>
<%= text_field_tag :search, params[:search] %>
<%= collection_select(:type_id, :type_id, Type.where("type_for = 'asset'"), :id, :name) %>
<%= submit_tag "Search", :search => nil %>
<% end %>
my asset.rb model:
class Asset < ActiveRecord::Base
has_many :children_assets, :class_name => "Asset"
has_and_belongs_to_many :groups, :join_table => "assets_groups"
belongs_to :parent_asset,
:class_name => "Asset",
:foreign_key => "parent_asset_id"
belongs_to :asset_type,
:class_name => "Type",
:conditions => "type_for = 'asset'"
belongs_to :asset_status,
:class_name => "Status",
:conditions => "status_for = 'asset'"
belongs_to :location
belongs_to :funding_source
has_many :transactions
def self.search(search)
if search
find(:all, :conditions => ['nmc_name LIKE ? AND type_id = ?', "%#{search}%", "%#{search}"])
else
find(:all)
end
end
end
in the asset_controller.rb
def index
unless params[:search].nil?
#title = "Assets"
#search = params[:search]
#assets = Asset.search(params[:search]).paginate(page: params[:page], per_page: 25)
else
#title = "Assets"
#assets = Asset.where('').paginate(page: params[:page], per_page: 25)
end
end
I just dont understand what it is that I'm not seeing here. I can run a similar mysql query and get the result I want. I just dont know how to format this in rails...
Any guidance on this would be amazing right now. Thanks!
It looks as though you're trying to search for a specific type of asset, but your search method in the Asset model is only using one of the user supplied parameters.
Judging by the form you're using, your controller will be receiving the params
params = {
search: 'Search Text',
type_id: 1
}
In your controller, you're only using search, so I'd change your method to include this:
#assets = Asset.search(params[:search], params[:type_id]).paginate(page: params[:page], per_page: 25)
Then amend the Assets model to use it
def self.search(search, type_id)
if search
find(:all, :conditions => ['nmc_name LIKE ? AND type_id = ?', "%#{search}%", "%#{type_id}"])
else
find(:all)
end
end

Remove extra appended characters to a text stored in MySQL

I have an api controller that collects information from the api users. However, some user information when sent and stored in the MySQL database sometimes its appears with added underscores like this "this is a test ________" instead of this is a test. However when run through the browser it stores just fine.
What could be the problem.
Controller extract;
#message.message = CGI.unescape(params[:message]).strip
As a temporary fix, any ideas how i can strip away all the 6 underscores that are being added to the message either in storage or when it comes.
class Api::V1::Json::MessagesController < ApplicationController
before_filter :authenticate
require 'uri'
require 'cgi'
def sms
#message = Message.new
##message.to = decoded_to.gsub(/[^\d]/,"")
#message.to = CGI.unescape(params[:to]).strip.gsub("+","").gsub(/\s+/, "")
#message.from = CGI.unescape(params[:from])
#message.message = CGI.unescape(params[:message]).strip
#message.user_id = current_user.id
#message.status = 'Queued'
if #message.save
MessageWorker.perform_async(#message.id, [""], current_user.id)
render json: {status: "Success"}
else
render json: {status: "Failed" }
end
end
private
def authenticate
error!('Unauthorized. Invalid token.', 401) unless current_user
end
def current_user
# find token. Check if valid.
user_token = params[:token]
token = ApiKey.where(:access_token => user_token).first
if token
#current_user = User.find(token.user_id)
else
false
end
end
end
and the model is;
class Message < ActiveRecord::Base
attr_accessible :message, :phone, :status, :to, :from, :user_id
attr_accessor :schedule
validates :message, :presence => true
validates :from, :presence => true
validates :to, :presence => true
validates :status, :presence => true
validates_length_of :message, :maximum => 1600, :allow_blank => true
validates_length_of :from, :maximum => 11, :allow_blank => false
belongs_to :user
Change your Model like this
class Message < ActiveRecord::Base
before_save :strip_underscore
def strip_underscore
self.message.gsub("______","")
end
end

Rails create and update array in database

So I have a service object that submits up votes and voted users into a database:
Here's the posts model:
class Post < ActiveRecord::Base
attr_accessible :comment_count, :downvote, :id, :text, :title, :upvote, :url, :user_id, :users_voted_up_by, :users_voted_down_by
serialize :users_voted_up_by, Array
serialize :users_voted_down_by, Array
belongs_to :user
end
Here's the User model:
class User < ActiveRecord::Base
attr_accessible :email, :password, :password_confirmation, :username, :good_karma, :bad_karma, :posts_voted_up_on, :posts_voted_down_on
serialize :posts_voted_up_on, Array
serialize :posts_voted_down_on, Array
has_many :posts
attr_accessor :password
before_save :encrypt_password
validates_confirmation_of :password
validates_presence_of :password, :on => :create
validates_presence_of :email
validates_uniqueness_of :email
validates_presence_of :username
validates_uniqueness_of :username
def self.authenticate(email, password)
user = find_by_email(email)
if user && user.password_hash == BCrypt::Engine.hash_secret(password, user.password_salt)
user
else
nil
end
end
def encrypt_password
if password.present?
self.password_salt = BCrypt::Engine.generate_salt
self.password_hash = BCrypt::Engine.hash_secret(password, password_salt)
end
end
end
Now here's my Voter class that controls up votes and down votes.
class Voter
def initialize(post, user)
#post = post
#user = user
end
def upvote
return false unless #post.users_voted_up_by
#post.upvote += 1
#post.users_voted_up_by << #user.username
#user.good_karma += 1
#post.save && #user.save
end
def downvote
return false unless #post.users_voted_down_by
#post.upvote += 1
#post.users_voted_down_by << #user.username
#user.bad_karma += 1
#post.save && #user.save
end
end
It adds and retrevies the first one fine:
User1
But when I perform another "upvote" with a different user, instead of adding it to the array it just adds it to the string like this:
User1User2
Am I not using the line correctly?
#post.users_voted_up_by << #user.username
You're trying to model a many-to-many relationship via serialized arrays. In my opinion this is a misuse of Rails serialization feature.
The more appropriate way to design your problem is to create a Vote model which belongs to User and has one Post. It is better both in design perspective and db-performance perspective.
Once you do that you won't add users to a post votes array, you will just create a new Vote instance with the voter (user) id and the post id.