I am trying to add User details into 2 tables The table Associations are
Distributor Table:
class Distributor < ApplicationRecord
has_one :authentication, dependent: :destroy
end
Authentication Table
class Authentication < ApplicationRecord
belongs_to :distributor
belongs_to :user
validates :email,presence: true
before_save :create_remember_token
private
def create_remember_token
self.remember_token = SecureRandom.urlsafe_base64
end
end
Authentication table details
Authentication(id: integer, email: string, password: string, created_at: datetime, updated_at: datetime, admin: boolean, remember_token: string, user_id: integer, mail_confirmation: boolean, distributor: boolean, distributor_id: integer)
Controller for create
def create
#distributor = Distributor.new(distributor_params)
if #distributor.save
params[:distributor][:distributor_id] = #distributor.id
params[:distributor][:password] = "Default"
params[:distributor][:distributor] = "true"
#authe_distributor = Authentication.new(authen_distributor_params)
if #authe_distributor.save
redirect_to #distributor
else
render 'new'
end
else
render 'new'
end
end
whenever i am trying to add the details i getting this error.. anyone tell me what i am doing wrong..
Your Authentication model has both a column and an association with the same name: distributor. This is problematic because all though you are trying to set the column named distributor to the "true" rails ends up trying to set the association to that value which is what causes the exception.
Rename either the column or the association so that you no longer have this clash and the problem should go away.
Related
I am currently trying to implement a has_one relationship where you are able to search through all of blogs active posts. My problem comes where even though i explicitly search through the active_post relation it will return everything in its posts instead and not just the active ones which is what i want.
# == Schema Information
#
# Table name: blog
#
# id :integer not null, primary key
# name :string(200)
# context :string(20)
# show_on_summary :boolean
#
class Blog < ApplicationRecord
has_many :posts, dependent: :destroy, autosave: true
has_one :active_post, -> { order('created_at DESC').where(posts: { status_code: 'active' }) }, class_name: 'Post', foreign_key: :blog_id
# == Schema Information
#
# Table name: posts
#
# id :integer not null, primary key
# created_at :datetime not null
# updated_at :datetime not null
# status_code :string(20)
# blog_id :integer
#
class Post < ApplicationRecord
STATUS_CODES = %w(active pending deactivated)
belongs_to :blog
and yet when i run the code below where i have a list of posts of any type of status_code and am trying to find the blog where only their active_post relation matches, it will return every blog that has a post in that list regardless of status_code.
Blog.where(active_post: list_of_posts)
When i am debugging i can go to the individual blogs and look at their 'active_post' relation and it will show the correct latest active post. if there is no active post it will return nil which is what i want.
I don't think you can do it without manually joining your table
Blog.joins(:active_post).where(posts: { id: list_of_posts })
When you join your table, you ensure that the relation is going to be used, and specifying the "posts"."id" column also ensures that it won't use a subquery to match the Blogs ids
I think you don't need to specify the :posts table in the where clause:
class Blog < ApplicationRecord
has_many :posts, dependent: :destroy, autosave: true
has_one :active_post, -> { order('created_at DESC').where(status_code: 'active') }, class_name: 'Post', foreign_key: :blog_id
#...
end
Blog.first.active_post
# => return the latest post
I have set up a simple has-many and belongs-to association using DataMapper with Sinatra. My User model has many 'peeps', and my Peep model belongs to user. See below for the classes....
I am able to successfully create a new peep which belongs to a particular user, by passing the user_id directly into the peep on initialization, like this:
Method 1
new_peep = Peep.create(content: params[:content], user_id: current_user.id)
This adds 1 to Peep to Peep.count.
However, my understanding is that I should be able to create the association by assigning the current_user to new_peep.user. But when I try that, the peep object won't save.
I've tried this:
Method 2
new_peep = Peep.create(content: params[:content], user: current_user)
Current user here is User.get(session[:current_user_id])
The resulting new_peep has an id of nil, but does have user_id set to the current_user's id. New_peep looks exactly like the new_peep that successfully gets created using Method 1, except it has no id because it hasn't successfully saved. I've tried calling new_peep.save separately, but I still get the below for the peep object:
<Peep #id=nil #content="This is a test peep" #created_at=#<DateTime: 2016-05-08T12:42:52+01:00 ((2457517j,42172s,0n),+3600s,2299161j)> #user_id=1>, #errors={}
Note that there are no validation errors. Most problems other people seem to have had with saving records come down to a validation criteria not being met.
I assumed this was something to do with the belongs_to association not working, but I can (after creating new_peep using Method 1 above) still call new_peep.user and access the correct user. So it seems to me the belongs_to is working as a reader but not a setter.
This problem also means I cannot create a peep by adding one into the user.peeps collection then saving user, which means there's virtually no point in peep belonging to user.
I've seen other people have had problems saving records that don't have any changes to save - but this is a completely new record, so all its attributes are being updated.
I'd really like to know what's going on - this has baffled me for too long!
Here are my classes:
class Peep
include DataMapper::Resource
property :id, Serial
property :content, Text
property :created_at, DateTime
belongs_to :user, required: false
def created_at_formatted
created_at.strftime("%H:%M, %A %-d %b %Y")
end
end
class User
include DataMapper::Resource
include BCrypt
attr_accessor :password_confirmation
attr_reader :password
property :id, Serial
property :email, String, unique: true, required: true
property :username, String, unique: true, required: true
property :name, String
property :password_hash, Text
def self.authenticate(params)
user = first(email: params[:email])
if user && Password.new(user.password_hash) == params[:password]
user
else
false
end
end
def password=(actual_password)
#password = actual_password
self.password_hash = Password.create(actual_password)
end
validates_confirmation_of :password
validates_presence_of :password
has n, :peeps
end
When you create the Peep you don't create it through User, maybe that's why it has no primary id, since it belongs to User. Also you're assigning it the foreign key user_id as if you have a property defined as such. Although the database has it, in DataMapper you don't pass in the foreign key id, it does it for you.
Try replacing
new_peep = Peep.create(content: params[:content], user: current_user)
with:
new_peep = current_user.peeps.create(content: params[:content], created_at: Time.now)
I have 2 models, entries:
schema "entries" do
belongs_to :exception, Proj.Exception
field :application, :string
end
And exceptions:
schema "exceptions" do
field :name, :string
end
The migration script:
def change do
create table(:exceptions) do
add :name, :string, null: false
end
create table(:entries) do
add :exception_id, references(:exceptions), null: false
add :application, :string, null: false
end
end
My goal is to store exceptions that happen in another system. I want the project to be able to store each exception in the second table exception if they are not already there and then store the application name and the exception id in the first table entries. There will be 1000s of records in entries and a handful in exceptions.
Assuming entry_params uses this JSON format:
{
exception: "NPE",
application: "SomeApp"
}
the method that should create the entries:
def create(conn, %{"entry" => entry_params}) do
exception = Repo.get_by(Exception, name: entry_params["exception"]) ||
Repo.insert!(%Exception{name: entry_params["exception"]})
changeset =
Entry.changeset(%Entry{}, entry_params)
|> Ecto.Changeset.put_assoc(:exception, exception)
Repo.insert!(changeset)
end
This will print out:
** (ArgumentError) unknown assoc `exception` in `put_assoc`
If I change the entries model to use has_one instead of belongs_to (and I think belongs_to "feels" bad here. An entry does not belong to an exception, it just has an exception) it throws the following:
** (Postgrex.Error) ERROR (not_null_violation): null value in column "exception_id" violates not-null constraint
table: entries
column: exception_id
What I want basically to first create an Exception (if it does not exist) and than create a new Entry of a system error and put the previously begotten Exception in the entry as an association.
What is wrong here?
Typo. belongs_to :exception, Proj.Exception should be belongs_to :exceptions, Proj.Exception
Association. Based on the data model in the question, I think the put_assoc is the wrong way around because in the data schema in the question, an exception has_many entries and an entry belongs_to exceptions. Ecto.Changeset.put_assoc(entries_changeset, :exception, exception) should be Ecto.Changeset.put_assoc(exception_changeset, :entries, entries)
Attempted solution:
entries schema:
schema "entries" do
field :application, :string
belongs_to :exceptions, Proj.Exception, on_replace: :nilify
end
exceptions schema:
schema "exceptions" do
field :name, :string
has_many :entry, Proj.Entry, on_delete: :delete_all, on_replace: :delete
end
migration script:
def change do
create table(:exceptions) do
add :name, :string, null: false
end
create table(:entries) do
add :application, :string, null: false
add :exception_id, references(:exceptions)
end
end
Assuming entry_params uses this JSON format:
{
exception: "NPE",
application: "SomeApp"
}
create or update the exceptions and the associated entries:
def create(conn, %{"entry" => entry_params}) do
new_entry = Entry.changeset(%Entry{}, entry_params)
changeset =
case Repo.get_by(Exception, name: entry_params["exception"]) do
:nil ->
exception = %Exception{name: entry_params["exception"]} |> Repo.insert!
Ecto.Changeset.build_assoc(exception, :entries, [new_entry])
struct ->
changeset = Ecto.Changeset.change(struct)
data = Ecto.Changeset.preload(changeset, :entries) |> Map.get(:model) # Ecto 1.x
# data = Ecto.Changeset.preload(changeset, :entries) |> Map.get(:data) # Ecto 2.0.x
Ecto.Changeset.put_assoc(changeset, :entries, [new_entry | data.entries])
end
Repo.insert!(changeset)
end
The users of my Rails app are receiving a lot of emails (lets say they represent signups from new customers of my users). When an email is received a customer should be created, and the email should be saved as well. However, if the customer already exists (recognized by the email address of the email), the email email should not be saved to the database. I thought this was handled by Email.new, and then only save if the email address is recognized. But it seems that Email.new saves the record to the database. So how do I work with an email before actually deciding wether I want to save it?
Example code:
class Email
include Mongoid::Document
field :mail_address, type: String
belongs_to :user, :inverse_of => :emails
belongs_to :customer, :inverse_of => :emails
def self.receive_email(user, mail)
puts user.emails.size # => 0
email = Email.new(mail_address: mail.fetch(:mail_address), user: user) # Here I want to create a new instance of Email without saving it
puts user.emails.size # => 1
is_spam = email.test_if_spam
return is_spam if is_spam == true
is_duplicate = email.test_if_duplicate(user)
end
def test_if_spam
spam = true if self.mail_address == "spam#example.com"
end
def test_if_duplicate(user)
self.save
customer = Customer.create_or_update_customer(user, self)
self.save if customer == "created" # Here I want to save the email if it passes the customer "test"
end
end
class Customer
include Mongoid::Document
field :mail_address, type: String
belongs_to :user, :inverse_of => :customers
has_many :orders, :inverse_of => :customer
def self.create_or_update_customer(user, mail)
if user.customers.where(mail_address: mail.mail_address).size == 0
customer = mail.create_customer(mail_address: mail.mail_address, user: user)
return "created"
end
end
end
I'm going to suggest a somewhat fundamental reworking of your function. Try rewriting your function like this:
class Email
def self.save_unless_customer_exists(user, mail)
email = Email.new(
mail_address: mail.fetch(:mail_address),
user: user
)
return if email.customer or email.is_spam? or email.is_duplicate?
Customer.create!(user: user)
email.save!
end
end
You won't be able to drop that code in and expect it to work, because you'd have to define is_spam? and is_duplicate?, but hopefully you can at least see where I'm coming from.
I'd also recommend writing some automated tests for these functions if you haven't already. It will help you pin down the problem.
I'm dealing with a problem on a after_save callback. I'm sure there is a easy solution, but I can't figure it out.
I have 3 models: User, Product, Bid. The Product table contains a boolean field "available", which is set default to true. If a User places a bid, the available field should be set to false.
I thought this should work with a callback on the bid model.
I can access and set the available field in the console by typing:
b = Bid.last
b.product.available = false
=> false
However I can't change it via the controller, so I think it doesn't execute the callback. What am I doing wrong?
Thank you all for your help!
product.rb
class Product < ActiveRecord::Base
has_one :bid
belongs_to :user
end
bid.rb
class Bid < ActiveRecord::Base
attr_accessible :product_id, :user_id, :product
belongs_to :product
belongs_to :user
after_save :set_product_status
def set_product_status
self.product.available = false
end
end
bids_controller.rb
...
def create
#user = current_user
product = Product.find(params[:product_id])
#bid = #user.bids.build(product: product)
respond_to do |format|
if #bid.save
...
Since bid belongs_to product, you should save the product too.
def set_product_status
self.product.available = false
self.product.save
end