1

I would like to use the same value generated from the autokey plugin in the keystone.List constructor in the schema object I later pass to List.add.

Here's more precisely what I'm trying to do:

var Thing = new keystone.List('Thing', {
    autokey: { path: 'slug', from: 'title', unique: true }
});

Thing.add({
    image: {
        type: Types.LocalFile,
        dest: 'public/images/things/'+Thing.slug,
        prefix: '/images/things/'+Thing.slug+'/',
        format: function( ThingDoc, file ) { return '<img src="/images/things/'+ThingDoc.slug+'/'+file.filename+'" style="max-width:300px" />' }
    }
});

So that of course can't work. How can I re-use whatever the autokeyed slug value is at a later point in my schema?

Incidentally, the reference works in the 'format' function because when that function is called it can just pull a stored value through the instantiated model.

JME
  • 3,592
  • 17
  • 24
jesse_ae
  • 11
  • 1

1 Answers1

1

In your use-case above, it seems that dest and prefix represent generated values that need not be available in the Keystone Admin UI. Mongoose has a feature called virtual attributes which you can use precisely for this use case. Virtual attributes are defined by invoking the .virtual() method of the Mongoose schema. Since Keystone exposes the mongoose schema of any List in its .schema property, you could easily convert dest and prefix into virtual attributes.

In addition to virtual attributes, Mongoose also provides instance methods, from which you can reference any defined virtual attributes. You convert format into an instance method, then simply reference the prefix virtual attribute from within it.

Finally, make sure that you define any virtual attributes and instance methods prior to calling the List's .register() method. Check out the Keystone documentation on schema plugins for more details.

Here's your code example using virtual attributes and instance methods.

Sorry for the long and winded useless explanation above. I didn't realize that dest and prefix were part of the LocalFile schema.

Having a custom path per upload doesn't make much sense to me. A custom/separate path to store a single file doesn't seem very useful, IMHO. However, having a custom filename that defaults to the auto created slug does. Here's how I would do it. Hopefully this works for you.

var Thing = new keystone.List('Thing', {
  autokey: { path: 'slug', from: 'title', unique: true }
  }
});

Thing.add({
    image: {
      type: Types.LocalFile,
      dest: 'public/images/things/',
      prefix: '/images/things/',
      filename: function(item, file) {
        return item.slug;
      },
      format: function(item, file) { 
        return '<img src="' + item.prefix + item.filename + '" style="max-width:300px" />';
      }
});

Thing.register();

NOTE: Mongoose allows you to define both a getter and a setter (using the .get() and .set() methods respectively) for each defined virtual attribute. However, since dest and prefix appear to be read-only in your use-case, I only defined a getter for them in my sample code above.

JME
  • 3,592
  • 17
  • 24
  • I like the idea of using virtuals for these properties, especially since Keystone only uses them for its internal upload processing and an entirely different set of image properties are actually stored. – jesse_ae Jan 23 '15 at 16:28
  • Hit enter once too many times...comment continued: As it is though the virtuals won't work since Keystone explicitly checks for those properties to be set in the image options object. I'm hoping I can convince someone on the Keystone project to open up more fine grain control over these properties with pre/post hooks. Thank you for your thoroughly explained suggestion. – jesse_ae Jan 23 '15 at 16:36
  • You're absolutely correct. I forgot that `dest`, `prefix` and `format` where part of the `LocalFile` schema. Now that I went back and checked the code for `LocalFile` is your intention to store each uploaded file in a separate folder (i.e. one file per folder). This doesn't make sense to me right now. The idea behind `path` and `prefix` is to a single path to store all the files uploaded. I can understand if you want a custom filename. If this is the case you can customize it by setting the `filename` property to a function that returns the custom name. I will update my answer accordingly. – JME Jan 25 '15 at 07:09
  • My reasoning is this: I'm building a product catalog, and each product will have up to 8 product shots (though there's no need to set a limit there). I already use the `filename` property to prefix images for easy lookup so dumping everything in one directory is okay. I want to be able to support hundreds, if not thousands, of products though, so I like the idea of keeping each product's images (and whatever other files might later be associated) separate. I hope that's justification enough for you. Thanks again for your time. – jesse_ae Jan 25 '15 at 16:47
  • I see your point. That is an interesting use case. I will create a pull request to add this feature to Keystone (I will post the progress here). If the PR gets merged I will post it here and edit my answer. – JME Jan 25 '15 at 22:31
  • Thanks a lot! I'll be on the lookout for your PR. – jesse_ae Jan 26 '15 at 17:42
  • A discussion on how to implement this feature is currently underway on [GitHub](https://github.com/keystonejs/keystone/issues/937). – JME Jan 28 '15 at 22:16