Răsfoiți Sursa

HUE-9614 [Deprecation Warning] on_delete will be a required arg for ForeignKey in Django 2.0

ayush.goyal 5 ani în urmă
părinte
comite
fa87482247

+ 17 - 14
apps/beeswax/src/beeswax/models.py

@@ -64,14 +64,15 @@ class QueryHistory(models.Model):
                  (librdbms_dbms.MYSQL, 'MySQL'), (librdbms_dbms.POSTGRESQL, 'PostgreSQL'),
                  (librdbms_dbms.MYSQL, 'MySQL'), (librdbms_dbms.POSTGRESQL, 'PostgreSQL'),
                  (librdbms_dbms.SQLITE, 'sqlite'), (librdbms_dbms.ORACLE, 'oracle'))
                  (librdbms_dbms.SQLITE, 'sqlite'), (librdbms_dbms.ORACLE, 'oracle'))
 
 
-  owner = models.ForeignKey(User, db_index=True)
+  owner = models.ForeignKey(User, on_delete=models.CASCADE, db_index=True)
   query = models.TextField()
   query = models.TextField()
 
 
   last_state = models.IntegerField(db_index=True)
   last_state = models.IntegerField(db_index=True)
   has_results = models.BooleanField(default=False)          # If true, this query will eventually return tabular results.
   has_results = models.BooleanField(default=False)          # If true, this query will eventually return tabular results.
   submission_date = models.DateTimeField(auto_now_add=True)
   submission_date = models.DateTimeField(auto_now_add=True)
   # In case of multi statements in a query, these are the id of the currently running statement
   # In case of multi statements in a query, these are the id of the currently running statement
-  server_id = models.CharField(max_length=1024, null=True)  # Aka secret, only query in the "submitted" state is allowed to have no server_id
+  # Aka secret, only query in the "submitted" state is allowed to have no server_id
+  server_id = models.CharField(max_length=1024, null=True)
   server_guid = models.CharField(max_length=1024, null=True, default=None)
   server_guid = models.CharField(max_length=1024, null=True, default=None)
   statement_number = models.SmallIntegerField(default=0)    # The index of the currently running statement
   statement_number = models.SmallIntegerField(default=0)    # The index of the currently running statement
   operation_type = models.SmallIntegerField(null=True)
   operation_type = models.SmallIntegerField(null=True)
@@ -84,7 +85,8 @@ class QueryHistory(models.Model):
   server_type = models.CharField(max_length=128, help_text=_('Type of the query server.'), default=BEESWAX, choices=SERVER_TYPE)
   server_type = models.CharField(max_length=128, help_text=_('Type of the query server.'), default=BEESWAX, choices=SERVER_TYPE)
   query_type = models.SmallIntegerField(help_text=_('Type of the query.'), default=HQL, choices=((HQL, 'HQL'), (IMPALA, 'IMPALA')))
   query_type = models.SmallIntegerField(help_text=_('Type of the query.'), default=HQL, choices=((HQL, 'HQL'), (IMPALA, 'IMPALA')))
 
 
-  design = models.ForeignKey('SavedQuery', to_field='id', null=True) # Some queries (like read/create table) don't have a design
+  # Some queries (like read/create table) don't have a design
+  design = models.ForeignKey('SavedQuery', on_delete=models.CASCADE, to_field='id', null=True)
   notify = models.BooleanField(default=False)                        # Notify on completion
   notify = models.BooleanField(default=False)                        # Notify on completion
 
 
   is_redacted = models.BooleanField(default=False)
   is_redacted = models.BooleanField(default=False)
@@ -220,14 +222,14 @@ def make_query_context(type, info):
 class HiveServerQueryHistory(QueryHistory):
 class HiveServerQueryHistory(QueryHistory):
   # Map from (thrift) server state
   # Map from (thrift) server state
   STATE_MAP = {
   STATE_MAP = {
-    TOperationState.INITIALIZED_STATE : QueryHistory.STATE.submitted,
-    TOperationState.RUNNING_STATE     : QueryHistory.STATE.running,
-    TOperationState.FINISHED_STATE    : QueryHistory.STATE.available,
-    TOperationState.CANCELED_STATE    : QueryHistory.STATE.failed,
-    TOperationState.CLOSED_STATE      : QueryHistory.STATE.expired,
-    TOperationState.ERROR_STATE       : QueryHistory.STATE.failed,
-    TOperationState.UKNOWN_STATE      : QueryHistory.STATE.failed,
-    TOperationState.PENDING_STATE     : QueryHistory.STATE.submitted,
+    TOperationState.INITIALIZED_STATE: QueryHistory.STATE.submitted,
+    TOperationState.RUNNING_STATE: QueryHistory.STATE.running,
+    TOperationState.FINISHED_STATE: QueryHistory.STATE.available,
+    TOperationState.CANCELED_STATE: QueryHistory.STATE.failed,
+    TOperationState.CLOSED_STATE: QueryHistory.STATE.expired,
+    TOperationState.ERROR_STATE: QueryHistory.STATE.failed,
+    TOperationState.UKNOWN_STATE: QueryHistory.STATE.failed,
+    TOperationState.PENDING_STATE: QueryHistory.STATE.submitted,
   }
   }
 
 
   node_type = HIVE_SERVER2
   node_type = HIVE_SERVER2
@@ -266,7 +268,7 @@ class SavedQuery(models.Model):
   TYPES_MAPPING = {'beeswax': HQL, 'hql': HQL, 'impala': IMPALA, 'rdbms': RDBMS, 'spark': SPARK}
   TYPES_MAPPING = {'beeswax': HQL, 'hql': HQL, 'impala': IMPALA, 'rdbms': RDBMS, 'spark': SPARK}
 
 
   type = models.IntegerField(null=False)
   type = models.IntegerField(null=False)
-  owner = models.ForeignKey(User, db_index=True)
+  owner = models.ForeignKey(User, on_delete=models.CASCADE, db_index=True)
   # Data is a json of dictionary. See the beeswax.design module.
   # Data is a json of dictionary. See the beeswax.design module.
   data = models.TextField(max_length=65536)
   data = models.TextField(max_length=65536)
   name = models.CharField(max_length=80)
   name = models.CharField(max_length=80)
@@ -457,7 +459,7 @@ class Session(models.Model):
   """
   """
   A sessions is bound to a user and an application (e.g. Bob with the Impala application).
   A sessions is bound to a user and an application (e.g. Bob with the Impala application).
   """
   """
-  owner = models.ForeignKey(User, db_index=True)
+  owner = models.ForeignKey(User, on_delete=models.CASCADE, db_index=True)
   status_code = models.PositiveSmallIntegerField()  # ttypes.TStatusCode
   status_code = models.PositiveSmallIntegerField()  # ttypes.TStatusCode
   secret = models.TextField(max_length='100')
   secret = models.TextField(max_length='100')
   guid = models.TextField(max_length='100')
   guid = models.TextField(max_length='100')
@@ -485,7 +487,8 @@ class Session(models.Model):
 
 
 
 
 class QueryHandle(object):
 class QueryHandle(object):
-  def __init__(self, secret=None, guid=None, operation_type=None, has_result_set=None, modified_row_count=None, log_context=None, session_guid=None, session_id=None):
+  def __init__(self, secret=None, guid=None, operation_type=None,
+   has_result_set=None, modified_row_count=None, log_context=None, session_guid=None, session_id=None):
     self.secret = secret
     self.secret = secret
     self.guid = guid
     self.guid = guid
     self.operation_type = operation_type
     self.operation_type = operation_type

+ 6 - 6
apps/jobsub/src/jobsub/models.py

@@ -37,7 +37,7 @@ class JobDesign(models.Model):
 
 
   Contains CMS information for "job designs".
   Contains CMS information for "job designs".
   """
   """
-  owner = models.ForeignKey(User)
+  owner = models.ForeignKey(User, on_delete=models.CASCADE)
   name = models.CharField(max_length=40)
   name = models.CharField(max_length=40)
   description = models.CharField(max_length=1024)
   description = models.CharField(max_length=1024)
   last_modified = models.DateTimeField(auto_now=True)
   last_modified = models.DateTimeField(auto_now=True)
@@ -98,7 +98,7 @@ class OozieAction(models.Model):
   reference. See
   reference. See
   https://docs.djangoproject.com/en/dev/topics/db/models/#multi-table-inheritance
   https://docs.djangoproject.com/en/dev/topics/db/models/#multi-table-inheritance
   """
   """
-  PARAM_FIELDS = ( )    # Nothing is parameterized by default
+  PARAM_FIELDS = ()    # Nothing is parameterized by default
 
 
   # This allows the code to easily figure out which subclass to access
   # This allows the code to easily figure out which subclass to access
   action_type = models.CharField(max_length=64, blank=False)
   action_type = models.CharField(max_length=64, blank=False)
@@ -129,7 +129,7 @@ class OozieDesign(models.Model):
   stored in the Oozie*Action models.
   stored in the Oozie*Action models.
   """
   """
   # Generic stuff
   # Generic stuff
-  owner = models.ForeignKey(User)
+  owner = models.ForeignKey(User, on_delete=models.CASCADE)
   name = models.CharField(max_length=64, blank=False,
   name = models.CharField(max_length=64, blank=False,
       help_text=_('Name of the design, which must be unique per user.'))
       help_text=_('Name of the design, which must be unique per user.'))
   description = models.CharField(max_length=1024, blank=True)
   description = models.CharField(max_length=1024, blank=True)
@@ -138,7 +138,7 @@ class OozieDesign(models.Model):
   # Action. Avoid using `root_action' directly, because it only gives you the
   # Action. Avoid using `root_action' directly, because it only gives you the
   # intermediate table (i.e. OozieAction). You want to use `get_root_action()'
   # intermediate table (i.e. OozieAction). You want to use `get_root_action()'
   # most of the time.
   # most of the time.
-  root_action = models.ForeignKey(OozieAction)
+  root_action = models.ForeignKey(OozieAction, on_delete=models.CASCADE)
 
 
   def get_root_action(self):
   def get_root_action(self):
     """Return the concrete action object, not just a generic OozieAction"""
     """Return the concrete action object, not just a generic OozieAction"""
@@ -260,7 +260,7 @@ class JobHistory(models.Model):
 
 
   Contains informatin on submitted jobs/workflows.
   Contains informatin on submitted jobs/workflows.
   """
   """
-  owner = models.ForeignKey(User)
+  owner = models.ForeignKey(User, on_delete=models.CASCADE)
   submission_date = models.DateTimeField(auto_now=True)
   submission_date = models.DateTimeField(auto_now=True)
   job_id = models.CharField(max_length=128)
   job_id = models.CharField(max_length=128)
-  design = models.ForeignKey(OozieDesign)
+  design = models.ForeignKey(OozieDesign, on_delete=models.CASCADE)

+ 179 - 83
apps/oozie/src/oozie/models.py

@@ -28,7 +28,7 @@ import sys
 import time
 import time
 import zipfile
 import zipfile
 
 
-from datetime import datetime,  timedelta
+from datetime import datetime, timedelta
 from string import Template
 from string import Template
 from itertools import chain
 from itertools import chain
 
 
@@ -120,7 +120,13 @@ class Job(models.Model):
   """
   """
   Base class for Oozie Workflows, Coordinators and Bundles.
   Base class for Oozie Workflows, Coordinators and Bundles.
   """
   """
-  owner = models.ForeignKey(User, db_index=True, verbose_name=_t('Owner'), help_text=_t('Person who can modify the job.')) # Deprecated
+  owner = models.ForeignKey(
+    User,
+    on_delete=models.CASCADE,
+    db_index=True,
+    verbose_name=_t('Owner'),
+    help_text=_t('Person who can modify the job.')
+  ) # Deprecated
   name = models.CharField(max_length=255, blank=False, validators=[name_validator], # Deprecated
   name = models.CharField(max_length=255, blank=False, validators=[name_validator], # Deprecated
       help_text=_t('Name of the job, which must be unique per user.'), verbose_name=_t('Name'))
       help_text=_t('Name of the job, which must be unique per user.'), verbose_name=_t('Name'))
   description = models.CharField(max_length=1024, blank=True, verbose_name=_t('Description'), # Deprecated
   description = models.CharField(max_length=1024, blank=True, verbose_name=_t('Description'), # Deprecated
@@ -340,8 +346,8 @@ class WorkflowManager(models.Manager):
 
 
 class Workflow(Job):
 class Workflow(Job):
   is_single = models.BooleanField(default=False)
   is_single = models.BooleanField(default=False)
-  start = models.ForeignKey('Start', related_name='start_workflow', blank=True, null=True)
-  end  = models.ForeignKey('End', related_name='end_workflow',  blank=True, null=True)
+  start = models.ForeignKey('Start', on_delete=models.CASCADE, related_name='start_workflow', blank=True, null=True)
+  end = models.ForeignKey('End', on_delete=models.CASCADE, related_name='end_workflow', blank=True, null=True)
   job_xml = models.CharField(max_length=PATH_MAX, default='', blank=True, verbose_name=_t('Job XML'),
   job_xml = models.CharField(max_length=PATH_MAX, default='', blank=True, verbose_name=_t('Job XML'),
                              help_text=_t('Refer to a Hadoop JobConf job.xml file bundled in the workflow deployment directory. '
                              help_text=_t('Refer to a Hadoop JobConf job.xml file bundled in the workflow deployment directory. '
                                           'Properties specified in the Job Properties element override properties specified in the '
                                           'Properties specified in the Job Properties element override properties specified in the '
@@ -554,7 +560,9 @@ class Workflow(Job):
     actions_index = dict([(action.name, action) for action in actions])
     actions_index = dict([(action.name, action) for action in actions])
     controls_index = dict([(control.name.strip(':'), control) for control in controls])
     controls_index = dict([(control.name.strip(':'), control) for control in controls])
 
 
-    return django_mako.render_to_string(template, {'nodes': self.get_hierarchy(), 'index': index, 'actions': actions_index, 'controls': controls_index})
+    return django_mako.render_to_string(
+      template, {'nodes': self.get_hierarchy(), 'index': index, 'actions': actions_index, 'controls': controls_index}
+    )
 
 
   @classmethod
   @classmethod
   def gen_status_graph_from_xml(cls, user, oozie_workflow):
   def gen_status_graph_from_xml(cls, user, oozie_workflow):
@@ -566,7 +574,7 @@ class Workflow(Job):
         workflow.save()
         workflow.save()
 
 
         import_workflow(workflow, oozie_workflow.definition)
         import_workflow(workflow, oozie_workflow.definition)
-        graph =  workflow.gen_status_graph(oozie_workflow)
+        graph = workflow.gen_status_graph(oozie_workflow)
         node_list = workflow.node_list
         node_list = workflow.node_list
         workflow.delete(skip_trash=True)
         workflow.delete(skip_trash=True)
         return graph, node_list
         return graph, node_list
@@ -630,8 +638,8 @@ class Link(models.Model):
   # Links to exclude when using get_children_link(), get_parent_links() in the API
   # Links to exclude when using get_children_link(), get_parent_links() in the API
   META_LINKS = ('related',)
   META_LINKS = ('related',)
 
 
-  parent = models.ForeignKey('Node', related_name='child_node')
-  child = models.ForeignKey('Node', related_name='parent_node', verbose_name='')
+  parent = models.ForeignKey('Node', on_delete=models.CASCADE, related_name='child_node')
+  child = models.ForeignKey('Node', on_delete=models.CASCADE, related_name='parent_node', verbose_name='')
 
 
   name = models.CharField(max_length=40)
   name = models.CharField(max_length=40)
   comment = models.CharField(max_length=1024, default='', blank=True)
   comment = models.CharField(max_length=1024, default='', blank=True)
@@ -662,7 +670,7 @@ class Node(models.Model):
                                  help_text=_t('The purpose of the action.'))
                                  help_text=_t('The purpose of the action.'))
   node_type = models.CharField(max_length=64, blank=False, verbose_name=_t('Type'),
   node_type = models.CharField(max_length=64, blank=False, verbose_name=_t('Type'),
                                help_text=_t('The type of action (e.g. MapReduce, Pig...)'))
                                help_text=_t('The type of action (e.g. MapReduce, Pig...)'))
-  workflow = models.ForeignKey(Workflow)
+  workflow = models.ForeignKey(Workflow, on_delete=models.CASCADE)
   children = models.ManyToManyField('self', related_name='parents', symmetrical=False, through=Link)
   children = models.ManyToManyField('self', related_name='parents', symmetrical=False, through=Link)
   data = models.TextField(blank=True, default=json.dumps({}))
   data = models.TextField(blank=True, default=json.dumps({}))
 
 
@@ -871,8 +879,11 @@ class Mapreduce(Action):
       help_text=_t('List of names or paths of the archives to be added to the distributed cache.'))
       help_text=_t('List of names or paths of the archives to be added to the distributed cache.'))
   job_properties = models.TextField(default='[]', verbose_name=_t('Hadoop job properties'),
   job_properties = models.TextField(default='[]', verbose_name=_t('Hadoop job properties'),
                                     help_text=_t('For the job configuration (e.g. mapred.job.queue.name=production)'))
                                     help_text=_t('For the job configuration (e.g. mapred.job.queue.name=production)'))
-  jar_path = models.CharField(max_length=PATH_MAX, verbose_name=_t('Jar name'),
-                              help_text=_t('Name or path to the %(program)s jar file on HDFS. E.g. examples.jar.') % {'program': 'MapReduce'})
+  jar_path = models.CharField(
+    max_length=PATH_MAX,
+    verbose_name=_t('Jar name'),
+    help_text=_t('Name or path to the %(program)s jar file on HDFS. E.g. examples.jar.') % {'program': 'MapReduce'}
+  )
   prepares = models.TextField(default="[]", verbose_name=_t('Prepares'),
   prepares = models.TextField(default="[]", verbose_name=_t('Prepares'),
                               help_text=_t('List of absolute paths to delete and then to create before starting the application. '
                               help_text=_t('List of absolute paths to delete and then to create before starting the application. '
                                            'This should be used exclusively for directory cleanup.'))
                                            'This should be used exclusively for directory cleanup.'))
@@ -948,11 +959,14 @@ class Java(Action):
                              help_text=_t('Refer to a Hadoop JobConf job.xml file bundled in the workflow deployment directory. '
                              help_text=_t('Refer to a Hadoop JobConf job.xml file bundled in the workflow deployment directory. '
                                           'Properties specified in the Job Properties element override properties specified in the '
                                           'Properties specified in the Job Properties element override properties specified in the '
                                           'files specified in the Job XML element.'))
                                           'files specified in the Job XML element.'))
-  capture_output = models.BooleanField(default=False, verbose_name=_t('Capture output'),
-                              help_text=_t('Capture output of the stdout of the %(program)s command execution. The %(program)s '
-                                           'command output must be in Java Properties file format and it must not exceed 2KB. '
-                                           'From within the workflow definition, the output of an %(program)s action node is accessible '
-                                           'via the String action:output(String node, String key) function') % {'program': node_type.title()})
+  capture_output = models.BooleanField(
+    default=False,
+    verbose_name=_t('Capture output'),
+    help_text=_t('Capture output of the stdout of the %(program)s command execution. The %(program)s '
+                  'command output must be in Java Properties file format and it must not exceed 2KB. '
+                  'From within the workflow definition, the output of an %(program)s action node is accessible '
+                  'via the String action:output(String node, String key) function') % {'program': node_type.title()}
+  )
 
 
   def get_properties(self):
   def get_properties(self):
     return json.loads(self.job_properties)
     return json.loads(self.job_properties)
@@ -1009,10 +1023,17 @@ class Hive(Action):
   PARAM_FIELDS = ('files', 'archives', 'job_properties', 'params', 'prepares', 'sla', 'credentials')
   PARAM_FIELDS = ('files', 'archives', 'job_properties', 'params', 'prepares', 'sla', 'credentials')
   node_type = 'hive'
   node_type = 'hive'
 
 
-  script_path = models.CharField(max_length=256, blank=False, verbose_name=_t('Script name'),
-                                 help_text=_t('Script name or path to the %(type)s script. E.g. my_script.sql.') % {'type': node_type.title()})
-  params = models.TextField(default="[]", verbose_name=_t('Parameters'),
-                            help_text=_t('The %(type)s parameters of the script. E.g. N=5, INPUT=${inputDir}')  % {'type': node_type.title()})
+  script_path = models.CharField(
+    max_length=256,
+    blank=False,
+    verbose_name=_t('Script name'),
+    help_text=_t('Script name or path to the %(type)s script. E.g. my_script.sql.') % {'type': node_type.title()}
+  )
+  params = models.TextField(
+    default="[]",
+    verbose_name=_t('Parameters'),
+    help_text=_t('The %(type)s parameters of the script. E.g. N=5, INPUT=${inputDir}')  % {'type': node_type.title()}
+  )
   files = models.TextField(default="[]", verbose_name=_t('Files'),
   files = models.TextField(default="[]", verbose_name=_t('Files'),
       help_text=_t('List of names or paths of files to be added to the distributed cache and the task running directory.'))
       help_text=_t('List of names or paths of files to be added to the distributed cache and the task running directory.'))
   archives = models.TextField(default="[]", verbose_name=_t('Archives'),
   archives = models.TextField(default="[]", verbose_name=_t('Archives'),
@@ -1023,8 +1044,14 @@ class Hive(Action):
   prepares = models.TextField(default="[]", verbose_name=_t('Prepares'),
   prepares = models.TextField(default="[]", verbose_name=_t('Prepares'),
                               help_text=_t('List of absolute paths to delete, then create, before starting the application. '
                               help_text=_t('List of absolute paths to delete, then create, before starting the application. '
                                            'This should be used exclusively for directory cleanup.'))
                                            'This should be used exclusively for directory cleanup.'))
-  job_xml = models.CharField(max_length=PATH_MAX, default='hive-config.xml', blank=True, verbose_name=_t('Job XML'),
-                             help_text=_t('Refer to a Hive hive-config.xml file bundled in the workflow deployment directory. Pick a name different than hive-site.xml.'))
+  job_xml = models.CharField(
+    max_length=PATH_MAX,
+    default='hive-config.xml',
+    blank=True,
+    verbose_name=_t('Job XML'),
+    help_text=_t('Refer to a Hive hive-config.xml file bundled in the workflow deployment directory. '
+                 'Pick a name different than hive-site.xml.')
+  )
 
 
   def get_properties(self):
   def get_properties(self):
     return json.loads(self.job_properties)
     return json.loads(self.job_properties)
@@ -1046,9 +1073,13 @@ class Sqoop(Action):
   PARAM_FIELDS = ('files', 'archives', 'job_properties', 'params', 'prepares', 'sla', 'credentials')
   PARAM_FIELDS = ('files', 'archives', 'job_properties', 'params', 'prepares', 'sla', 'credentials')
   node_type = 'sqoop'
   node_type = 'sqoop'
 
 
-  script_path = models.TextField(blank=True, verbose_name=_t('Command'), default='',
-                                 help_text=_t('The full %(type)s command. Either put it here or split it by spaces and insert the parts as multiple parameters below.')
-                                             % {'type': node_type.title()})
+  script_path = models.TextField(
+    blank=True,
+    verbose_name=_t('Command'),
+    default='',
+    help_text=_t('The full %(type)s command. Either put it here or split it by spaces and insert the parts as multiple parameters below.')
+                % {'type': node_type.title()}
+  )
   params = models.TextField(default="[]", verbose_name=_t('Parameters'),
   params = models.TextField(default="[]", verbose_name=_t('Parameters'),
                             help_text=_t('If no command is specified, split the command by spaces and insert the %(type)s parameters '
                             help_text=_t('If no command is specified, split the command by spaces and insert the %(type)s parameters '
                                          'here e.g. import, --connect, jdbc:hsqldb:file:db.hsqldb, ...') % {'type': node_type.title()})
                                          'here e.g. import, --connect, jdbc:hsqldb:file:db.hsqldb, ...') % {'type': node_type.title()})
@@ -1095,11 +1126,14 @@ class Ssh(Action):
                              help_text=_t('The command that will be executed.'))
                              help_text=_t('The command that will be executed.'))
   params = models.TextField(default="[]", verbose_name=_t('Arguments'),
   params = models.TextField(default="[]", verbose_name=_t('Arguments'),
                             help_text=_t('The arguments of the %(type)s command.')  % {'type': node_type.title()})
                             help_text=_t('The arguments of the %(type)s command.')  % {'type': node_type.title()})
-  capture_output = models.BooleanField(default=False, verbose_name=_t('Capture output'),
-                              help_text=_t('Capture output of the stdout of the %(program)s command execution. The %(program)s '
-                                           'command output must be in Java properties file format and it must not exceed 2KB. '
-                                           'From within the workflow definition, the output of an %(program)s action node is accessible '
-                                           'via the String action:output(String node, String key) function') % {'program': node_type.title()})
+  capture_output = models.BooleanField(
+    default=False,
+    verbose_name=_t('Capture output'),
+    help_text=_t('Capture output of the stdout of the %(program)s command execution. The %(program)s '
+                  'command output must be in Java properties file format and it must not exceed 2KB. '
+                  'From within the workflow definition, the output of an %(program)s action node is accessible '
+                  'via the String action:output(String node, String key) function') % {'program': node_type.title()}
+  )
 
 
   def get_params(self):
   def get_params(self):
     return json.loads(self.params)
     return json.loads(self.params)
@@ -1126,11 +1160,14 @@ class Shell(Action):
                              help_text=_t('Refer to a Hadoop JobConf job.xml file bundled in the workflow deployment directory. '
                              help_text=_t('Refer to a Hadoop JobConf job.xml file bundled in the workflow deployment directory. '
                                           'Properties specified in the Job Properties element override properties specified in the '
                                           'Properties specified in the Job Properties element override properties specified in the '
                                           'files specified in the Job XML element.'))
                                           'files specified in the Job XML element.'))
-  capture_output = models.BooleanField(default=False, verbose_name=_t('Capture output'),
-                              help_text=_t('Capture output of the stdout of the %(program)s command execution. The %(program)s '
-                                           'command output must be in Java Properties file format and it must not exceed 2KB. '
-                                           'From within the workflow definition, the output of an %(program)s action node is accessible '
-                                           'via the String action:output(String node, String key) function') % {'program': node_type.title()})
+  capture_output = models.BooleanField(
+    default=False,
+    verbose_name=_t('Capture output'),
+    help_text=_t('Capture output of the stdout of the %(program)s command execution. The %(program)s '
+                  'command output must be in Java Properties file format and it must not exceed 2KB. '
+                  'From within the workflow definition, the output of an %(program)s action node is accessible '
+                  'via the String action:output(String node, String key) function') % {'program': node_type.title()}
+  )
 
 
   def get_properties(self):
   def get_properties(self):
     return json.loads(self.job_properties)
     return json.loads(self.job_properties)
@@ -1152,9 +1189,12 @@ class DistCp(Action):
   PARAM_FIELDS = ('job_properties', 'params', 'prepares', 'sla', 'credentials')
   PARAM_FIELDS = ('job_properties', 'params', 'prepares', 'sla', 'credentials')
   node_type = 'distcp'
   node_type = 'distcp'
 
 
-  params = models.TextField(default="[]", verbose_name=_t('Arguments'),
-                            help_text=_t('The arguments of the %(type)s command. Put options first, then source paths, then destination path.')
-                                        % {'type': node_type.title()})
+  params = models.TextField(
+    default="[]",
+    verbose_name=_t('Arguments'),
+    help_text=_t('The arguments of the %(type)s command. Put options first, then source paths, then destination path.')
+                % {'type': node_type.title()}
+  )
   job_properties = models.TextField(default='[]', verbose_name=_t('Hadoop job properties'),
   job_properties = models.TextField(default='[]', verbose_name=_t('Hadoop job properties'),
                                     help_text=_t('For the job configuration (e.g. mapred.job.queue.name=production'))
                                     help_text=_t('For the job configuration (e.g. mapred.job.queue.name=production'))
   prepares = models.TextField(default="[]", verbose_name=_t('Prepares'),
   prepares = models.TextField(default="[]", verbose_name=_t('Prepares'),
@@ -1188,9 +1228,13 @@ class Fs(Action):
                                          'If the directory already exist it does a no-op.'))
                                          'If the directory already exist it does a no-op.'))
   moves = models.TextField(default="[]", verbose_name=_t('Move file'), blank=True,
   moves = models.TextField(default="[]", verbose_name=_t('Move file'), blank=True,
                             help_text=_t('Move a file or directory to another path.'))
                             help_text=_t('Move a file or directory to another path.'))
-  chmods = models.TextField(default="[]", verbose_name=_t('Change permissions'), blank=True,
-                            help_text=_t('Change the permissions for the specified path. Permissions can be specified using the Unix Symbolic '
-                                         'representation (e.g. -rwxrw-rw-) or an octal representation (755).'))
+  chmods = models.TextField(
+    default="[]",
+    verbose_name=_t('Change permissions'),
+    blank=True,
+    help_text=_t('Change the permissions for the specified path. Permissions can be specified using the Unix Symbolic '
+                  'representation (e.g. -rwxrw-rw-) or an octal representation (755).')
+  )
   touchzs = models.TextField(default="[]", verbose_name=_t('Create or touch a file'), blank=True,
   touchzs = models.TextField(default="[]", verbose_name=_t('Create or touch a file'), blank=True,
                             help_text=_t('Creates a zero length file in the specified path if none exists or touch it.'))
                             help_text=_t('Creates a zero length file in the specified path if none exists or touch it.'))
 
 
@@ -1225,12 +1269,22 @@ class SubWorkflow(Action):
   PARAM_FIELDS = ('subworkflow', 'propagate_configuration', 'job_properties', 'sla', 'credentials')
   PARAM_FIELDS = ('subworkflow', 'propagate_configuration', 'job_properties', 'sla', 'credentials')
   node_type = 'subworkflow'
   node_type = 'subworkflow'
 
 
-  sub_workflow = models.ForeignKey(Workflow, default=None, db_index=True, blank=True, null=True, verbose_name=_t('Sub-workflow'),
-                            help_text=_t('The sub-workflow application to include. You must own all the sub-workflows.'))
+  sub_workflow = models.ForeignKey(
+    Workflow,
+    on_delete=models.CASCADE,
+    default=None,
+    db_index=True,
+    blank=True, null=True,
+    verbose_name=_t('Sub-workflow'),
+    help_text=_t('The sub-workflow application to include. You must own all the sub-workflows.')
+  )
   propagate_configuration = models.BooleanField(default=True, verbose_name=_t('Propagate configuration'), blank=True,
   propagate_configuration = models.BooleanField(default=True, verbose_name=_t('Propagate configuration'), blank=True,
                             help_text=_t('If the workflow job configuration should be propagated to the child workflow.'))
                             help_text=_t('If the workflow job configuration should be propagated to the child workflow.'))
-  job_properties = models.TextField(default='[]', verbose_name=_t('Hadoop job properties'),
-                                    help_text=_t('Can be used to specify the job properties that are required to run the child workflow job.'))
+  job_properties = models.TextField(
+    default='[]',
+    verbose_name=_t('Hadoop job properties'),
+    help_text=_t('Can be used to specify the job properties that are required to run the child workflow job.')
+  )
 
 
   def get_properties(self):
   def get_properties(self):
     return json.loads(self.job_properties)
     return json.loads(self.job_properties)
@@ -1240,17 +1294,21 @@ class Generic(Action):
   PARAM_FIELDS = ('xml', 'credentials', 'sla', 'credentials')
   PARAM_FIELDS = ('xml', 'credentials', 'sla', 'credentials')
   node_type = 'generic'
   node_type = 'generic'
 
 
-  xml = models.TextField(default='', verbose_name=_t('XML of the custom action'),
-                         help_text=_t('This will be inserted verbatim in the action %(action)s. '
-                                      'E.g. all the XML content like %(xml_action)s '
-                                      'will be inserted into the action and produce %(full_action)s') % {
-                                      'action': '<action name="email">...</action>',
-                                      'xml_action': '<email><cc>hue@hue.org</cc></email>',
-                                      'full_action': '<action name="email"><email><cc>hue@hue.org</cc></email><ok/><error/></action>'})
+  xml = models.TextField(
+    default='',
+    verbose_name=_t('XML of the custom action'),
+    help_text=_t('This will be inserted verbatim in the action %(action)s. '
+                'E.g. all the XML content like %(xml_action)s '
+                'will be inserted into the action and produce %(full_action)s') % {
+                'action': '<action name="email">...</action>',
+                'xml_action': '<email><cc>hue@hue.org</cc></email>',
+                'full_action': '<action name="email"><email><cc>hue@hue.org<'
+                '/cc></email><ok/><error/></action>'}
+  )
 
 
 
 
-Action.types = (Mapreduce.node_type, Streaming.node_type, Java.node_type, Pig.node_type, Hive.node_type, Sqoop.node_type, Ssh.node_type, Shell.node_type,
-                DistCp.node_type, Fs.node_type, Email.node_type, SubWorkflow.node_type, Generic.node_type)
+Action.types = (Mapreduce.node_type, Streaming.node_type, Java.node_type, Pig.node_type, Hive.node_type, Sqoop.node_type, Ssh.node_type,
+  Shell.node_type, DistCp.node_type, Fs.node_type, Email.node_type, SubWorkflow.node_type, Generic.node_type)
 
 
 
 
 class ControlFlow(Node):
 class ControlFlow(Node):
@@ -1411,13 +1469,19 @@ class Coordinator(Job):
                                                            'data is periodically created.')) # unused
                                                            'data is periodically created.')) # unused
   frequency_unit = models.CharField(max_length=20, choices=FREQUENCY_UNITS, default='days', verbose_name=_t('Frequency unit'),
   frequency_unit = models.CharField(max_length=20, choices=FREQUENCY_UNITS, default='days', verbose_name=_t('Frequency unit'),
                                     help_text=_t('The unit of the rate at which data is periodically created.')) # unused
                                     help_text=_t('The unit of the rate at which data is periodically created.')) # unused
-  timezone = models.CharField(max_length=24, choices=TIMEZONES, default='America/Los_Angeles', verbose_name=_t('Timezone'),
-                              help_text=_t('The timezone of the coordinator. Only used for managing the daylight saving time changes when combining several coordinators.'))
+  timezone = models.CharField(
+    max_length=24,
+    choices=TIMEZONES,
+    default='America/Los_Angeles',
+    verbose_name=_t('Timezone'),
+    help_text=_t('The timezone of the coordinator. '
+      'Only used for managing the daylight saving time changes when combining several coordinators.')
+  )
   start = models.DateTimeField(auto_now=True, verbose_name=_t('Start'),
   start = models.DateTimeField(auto_now=True, verbose_name=_t('Start'),
                                help_text=_t('When to start the first workflow.'))
                                help_text=_t('When to start the first workflow.'))
   end = models.DateTimeField(auto_now=True, verbose_name=_t('End'),
   end = models.DateTimeField(auto_now=True, verbose_name=_t('End'),
                              help_text=_t('When to start the last workflow.'))
                              help_text=_t('When to start the last workflow.'))
-  coordinatorworkflow = models.ForeignKey(Workflow, null=True, verbose_name=_t('Workflow'),
+  coordinatorworkflow = models.ForeignKey(Workflow, on_delete=models.CASCADE, null=True, verbose_name=_t('Workflow'),
                                help_text=_t('The workflow to schedule repeatedly.'))
                                help_text=_t('The workflow to schedule repeatedly.'))
   timeout = models.SmallIntegerField(null=True, blank=True, verbose_name=_t('Timeout'),
   timeout = models.SmallIntegerField(null=True, blank=True, verbose_name=_t('Timeout'),
                                      help_text=_t('Number of minutes the coordinator action will be in '
                                      help_text=_t('Number of minutes the coordinator action will be in '
@@ -1433,11 +1497,19 @@ class Coordinator(Job):
                                               'actions in the coordinator engine. The different execution strategies are \'oldest first\', '
                                               'actions in the coordinator engine. The different execution strategies are \'oldest first\', '
                                               '\'newest first\' and \'last one only\'. A backlog normally happens because of delayed '
                                               '\'newest first\' and \'last one only\'. A backlog normally happens because of delayed '
                                               'input data, concurrency control or because manual re-runs of coordinator jobs.'))
                                               'input data, concurrency control or because manual re-runs of coordinator jobs.'))
-  throttle = models.PositiveSmallIntegerField(null=True, blank=True, choices=FREQUENCY_NUMBERS, verbose_name=_t('Throttle'),
-                                 help_text=_t('The materialization or creation throttle value for its coordinator actions. '
-                                              'Number of maximum coordinator actions that are allowed to be in WAITING state concurrently.'))
-  job_properties = models.TextField(default='[]', verbose_name=_t('Workflow properties'),
-                                    help_text=_t('Additional properties to transmit to the workflow, e.g. limit=100, and EL functions, e.g. username=${coord:user()}'))
+  throttle = models.PositiveSmallIntegerField(
+    null=True,
+    blank=True,
+    choices=FREQUENCY_NUMBERS,
+    verbose_name=_t('Throttle'),
+    help_text=_t('The materialization or creation throttle value for its coordinator actions. '
+                'Number of maximum coordinator actions that are allowed to be in WAITING state concurrently.')
+  )
+  job_properties = models.TextField(
+    default='[]',
+    verbose_name=_t('Workflow properties'),
+    help_text=_t('Additional properties to transmit to the workflow, e.g. limit=100, and EL functions, e.g. username=${coord:user()}')
+  )
 
 
   HUE_ID = 'hue-id-c'
   HUE_ID = 'hue-id-c'
   ICON = 'oozie/art/icon_oozie_coordinator_48.png'
   ICON = 'oozie/art/icon_oozie_coordinator_48.png'
@@ -1459,7 +1531,8 @@ class Coordinator(Job):
     if mapping is None:
     if mapping is None:
       mapping = {}
       mapping = {}
     tmpl = "editor/gen/coordinator.xml.mako"
     tmpl = "editor/gen/coordinator.xml.mako"
-    return re.sub(re.compile('\s*\n+', re.MULTILINE), '\n', django_mako.render_to_string(tmpl, {'coord': self, 'mapping': mapping})).encode('utf-8', 'xmlcharrefreplace')
+    return re.sub(re.compile('\s*\n+', re.MULTILINE), '\n', 
+      django_mako.render_to_string(tmpl, {'coord': self, 'mapping': mapping})).encode('utf-8', 'xmlcharrefreplace')
 
 
   def clone(self, new_owner=None):
   def clone(self, new_owner=None):
     datasets = Dataset.objects.filter(coordinator=self)
     datasets = Dataset.objects.filter(coordinator=self)
@@ -1684,24 +1757,43 @@ class Dataset(models.Model):
                                                            'data is periodically created.'))
                                                            'data is periodically created.'))
   frequency_unit = models.CharField(max_length=20, choices=FREQUENCY_UNITS, default='days', verbose_name=_t('Frequency unit'),
   frequency_unit = models.CharField(max_length=20, choices=FREQUENCY_UNITS, default='days', verbose_name=_t('Frequency unit'),
                                     help_text=_t('The unit of the rate at which data is periodically created.'))
                                     help_text=_t('The unit of the rate at which data is periodically created.'))
-  uri = models.CharField(max_length=1024, default='/data/${YEAR}${MONTH}${DAY}', verbose_name=_t('URI'),
-                         help_text=_t('The URI template that identifies the dataset and can be resolved into concrete URIs to identify a particular '
-                                      'dataset instance. The URI consist of constants (e.g. ${YEAR}/${MONTH}) and '
-                                      'configuration properties (e.g. /home/${USER}/projects/${PROJECT})'))
-  timezone = models.CharField(max_length=24, choices=TIMEZONES, default='America/Los_Angeles', verbose_name=_t('Timezone'),
-                              help_text=_t('The timezone of the dataset. Only used for managing the daylight saving time changes when combining several datasets.'))
+  uri = models.CharField(
+    max_length=1024,
+    default='/data/${YEAR}${MONTH}${DAY}',
+    verbose_name=_t('URI'),
+    help_text=_t('The URI template that identifies the dataset and can be resolved into concrete URIs to identify a particular '
+                'dataset instance. The URI consist of constants (e.g. ${YEAR}/${MONTH}) and '
+                'configuration properties (e.g. /home/${USER}/projects/${PROJECT})')
+  )
+  timezone = models.CharField(
+    max_length=24,
+    choices=TIMEZONES,
+    default='America/Los_Angeles',
+    verbose_name=_t('Timezone'),
+    help_text=_t('The timezone of the dataset. Only used for managing the daylight saving time changes when combining several datasets.')
+  )
   done_flag = models.CharField(max_length=64, blank=True, default='', verbose_name=_t('Done flag'),
   done_flag = models.CharField(max_length=64, blank=True, default='', verbose_name=_t('Done flag'),
                                help_text=_t('The done file for the data set. If the Done flag is not specified, then Oozie '
                                help_text=_t('The done file for the data set. If the Done flag is not specified, then Oozie '
                                             'configures Hadoop to create a _SUCCESS file in the output directory. If Done '
                                             'configures Hadoop to create a _SUCCESS file in the output directory. If Done '
                                             'flag is set to empty, then Coordinator looks for the existence of the directory itself.'))
                                             'flag is set to empty, then Coordinator looks for the existence of the directory itself.'))
-  coordinator = models.ForeignKey(Coordinator, verbose_name=_t('Coordinator'),
+  coordinator = models.ForeignKey(Coordinator, on_delete=models.CASCADE, verbose_name=_t('Coordinator'),
                                   help_text=_t('The coordinator associated with this data.'))
                                   help_text=_t('The coordinator associated with this data.'))
   instance_choice = models.CharField(max_length=10, default='default', verbose_name=_t('Instance type'),
   instance_choice = models.CharField(max_length=10, default='default', verbose_name=_t('Instance type'),
                                help_text=_t('Customize the date instance(s), e.g. define a range of dates, use EL functions...'))
                                help_text=_t('Customize the date instance(s), e.g. define a range of dates, use EL functions...'))
-  advanced_start_instance  = models.CharField(max_length=128, default='0', verbose_name=_t('Start instance'),
-                               help_text=_t('Shift the frequency for gettting past/future start date or enter verbatim the Oozie start instance, e.g. ${coord:current(0)}'))
-  advanced_end_instance  = models.CharField(max_length=128, blank=True, default='0', verbose_name=_t('End instance'),
-                               help_text=_t('Optional: Shift the frequency for gettting past/future end dates or enter verbatim the Oozie end instance.'))
+  advanced_start_instance = models.CharField(
+    max_length=128,
+    default='0',
+    verbose_name=_t('Start instance'),
+    help_text=_t('Shift the frequency for gettting past/future start date or enter verbatim the Oozie start instance, '
+                 'e.g. ${coord:current(0)}')
+  )
+  advanced_end_instance = models.CharField(
+    max_length=128,
+    blank=True,
+    default='0',
+    verbose_name=_t('End instance'),
+    help_text=_t('Optional: Shift the frequency for gettting past/future end dates or enter verbatim the Oozie end instance.')
+  )
 
 
   objects = DatasetManager()
   objects = DatasetManager()
 
 
@@ -1753,23 +1845,27 @@ class Dataset(models.Model):
 class DataInput(models.Model):
 class DataInput(models.Model):
   name = models.CharField(max_length=40, validators=[name_validator], verbose_name=_t('Name of an input variable in the workflow.'),
   name = models.CharField(max_length=40, validators=[name_validator], verbose_name=_t('Name of an input variable in the workflow.'),
                           help_text=_t('The name of the variable of the workflow to automatically fill up.'))
                           help_text=_t('The name of the variable of the workflow to automatically fill up.'))
-  dataset = models.OneToOneField(Dataset, verbose_name=_t('The dataset representing format of the data input.'),
+  dataset = models.OneToOneField(Dataset, on_delete=models.CASCADE, verbose_name=_t('The dataset representing format of the data input.'),
                                  help_text=_t('The pattern of the input data we want to process.'))
                                  help_text=_t('The pattern of the input data we want to process.'))
-  coordinator = models.ForeignKey(Coordinator)
+  coordinator = models.ForeignKey(Coordinator, on_delete=models.CASCADE)
 
 
 
 
 class DataOutput(models.Model):
 class DataOutput(models.Model):
   name = models.CharField(max_length=40, validators=[name_validator], verbose_name=_t('Name of an output variable in the workflow'),
   name = models.CharField(max_length=40, validators=[name_validator], verbose_name=_t('Name of an output variable in the workflow'),
                           help_text=_t('The name of the variable of the workflow to automatically filled up.'))
                           help_text=_t('The name of the variable of the workflow to automatically filled up.'))
-  dataset = models.OneToOneField(Dataset, verbose_name=_t('The dataset representing the format of the data output.'),
-                                 help_text=_t('The pattern of the output data we want to generate.'))
-  coordinator = models.ForeignKey(Coordinator)
+  dataset = models.OneToOneField(
+    Dataset,
+    on_delete=models.CASCADE,
+    verbose_name=_t('The dataset representing the format of the data output.'),
+    help_text=_t('The pattern of the output data we want to generate.')
+  )
+  coordinator = models.ForeignKey(Coordinator, on_delete=models.CASCADE)
 
 
 
 
 class BundledCoordinator(models.Model):
 class BundledCoordinator(models.Model):
-  bundle = models.ForeignKey('Bundle', verbose_name=_t('Bundle'),
+  bundle = models.ForeignKey('Bundle', on_delete=models.CASCADE, verbose_name=_t('Bundle'),
                              help_text=_t('The bundle regrouping all the coordinators.'))
                              help_text=_t('The bundle regrouping all the coordinators.'))
-  coordinator = models.ForeignKey(Coordinator, verbose_name=_t('Coordinator'),
+  coordinator = models.ForeignKey(Coordinator, on_delete=models.CASCADE, verbose_name=_t('Coordinator'),
                                   help_text=_t('The coordinator to batch with other coordinators.'))
                                   help_text=_t('The coordinator to batch with other coordinators.'))
 
 
   parameters = models.TextField(default='[{"name":"oozie.use.system.libpath","value":"true"}]', verbose_name=_t('Parameters'),
   parameters = models.TextField(default='[{"name":"oozie.use.system.libpath","value":"true"}]', verbose_name=_t('Parameters'),
@@ -1901,10 +1997,10 @@ class History(models.Model):
   """
   """
   Contains information on submitted workflows/coordinators.
   Contains information on submitted workflows/coordinators.
   """
   """
-  submitter = models.ForeignKey(User, db_index=True)
+  submitter = models.ForeignKey(User, on_delete=models.CASCADE, db_index=True)
   submission_date = models.DateTimeField(auto_now=True, db_index=True)
   submission_date = models.DateTimeField(auto_now=True, db_index=True)
   oozie_job_id = models.CharField(max_length=128)
   oozie_job_id = models.CharField(max_length=128)
-  job = models.ForeignKey(Job, db_index=True)
+  job = models.ForeignKey(Job, on_delete=models.CASCADE, db_index=True)
   properties = models.TextField()
   properties = models.TextField()
 
 
   objects = HistoryManager()
   objects = HistoryManager()

+ 7 - 1
apps/pig/src/pig/models.py

@@ -33,7 +33,13 @@ from useradmin.models import User
 
 
 
 
 class Document(models.Model):
 class Document(models.Model):
-  owner = models.ForeignKey(User, db_index=True, verbose_name=_t('Owner'), help_text=_t('User who can modify the job.'))
+  owner = models.ForeignKey(
+    User,
+    on_delete=models.CASCADE,
+    db_index=True,
+    verbose_name=_t('Owner'),
+    help_text=_t('User who can modify the job.')
+  )
   is_design = models.BooleanField(default=True, db_index=True, verbose_name=_t('Is a user document, not a document submission.'),
   is_design = models.BooleanField(default=True, db_index=True, verbose_name=_t('Is a user document, not a document submission.'),
                                      help_text=_t('If the document is not a submitted job but a real query, script, workflow.'))
                                      help_text=_t('If the document is not a submitted job but a real query, script, workflow.'))
 
 

+ 28 - 17
apps/search/src/search/models.py

@@ -102,10 +102,11 @@ class Facet(models.Model):
 
 
         date_facets = tuple([
         date_facets = tuple([
            ('facet.date', field_facet['field']),
            ('facet.date', field_facet['field']),
-           ('f.%s.facet.date.start' % field_facet['field'], 'NOW-%(frequency)s%(unit)s/%(rounder)s' % {"frequency": start["frequency"], "unit": start["unit"], "rounder": gap["unit"]}),
+           ('f.%s.facet.date.start' % field_facet['field'], 'NOW-%(frequency)s%(unit)s/%(rounder)s' % {"frequency": start["frequency"],
+            "unit": start["unit"], "rounder": gap["unit"]}),
            ('f.%s.facet.date.end' % field_facet['field'], 'NOW-%(frequency)s%(unit)s' % end),
            ('f.%s.facet.date.end' % field_facet['field'], 'NOW-%(frequency)s%(unit)s' % end),
-           ('f.%s.facet.date.gap' % field_facet['field'], '+%(frequency)s%(unit)s' % gap),]
-        )
+           ('f.%s.facet.date.gap' % field_facet['field'], '+%(frequency)s%(unit)s' % gap),
+        ])
         params += date_facets
         params += date_facets
 
 
     return params
     return params
@@ -237,11 +238,19 @@ class Collection(models.Model):
       help_text=_t('Hue properties (e.g. results by pages number)')
       help_text=_t('Hue properties (e.g. results by pages number)')
   )
   )
 
 
-  facets = models.ForeignKey(Facet)
-  result = models.ForeignKey(Result)
-  sorting = models.ForeignKey(Sorting)
-
-  owner = models.ForeignKey(User, db_index=True, verbose_name=_t('Owner'), help_text=_t('User who created the job.'), default=None, null=True)
+  facets = models.ForeignKey(Facet, on_delete=models.CASCADE)
+  result = models.ForeignKey(Result, on_delete=models.CASCADE)
+  sorting = models.ForeignKey(Sorting, on_delete=models.CASCADE)
+
+  owner = models.ForeignKey(
+    User,
+    on_delete=models.CASCADE,
+    db_index=True,
+    verbose_name=_t('Owner'),
+    help_text=_t('User who created the job.'),
+    default=None,
+    null=True
+  )
 
 
   _ATTRIBUTES = ['collection', 'layout', 'autocomplete']
   _ATTRIBUTES = ['collection', 'layout', 'autocomplete']
   ICON = 'search/art/icon_search_48.png'
   ICON = 'search/art/icon_search_48.png'
@@ -316,7 +325,9 @@ class Collection(models.Model):
       id_field = id_field[0]
       id_field = id_field[0]
 
 
     TEMPLATE = {
     TEMPLATE = {
-      "extracode": escape("<style type=\"text/css\">\nem {\n  font-weight: bold;\n  background-color: yellow;\n}</style>\n\n<script>\n</script>"),
+      "extracode": escape(
+        "<style type=\"text/css\">\nem {\n  font-weight: bold;\n  background-color: yellow;\n}</style>\n\n<script>\n</script>"
+      ),
       "highlighting": [""],
       "highlighting": [""],
       "properties": {"highlighting_enabled": True},
       "properties": {"highlighting_enabled": True},
       "template": """
       "template": """
@@ -417,16 +428,16 @@ class Collection(models.Model):
     props['collection']['template']['extracode'] = escape(self.result.get_extracode())
     props['collection']['template']['extracode'] = escape(self.result.get_extracode())
     props['collection']['template']['isGridLayout'] = False
     props['collection']['template']['isGridLayout'] = False
     props['layout'] = [
     props['layout'] = [
-          {"size":2,"rows":[{"widgets":[]}],"drops":["temp"],"klass":"card card-home card-column span2"},
-          {"size":10,"rows":[{"widgets":[
-              {"size":12,"name":"Grid Results","id":"52f07188-f30f-1296-2450-f77e02e1a5c0","widgetType":"html-resultset-widget",
-               "properties":{},"offset":0,"isLoading":True,"klass":"card card-widget span12"}]
-          }], "drops":["temp"],"klass":"card card-home card-column span10"}
+          {"size": 2, "rows": [{"widgets": []}], "drops": ["temp"], "klass": "card card-home card-column span2"},
+          {"size": 10, "rows": [{"widgets": [
+              {"size": 12, "name": "Grid Results", "id": "52f07188-f30f-1296-2450-f77e02e1a5c0", "widgetType": "html-resultset-widget",
+               "properties": {}, "offset": 0, "isLoading": True, "klass": "card card-widget span12"}]
+          }], "drops": ["temp"], "klass": "card card-home card-column span10"}
      ]
      ]
 
 
     from search.views import _create_facet
     from search.views import _create_facet
 
 
-    props['collection']['facets'] =[]
+    props['collection']['facets'] = []
     facets = self.facets.get_data()
     facets = self.facets.get_data()
 
 
     for facet_id in facets['order']:
     for facet_id in facets['order']:
@@ -435,6 +446,6 @@ class Collection(models.Model):
           props['collection']['facets'].append(
           props['collection']['facets'].append(
               _create_facet({'name': self.name}, user, facet_id, facet['label'], facet['field'], 'facet-widget'))
               _create_facet({'name': self.name}, user, facet_id, facet['label'], facet['field'], 'facet-widget'))
           props['layout'][0]['rows'][0]['widgets'].append({
           props['layout'][0]['rows'][0]['widgets'].append({
-              "size":12,"name": facet['label'], "id":facet_id, "widgetType": "facet-widget",
-              "properties":{},"offset":0,"isLoading":True,"klass":"card card-widget span12"
+              "size": 12, "name": facet['label'], "id": facet_id, "widgetType": "facet-widget",
+              "properties": {}, "offset": 0, "isLoading": True, "klass": "card card-widget span12"
           })
           })

+ 1 - 1
apps/useradmin/src/useradmin/models.py

@@ -82,7 +82,7 @@ class UserProfile(models.Model):
     HUE = 1
     HUE = 1
     EXTERNAL = 2
     EXTERNAL = 2
 
 
-  user = models.OneToOneField(User, unique=True)
+  user = models.OneToOneField(User, on_delete=models.CASCADE, unique=True)
   home_directory = models.CharField(editable=True, max_length=1024, null=True)
   home_directory = models.CharField(editable=True, max_length=1024, null=True)
   creation_method = models.CharField(editable=True, null=False, max_length=64, default=CreationMethod.HUE.name)
   creation_method = models.CharField(editable=True, null=False, max_length=64, default=CreationMethod.HUE.name)
   first_login = models.BooleanField(default=True, verbose_name=_t('First Login'), help_text=_t('If this is users first login.'))
   first_login = models.BooleanField(default=True, verbose_name=_t('First Login'), help_text=_t('If this is users first login.'))

+ 6 - 6
apps/useradmin/src/useradmin/permissions.py

@@ -37,15 +37,15 @@ class LdapGroup(models.Model):
   Groups that come from LDAP originally will have an LdapGroup
   Groups that come from LDAP originally will have an LdapGroup
   record generated at creation time.
   record generated at creation time.
   """
   """
-  group = models.ForeignKey(Group, related_name="group")
+  group = models.ForeignKey(Group, on_delete=models.CASCADE, related_name="group")
 
 
 
 
 class GroupPermission(models.Model):
 class GroupPermission(models.Model):
   """
   """
   Represents the permissions a group has.
   Represents the permissions a group has.
   """
   """
-  group = models.ForeignKey(Group)
-  hue_permission = models.ForeignKey("HuePermission")
+  group = models.ForeignKey(Group, on_delete=models.CASCADE)
+  hue_permission = models.ForeignKey("HuePermission", on_delete=models.CASCADE)
 
 
 
 
 class BasePermission(models.Model):
 class BasePermission(models.Model):
@@ -53,8 +53,8 @@ class BasePermission(models.Model):
   Set of non-object specific permissions that an app supports.
   Set of non-object specific permissions that an app supports.
 
 
   Currently only assign permissions to groups (not users or roles).
   Currently only assign permissions to groups (not users or roles).
-  Could someday support external permissions of Apache Ranger permissions, AWS IAM... This could be done via subclasses or creating new types
-  of connectors.
+  Could someday support external permissions of Apache Ranger permissions, AWS IAM...
+  This could be done via subclasses or creating new types of connectors.
   """
   """
   app = models.CharField(max_length=30)
   app = models.CharField(max_length=30)
   action = models.CharField(max_length=100)
   action = models.CharField(max_length=100)
@@ -74,7 +74,7 @@ class BasePermission(models.Model):
 
 
 
 
 class ConnectorPermission(BasePermission):
 class ConnectorPermission(BasePermission):
-  connector = models.ForeignKey(Connector)
+  connector = models.ForeignKey(Connector, on_delete=models.CASCADE)
 
 
   class Meta(object):
   class Meta(object):
     abstract = True
     abstract = True

+ 16 - 8
desktop/core/src/desktop/models.py

@@ -133,7 +133,7 @@ class UserPreferences(models.Model):
   Holds arbitrary key/value strings.
   Holds arbitrary key/value strings.
   Note: ideally user/jkeu should be unique together.
   Note: ideally user/jkeu should be unique together.
   """
   """
-  user = models.ForeignKey(User)
+  user = models.ForeignKey(User, on_delete=models.CASCADE)
   key = models.CharField(max_length=20)
   key = models.CharField(max_length=20)
   value = models.TextField(max_length=4096)
   value = models.TextField(max_length=4096)
 
 
@@ -183,7 +183,7 @@ class DefaultConfiguration(models.Model):
 
 
   is_default = models.BooleanField(default=False, db_index=True)
   is_default = models.BooleanField(default=False, db_index=True)
   groups = models.ManyToManyField(Group, db_index=True, db_table='defaultconfiguration_groups')
   groups = models.ManyToManyField(Group, db_index=True, db_table='defaultconfiguration_groups')
-  user = models.ForeignKey(User, blank=True, null=True, db_index=True)
+  user = models.ForeignKey(User, on_delete=models.CASCADE, blank=True, null=True, db_index=True)
 
 
   objects = DefaultConfigurationManager()
   objects = DefaultConfigurationManager()
 
 
@@ -307,7 +307,7 @@ class DocumentTag(models.Model):
   """
   """
   Reserved tags can't be manually removed by the user.
   Reserved tags can't be manually removed by the user.
   """
   """
-  owner = models.ForeignKey(User, db_index=True)
+  owner = models.ForeignKey(User, on_delete=models.CASCADE, db_index=True)
   tag = models.SlugField()
   tag = models.SlugField()
 
 
   DEFAULT = 'default' # Always there
   DEFAULT = 'default' # Always there
@@ -637,7 +637,7 @@ class DocumentManager(models.Manager):
 
 
 class Document(models.Model):
 class Document(models.Model):
 
 
-  owner = models.ForeignKey(User, db_index=True, verbose_name=_t('Owner'),
+  owner = models.ForeignKey(User, on_delete=models.CASCADE, db_index=True, verbose_name=_t('Owner'),
                             help_text=_t('User who can own the job.'), related_name='doc_owner')
                             help_text=_t('User who can own the job.'), related_name='doc_owner')
   name = models.CharField(default='', max_length=255)
   name = models.CharField(default='', max_length=255)
   description = models.TextField(default='')
   description = models.TextField(default='')
@@ -648,7 +648,7 @@ class Document(models.Model):
 
 
   tags = models.ManyToManyField(DocumentTag, db_index=True)
   tags = models.ManyToManyField(DocumentTag, db_index=True)
 
 
-  content_type = models.ForeignKey(ContentType)
+  content_type = models.ForeignKey(ContentType, on_delete=models.CASCADE)
   object_id = models.PositiveIntegerField()
   object_id = models.PositiveIntegerField()
   content_object = GenericForeignKey('content_type', 'object_id')
   content_object = GenericForeignKey('content_type', 'object_id')
 
 
@@ -901,7 +901,7 @@ class DocumentPermission(models.Model):
   READ_PERM = 'read'
   READ_PERM = 'read'
   WRITE_PERM = 'write'
   WRITE_PERM = 'write'
 
 
-  doc = models.ForeignKey(Document)
+  doc = models.ForeignKey(Document, on_delete=models.CASCADE)
 
 
   users = models.ManyToManyField(User, db_index=True, db_table='documentpermission_users')
   users = models.ManyToManyField(User, db_index=True, db_table='documentpermission_users')
   groups = models.ManyToManyField(Group, db_index=True, db_table='documentpermission_groups')
   groups = models.ManyToManyField(Group, db_index=True, db_table='documentpermission_groups')
@@ -1133,7 +1133,14 @@ class Document2(models.Model):
   TRASH_DIR = '.Trash'
   TRASH_DIR = '.Trash'
   EXAMPLES_DIR = 'examples'
   EXAMPLES_DIR = 'examples'
 
 
-  owner = models.ForeignKey(User, db_index=True, verbose_name=_t('Owner'), help_text=_t('Creator.'), related_name='doc2_owner')
+  owner = models.ForeignKey(
+    User,
+    on_delete=models.CASCADE,
+    db_index=True,
+    verbose_name=_t('Owner'),
+    help_text=_t('Creator.'),
+    related_name='doc2_owner'
+  )
   name = models.CharField(default='', max_length=255)
   name = models.CharField(default='', max_length=255)
   description = models.TextField(default='')
   description = models.TextField(default='')
   uuid = models.CharField(default=uuid_default, max_length=36, db_index=True)
   uuid = models.CharField(default=uuid_default, max_length=36, db_index=True)
@@ -1145,6 +1152,7 @@ class Document2(models.Model):
   )
   )
   connector = models.ForeignKey(
   connector = models.ForeignKey(
       Connector,
       Connector,
+      on_delete=models.CASCADE,
       verbose_name=_t('Connector'),
       verbose_name=_t('Connector'),
       help_text=_t('Connector.'),
       help_text=_t('Connector.'),
       blank=True,
       blank=True,
@@ -1652,7 +1660,7 @@ class Document2Permission(models.Model):
   LINK_READ_PERM = 'link_read'
   LINK_READ_PERM = 'link_read'
   LINK_WRITE_PERM = 'link_write'
   LINK_WRITE_PERM = 'link_write'
 
 
-  doc = models.ForeignKey(Document2)
+  doc = models.ForeignKey(Document2, on_delete=models.CASCADE)
 
 
   users = models.ManyToManyField(User, db_index=True, db_table='documentpermission2_users')
   users = models.ManyToManyField(User, db_index=True, db_table='documentpermission2_users')
   groups = models.ManyToManyField(Group, db_index=True, db_table='documentpermission2_groups')
   groups = models.ManyToManyField(Group, db_index=True, db_table='documentpermission2_groups')