nautobot 1.6.19__py3-none-any.whl → 1.6.21__py3-none-any.whl

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -199,7 +199,13 @@ class ModelViewSetMixin:
199
199
  # If using brief mode, clear all prefetches from the queryset and append only brief_prefetch_fields (if any)
200
200
  if self.brief:
201
201
  # v2 TODO(jathan): Replace prefetch_related with select_related
202
- return super().get_queryset().prefetch_related(None).prefetch_related(*self.brief_prefetch_fields)
202
+ return (
203
+ super()
204
+ .get_queryset()
205
+ .select_related(None)
206
+ .prefetch_related(None)
207
+ .prefetch_related(*self.brief_prefetch_fields)
208
+ )
203
209
 
204
210
  return super().get_queryset()
205
211
 
@@ -68,7 +68,7 @@
68
68
  class="remove-filter-param"
69
69
  title="Remove all items"
70
70
  data-field-type="parent"
71
- data-field-value={{ field.name }}
71
+ data-field-value="{{ field.name }}"
72
72
  >×</span>
73
73
  <ul class="filter-selection-rendered">
74
74
  {% for value in field.values %}
@@ -79,8 +79,8 @@
79
79
  <span
80
80
  class="filter-selection-choice-remove remove-filter-param"
81
81
  data-field-type="child"
82
- data-field-parent={{ field.name }}
83
- data-field-value={{ value.name }}
82
+ data-field-parent="{{ field.name }}"
83
+ data-field-value="{{ value.name }}"
84
84
  >×</span>{{ value.display }}
85
85
  </li>
86
86
  {% endfor %}
@@ -185,18 +185,19 @@
185
185
 
186
186
  // Remove applied filters
187
187
  $(".remove-filter-param").on("click", function(){
188
- let query_params = location.search;
188
+ let query_params = new URLSearchParams(location.search);
189
189
  let type = $(this).attr("data-field-type");
190
190
  let field_value = $(this).attr("data-field-value");
191
- let query_string = location.search.substr(1).split("&");
192
191
 
193
192
  if (type === "parent") {
194
- query_string = query_string.filter(item => item.search(field_value) < 0);
193
+ // Remove all instances of this query param
194
+ query_params.delete(field_value);
195
195
  } else {
196
+ // Remove this specific instance of this query param
196
197
  let parent = $(this).attr("data-field-parent");
197
- query_string = query_string.filter(item => item.search(parent + "=" + field_value) < 0)
198
+ query_params.delete(parent, field_value);
198
199
  }
199
- location.replace("?" + query_string.join("&"))
200
+ location.assign("?" + query_params);
200
201
  })
201
202
 
202
203
  // On submit of filter form
@@ -210,10 +211,18 @@
210
211
  q_field_phantom.val(q_field.val())
211
212
  dynamic_form.append(q_field_phantom);
212
213
 
213
- let search_query = $("#dynamic-filter-form, #default-filter form").serialize()
214
- // Remove duplicates generated by filter merging on both dynamic and default filter forms.
215
- search_query = "&" + search_query.replace(/([^&]+=[^&]+&)(?=.*\1)/g, "")
216
- location.replace("?" + search_query)
214
+ // Get the serialized data from the forms and:
215
+ // 1) filter out query_params which values are empty e.g. ?sam=&dan=2 becomes ?dan=2
216
+ // 2) combine the two forms into a single set of data without duplicate entries
217
+ let search_query = new URLSearchParams()
218
+ let dynamic_query = new URLSearchParams(new FormData(document.getElementById("dynamic-filter-form")));
219
+ dynamic_query.forEach((value, key) => { if (value != "") { search_query.append(key, value); }});
220
+ let default_query = new URLSearchParams(new FormData(document.getElementById("default-filter").firstElementChild));
221
+ default_query.forEach((value, key) => {
222
+ if (value != "" && !search_query.has(key, value)) { search_query.append(key, value); }
223
+ });
224
+ $("#FilterForm_modal").modal("hide");
225
+ location.assign("?" + search_query);
217
226
  })
218
227
 
219
228
  // On submit of filter search form
@@ -163,6 +163,25 @@ class FilterFormsTestCase(TestCase):
163
163
  response.content.decode(response.charset),
164
164
  )
165
165
 
166
+ def test_filtering_crafted_query_params(self):
167
+ """Test for reflected-XSS vulnerability GHSA-jxgr-gcj5-cqqg."""
168
+ self.add_permissions("dcim.view_location")
169
+ query_param = "?location_type=1 onmouseover=alert('hi') foo=bar"
170
+ url = reverse("dcim:location_list") + query_param
171
+ response = self.client.get(url)
172
+ self.assertHttpStatus(response, 200)
173
+ response_content = response.content.decode(response.charset)
174
+ # The important thing here is that the data-field-parent and data-field-value are correctly quoted
175
+ self.assertInHTML(
176
+ """
177
+ <span class="filter-selection-choice-remove remove-filter-param"
178
+ data-field-type="child"
179
+ data-field-parent="location_type"
180
+ data-field-value="1 onmouseover=alert(&#x27;hi&#x27;) foo=bar"
181
+ >×</span>""", # noqa: RUF001 - ambiguous-unicode-character-string
182
+ response_content,
183
+ )
184
+
166
185
 
167
186
  class ForceScriptNameTestcase(TestCase):
168
187
  """Basic test to assert that `settings.FORCE_SCRIPT_NAME` works as intended."""
@@ -5055,13 +5055,7 @@ to the end of <code>fields</code> if they are applicable to this model and this
5055
5055
 
5056
5056
  <details class="quote">
5057
5057
  <summary>Source code in <code>nautobot/core/api/views.py</code></summary>
5058
- <div class="highlight"><table class="highlighttable"><tr><td class="linenos"><div class="linenodiv"><pre><span></span><span class="normal"><a href="#__codelineno-0-256">256</a></span>
5059
- <span class="normal"><a href="#__codelineno-0-257">257</a></span>
5060
- <span class="normal"><a href="#__codelineno-0-258">258</a></span>
5061
- <span class="normal"><a href="#__codelineno-0-259">259</a></span>
5062
- <span class="normal"><a href="#__codelineno-0-260">260</a></span>
5063
- <span class="normal"><a href="#__codelineno-0-261">261</a></span>
5064
- <span class="normal"><a href="#__codelineno-0-262">262</a></span>
5058
+ <div class="highlight"><table class="highlighttable"><tr><td class="linenos"><div class="linenodiv"><pre><span></span><span class="normal"><a href="#__codelineno-0-262">262</a></span>
5065
5059
  <span class="normal"><a href="#__codelineno-0-263">263</a></span>
5066
5060
  <span class="normal"><a href="#__codelineno-0-264">264</a></span>
5067
5061
  <span class="normal"><a href="#__codelineno-0-265">265</a></span>
@@ -5111,63 +5105,69 @@ to the end of <code>fields</code> if they are applicable to this model and this
5111
5105
  <span class="normal"><a href="#__codelineno-0-309">309</a></span>
5112
5106
  <span class="normal"><a href="#__codelineno-0-310">310</a></span>
5113
5107
  <span class="normal"><a href="#__codelineno-0-311">311</a></span>
5114
- <span class="normal"><a href="#__codelineno-0-312">312</a></span></pre></div></td><td class="code"><div><pre><span></span><code><a id="__codelineno-0-256" name="__codelineno-0-256"></a><span class="k">class</span> <span class="nc">ModelViewSet</span><span class="p">(</span>
5115
- <a id="__codelineno-0-257" name="__codelineno-0-257"></a> <span class="n">NautobotAPIVersionMixin</span><span class="p">,</span>
5116
- <a id="__codelineno-0-258" name="__codelineno-0-258"></a> <span class="n">BulkUpdateModelMixin</span><span class="p">,</span>
5117
- <a id="__codelineno-0-259" name="__codelineno-0-259"></a> <span class="n">BulkDestroyModelMixin</span><span class="p">,</span>
5118
- <a id="__codelineno-0-260" name="__codelineno-0-260"></a> <span class="n">ModelViewSetMixin</span><span class="p">,</span>
5119
- <a id="__codelineno-0-261" name="__codelineno-0-261"></a> <span class="n">ModelViewSet_</span><span class="p">,</span>
5120
- <a id="__codelineno-0-262" name="__codelineno-0-262"></a><span class="p">):</span>
5121
- <a id="__codelineno-0-263" name="__codelineno-0-263"></a><span class="w"> </span><span class="sd">&quot;&quot;&quot;</span>
5122
- <a id="__codelineno-0-264" name="__codelineno-0-264"></a><span class="sd"> Extend DRF&#39;s ModelViewSet to support bulk update and delete functions.</span>
5123
- <a id="__codelineno-0-265" name="__codelineno-0-265"></a><span class="sd"> &quot;&quot;&quot;</span>
5124
- <a id="__codelineno-0-266" name="__codelineno-0-266"></a>
5125
- <a id="__codelineno-0-267" name="__codelineno-0-267"></a> <span class="k">def</span> <span class="nf">_validate_objects</span><span class="p">(</span><span class="bp">self</span><span class="p">,</span> <span class="n">instance</span><span class="p">):</span>
5126
- <a id="__codelineno-0-268" name="__codelineno-0-268"></a><span class="w"> </span><span class="sd">&quot;&quot;&quot;</span>
5127
- <a id="__codelineno-0-269" name="__codelineno-0-269"></a><span class="sd"> Check that the provided instance or list of instances are matched by the current queryset. This confirms that</span>
5128
- <a id="__codelineno-0-270" name="__codelineno-0-270"></a><span class="sd"> any newly created or modified objects abide by the attributes granted by any applicable ObjectPermissions.</span>
5129
- <a id="__codelineno-0-271" name="__codelineno-0-271"></a><span class="sd"> &quot;&quot;&quot;</span>
5130
- <a id="__codelineno-0-272" name="__codelineno-0-272"></a> <span class="k">if</span> <span class="nb">isinstance</span><span class="p">(</span><span class="n">instance</span><span class="p">,</span> <span class="nb">list</span><span class="p">):</span>
5131
- <a id="__codelineno-0-273" name="__codelineno-0-273"></a> <span class="c1"># Check that all instances are still included in the view&#39;s queryset</span>
5132
- <a id="__codelineno-0-274" name="__codelineno-0-274"></a> <span class="n">conforming_count</span> <span class="o">=</span> <span class="bp">self</span><span class="o">.</span><span class="n">queryset</span><span class="o">.</span><span class="n">filter</span><span class="p">(</span><span class="n">pk__in</span><span class="o">=</span><span class="p">[</span><span class="n">obj</span><span class="o">.</span><span class="n">pk</span> <span class="k">for</span> <span class="n">obj</span> <span class="ow">in</span> <span class="n">instance</span><span class="p">])</span><span class="o">.</span><span class="n">count</span><span class="p">()</span>
5133
- <a id="__codelineno-0-275" name="__codelineno-0-275"></a> <span class="k">if</span> <span class="n">conforming_count</span> <span class="o">!=</span> <span class="nb">len</span><span class="p">(</span><span class="n">instance</span><span class="p">):</span>
5134
- <a id="__codelineno-0-276" name="__codelineno-0-276"></a> <span class="k">raise</span> <span class="n">ObjectDoesNotExist</span>
5135
- <a id="__codelineno-0-277" name="__codelineno-0-277"></a> <span class="k">else</span><span class="p">:</span>
5136
- <a id="__codelineno-0-278" name="__codelineno-0-278"></a> <span class="c1"># Check that the instance is matched by the view&#39;s queryset</span>
5137
- <a id="__codelineno-0-279" name="__codelineno-0-279"></a> <span class="bp">self</span><span class="o">.</span><span class="n">queryset</span><span class="o">.</span><span class="n">get</span><span class="p">(</span><span class="n">pk</span><span class="o">=</span><span class="n">instance</span><span class="o">.</span><span class="n">pk</span><span class="p">)</span>
5138
- <a id="__codelineno-0-280" name="__codelineno-0-280"></a>
5139
- <a id="__codelineno-0-281" name="__codelineno-0-281"></a> <span class="k">def</span> <span class="nf">perform_create</span><span class="p">(</span><span class="bp">self</span><span class="p">,</span> <span class="n">serializer</span><span class="p">):</span>
5140
- <a id="__codelineno-0-282" name="__codelineno-0-282"></a> <span class="n">model</span> <span class="o">=</span> <span class="bp">self</span><span class="o">.</span><span class="n">queryset</span><span class="o">.</span><span class="n">model</span>
5141
- <a id="__codelineno-0-283" name="__codelineno-0-283"></a> <span class="n">logger</span> <span class="o">=</span> <span class="n">logging</span><span class="o">.</span><span class="n">getLogger</span><span class="p">(</span><span class="s2">&quot;nautobot.core.api.views.ModelViewSet&quot;</span><span class="p">)</span>
5142
- <a id="__codelineno-0-284" name="__codelineno-0-284"></a> <span class="n">logger</span><span class="o">.</span><span class="n">info</span><span class="p">(</span><span class="sa">f</span><span class="s2">&quot;Creating new </span><span class="si">{</span><span class="n">model</span><span class="o">.</span><span class="n">_meta</span><span class="o">.</span><span class="n">verbose_name</span><span class="si">}</span><span class="s2">&quot;</span><span class="p">)</span>
5143
- <a id="__codelineno-0-285" name="__codelineno-0-285"></a>
5144
- <a id="__codelineno-0-286" name="__codelineno-0-286"></a> <span class="c1"># Enforce object-level permissions on save()</span>
5145
- <a id="__codelineno-0-287" name="__codelineno-0-287"></a> <span class="k">try</span><span class="p">:</span>
5146
- <a id="__codelineno-0-288" name="__codelineno-0-288"></a> <span class="k">with</span> <span class="n">transaction</span><span class="o">.</span><span class="n">atomic</span><span class="p">():</span>
5147
- <a id="__codelineno-0-289" name="__codelineno-0-289"></a> <span class="n">instance</span> <span class="o">=</span> <span class="n">serializer</span><span class="o">.</span><span class="n">save</span><span class="p">()</span>
5148
- <a id="__codelineno-0-290" name="__codelineno-0-290"></a> <span class="bp">self</span><span class="o">.</span><span class="n">_validate_objects</span><span class="p">(</span><span class="n">instance</span><span class="p">)</span>
5149
- <a id="__codelineno-0-291" name="__codelineno-0-291"></a> <span class="k">except</span> <span class="n">ObjectDoesNotExist</span><span class="p">:</span>
5150
- <a id="__codelineno-0-292" name="__codelineno-0-292"></a> <span class="k">raise</span> <span class="n">PermissionDenied</span><span class="p">()</span>
5151
- <a id="__codelineno-0-293" name="__codelineno-0-293"></a>
5152
- <a id="__codelineno-0-294" name="__codelineno-0-294"></a> <span class="k">def</span> <span class="nf">perform_update</span><span class="p">(</span><span class="bp">self</span><span class="p">,</span> <span class="n">serializer</span><span class="p">):</span>
5153
- <a id="__codelineno-0-295" name="__codelineno-0-295"></a> <span class="n">model</span> <span class="o">=</span> <span class="bp">self</span><span class="o">.</span><span class="n">queryset</span><span class="o">.</span><span class="n">model</span>
5154
- <a id="__codelineno-0-296" name="__codelineno-0-296"></a> <span class="n">logger</span> <span class="o">=</span> <span class="n">logging</span><span class="o">.</span><span class="n">getLogger</span><span class="p">(</span><span class="s2">&quot;nautobot.core.api.views.ModelViewSet&quot;</span><span class="p">)</span>
5155
- <a id="__codelineno-0-297" name="__codelineno-0-297"></a> <span class="n">logger</span><span class="o">.</span><span class="n">info</span><span class="p">(</span><span class="sa">f</span><span class="s2">&quot;Updating </span><span class="si">{</span><span class="n">model</span><span class="o">.</span><span class="n">_meta</span><span class="o">.</span><span class="n">verbose_name</span><span class="si">}</span><span class="s2"> </span><span class="si">{</span><span class="n">serializer</span><span class="o">.</span><span class="n">instance</span><span class="si">}</span><span class="s2"> (PK: </span><span class="si">{</span><span class="n">serializer</span><span class="o">.</span><span class="n">instance</span><span class="o">.</span><span class="n">pk</span><span class="si">}</span><span class="s2">)&quot;</span><span class="p">)</span>
5156
- <a id="__codelineno-0-298" name="__codelineno-0-298"></a>
5157
- <a id="__codelineno-0-299" name="__codelineno-0-299"></a> <span class="c1"># Enforce object-level permissions on save()</span>
5158
- <a id="__codelineno-0-300" name="__codelineno-0-300"></a> <span class="k">try</span><span class="p">:</span>
5159
- <a id="__codelineno-0-301" name="__codelineno-0-301"></a> <span class="k">with</span> <span class="n">transaction</span><span class="o">.</span><span class="n">atomic</span><span class="p">():</span>
5160
- <a id="__codelineno-0-302" name="__codelineno-0-302"></a> <span class="n">instance</span> <span class="o">=</span> <span class="n">serializer</span><span class="o">.</span><span class="n">save</span><span class="p">()</span>
5161
- <a id="__codelineno-0-303" name="__codelineno-0-303"></a> <span class="bp">self</span><span class="o">.</span><span class="n">_validate_objects</span><span class="p">(</span><span class="n">instance</span><span class="p">)</span>
5162
- <a id="__codelineno-0-304" name="__codelineno-0-304"></a> <span class="k">except</span> <span class="n">ObjectDoesNotExist</span><span class="p">:</span>
5163
- <a id="__codelineno-0-305" name="__codelineno-0-305"></a> <span class="k">raise</span> <span class="n">PermissionDenied</span><span class="p">()</span>
5164
- <a id="__codelineno-0-306" name="__codelineno-0-306"></a>
5165
- <a id="__codelineno-0-307" name="__codelineno-0-307"></a> <span class="k">def</span> <span class="nf">perform_destroy</span><span class="p">(</span><span class="bp">self</span><span class="p">,</span> <span class="n">instance</span><span class="p">):</span>
5166
- <a id="__codelineno-0-308" name="__codelineno-0-308"></a> <span class="n">model</span> <span class="o">=</span> <span class="bp">self</span><span class="o">.</span><span class="n">queryset</span><span class="o">.</span><span class="n">model</span>
5167
- <a id="__codelineno-0-309" name="__codelineno-0-309"></a> <span class="n">logger</span> <span class="o">=</span> <span class="n">logging</span><span class="o">.</span><span class="n">getLogger</span><span class="p">(</span><span class="s2">&quot;nautobot.core.api.views.ModelViewSet&quot;</span><span class="p">)</span>
5168
- <a id="__codelineno-0-310" name="__codelineno-0-310"></a> <span class="n">logger</span><span class="o">.</span><span class="n">info</span><span class="p">(</span><span class="sa">f</span><span class="s2">&quot;Deleting </span><span class="si">{</span><span class="n">model</span><span class="o">.</span><span class="n">_meta</span><span class="o">.</span><span class="n">verbose_name</span><span class="si">}</span><span class="s2"> </span><span class="si">{</span><span class="n">instance</span><span class="si">}</span><span class="s2"> (PK: </span><span class="si">{</span><span class="n">instance</span><span class="o">.</span><span class="n">pk</span><span class="si">}</span><span class="s2">)&quot;</span><span class="p">)</span>
5169
- <a id="__codelineno-0-311" name="__codelineno-0-311"></a>
5170
- <a id="__codelineno-0-312" name="__codelineno-0-312"></a> <span class="k">return</span> <span class="nb">super</span><span class="p">()</span><span class="o">.</span><span class="n">perform_destroy</span><span class="p">(</span><span class="n">instance</span><span class="p">)</span>
5108
+ <span class="normal"><a href="#__codelineno-0-312">312</a></span>
5109
+ <span class="normal"><a href="#__codelineno-0-313">313</a></span>
5110
+ <span class="normal"><a href="#__codelineno-0-314">314</a></span>
5111
+ <span class="normal"><a href="#__codelineno-0-315">315</a></span>
5112
+ <span class="normal"><a href="#__codelineno-0-316">316</a></span>
5113
+ <span class="normal"><a href="#__codelineno-0-317">317</a></span>
5114
+ <span class="normal"><a href="#__codelineno-0-318">318</a></span></pre></div></td><td class="code"><div><pre><span></span><code><a id="__codelineno-0-262" name="__codelineno-0-262"></a><span class="k">class</span> <span class="nc">ModelViewSet</span><span class="p">(</span>
5115
+ <a id="__codelineno-0-263" name="__codelineno-0-263"></a> <span class="n">NautobotAPIVersionMixin</span><span class="p">,</span>
5116
+ <a id="__codelineno-0-264" name="__codelineno-0-264"></a> <span class="n">BulkUpdateModelMixin</span><span class="p">,</span>
5117
+ <a id="__codelineno-0-265" name="__codelineno-0-265"></a> <span class="n">BulkDestroyModelMixin</span><span class="p">,</span>
5118
+ <a id="__codelineno-0-266" name="__codelineno-0-266"></a> <span class="n">ModelViewSetMixin</span><span class="p">,</span>
5119
+ <a id="__codelineno-0-267" name="__codelineno-0-267"></a> <span class="n">ModelViewSet_</span><span class="p">,</span>
5120
+ <a id="__codelineno-0-268" name="__codelineno-0-268"></a><span class="p">):</span>
5121
+ <a id="__codelineno-0-269" name="__codelineno-0-269"></a><span class="w"> </span><span class="sd">&quot;&quot;&quot;</span>
5122
+ <a id="__codelineno-0-270" name="__codelineno-0-270"></a><span class="sd"> Extend DRF&#39;s ModelViewSet to support bulk update and delete functions.</span>
5123
+ <a id="__codelineno-0-271" name="__codelineno-0-271"></a><span class="sd"> &quot;&quot;&quot;</span>
5124
+ <a id="__codelineno-0-272" name="__codelineno-0-272"></a>
5125
+ <a id="__codelineno-0-273" name="__codelineno-0-273"></a> <span class="k">def</span> <span class="nf">_validate_objects</span><span class="p">(</span><span class="bp">self</span><span class="p">,</span> <span class="n">instance</span><span class="p">):</span>
5126
+ <a id="__codelineno-0-274" name="__codelineno-0-274"></a><span class="w"> </span><span class="sd">&quot;&quot;&quot;</span>
5127
+ <a id="__codelineno-0-275" name="__codelineno-0-275"></a><span class="sd"> Check that the provided instance or list of instances are matched by the current queryset. This confirms that</span>
5128
+ <a id="__codelineno-0-276" name="__codelineno-0-276"></a><span class="sd"> any newly created or modified objects abide by the attributes granted by any applicable ObjectPermissions.</span>
5129
+ <a id="__codelineno-0-277" name="__codelineno-0-277"></a><span class="sd"> &quot;&quot;&quot;</span>
5130
+ <a id="__codelineno-0-278" name="__codelineno-0-278"></a> <span class="k">if</span> <span class="nb">isinstance</span><span class="p">(</span><span class="n">instance</span><span class="p">,</span> <span class="nb">list</span><span class="p">):</span>
5131
+ <a id="__codelineno-0-279" name="__codelineno-0-279"></a> <span class="c1"># Check that all instances are still included in the view&#39;s queryset</span>
5132
+ <a id="__codelineno-0-280" name="__codelineno-0-280"></a> <span class="n">conforming_count</span> <span class="o">=</span> <span class="bp">self</span><span class="o">.</span><span class="n">queryset</span><span class="o">.</span><span class="n">filter</span><span class="p">(</span><span class="n">pk__in</span><span class="o">=</span><span class="p">[</span><span class="n">obj</span><span class="o">.</span><span class="n">pk</span> <span class="k">for</span> <span class="n">obj</span> <span class="ow">in</span> <span class="n">instance</span><span class="p">])</span><span class="o">.</span><span class="n">count</span><span class="p">()</span>
5133
+ <a id="__codelineno-0-281" name="__codelineno-0-281"></a> <span class="k">if</span> <span class="n">conforming_count</span> <span class="o">!=</span> <span class="nb">len</span><span class="p">(</span><span class="n">instance</span><span class="p">):</span>
5134
+ <a id="__codelineno-0-282" name="__codelineno-0-282"></a> <span class="k">raise</span> <span class="n">ObjectDoesNotExist</span>
5135
+ <a id="__codelineno-0-283" name="__codelineno-0-283"></a> <span class="k">else</span><span class="p">:</span>
5136
+ <a id="__codelineno-0-284" name="__codelineno-0-284"></a> <span class="c1"># Check that the instance is matched by the view&#39;s queryset</span>
5137
+ <a id="__codelineno-0-285" name="__codelineno-0-285"></a> <span class="bp">self</span><span class="o">.</span><span class="n">queryset</span><span class="o">.</span><span class="n">get</span><span class="p">(</span><span class="n">pk</span><span class="o">=</span><span class="n">instance</span><span class="o">.</span><span class="n">pk</span><span class="p">)</span>
5138
+ <a id="__codelineno-0-286" name="__codelineno-0-286"></a>
5139
+ <a id="__codelineno-0-287" name="__codelineno-0-287"></a> <span class="k">def</span> <span class="nf">perform_create</span><span class="p">(</span><span class="bp">self</span><span class="p">,</span> <span class="n">serializer</span><span class="p">):</span>
5140
+ <a id="__codelineno-0-288" name="__codelineno-0-288"></a> <span class="n">model</span> <span class="o">=</span> <span class="bp">self</span><span class="o">.</span><span class="n">queryset</span><span class="o">.</span><span class="n">model</span>
5141
+ <a id="__codelineno-0-289" name="__codelineno-0-289"></a> <span class="n">logger</span> <span class="o">=</span> <span class="n">logging</span><span class="o">.</span><span class="n">getLogger</span><span class="p">(</span><span class="s2">&quot;nautobot.core.api.views.ModelViewSet&quot;</span><span class="p">)</span>
5142
+ <a id="__codelineno-0-290" name="__codelineno-0-290"></a> <span class="n">logger</span><span class="o">.</span><span class="n">info</span><span class="p">(</span><span class="sa">f</span><span class="s2">&quot;Creating new </span><span class="si">{</span><span class="n">model</span><span class="o">.</span><span class="n">_meta</span><span class="o">.</span><span class="n">verbose_name</span><span class="si">}</span><span class="s2">&quot;</span><span class="p">)</span>
5143
+ <a id="__codelineno-0-291" name="__codelineno-0-291"></a>
5144
+ <a id="__codelineno-0-292" name="__codelineno-0-292"></a> <span class="c1"># Enforce object-level permissions on save()</span>
5145
+ <a id="__codelineno-0-293" name="__codelineno-0-293"></a> <span class="k">try</span><span class="p">:</span>
5146
+ <a id="__codelineno-0-294" name="__codelineno-0-294"></a> <span class="k">with</span> <span class="n">transaction</span><span class="o">.</span><span class="n">atomic</span><span class="p">():</span>
5147
+ <a id="__codelineno-0-295" name="__codelineno-0-295"></a> <span class="n">instance</span> <span class="o">=</span> <span class="n">serializer</span><span class="o">.</span><span class="n">save</span><span class="p">()</span>
5148
+ <a id="__codelineno-0-296" name="__codelineno-0-296"></a> <span class="bp">self</span><span class="o">.</span><span class="n">_validate_objects</span><span class="p">(</span><span class="n">instance</span><span class="p">)</span>
5149
+ <a id="__codelineno-0-297" name="__codelineno-0-297"></a> <span class="k">except</span> <span class="n">ObjectDoesNotExist</span><span class="p">:</span>
5150
+ <a id="__codelineno-0-298" name="__codelineno-0-298"></a> <span class="k">raise</span> <span class="n">PermissionDenied</span><span class="p">()</span>
5151
+ <a id="__codelineno-0-299" name="__codelineno-0-299"></a>
5152
+ <a id="__codelineno-0-300" name="__codelineno-0-300"></a> <span class="k">def</span> <span class="nf">perform_update</span><span class="p">(</span><span class="bp">self</span><span class="p">,</span> <span class="n">serializer</span><span class="p">):</span>
5153
+ <a id="__codelineno-0-301" name="__codelineno-0-301"></a> <span class="n">model</span> <span class="o">=</span> <span class="bp">self</span><span class="o">.</span><span class="n">queryset</span><span class="o">.</span><span class="n">model</span>
5154
+ <a id="__codelineno-0-302" name="__codelineno-0-302"></a> <span class="n">logger</span> <span class="o">=</span> <span class="n">logging</span><span class="o">.</span><span class="n">getLogger</span><span class="p">(</span><span class="s2">&quot;nautobot.core.api.views.ModelViewSet&quot;</span><span class="p">)</span>
5155
+ <a id="__codelineno-0-303" name="__codelineno-0-303"></a> <span class="n">logger</span><span class="o">.</span><span class="n">info</span><span class="p">(</span><span class="sa">f</span><span class="s2">&quot;Updating </span><span class="si">{</span><span class="n">model</span><span class="o">.</span><span class="n">_meta</span><span class="o">.</span><span class="n">verbose_name</span><span class="si">}</span><span class="s2"> </span><span class="si">{</span><span class="n">serializer</span><span class="o">.</span><span class="n">instance</span><span class="si">}</span><span class="s2"> (PK: </span><span class="si">{</span><span class="n">serializer</span><span class="o">.</span><span class="n">instance</span><span class="o">.</span><span class="n">pk</span><span class="si">}</span><span class="s2">)&quot;</span><span class="p">)</span>
5156
+ <a id="__codelineno-0-304" name="__codelineno-0-304"></a>
5157
+ <a id="__codelineno-0-305" name="__codelineno-0-305"></a> <span class="c1"># Enforce object-level permissions on save()</span>
5158
+ <a id="__codelineno-0-306" name="__codelineno-0-306"></a> <span class="k">try</span><span class="p">:</span>
5159
+ <a id="__codelineno-0-307" name="__codelineno-0-307"></a> <span class="k">with</span> <span class="n">transaction</span><span class="o">.</span><span class="n">atomic</span><span class="p">():</span>
5160
+ <a id="__codelineno-0-308" name="__codelineno-0-308"></a> <span class="n">instance</span> <span class="o">=</span> <span class="n">serializer</span><span class="o">.</span><span class="n">save</span><span class="p">()</span>
5161
+ <a id="__codelineno-0-309" name="__codelineno-0-309"></a> <span class="bp">self</span><span class="o">.</span><span class="n">_validate_objects</span><span class="p">(</span><span class="n">instance</span><span class="p">)</span>
5162
+ <a id="__codelineno-0-310" name="__codelineno-0-310"></a> <span class="k">except</span> <span class="n">ObjectDoesNotExist</span><span class="p">:</span>
5163
+ <a id="__codelineno-0-311" name="__codelineno-0-311"></a> <span class="k">raise</span> <span class="n">PermissionDenied</span><span class="p">()</span>
5164
+ <a id="__codelineno-0-312" name="__codelineno-0-312"></a>
5165
+ <a id="__codelineno-0-313" name="__codelineno-0-313"></a> <span class="k">def</span> <span class="nf">perform_destroy</span><span class="p">(</span><span class="bp">self</span><span class="p">,</span> <span class="n">instance</span><span class="p">):</span>
5166
+ <a id="__codelineno-0-314" name="__codelineno-0-314"></a> <span class="n">model</span> <span class="o">=</span> <span class="bp">self</span><span class="o">.</span><span class="n">queryset</span><span class="o">.</span><span class="n">model</span>
5167
+ <a id="__codelineno-0-315" name="__codelineno-0-315"></a> <span class="n">logger</span> <span class="o">=</span> <span class="n">logging</span><span class="o">.</span><span class="n">getLogger</span><span class="p">(</span><span class="s2">&quot;nautobot.core.api.views.ModelViewSet&quot;</span><span class="p">)</span>
5168
+ <a id="__codelineno-0-316" name="__codelineno-0-316"></a> <span class="n">logger</span><span class="o">.</span><span class="n">info</span><span class="p">(</span><span class="sa">f</span><span class="s2">&quot;Deleting </span><span class="si">{</span><span class="n">model</span><span class="o">.</span><span class="n">_meta</span><span class="o">.</span><span class="n">verbose_name</span><span class="si">}</span><span class="s2"> </span><span class="si">{</span><span class="n">instance</span><span class="si">}</span><span class="s2"> (PK: </span><span class="si">{</span><span class="n">instance</span><span class="o">.</span><span class="n">pk</span><span class="si">}</span><span class="s2">)&quot;</span><span class="p">)</span>
5169
+ <a id="__codelineno-0-317" name="__codelineno-0-317"></a>
5170
+ <a id="__codelineno-0-318" name="__codelineno-0-318"></a> <span class="k">return</span> <span class="nb">super</span><span class="p">()</span><span class="o">.</span><span class="n">perform_destroy</span><span class="p">(</span><span class="n">instance</span><span class="p">)</span>
5171
5171
  </code></pre></div></td></tr></table></div>
5172
5172
  </details>
5173
5173
 
@@ -5789,13 +5789,13 @@ to the end of <code>fields</code> if they are applicable to this model and this
5789
5789
 
5790
5790
  <details class="quote">
5791
5791
  <summary>Source code in <code>nautobot/core/api/views.py</code></summary>
5792
- <div class="highlight"><table class="highlighttable"><tr><td class="linenos"><div class="linenodiv"><pre><span></span><span class="normal"><a href="#__codelineno-0-315">315</a></span>
5793
- <span class="normal"><a href="#__codelineno-0-316">316</a></span>
5794
- <span class="normal"><a href="#__codelineno-0-317">317</a></span>
5795
- <span class="normal"><a href="#__codelineno-0-318">318</a></span></pre></div></td><td class="code"><div><pre><span></span><code><a id="__codelineno-0-315" name="__codelineno-0-315"></a><span class="k">class</span> <span class="nc">ReadOnlyModelViewSet</span><span class="p">(</span><span class="n">NautobotAPIVersionMixin</span><span class="p">,</span> <span class="n">ModelViewSetMixin</span><span class="p">,</span> <span class="n">ReadOnlyModelViewSet_</span><span class="p">):</span>
5796
- <a id="__codelineno-0-316" name="__codelineno-0-316"></a><span class="w"> </span><span class="sd">&quot;&quot;&quot;</span>
5797
- <a id="__codelineno-0-317" name="__codelineno-0-317"></a><span class="sd"> Extend DRF&#39;s ReadOnlyModelViewSet to support queryset restriction.</span>
5798
- <a id="__codelineno-0-318" name="__codelineno-0-318"></a><span class="sd"> &quot;&quot;&quot;</span>
5792
+ <div class="highlight"><table class="highlighttable"><tr><td class="linenos"><div class="linenodiv"><pre><span></span><span class="normal"><a href="#__codelineno-0-321">321</a></span>
5793
+ <span class="normal"><a href="#__codelineno-0-322">322</a></span>
5794
+ <span class="normal"><a href="#__codelineno-0-323">323</a></span>
5795
+ <span class="normal"><a href="#__codelineno-0-324">324</a></span></pre></div></td><td class="code"><div><pre><span></span><code><a id="__codelineno-0-321" name="__codelineno-0-321"></a><span class="k">class</span> <span class="nc">ReadOnlyModelViewSet</span><span class="p">(</span><span class="n">NautobotAPIVersionMixin</span><span class="p">,</span> <span class="n">ModelViewSetMixin</span><span class="p">,</span> <span class="n">ReadOnlyModelViewSet_</span><span class="p">):</span>
5796
+ <a id="__codelineno-0-322" name="__codelineno-0-322"></a><span class="w"> </span><span class="sd">&quot;&quot;&quot;</span>
5797
+ <a id="__codelineno-0-323" name="__codelineno-0-323"></a><span class="sd"> Extend DRF&#39;s ReadOnlyModelViewSet to support queryset restriction.</span>
5798
+ <a id="__codelineno-0-324" name="__codelineno-0-324"></a><span class="sd"> &quot;&quot;&quot;</span>
5799
5799
  </code></pre></div></td></tr></table></div>
5800
5800
  </details>
5801
5801