Need a new relationship type for all attributes are of a subclass type #479

Closed
rickgn opened this Issue Jul 15, 2014 · 9 comments

Comments

Projects
None yet
3 participants

rickgn commented Jul 15, 2014

This is a case where "HasOne" and "HasMany" don't work. What I need is a relationship type that allows for all attributes of a particular subclass. For example:

parent: {

   a: {  "type":  "sub-one" },
   b: {  "type":  "sub-one },
   c: {  "type":  "sub-one" }
  // .. unlimited number of attributes... 
}

The problem here is that I don't know how many attributes there will be. Therefore I cannot create "HasOne" for each type. Also, I cannot create "Has Many" because the JSON input is already defined and I have no control over it. Therefore using a Collection is not an option because it won't serialize or deserialize correctly.

My current hack is that I'm watching the "change" event and triggering a function that iterates over each object that is not of subclass Backbone.RelationalModel, and replacing the attribute with the new, proper subclass object. This is a terrible hack for many obvious reasons.

What I'm looking for is a new relationship type that basically says:
"for each attribute that is an object type, make it a SubModelType of 'sub-one' "

Has anyone heard of something like this? Maybe someone has already done this? If not, I'll be happy to contribute the relationship type towards a future release. But it's always better if someone else has already invented the wheel ;-)

Can you parse the JSON input before it is .set() on the model? You can then treat it as a collection. You can set http://backbonerelational.org/#relations-parse to true on the relation.

I mean, if you do enable parsing on the relation with parse: true, then Model#parse() will be called before the data is set and you can do whatever you with that data.

rickgn commented Jul 15, 2014

Let me be more precise about the use case. The JSON is in JSON-Schema format. Here is a sample:

"user": {
    "type": 'object',
    "properties": {
         firstname: {
        "type": 'string',
        minLength: 2,
        maxLength: 15,
        title: "Last Name",
        "description": "Must be between 2 and 25 characters"
    },
    lastname: {
        "type": 'string',
        minLength: 2,
        maxLength: 25,
        title: "Last Name",
        "description": "Must be between 2 and 25 characters"
    },
    gender: {
        "type": 'string',
        "enum": ['male', 'female'],
        title: "Gender",
        "description": 'Must be either "male" or "female"'
    },
    email: {
        "type": 'string',
        format: 'email',
        title: "Email Address",
        "description": "must be a valid email format"
    },
    password: {
        "type": 'string',
        minLength: 6,
        title: "Password",
        "description": "must be at least 8 characters"
    }

}

The issue is that the "properties" object has direct relations to each of the sub-types, which represent data property types. I have no control over exactly how many properties there will be. Otherwise this would be a 10-second copy-paste some HasOne relationships and I'm done. Unfortunately that's not the case here.

I've been messing with the parse function you described but I haven't gotten it to work yet. It doesn't seem to have an effect in this case.

The real issue is that the reverse relationship is broken. I can parse all day long, but the child object has no parent. So when I try to walk the hierarchy upwards I get stuck at the properties object.

You can totally parse these into an array, which will be set as a
collection of "has many" properties, of which there are many types.

If the parse function is not working for you, step through the code around
there or look at the tests which will demonstrate how it works.

On Tuesday, July 15, 2014, Rick notifications@github.com wrote:

Let me be more precise about the use case. The JSON is in JSON-Schema
format. Here is a sample:

"user": {
"type": 'object',
"properties": {
firstname: {
"type": 'string',
minLength: 2,
maxLength: 15,
title: "Last Name",
"description": "Must be between 2 and 25 characters"
},
lastname: {
"type": 'string',
minLength: 2,
maxLength: 25,
title: "Last Name",
"description": "Must be between 2 and 25 characters"
},
gender: {
"type": 'string',
"enum": ['male', 'female'],
title: "Gender",
"description": 'Must be either "male" or "female"'
},
email: {
"type": 'string',
format: 'email',
title: "Email Address",
"description": "must be a valid email format"
},
password: {
"type": 'string',
minLength: 6,
title: "Password",
"description": "must be at least 8 characters"
}
}

The issue is that the "properties" object has direct relations to each of
the sub-types, which represent data property types. I have no control over
exactly how many properties there will be. Otherwise this would be a
10-second copy-paste some HasOne relationships and I'm done. Unfortunately
that's not the case here.

I've been messing with the parse function you described but I haven't
gotten it to work yet. It doesn't seem to have an effect in this case.

The real issue is that the reverse relationship is broken. I can parse all
day long, but the child object has no parent. So when I try to walk the
hierarchy upwards I get stuck at the properties object.


Reply to this email directly or view it on GitHub
#479 (comment)
.

And each of the user's "has many" properties will also have a reverse
reference to the user. I think I understand your use case and it should
work.

Anyway, off the bed with me.

On Tuesday, July 15, 2014, Dmitry Minkovsky dminkovsky@gmail.com wrote:

You can totally parse these into an array, which will be set as a
collection of "has many" properties, of which there are many types.

If the parse function is not working for you, step through the code around
there or look at the tests which will demonstrate how it works.

On Tuesday, July 15, 2014, Rick <notifications@github.com
javascript:_e(%7B%7D,'cvml','notifications@github.com');> wrote:

Let me be more precise about the use case. The JSON is in JSON-Schema
format. Here is a sample:

"user": {
"type": 'object',
"properties": {
firstname: {
"type": 'string',
minLength: 2,
maxLength: 15,
title: "Last Name",
"description": "Must be between 2 and 25 characters"
},
lastname: {
"type": 'string',
minLength: 2,
maxLength: 25,
title: "Last Name",
"description": "Must be between 2 and 25 characters"
},
gender: {
"type": 'string',
"enum": ['male', 'female'],
title: "Gender",
"description": 'Must be either "male" or "femaleness "'
},
email: {
"type": 'string',
format: 'email',
title: "Email Address",
"description": "must be a valid email format"
},
password: {
"type": 'string',
minLength: 6,
title: "Password",
"description": "must be at least 8 characters"
}
}

The issue is that the "properties" object has direct relations to each of
the sub-types, which represent data property types. I have no control over
exactly how many properties there will be. Otherwise this would be a
10-second copy-paste some HasOne relationships and I'm done. Unfortunately
that's not the case here.

I've been messing with the parse function you described but I haven't
gotten it to work yet. It doesn't seem to have an effect in this case.

The real issue is that the reverse relationship is broken. I can parse
all day long, but the child object has no parent. So when I try to walk the
hierarchy upwards I get stuck at the properties object.


Reply to this email directly or view it on GitHub
#479 (comment)
.

rickgn commented Jul 15, 2014

I mentioned earlier that that HasMany is not an option because the RelationalModel don't serialize back to its original format correctly. I realize that the JSON is a bit odd, but it's a proposed industry standard from W3C and there is no way to work around it.

If I created a collection the output would be:

{
  "properties": [
    {"type": "string"} 
   .... 
  ]
}

But what is needed is:

{
  "properties"{ 

    "name": {"type": "string"} 
   .... 
  }
}

Yes for sure, it will not serialize back. To serialize back you'll need to
wrap toJSON.

Btw, not suggesting you shouldn't contribute. Suggesting ways to solve this
with existing BR.

On Tuesday, July 15, 2014, Rick notifications@github.com wrote:

I mentioned earlier that that HasMany is not an option because the
RelationalModel don't serialize back to its original format correctly. I
realize that the JSON is a bit odd, but it's a proposed industry standard
from W3C and there is no way to work around it.

If I created a collection the output would be:

{
"properties": [
{"type": "string"}
....
]}

But what is needed is:

{
"properties"{

"name": {"type": "string"}

....
}}


Reply to this email directly or view it on GitHub
#479 (comment)
.

rickgn commented Jul 15, 2014

I appreciate the input. I've been using Backbone-Relational for close to a year now and I've created some pretty sophisticated models. Normally it takes me like less than an hour to create an entire model many levels deep (not bragging - it's just be because I've created so many of them recently and it's all copy-paste to me now).

But this problem has me stumped...

So my thought is to start with
Backbone.HasOne = Backbone.Relation.extend({ })

Copy it, and make something like
Backbone.HasAllObjectsAutoJoined = Backbone.Relation.extend({})
using "HasOne" as the reverse relation.

When HasAllObjectsAutoJoined is used, all attributes will attempt to have a one-to-one relationship automatically, provided that the attribute is of type 'object'. Arrays would follow the same rules as "HasMany" if defined. Potentially I could still have other attributes with "HasOne" declared because I could look at all relationships first, try to create them, then everything else that is Object (not child ) would attempt to join via the auto-join.

So maybe the keyword is " HasElse " ?

I tend to believe that if it sounds cool, it should be a feature. Thoughts?

rickgn commented Jul 15, 2014

I discussed the issue with my team a bit and we came up wtih "HasMap" as the appropriate keyword. This is essentially a map object where each key is a direct relationship, and there can be an unlimited number of keys. What needs to happen is as follows:

For each object in related object:

  1. Get a list of each object that is not defined as a 'HasOne or 'HasMany' type elsewhere in the relations array. The difference being that the list of keys comes from the data its self, not the relation definition. Pseudo example:
    keys_to_convert = difference ((this.attributes keys filtered by type 'Object') minus (each key from relations))

  2. Convert each object in the conversion list to an object of type defined in 'relatedModel' in the options. (currently it appears as unparsed "Object" and not "child" type). So at this point there would be no unparsed object types. Only primitives would remain unconverted.

  3. Create a bi-directional 1:1 relationship between parent and child.

I can handle 1 and 2 okay. But I'm hoping I can get some advice on how to accomplish 3. How do I create the relationships? I've read the code for HasOne and HasMany. I just don't understand the fields that the Backbone.Relation object includes. There's very little documentation in this area. From what I can see in the code there are a lot of moving parts in Relations and I'm afraid to miss something critical.

@bpatram bpatram added the enhancement label Mar 26, 2016

@bpatram bpatram closed this Mar 26, 2016

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment