mirror of
https://github.com/ansible/awx.git
synced 2026-06-11 09:56:20 -02:30
Fix TypeError when update_fields=None in model save methods (#16466)
Django allows passing update_fields=None to model.save() to mean 'update all fields'.
However, kwargs.get('update_fields', []) returns None when the key exists with a None value,
rather than the default empty list.
This caused crashes in mark_field_for_save() and other code that assumes update_fields is
a list when doing membership tests ('field' not in update_fields) or append operations.
Changed pattern from:
update_fields = kwargs.get('update_fields', [])
To:
update_fields = kwargs.get('update_fields') or []
This correctly handles:
- Key missing: get() returns None → or [] gives []
- Key present with None: get() returns None → or [] gives []
- Key present with list: get() returns list → or [] keeps the list
Fixed in 12 locations across 8 model files:
- awx/main/models/base.py (3 instances)
- awx/main/models/unified_jobs.py (2)
- awx/main/models/jobs.py (2)
- awx/main/models/ad_hoc_commands.py
- awx/main/models/inventory.py
- awx/main/models/notifications.py
- awx/main/models/projects.py
- awx/main/models/workflow.py
Fixes task manager crashes with:
TypeError: argument of type 'NoneType' is not iterable
This commit is contained in:
@@ -211,7 +211,7 @@ class AdHocCommand(UnifiedJob, JobNotificationMixin):
|
||||
return AdHocCommand.objects.create(**data)
|
||||
|
||||
def save(self, *args, **kwargs):
|
||||
update_fields = kwargs.get('update_fields', [])
|
||||
update_fields = kwargs.get('update_fields') or []
|
||||
|
||||
def add_to_update_fields(name):
|
||||
if name not in update_fields:
|
||||
|
||||
@@ -177,7 +177,7 @@ class CreatedModifiedModel(BaseModel):
|
||||
)
|
||||
|
||||
def save(self, *args, **kwargs):
|
||||
update_fields = list(kwargs.get('update_fields', []))
|
||||
update_fields = list(kwargs.get('update_fields') or [])
|
||||
# Manually perform auto_now_add and auto_now logic.
|
||||
if not self.pk and not self.created:
|
||||
self.created = now()
|
||||
@@ -207,7 +207,7 @@ class PasswordFieldsModel(BaseModel):
|
||||
new_instance = not bool(self.pk)
|
||||
# If update_fields has been specified, add our field names to it,
|
||||
# if it hasn't been specified, then we're just doing a normal save.
|
||||
update_fields = kwargs.get('update_fields', [])
|
||||
update_fields = kwargs.get('update_fields') or []
|
||||
# When first saving to the database, don't store any password field
|
||||
# values, but instead save them until after the instance is created.
|
||||
# Otherwise, store encrypted values to the database.
|
||||
@@ -322,7 +322,7 @@ class PrimordialModel(HasEditsMixin, CreatedModifiedModel):
|
||||
self._prior_values_store = {}
|
||||
|
||||
def save(self, *args, **kwargs):
|
||||
update_fields = kwargs.get('update_fields', [])
|
||||
update_fields = kwargs.get('update_fields') or []
|
||||
user = get_current_user()
|
||||
if user and not user.id:
|
||||
user = None
|
||||
|
||||
@@ -1149,7 +1149,7 @@ class InventorySource(UnifiedJobTemplate, InventorySourceOptions, CustomVirtualE
|
||||
|
||||
# If update_fields has been specified, add our field names to it,
|
||||
# if it hasn't been specified, then we're just doing a normal save.
|
||||
update_fields = kwargs.get('update_fields', [])
|
||||
update_fields = kwargs.get('update_fields') or []
|
||||
is_new_instance = not bool(self.pk)
|
||||
|
||||
# Set name automatically. Include PK (or placeholder) to make sure the names are always unique.
|
||||
|
||||
@@ -347,7 +347,7 @@ class JobTemplate(
|
||||
return actual_slice_count
|
||||
|
||||
def save(self, *args, **kwargs):
|
||||
update_fields = kwargs.get('update_fields', [])
|
||||
update_fields = kwargs.get('update_fields') or []
|
||||
# if project is deleted for some reason, then keep the old organization
|
||||
# to retain ownership for organization admins
|
||||
if self.project and self.project.organization_id != self.organization_id:
|
||||
@@ -1165,7 +1165,7 @@ class JobHostSummary(CreatedModifiedModel):
|
||||
# if it hasn't been specified, then we're just doing a normal save.
|
||||
if self.host is not None:
|
||||
self.host_name = self.host.name
|
||||
update_fields = kwargs.get('update_fields', [])
|
||||
update_fields = kwargs.get('update_fields') or []
|
||||
self.failed = bool(self.dark or self.failures)
|
||||
update_fields.append('failed')
|
||||
super(JobHostSummary, self).save(*args, **kwargs)
|
||||
|
||||
@@ -99,7 +99,7 @@ class NotificationTemplate(CommonModelNameNotUnique):
|
||||
|
||||
def save(self, *args, **kwargs):
|
||||
new_instance = not bool(self.pk)
|
||||
update_fields = kwargs.get('update_fields', [])
|
||||
update_fields = kwargs.get('update_fields') or []
|
||||
|
||||
# preserve existing notification messages if not overwritten by new messages
|
||||
if not new_instance:
|
||||
|
||||
@@ -367,7 +367,7 @@ class Project(UnifiedJobTemplate, ProjectOptions, ResourceMixin, CustomVirtualEn
|
||||
pre_save_vals = getattr(self, '_prior_values_store', {})
|
||||
# If update_fields has been specified, add our field names to it,
|
||||
# if it hasn't been specified, then we're just doing a normal save.
|
||||
update_fields = kwargs.get('update_fields', [])
|
||||
update_fields = kwargs.get('update_fields') or []
|
||||
self._skip_update = bool(kwargs.pop('skip_update', False))
|
||||
# Create auto-generated local path if project uses SCM.
|
||||
if self.pk and self.scm_type and not self.local_path.startswith('_'):
|
||||
|
||||
@@ -305,7 +305,7 @@ class UnifiedJobTemplate(PolymorphicModel, CommonModelNameNotUnique, ExecutionEn
|
||||
def save(self, *args, **kwargs):
|
||||
# If update_fields has been specified, add our field names to it,
|
||||
# if it hasn't been specified, then we're just doing a normal save.
|
||||
update_fields = kwargs.get('update_fields', [])
|
||||
update_fields = kwargs.get('update_fields') or []
|
||||
# Update status and last_updated fields.
|
||||
if not getattr(_inventory_updates, 'is_updating', False):
|
||||
updated_fields = self._set_status_and_last_job_run(save=False)
|
||||
@@ -877,7 +877,7 @@ class UnifiedJob(
|
||||
"""
|
||||
# If update_fields has been specified, add our field names to it,
|
||||
# if it hasn't been specified, then we're just doing a normal save.
|
||||
update_fields = kwargs.get('update_fields', [])
|
||||
update_fields = kwargs.get('update_fields') or []
|
||||
|
||||
# Get status before save...
|
||||
status_before = self.status or 'new'
|
||||
|
||||
@@ -900,7 +900,7 @@ class WorkflowApproval(UnifiedJob, JobNotificationMixin):
|
||||
return 'workflow_approval_template'
|
||||
|
||||
def save(self, *args, **kwargs):
|
||||
update_fields = list(kwargs.get('update_fields', []))
|
||||
update_fields = list(kwargs.get('update_fields') or [])
|
||||
if self.timeout != 0 and ((not self.pk) or (not update_fields) or ('timeout' in update_fields)):
|
||||
if not self.created: # on creation, created will be set by parent class, so we fudge it here
|
||||
created = now()
|
||||
|
||||
Reference in New Issue
Block a user