DataMapper wont save entries because of under-the-hood validation - ruby

I need pages to be attached to layouts only when users opting for this.
I mean when users editing pages, there are a dropdown to select layout from.
It works well if some layout selected.
However if user selecting <option value='0'> None option,
DataMapper throw an error saying layout_id should be greater than zero.
I think this should not happen cause i set required: false on belongs_to :layout association.
Here are my models:
class Layout
include DataMapper::Resource
property :id, Serial
property :name, String
end
class Page
include DataMapper::Resource
property :id, Serial
property :name, String
belongs_to :layout, required: false
end

You right about "under-the-hood" validation.
It is automatically added by belong_to association.
And you can get rid of it by redefining layout_id property.
In Page model simply add:
property :layout_id, Integer, index: true
This will keep the association but will redefine layout_id property
so it wont have automatically added validations on it.
However note that this will work only after Page.auto_migrate!
Or you can manually remove foreign key from your pages table.
Also, make sure layout_id is a index, otherwise you'll have performance issues.

Related

Returning an array graphQL

I'm currently trying to learn GraphQL but running into a problem. The problem is possibly not understanding the right way in writing my types. When I had my user type as
module Types
UserType = GraphQL::ObjectType.define do
name 'User'
field :id, !types.ID
field :username, !types.String
field :email, !types.String, property: :email_address
field :workspaces, types[Types::WorkspaceType]
end
end
I'm able to return an array of workspaces without a problem. Now the problem comes when I try to return an array of users that are associated with that workspace. My workspace type looks like this
class Types::WorkspaceType < Types::BaseObject
field :id, ID, null: false
field :user_id, ID, null: false
field :name, String, null: false
field :members, types[Types::UserType]
end
When I try to run the query in fetching the workspace I'm getting an error that says
"error": {
"message": "undefined local variable or method `types' for Types::UserType:Class",
The backend I'm using is Ruby on Rails. For those with some experience with GraphQL, I would love feedback in what I'm doing wrong and also what is the right way in connecting types between each model. If there are any extra information you'll need or want clarification with please feel free to ask.
Thank you.
The graphql gem has two different "styles" of declaring GraphQL objects using a Ruby DSL (and it can also directly import the GraphQL schema language). The older style (now expunged from the docs) uses GraphQL::ObjectType.define, and needs to qualify most references with the types syntax. The newer style uses Ruby class inheritance, and doesn't use types.
In short: I think your code will work if you just change the last field definition to
field :members, [Types::UserType]

How to get aggregate totalling with data from two Models in DataMapper?

I am new to DataMapper and Ruby, and have been struggling to work out a way to format the expression which will give me what I am after. Let me explain. I have two Models in my project:
Class Manufacturer
include DataMapper::Resource
property :id, Serial
property :name, String
...<snip>...
has n, :items
end
Class Item
include DataMapper::Resource
property :id, Serial
property :name, String
...<snip>...
belongs_to :manufacturers
end
What I am trying to get is a collection which will give me the list of all manufacturers, plus a count of all items that they have produced. e.g.:
"Acme Industries", 32
"Bart Enterprises", 12
"Coco Mondo", 0
"XYZ Corp.", 55
That is, the :name from the Manufacturer model, and the count(:id) from the Item model. I've got as far as:
Manufacturer.all.items.aggregate(:manufacturer_id, :all.count)
which gives me the :manufacturer_id property and the correct count of items. Close, but no banana.
How can I get the manufacturer name rather that the id property in this case?
I tried to learn how to use DataMapper a month ago and I could find very limited support in stackoverflow and in the internet. It turns out that the project is not supported any more and the team behind is now developing another orm called ROM.
Since you are in the beginning I would recommend you to use another orm with better support(sequel,ActiveRecords etc)

cascade_callbacks not working for embedded doc in mongoid

I'm using Mongoid, and I have one class (Order) that "embeds_many" of another class (LineItem). Specifically, in Order, I have:
embeds_many :line_items, cascade_callbacks: true
And in LineItem, I have:
embedded_in :order
Everything works as it should except for the cascading_callbacks. When I make a change to an Order object and call "save", I would expect the "after_save" callbacks on all of the embedded LineItem objects to be called. This is not happening. I thought that was the point of cascading_callbacks.
Am I incorrect? Is something else required to make the save-related callbacks fire for each embedded document when the parent is saved and cascade_callbacks is set to true in the relationship?
I think, when you specify cascade callbacks: true in the parent document, the callbacks of the embedded documents are fired only when you make any change in the corresponding embedded document.
To check, change something in the embedded document and save the parent document. In this case I think the callbacks will be fired

Mongoid: How to implement a relationship between embedded documents?

I have a situation where I have a parent document and I want to have two different types of embedded documents: one as a parent, and another as a child with an optional parent. For example:
class ParentDoc
include Mongoid::Document
embeds_many :special_docs
embeds_many :special_doc_groupings
end
class SpecialDoc
include Mongoid::Document
embedded_in :parent_doc
belongs_to :special_doc_groupings
end
class SpecialDocGrouping
include Mongoid::Document
embedded_in :parent_doc
has_many :special_docs
end
In this example, SpecialDocs and SpecialDocGroupings can exist without a relationship, or optionally can have a parent-child relationship.
However, this is an invalid Mongoid association because we get this error:
Mongoid::Errors::MixedRelations:
Problem:
Referencing a(n) SpecialDoc document from the SpecialDocGrouping document via a relational association is not allowed since the SpecialDoc is embedded.
Summary:
In order to properly access a(n) SpecialDoc from SpecialDocGrouping the reference would need to go through the root document of SpecialDoc. In a simple case this would require Mongoid to store an extra foreign key for the root, in more complex cases where SpecialDoc is multiple levels deep a key would need to be stored for each parent up the hierarchy.
Resolution:
Consider not embedding SpecialDoc, or do the key storage and access in a custom manner in the application code.
I don't see anything wrong with the type of association that I'm trying to create, besides the fact that it's not supported by Mongoid.
How can I implement this type of association myself?
The association is not valid because when you reference embedded model Mongoid does not store the parent key as foreign key.
This means that if you have:
Class Parent
embeds_many :children
end
Class Child
embedded_in :parent
end
You cannot reference Child document storing only its foreign key, but you need to store all the parents keys until you reach the root.
In this case the root is represented by the first parent and you need to store 2 keys.
You can accomplish this manually, and create this type of association without any problem.
Your case is a bit different (and easier) because you want to create the association between two models embedded in the same parent. That means theoretically you don't need to store the parent key because the models share the same root.
Mongoid does not handle this scenario, so you need to manually create your association rules, and methods.
Class Bar
embeds_many :beers
embeds_many :glasses
end
Class Beer
embedded_in :bar
# Manual has_many :glasses association
def parent
self.bar
end
def glasses
parent.glasses.where(:beer_id => self.id)
end
end
Class Glass
embedded_in :bar
# Manual belongs_to :beer association
field :beer_id, type: Moped::BSON::ObjectId
def parent
self.bar
end
def beer
parent.beers.find(self.beer_id)
end
end
(the code is not tested)

Validating nested models?

To be more specific, "How do I validate that a model requires at least x valid associated models to be created?". I've been trying to validate nested models that get created in the same form as the parent (and ultimately show immediate validations a la jQuery). As a popular example, lets assume the following models and schema.
class Project
include DataMapper::Resource
property :id, Serial
property :title, String, :nullable => false
has 2..n, :tasks
end
class Task
include DataMapper::Resource
property :id, Serial
property :project_id, Integer, :key => true
property :title, String, :nullable => false
belongs_to :project
end
All the validations are done in the schema definitions, as you can see. The important one here is "has 2..n, :tasks". This validation actually works normally, given that the nested task attributes in the params hash will produce valid tasks. If they produce an invalid task, however, then the task won't get created and you'll end up with a Project that has less than 2 tasks, and thus an invalid project object.
As I understand it, this is because it can't figure out if the task attributes are valid or not until it attempts to save the tasks, and since - as far as I know - the tasks can't get saved before the project, the project is unaware if the tasks will be valid or not. Am I correct in assuming this?
Anyway, I was hoping there would be a quick answer, but it seems a lot less trivial then I'd hoped. If you've got any suggestions at all, that would be greatly appreciated.
I actually found a nice solution here using transactions in DataMapper. Basically this transaction tries to save the parent object as well as all the child objects. As soon as one fails to save, then the transaction stops and nothing gets created. If all goes well, then the objects will save successfully.
class Project
def make
transaction do |trans|
trans.rollback unless save
tasks.each do |task|
unless task.save
trans.rollback
break
end
end
end
end
end
This assures that everything is valid before it anything gets saved. I just needed to change my #save and #update methods to #make in my controller code.
SET CONSTRAINTS DEFERRED might be useful if your database engine supports that.
Otherwise, maybe write a stored procedure to do the inserts, and then say that its the resonsibility of the stored procedure to ensure that only correct, validated data is inserted.
There is a model method valid? that runs the validations on a model object before it is saved. So, the simple way to validate the associations would be to use validates_with_block' or 'validates_with_method to check the validations on the associations.
It would look something like this
validates_with_block do
if #tasks.all?{|t|t.valid?}
true
else
[false, "you have an invalid task"]
end
end
Or you could look at dm-association-validator or dm-accepts-nested-attributes
Edit: for extra crazy. run validations on the tasks, then check to see if the only errors are ones related to the association.
validates_with_block do
if #tasks.all?{|t|t.valid?;!t.errors.any?{|e|e[0]==:project}}
true
else
[false, "you have an invalid task"]
end
end

Resources