Skip to content

Commit a17699d

Browse files
authored
19644 Make atomic use correct database instead of default (#19651)
* 19644 set atomic transactions to appropriate database * 19644 set atomic transactions for Job Script run * 19644 set atomic transactions to appropriate database * 19644 set atomic transactions to appropriate database * 19644 fix review comments * 19644 fix review comments
1 parent f97d07a commit a17699d

File tree

11 files changed

+45
-42
lines changed

11 files changed

+45
-42
lines changed

netbox/circuits/views.py

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
from django.contrib import messages
2-
from django.db import transaction
2+
from django.db import router, transaction
33
from django.shortcuts import get_object_or_404, redirect, render
44
from django.utils.translation import gettext_lazy as _
55

@@ -384,7 +384,7 @@ def post(self, request, pk):
384384

385385
if termination_a and termination_z:
386386
# Use a placeholder to avoid an IntegrityError on the (circuit, term_side) unique constraint
387-
with transaction.atomic():
387+
with transaction.atomic(using=router.db_for_write(CircuitTermination)):
388388
termination_a.term_side = '_'
389389
termination_a.save()
390390
termination_z.term_side = 'A'

netbox/dcim/utils.py

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
from django.apps import apps
22
from django.contrib.contenttypes.models import ContentType
3-
from django.db import transaction
3+
from django.db import router, transaction
44

55

66
def compile_path_node(ct_id, object_id):
@@ -53,7 +53,7 @@ def rebuild_paths(terminations):
5353
for obj in terminations:
5454
cable_paths = CablePath.objects.filter(_nodes__contains=obj)
5555

56-
with transaction.atomic():
56+
with transaction.atomic(using=router.db_for_write(CablePath)):
5757
for cp in cable_paths:
5858
cp.delete()
5959
create_cablepath(cp.origins)

netbox/dcim/views.py

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
from django.contrib import messages
22
from django.contrib.contenttypes.models import ContentType
33
from django.core.paginator import EmptyPage, PageNotAnInteger
4-
from django.db import transaction
4+
from django.db import router, transaction
55
from django.db.models import Prefetch
66
from django.forms import ModelMultipleChoiceField, MultipleHiddenInput, modelformset_factory
77
from django.shortcuts import get_object_or_404, redirect, render
@@ -124,7 +124,7 @@ def post(self, request):
124124

125125
if form.is_valid():
126126

127-
with transaction.atomic():
127+
with transaction.atomic(using=router.db_for_write(Cable)):
128128
count = 0
129129
cable_ids = set()
130130
for obj in self.queryset.filter(pk__in=form.cleaned_data['pk']):
@@ -3746,7 +3746,7 @@ def post(self, request, pk):
37463746

37473747
if vc_form.is_valid() and formset.is_valid():
37483748

3749-
with transaction.atomic():
3749+
with transaction.atomic(using=router.db_for_write(Device)):
37503750

37513751
# Save the VirtualChassis
37523752
vc_form.save()

netbox/extras/jobs.py

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -39,6 +39,9 @@ def run_script(self, script, request, data, commit):
3939

4040
try:
4141
try:
42+
# A script can modify multiple models so need to do an atomic lock on
43+
# both the default database (for non ChangeLogged models) and potentially
44+
# any other database (for ChangeLogged models)
4245
with transaction.atomic():
4346
script.output = script.run(data, commit)
4447
if not commit:

netbox/ipam/api/views.py

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@
22

33
from django.contrib.contenttypes.prefetch import GenericPrefetch
44
from django.core.exceptions import ObjectDoesNotExist, PermissionDenied
5-
from django.db import transaction
5+
from django.db import router, transaction
66
from django.shortcuts import get_object_or_404
77
from django.utils.translation import gettext as _
88
from django_pglocks import advisory_lock
@@ -295,7 +295,7 @@ def post(self, request, pk):
295295

296296
# Create the new IP address(es)
297297
try:
298-
with transaction.atomic():
298+
with transaction.atomic(using=router.db_for_write(self.queryset.model)):
299299
created = serializer.save()
300300
self._validate_objects(created)
301301
except ObjectDoesNotExist:

netbox/netbox/api/viewsets/__init__.py

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@
22
from functools import cached_property
33

44
from django.core.exceptions import ObjectDoesNotExist, PermissionDenied
5-
from django.db import transaction
5+
from django.db import router, transaction
66
from django.db.models import ProtectedError, RestrictedError
77
from django_pglocks import advisory_lock
88
from netbox.constants import ADVISORY_LOCK_KEYS
@@ -170,7 +170,7 @@ def perform_create(self, serializer):
170170

171171
# Enforce object-level permissions on save()
172172
try:
173-
with transaction.atomic():
173+
with transaction.atomic(using=router.db_for_write(model)):
174174
instance = serializer.save()
175175
self._validate_objects(instance)
176176
except ObjectDoesNotExist:
@@ -190,7 +190,7 @@ def perform_update(self, serializer):
190190

191191
# Enforce object-level permissions on save()
192192
try:
193-
with transaction.atomic():
193+
with transaction.atomic(using=router.db_for_write(model)):
194194
instance = serializer.save()
195195
self._validate_objects(instance)
196196
except ObjectDoesNotExist:

netbox/netbox/api/viewsets/mixins.py

Lines changed: 16 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
from django.core.exceptions import ObjectDoesNotExist
2-
from django.db import transaction
2+
from django.db import router, transaction
33
from django.http import Http404
44
from rest_framework import status
55
from rest_framework.response import Response
@@ -56,22 +56,22 @@ class SequentialBulkCreatesMixin:
5656
which depends on the evaluation of existing objects (such as checking for free space within a rack) functions
5757
appropriately.
5858
"""
59-
@transaction.atomic
6059
def create(self, request, *args, **kwargs):
61-
if not isinstance(request.data, list):
62-
# Creating a single object
63-
return super().create(request, *args, **kwargs)
64-
65-
return_data = []
66-
for data in request.data:
67-
serializer = self.get_serializer(data=data)
68-
serializer.is_valid(raise_exception=True)
69-
self.perform_create(serializer)
70-
return_data.append(serializer.data)
60+
with transaction.atomic(using=router.db_for_write(self.queryset.model)):
61+
if not isinstance(request.data, list):
62+
# Creating a single object
63+
return super().create(request, *args, **kwargs)
64+
65+
return_data = []
66+
for data in request.data:
67+
serializer = self.get_serializer(data=data)
68+
serializer.is_valid(raise_exception=True)
69+
self.perform_create(serializer)
70+
return_data.append(serializer.data)
7171

72-
headers = self.get_success_headers(serializer.data)
72+
headers = self.get_success_headers(serializer.data)
7373

74-
return Response(return_data, status=status.HTTP_201_CREATED, headers=headers)
74+
return Response(return_data, status=status.HTTP_201_CREATED, headers=headers)
7575

7676

7777
class BulkUpdateModelMixin:
@@ -113,7 +113,7 @@ def bulk_update(self, request, *args, **kwargs):
113113
return Response(data, status=status.HTTP_200_OK)
114114

115115
def perform_bulk_update(self, objects, update_data, partial):
116-
with transaction.atomic():
116+
with transaction.atomic(using=router.db_for_write(self.queryset.model)):
117117
data_list = []
118118
for obj in objects:
119119
data = update_data.get(obj.id)
@@ -157,7 +157,7 @@ def bulk_destroy(self, request, *args, **kwargs):
157157
return Response(status=status.HTTP_204_NO_CONTENT)
158158

159159
def perform_bulk_destroy(self, objects):
160-
with transaction.atomic():
160+
with transaction.atomic(using=router.db_for_write(self.queryset.model)):
161161
for obj in objects:
162162
if hasattr(obj, 'snapshot'):
163163
obj.snapshot()

netbox/netbox/views/generic/bulk_views.py

Lines changed: 7 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,7 @@
66
from django.contrib.contenttypes.fields import GenericForeignKey, GenericRel
77
from django.contrib.contenttypes.models import ContentType
88
from django.core.exceptions import FieldDoesNotExist, ObjectDoesNotExist, ValidationError
9-
from django.db import transaction, IntegrityError
9+
from django.db import IntegrityError, router, transaction
1010
from django.db.models import ManyToManyField, ProtectedError, RestrictedError
1111
from django.db.models.fields.reverse_related import ManyToManyRel
1212
from django.forms import ModelMultipleChoiceField, MultipleHiddenInput
@@ -278,7 +278,7 @@ def post(self, request):
278278
logger.debug("Form validation was successful")
279279

280280
try:
281-
with transaction.atomic():
281+
with transaction.atomic(using=router.db_for_write(model)):
282282
new_objs = self._create_objects(form, request)
283283

284284
# Enforce object-level permissions
@@ -501,7 +501,7 @@ def post(self, request):
501501

502502
try:
503503
# Iterate through data and bind each record to a new model form instance.
504-
with transaction.atomic():
504+
with transaction.atomic(using=router.db_for_write(model)):
505505
new_objs = self.create_and_update_objects(form, request)
506506

507507
# Enforce object-level permissions
@@ -681,7 +681,7 @@ def post(self, request, **kwargs):
681681
if form.is_valid():
682682
logger.debug("Form validation was successful")
683683
try:
684-
with transaction.atomic():
684+
with transaction.atomic(using=router.db_for_write(model)):
685685
updated_objects = self._update_objects(form, request)
686686

687687
# Enforce object-level permissions
@@ -778,7 +778,7 @@ def post(self, request):
778778

779779
if form.is_valid():
780780
try:
781-
with transaction.atomic():
781+
with transaction.atomic(using=router.db_for_write(self.queryset.model)):
782782
renamed_pks = self._rename_objects(form, selected_objects)
783783

784784
if '_apply' in request.POST:
@@ -875,7 +875,7 @@ def post(self, request, **kwargs):
875875
queryset = self.queryset.filter(pk__in=pk_list)
876876
deleted_count = queryset.count()
877877
try:
878-
with transaction.atomic():
878+
with transaction.atomic(using=router.db_for_write(model)):
879879
for obj in queryset:
880880
# Take a snapshot of change-logged models
881881
if hasattr(obj, 'snapshot'):
@@ -980,7 +980,7 @@ def post(self, request):
980980
}
981981

982982
try:
983-
with transaction.atomic():
983+
with transaction.atomic(using=router.db_for_write(self.queryset.model)):
984984

985985
for obj in data['pk']:
986986

netbox/netbox/views/generic/feature_views.py

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
from django.contrib.auth.mixins import LoginRequiredMixin
22
from django.contrib.contenttypes.models import ContentType
33
from django.contrib import messages
4-
from django.db import transaction
4+
from django.db import router, transaction
55
from django.db.models import Q
66
from django.shortcuts import get_object_or_404, redirect, render
77
from django.utils.translation import gettext_lazy as _
@@ -240,7 +240,7 @@ def post(self, request):
240240
data_file__isnull=False
241241
)
242242

243-
with transaction.atomic():
243+
with transaction.atomic(using=router.db_for_write(self.queryset.model)):
244244
for obj in selected_objects:
245245
obj.sync(save=True)
246246

netbox/netbox/views/generic/object_views.py

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -282,7 +282,7 @@ def post(self, request, *args, **kwargs):
282282
logger.debug("Form validation was successful")
283283

284284
try:
285-
with transaction.atomic():
285+
with transaction.atomic(using=router.db_for_write(model)):
286286
object_created = form.instance.pk is None
287287
obj = form.save()
288288

@@ -570,7 +570,7 @@ def post(self, request):
570570

571571
if not form.errors and not component_form.errors:
572572
try:
573-
with transaction.atomic():
573+
with transaction.atomic(using=router.db_for_write(self.queryset.model)):
574574
# Create the new components
575575
new_objs = []
576576
for component_form in new_components:

0 commit comments

Comments
 (0)