Best Way to Find_Or_Create_By_Id But Update the Attributes If the Record Is Found

Sequelize findOrCreate returns SequelizeUniqueContraintError on a model with an additional unique keys

I was able to resolve this by passing the unique key to the 'findOrCreate' method of the model in the 'where' clause and setting values for all other table attributes in the 'defaults' clause.

For Eg. - Employee.findOrCreate({where: {employeeId: 'ABCD1234'}, defaults: {role: 'Analyst'}});

Where employeeId is a Unique Key. (I assume that the primary key 'id' field is set to Auto Increment)

Please refer to the example provided in the official tutorial. here

Sailsjs what attribute does findOrCreate use to find if a record exists?

findOrCreate takes 2 parameters and a callback
You should use first to determine whether record exists using unique identifying keys.

E.g.

var findCriteria = { id: 1, customer_email: 'abc@example.com' };
var recordToCreate = { id: 1, customer_email: 'abc@example.com', name: 'ABC' };
Model.findOrCreate(findCriteria, recordToCreate, console.log);

findOrCreate

Checks for the existence of the record in the first parameter. If it
can't be found, the record in the second parameter is created. If no
parameters are passed, it will return the first record that exists. If
no record is provided, it will either find a record with matching
criteria or create the record if the object can not be found. Eg.
Model.findOrCreate( findCriteria , recordToCreate , [callback] )

Sequelize MySQL Model extended as class findOrCreate method fails to create record

Default values don't work in your code because you indicated incorrect option for that. Replace default with defaultValue and it does the trick:

GuildPremium: {
type: DataTypes.BOOLEAN,
allowNull: false,
defaultValue: false
},
DiscordGuildPrefix: {
type: DataTypes.TEXT,
allowNull: false,
defaultValue: process.env.DefaultPrefix
},
GuildActive: {
type: DataTypes.BOOLEAN,
defaultValue: true,
comment: "Bot active in Guild?"
}

See Default values in the official documentation.

Create or Update Sequelize

From the docs, you don't need to query where to perform the update once you have the object. Also, the use of promise should simplify callbacks:

Implementation

function upsert(values, condition) {
return Model
.findOne({ where: condition })
.then(function(obj) {
// update
if(obj)
return obj.update(values);
// insert
return Model.create(values);
})
}

Usage

upsert({ first_name: 'Taku' }, { id: 1234 }).then(function(result){
res.status(200).send({success: true});
});

Note

  1. This operation is not atomic.
  2. Creates 2 network calls.

which means it is advisable to re-think the approach and probably just update values in one network call and either:

  1. Look at the value returned (i.e. rows_affected) and decide what to do.
  2. Return success if update operation succeeds. This is because whether the resource exists is not within this service's responsibility.

Sails findOrCreate returns record already exists if running asynchronously

I have experienced the same issue with a postgres adapter and believe the cause is a race condition. This seems to be a known problem: Waterline Docs: findOrCreate broken with high frequency calls #929

findOrCreate doesn't appear to be atomic, so I think what happens is that the first call executes a find query which returns no results before executing the create operation. If the second call executes its find before the create of the first call has completed, it also return an empty result which triggers another create.

The problem occurs if the create data contains unique values. As the same data will be added twice because two create operations are executed, the second one fails because the unique value already exists in the database. In case there are no values which are marked as unique the result is a duplicate.

A way to address this problem is to catch the error and in the catch block do a find query because we know that the entry exists now, followed by any operations you intended to execute in the first place. Unfortunately though, this approach does become pretty ugly in your example, because of the nested findOrCreate calls which require their own catch blocks each.

Find or Create with Eloquent

Below is the original accepted answer for: Laravel-4

There is already a method findOrFail available in Laravel and when this method is used it throws ModelNotFoundException on fail but in your case you can do it by creating a method in your model, for example, if you have a User model then you just put this function in the model

// Put this in any model and use
// Modelname::findOrCreate($id);
public static function findOrCreate($id)
{
$obj = static::find($id);
return $obj ?: new static;
}

From your controller, you can use

$user =  User::findOrCreate(5);
$user->first_name = 'John';
$user->last_name = 'Doe';
$user->save();

If a user with id of 5 exists, then it'll be updated, otherwise a new user will be created but the id will be last_user_id + 1 (auto incremented).

This is another way to do the same thing:

public function scopeFindOrCreate($query, $id)
{
$obj = $query->find($id);
return $obj ?: new static;
}

Instead of creating a static method, you can use a scope in the Model, so the method in the Model will be scopeMethodName and call Model::methodName(), same as you did in the static method, for example

$user =  User::findOrCreate(5);

Update:

The firstOrCreate is available in Laravel 5x, the answer is too old and it was given for Laravel-4.0 in 2013.

In Laravel 5.3, the firstOrCreate method has the following declaration:

public function firstOrCreate(array $attributes, array $values = [])

Which means you can use it like this:

User::firstOrCreate(['email' => $email], ['name' => $name]);

User's existence will be only checked via email, but when created, the new record will save both email and name.

API Docs

Rails find_or_create_by more than one attribute?

Multiple attributes can be connected with an and:

GroupMember.find_or_create_by_member_id_and_group_id(4, 7)

(use find_or_initialize_by if you don't want to save the record right away)

Edit: The above method is deprecated in Rails 4. The new way to do it will be:

GroupMember.where(:member_id => 4, :group_id => 7).first_or_create

and

GroupMember.where(:member_id => 4, :group_id => 7).first_or_initialize

Edit 2: Not all of these were factored out of rails just the attribute specific ones.

https://github.com/rails/rails/blob/4-2-stable/guides/source/active_record_querying.md

Example

GroupMember.find_or_create_by_member_id_and_group_id(4, 7)

became

GroupMember.find_or_create_by(member_id: 4, group_id: 7)

Rails - How to use Find Or Create

Related topic:

find_or_create_by in Rails 3 and updating for creating records

You can extend ActiveRecord with your own update_or_create method (see related topic) and then you can use this

@permission = Permission.update_or_create_by_user_id_and_role_id_and_creator_id(@user.id, 2, current_user.id) do |p|
p.group_id = @group.id
end

Or you can use find_or_create_by... method:

@permission = Permission.find_or_create_by_user_id_and_role_id_and_creator_id(@user.id, 2, current_user.id)
@permission.group = @group
@permission.save


Related Topics



Leave a reply



Submit