Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Models ignore defaultValue #176

Closed
marcusandre opened this issue Feb 26, 2013 · 13 comments
Closed

Models ignore defaultValue #176

marcusandre opened this issue Feb 26, 2013 · 13 comments

Comments

@marcusandre
Copy link

Hello,

when I set a default value in my model definition, it get's ignored while creating a new model.

module.exports = {
  attributes    : {
    username: {
      type: 'STRING',
      defaultValue: 'Batman'
    }
  }
};

Result:

{
  "id":1,
  "createdAt":"2013-02-26T15:26:13.966Z",
  "updatedAt":"2013-02-26T15:26:13.966Z"
}

Is there anything else to define, or do I have to create a Model manually by myself in a controller to get this values triggered.

Thanks.

@mikermcneil
Copy link
Member

Thanks for bringing this up, Marcus-- this bit is unfinished. I was planning on either exactly what you suggested, or even:

attributes: {
  username: {
    type: 'string'
  }
},
defaults: {
  username: 'Batman'
}

What are your thoughts? I think I like your idea better.

@marcusandre
Copy link
Author

Default values for models:

module.exports = {
  attributes: {
    username: {
      type: 'STRING',
      defaultsTo: 'Batman'
    }
  }
};

I think I would prefer it this way, because then the rule is directly attached to it's entity.

I spent a few hours with the framework today, and thought about some things that would be very useful for our models.

Attach functions to model items:

module.exports = {
  attributes: {
    title: 'STRING',
    slug: {
      type: 'STRING',

      // Pass the req.param('slug') to the sluggify function,
      // which will edit and return a new value.
      attach: 'sluggify'
    }
  }
};

// Okay, this should normally work with a callback function, etc.
var sluggify = function(string) {
  // edit the string
  return string;
};

Prevent specific model items

GET /users

{
  username: "Batman",
  email: "bruce@wayne-enterprises.dev",
  password: "sosupersecret",
  hash: "12(3%456/7&89!§$"
}
module.exports = {
  attributes: {
    username: 'STRING',
    // ...
  },

  prevent: {
    password: true,
    hash: true
  }
};

GET /users

{
  username: "Batman",
  email: "bruce@wayne-enterprises.dev"
}

This should only work when the model is directly accessed via GET, because someone might use the password and hash (etc.) for some validation purposes in his controller.

What do you think?

@mikermcneil
Copy link
Member

For anyone new to the framework reading this, let me just clarify that all of this stuff is possible now, you'd just have to write it in a controller.

I love it!

  • Particularly the prevent part.
  • defaultsTo makes sense to me.
  • For attach, I'm on board, I think, I just would need to talk through it a it to make sure I understand.

I think you've uncovered a really powerful concept. I wonder if it would make sense to put it in the controller? The main purpose of this is for when you're using the API scaffolds, right?

So what if we made the controller action declarative, and let you specify the format of the API you'd like to return?

e.g.

/*---------------------
    :: User 
    -> controller
---------------------*/
var UserController = {

    // To trigger this action locally, visit: `http://localhost:port/user/findAll`
    findAll:  {

        // Prevent password and hash from being displayed
        password: false,
        hash: false,

        // Synchronously modify the slug
        // (note that slug isn't a real model attribute-- it doesn't have to be.
        // this is nice for emulating a non-standard API format)
        slug: function () {
            // "this" refers to the model
            return this.name.replace(/ /g,'-');
        },

        // Example of asynchronous usage
        // Does this seem confusing to you?  I didn't want to expose 'res' 
        // because it would be very easy to mess up and send responses multiple times, 
        // since this is happening for every model in the result set
        relationshipToYou: function (req, cb) {

            var thisUsersId = this.id;

            // We can safely assume that the session exists,
            // with the idea we've already validated that in a policy middleware
            var myId = req.session.id;

            User.find(myId).done(function (err,my) {

                // Respond with relationship
                cb(err, _.isObject(my.relationships) && my.relationships[thisUsersId]);
            });
        }
    }

};
module.exports = UserController;

This way, you're doing it for the API call you wanted, but you're not necessarily limiting the output of your models in general. (You can still call User.findAll() and get the password-- which you'd need to anyway to make login work)

@marcusandre
Copy link
Author

So, that's pretty much exactly what I meant.

The fact that certain fields ​​(which were "acquired" by a function) rather have a virtual value in the controller makes more sense actually. Data that we have to know, has to be in the model, and data that we can generate is not important for the model per se, so it won't pollute a database table or something.

The declaration of protected data in your example makes even more sense to me: a model will not be unnecessarily complicated or burdened with tasks that it should not perform.

@mikermcneil
Copy link
Member

One other note here, in case someone else gets to it before me- I think we should support using a view as well. So if a matching view exists for the scaffolded controller action, the data is passed down as models (just like it is now). If no view exists, or accepts: application/json header is set, the JSON api is served instead.

@marcusandre
Copy link
Author

You mean that you can refer to the data of the models right away? (Template)
At first I thought that it's not the job of a model to serve an entire template, but after some deliberation, the model does nothing more than serving it's data. So this could also be very useful.

@mikermcneil
Copy link
Member

The API scaffold handles the asynchronicity for you-- the model isn't
serving that template, it's just that a RESTful API controller scaffold is
being used. (see
https://github.com/balderdashy/sails/blob/master/lib/scaffolds/controller.js
)

So it's just like if you wrote the controller yourself, it's just using the
existing one. And adding this functionality would make it even more
versatile. I think the endgame here is to first reduce, then eliminate
functional controller code altogether, replacing it with declarative
bindings.

On Wed, Feb 27, 2013 at 3:43 AM, Marcus André notifications@github.comwrote:

You mean that you can refer to the data of the models right away?
(Template)
At first I thought that it's not the job of a model to serve an entire
template, but after some deliberation, the model does nothing more than
serving it's data. So this could also be very useful.


Reply to this email directly or view it on GitHubhttps://github.com//issues/176#issuecomment-14164977
.

Mike McNeil
Founder
http://www.linkedin.com/in/mikermcneil/ http://twitter.com/mikermcneil
http://github.com/balderdashy

   C      O
  NFI    DEN
  TIA   L i
  nfo  rma
  tion in
   tended
only for t      he addressee(s).

If you are not the intended recipient, empl
oyee or agent responsible for delivery to the
intended recipient(s), please be aware that
any review, dissemination, use,distribut
ion or copying of this message and its
contents is strictly prohibited. If
you receive this email in error, ple
ase notify the sender and destroy any
paper or electronic copies immediately.

@marcusandre
Copy link
Author

Very interesting, sound's good to me!

What brings me to the next model question: What about optional API namespacing?

Let's prepend models with /api:
api/controllers/userController - GET /user serves a view with all users.
api/models/user - GET /api/user serves json data.

I think this shouldn't be handled in the model itsef, but in one of the configuration files.
Wouldn't that push the API forward in a more flexible way?

Maybe there is already a solution for that.

@mikermcneil
Copy link
Member

Hey marcus, if I'm understanding right, I think you'd want to use the routes file in /config/routes.js

'/api/user': {
  controller: 'user'
}

@mikermcneil
Copy link
Member

@marcusandre Hey Marcus, I'm going to go ahead and merge in defaultsTo and some other quick fixes this weekend and come back to the more robust solution for a 0.9.x release-- there are a lot of really cool things we can do here with this stream concept, but it's going to take me a bit of time to implement them, so I don't want to hold up core features like defaults in the mean time.

@marcusandre
Copy link
Author

Hi Mike, excuse me that it took so long.
I am really looking forward to the defaultTo option, because it makes many things easier.

And while we're on the subject, let's assume our model looks like this:

var person = {
  name: 'STRING'
  email address: 'STRING'
};

exports = person;

Now my question: Is it possible, or wouldn't it be useful, to constrain a model to its predefined attributes only?

Well, if we would create a new user with an email, username and age, the age would still be written to the database. How could we (maybe optionally) prevent this behavior? Like a fixed: true in our model definitions?

Thanks.

@mikermcneil
Copy link
Member

Yeah good thought- if you're using the scaffolds and you pass sails_filter=true as a parameter, it'll ignore attributes not explicitly in your models in create and update calls. But it would be nice to be able to prevent that at a model level. Fixed:true makes sense to me. Anyone else have any thoughts on nomenclature?

Mike's phone

On Mar 5, 2013, at 6:14, Marcus André notifications@github.com wrote:

Hi Mike, excuse me that it took so long.
I am really looking forward to the defaultTo option, because it makes many things easier.

And while we're on the subject, let's assume our model looks like this:

var person = {
name: 'STRING'
email address: 'STRING'
};

exports = person;
Now my question: Is it possible, or wouldn't it be useful, to constrain a model to its predefined attributes only?

Well, if we would create a new user with an email, username and age, the age would still be written to the database. How could we (maybe optionally) prevent this behavior? Like a fixed: true in our model definitions?

Thanks.


Reply to this email directly or view it on GitHub.

@mikermcneil
Copy link
Member

defaultsTo added in 0.8.892

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

No branches or pull requests

2 participants