GitHub PullRequestReviewCommentEvent at 2014-01-30 15:13:04
Repository: ckan
Actor: seanh
Payload: {"comment":{"url":"https://api.github.com/repos/ckan/ckan/pulls/comments/9308080","id":9308080,"diff_hunk":"@@ -0,0 +1,282 @@\n+============================\n+Adding custom fields to CKAN\n+============================\n+\n+Storing additional metadata for a dataset beyond the default metadata in CKAN\n+is a common scenario. CKAN provides a simple way to do this by allowing you to\n+abitrary key/value pairs against a dataset when creating or updating the\n+dataset, these appear under the \"Additional Information\" section on the web\n+interface and in 'extras' field of the JSON when accessed via in the API.\n+\n+These default extras can only take strings for their keys and values, no\n+validation is applied to the inputs and you cannot make them mandatory or\n+restrict the possible values you can take. By using CKAN's IDatasetForm plugin\n+interface, a CKAN plugin can add custom, first-class metadata fields to CKAN\n+datasets, and can do custom validation of these fields.  In this tutorial we\n+are assuming that you have read the :doc:`/extensions/tutorial`\n+\n+CKAN schemas and validation\n+---------------------------\n+When calls are made to the action functions, either via the web interface or\n+the CKAN API, the parameters passed to the action function usually have an\n+associated schema. For each parameter provided, the schema will contain a list\n+of functions that will take the value of the parameter and run it against each\n+function. These functions can provide validation, that can raise an error if\n+the value fails validation or it may be a converter that provides a lookup\n+for the action function to use.\n+\n+For example, the schemas can allow optional values by using the\n+:func:`~ckan.lib.navl.validators.ignore_missing` validator or check that a\n+dataset exists using :func:`~ckan.logic.validators.package_id_exists`. A list\n+of available validators and converters can be found at the :doc:`validators` \n+and :doc:`converters`.\n+\n+We will customizing these schemas to add our additional fields. The \n+:py:class:`~ckan.plugins.interfaces.IDatasetForm` interface allows us to \n+override the schemas for creation, updating and displaying of datasets.\n+\n+.. autosummary::\n+\n+   ~ckan.plugins.interfaces.IDatasetForm.create_package_schema\n+   ~ckan.plugins.interfaces.IDatasetForm.update_package_schema\n+   ~ckan.plugins.interfaces.IDatasetForm.show_package_schema\n+   ~ckan.plugins.interfaces.IDatasetForm.is_fallback\n+   ~ckan.plugins.interfaces.IDatasetForm.package_types\n+\n+The IDatasetForm also has other additional functions that allow you to\n+provide a custom template to be rendered for the CKAN frontend, but we will\n+not be using them for this tutorial.\n+\n+Adding custom fields to datasets\n+^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^\n+\n+Create a new plugin named ``ckanext-extrafields`` and create a class named\n+``ExampleIDatasetFormPlugins`` inside \n+``ckanext-extrafields/ckanext/extrafields/plugins.py`` that implements the \n+``IDatasetForm`` interface and inherits from ``SingletonPlugin`` and \n+``DefaultDatasetForm``.\n+\n+.. literalinclude:: ../../ckanext/example_idatasetform/plugin_v1.py\n+    :end-before: def create_package_schema(self):\n+\n+Updating the CKAN schema\n+^^^^^^^^^^^^^^^^^^^^^^^^\n+\n+The :py:meth:`~ckan.plugins.interfaces.IDatasetForm.create_package_schema`\n+function is used whenever a new dataset is created, we'll want update the \n+default schema and insert our custom field here.  We will fetch the default\n+schema defined in \n+:py:func:`~ckan.logic.schema.default_create_package_schema` by running \n+:py:meth:`~ckan.plugins.interfaces.IDatasetForm.create_package_schema`'s\n+super function and update it.\n+\n+.. literalinclude:: ../../ckanext/example_idatasetform/plugin_v1.py\n+    :pyobject: ExampleIDatasetFormPlugin.create_package_schema\n+\n+The CKAN schema is a dictionary where the key is the name of the field and the\n+value is a list of validators and converters. Here we have a validator to tell\n+CKAN to not raise a validation error if the value is missing and a converter to\n+convert the value to and save as an extra. We will want to change the\n+:py:meth:`~ckan.plugins.interfaces.IDatasetForm.update_package_schema` function\n+with the same update code.\n+\n+.. literalinclude:: ../../ckanext/example_idatasetform/plugin_v1.py\n+    :pyobject: ExampleIDatasetFormPlugin.update_package_schema\n+\n+The :py:meth:`~ckan.plugins.interfaces.IDatasetForm.show_package_schema` is used\n+when the :py:func:`~ckan.logic.action.get.package_show` action is called, we\n+want the default_show_package_schema to be updated to include our custom field.\n+This time, instead of converting to an an extras field. We want our field to be\n+converted *from* an extras field. So we want to use the\n+:py:meth:`~ckan.logic.converters.convert_from_extras` converter.\n+\n+.. literalinclude:: ../../ckanext/example_idatasetform/plugin_v1.py\n+    :emphasize-lines: 4\n+    :pyobject: ExampleIDatasetFormPlugin.show_package_schema\n+\n+\n+Package types\n+^^^^^^^^^^^^^\n+\n+The :py:meth:`~ckan.plugins.interfaces.IDatasetForm.package_types` function \n+defines a list of package types that this plugin handles. Each package has a \n+field containing it's type. Plugins can register to handle specific types of \n+packages and ignore others. Since our plugin is not for any specific type of \n+package and we want our plugin to be the default handler, we update the plugin\n+code to contain the following:\n+\n+.. literalinclude:: ../../ckanext/example_idatasetform/plugin_v1.py\n+    :lines: 34-\n+\n+Updating templates\n+^^^^^^^^^^^^^^^^^^\n+\n+In order for our new field to be visible on the CKAN front-end, we need to\n+update the templates. Add an additional line to make the plugin implement the\n+IConfigurer interface\n+\n+.. literalinclude:: ../../ckanext/example_idatasetform/plugin_v2.py\n+    :emphasize-lines: 3\n+    :start-after: import ckan.plugins.toolkit as tk\n+    :end-before: def create_package_schema(self):\n+\n+This interface allows to implement a function \n+:py:meth:`~ckan.plugins.interfaces.IDatasetForm.update_config` that allows us\n+to update the CKAN config, in our case we want to add an additional location\n+for CKAN to look for templates. Add the following code to your plugin. \n+\n+.. literalinclude:: ../../ckanext/example_idatasetform/plugin_v2.py\n+    :pyobject: ExampleIDatasetFormPlugin.update_config\n+\n+You will also need to add a directory under your extension directory to store\n+the templates. Create a directory called \n+``ckanext-extrafields/ckanext/extrafields/templates/`` and the subdirectories\n+``ckanext-extrafields/ckanext/extrafields/templates/package/snippets/``.\n+\n+We need to override a few templates in order to get our custom field rendered.\n+Firstly we need to remove the default custom field handling. Create a template\n+file in our templates directory called \n+``package/snippets/package_metadata_fields.html`` containing\n+\n+    \n+.. literalinclude:: ../../ckanext/example_idatasetform/templates/package/snippets/package_metadata_fields.html\n+    :language: jinja\n+    :end-before: {% block package_metadata_fields %}\n+\n+This overrides the custom_fields block with an empty block so the default CKAN\n+custom fields form does not render. Next add a template in our template \n+directory called ``package/snippets/package_basic_fields.html`` containing\n+\n+.. literalinclude:: ../../ckanext/example_idatasetform/templates/package/snippets/package_basic_fields.html\n+    :language: jinja\n+\n+This adds our custom_text field to the editing form. Finally we want to display\n+our custom_text field on the dataset page. Add another file called \n+``package/snippets/additional_info.html`` containing\n+\n+\n+.. literalinclude:: ../../ckanext/example_idatasetform/templates/package/snippets/additional_info.html\n+    :language: jinja\n+\n+This template overrides the the default extras rendering on the dataset page \n+and replaces it to just display our custom field.\n+\n+You're done! Make sure you have your plugin installed and setup as in the \n+`extension/tutorial`. Then run a development server and you should now have \n+an additional field called \"Custom Text\" when displaying and adding/editing a \n+dataset.\n+\n+Cleaning up the code\n+^^^^^^^^^^^^^^^^^^^^\n+\n+Before we continue further, we can clean up the\n+:py:meth:`~ckan.plugins.interfaces.IDatasetForm.create_package_schema`\n+and :py:meth:`~ckan.plugins.interfaces.IDatasetForm.update_package_schema`. \n+There is a bit of duplication that we could remove. Replace the two functions\n+with:\n+\n+.. literalinclude:: ../../ckanext/example_idatasetform/plugin_v3.py\n+    :start-after: p.implements(p.IDatasetForm)\n+    :end-before: def show_package_schema(self):\n+\n+Tag vocabularies\n+----------------\n+If you need to add a custom field where the input options are restrcited to a\n+provide list of options, you can use tag vocabularies `/tag-vocabularies`. We","path":"doc/extensions/adding-custom-fields.rst","position":185,"original_position":185,"commit_id":"5c45078fad938cda4fb4926a738b1d0c5eea10ad","original_commit_id":"5c45078fad938cda4fb4926a738b1d0c5eea10ad","user":{"login":"seanh","id":22498,"avatar_url":"https://gravatar.com/avatar/a1ffa12aad35d18f4463e4d18f9c54d8?d=https%3A%2F%2Fidenticons.github.com%2Ff23bbde4a364522ebe72204971af9e4d.png&r=x","gravatar_id":"a1ffa12aad35d18f4463e4d18f9c54d8","url":"https://api.github.com/users/seanh","html_url":"https://github.com/seanh","followers_url":"https://api.github.com/users/seanh/followers","following_url":"https://api.github.com/users/seanh/following{/other_user}","gists_url":"https://api.github.com/users/seanh/gists{/gist_id}","starred_url":"https://api.github.com/users/seanh/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/seanh/subscriptions","organizations_url":"https://api.github.com/users/seanh/orgs","repos_url":"https://api.github.com/users/seanh/repos","events_url":"https://api.github.com/users/seanh/events{/privacy}","received_events_url":"https://api.github.com/users/seanh/received_events","type":"User","site_admin":false},"body":"missing d","created_at":"2014-01-30T15:13:04Z","updated_at":"2014-01-30T15:13:04Z","html_url":"https://github.com/ckan/ckan/pull/790#discussion_r9308080","pull_request_url":"https://api.github.com/repos/ckan/ckan/pulls/790","_links":{"self":{"href":"https://api.github.com/repos/ckan/ckan/pulls/comments/9308080"},"html":{"href":"https://github.com/ckan/ckan/pull/790#discussion_r9308080"},"pull_request":{"href":"https://api.github.com/repos/ckan/ckan/pulls/790"}}}}