145 | | def test_inline_permissions(self): |
146 | | """ |
147 | | Make sure the admin respects permissions for objects that are edited |
148 | | inline. Ref #8060. |
149 | | """ |
150 | | user = User.objects.get(username='super') |
151 | | user.is_superuser = False |
152 | | user.save() |
153 | | |
154 | | author_ct = ContentType.objects.get_for_model(Author) |
155 | | holder_ct = ContentType.objects.get_for_model(Holder) |
156 | | book_ct = ContentType.objects.get_for_model(Book) |
157 | | inner_ct = ContentType.objects.get_for_model(Inner) |
158 | | |
159 | | permission = Permission.objects.get(codename='add_author', content_type=author_ct) |
160 | | user.user_permissions.add(permission) |
161 | | permission = Permission.objects.get(codename='change_author', content_type=author_ct) |
162 | | user.user_permissions.add(permission) |
163 | | permission = Permission.objects.get(codename='add_holder', content_type=holder_ct) |
164 | | user.user_permissions.add(permission) |
165 | | permission = Permission.objects.get(codename='change_holder', content_type=holder_ct) |
166 | | user.user_permissions.add(permission) |
167 | | |
168 | | author = Author.objects.create(pk=1, name=u'The Author') |
169 | | author.books.create(name=u'The inline Book') |
170 | | |
171 | | # Make sure both ForeignKey as well as ManyToMany inlines are properly removed |
172 | | response = self.client.get('/admin/admin_inlines/author/add/') |
173 | | # This would be a TabularInline |
174 | | self.assertNotContains(response, '<h2>Author-book relationships</h2>') |
175 | | self.assertNotContains(response, 'Add another Author-Book Relationship') |
176 | | self.assertNotContains(response, 'id="id_Author_books-TOTAL_FORMS"') |
177 | | response = self.client.get('/admin/admin_inlines/author/1/') |
178 | | self.assertNotContains(response, '<h2>Author-book relationships</h2>') |
179 | | self.assertNotContains(response, 'Add another Author-Book Relationship') |
180 | | self.assertNotContains(response, 'id="id_Author_books-TOTAL_FORMS"') |
181 | | |
182 | | response = self.client.get('/admin/admin_inlines/holder/add/') |
183 | | # This would be a StackedInline |
184 | | self.assertNotContains(response, '<h2>Inners</h2>') |
185 | | self.assertNotContains(response, 'Add another Inner') |
186 | | self.assertNotContains(response, 'id="id_inner_set-TOTAL_FORMS"') |
187 | | response = self.client.get(self.change_url) |
188 | | self.assertNotContains(response, '<h2>Inners</h2>') |
189 | | self.assertNotContains(response, 'Add another Inner') |
190 | | self.assertNotContains(response, 'id="id_inner_set-TOTAL_FORMS"') |
191 | | |
192 | | # Now let's add the missing add permissions and make sure the inlines are shown |
193 | | permission = Permission.objects.get(codename='add_book', content_type=book_ct) |
194 | | user.user_permissions.add(permission) |
195 | | permission = Permission.objects.get(codename='add_inner', content_type=inner_ct) |
196 | | user.user_permissions.add(permission) |
197 | | |
198 | | response = self.client.get('/admin/admin_inlines/author/add/') |
199 | | self.assertContains(response, '<h2>Author-book relationships</h2>') |
200 | | self.assertContains(response, 'Add another Author-Book Relationship') |
201 | | self.assertContains(response, 'value="3" id="id_Author_books-TOTAL_FORMS"') |
202 | | response = self.client.get('/admin/admin_inlines/holder/add/') |
203 | | self.assertContains(response, '<h2>Inners</h2>') |
204 | | self.assertContains(response, 'Add another Inner') |
205 | | self.assertContains(response, 'value="3" id="id_inner_set-TOTAL_FORMS"') |
206 | | |
207 | | # The inlines should be in the change view as well, but existing data |
208 | | # should not be shown |
209 | | response = self.client.get('/admin/admin_inlines/author/1/') |
210 | | self.assertContains(response, '<h2>Author-book relationships</h2>') |
211 | | self.assertContains(response, 'Add another Author-Book Relationship') |
212 | | self.assertContains(response, 'value="3" id="id_Author_books-TOTAL_FORMS"') |
213 | | self.assertNotContains(response, '<input type="hidden" name="Author_books-0-id" value="1"') |
214 | | response = self.client.get(self.change_url) |
215 | | self.assertContains(response, '<h2>Inners</h2>') |
216 | | self.assertContains(response, 'Add another Inner') |
217 | | self.assertContains(response, 'value="3" id="id_inner_set-TOTAL_FORMS"') |
218 | | self.assertNotContains(response, '<input type="hidden" name="inner_set-0-id" value="1"') |
219 | | |
220 | | # Add the change permissions and check that existing data is shown. |
221 | | permission = Permission.objects.get(codename='change_book', content_type=book_ct) |
222 | | user.user_permissions.add(permission) |
223 | | permission = Permission.objects.get(codename='change_inner', content_type=inner_ct) |
224 | | user.user_permissions.add(permission) |
225 | | response = self.client.get('/admin/admin_inlines/author/1/') |
226 | | self.assertContains(response, '<input type="hidden" name="Author_books-0-id" value="1"') |
227 | | # Deletion should not be possible. |
228 | | self.assertNotContains(response, 'id="id_Author_books-0-DELETE"') |
229 | | response = self.client.get(self.change_url) |
230 | | self.assertContains(response, '<input type="hidden" name="inner_set-0-id" value="1"') |
231 | | |
232 | | # Remove the add permissions. inlines should still be there, but |
233 | | # no possibility to add data |
234 | | permission = Permission.objects.get(codename='add_book', content_type=book_ct) |
235 | | user.user_permissions.remove(permission) |
236 | | permission = Permission.objects.get(codename='add_inner', content_type=inner_ct) |
237 | | user.user_permissions.remove(permission) |
238 | | response = self.client.get('/admin/admin_inlines/author/1/') |
239 | | self.assertContains(response, '<h2>Author-book relationships</h2>') |
240 | | self.assertContains(response, '<input type="hidden" name="Author_books-0-id" value="1"') |
241 | | self.assertContains(response, 'value="1" id="id_Author_books-TOTAL_FORMS"') |
242 | | response = self.client.get(self.change_url) |
243 | | self.assertContains(response, '<h2>Inners</h2>') |
244 | | self.assertContains(response, '<input type="hidden" name="inner_set-0-id" value="1"') |
245 | | self.assertContains(response, 'value="1" id="id_inner_set-TOTAL_FORMS"') |
246 | | |
247 | | # Check that deletion is possible with the appropriate permissions. |
248 | | # Deletion is only possible for the Author-Book relationship since the |
249 | | # foreign key from Inner to Holder does not allow NULL values. |
250 | | permission = Permission.objects.get(codename='delete_book', content_type=book_ct) |
251 | | user.user_permissions.add(permission) |
252 | | response = self.client.get('/admin/admin_inlines/author/1/') |
253 | | self.assertContains(response, 'id="id_Author_books-0-DELETE"') |
254 | | |
| 199 | |
| 200 | class TestInlinePermissions(TestCase): |
| 201 | """ |
| 202 | Make sure the admin respects permissions for objects that are edited |
| 203 | inline. Ref #8060. |
| 204 | """ |
| 205 | urls = "regressiontests.admin_inlines.urls" |
| 206 | fixtures = ['admin-views-users.xml'] |
| 207 | |
| 208 | def setUp(self): |
| 209 | self.user = User.objects.get(username='super') |
| 210 | self.user.is_superuser = False |
| 211 | self.user.save() |
| 212 | |
| 213 | self.author_ct = ContentType.objects.get_for_model(Author) |
| 214 | self.holder_ct = ContentType.objects.get_for_model(Holder) |
| 215 | self.book_ct = ContentType.objects.get_for_model(Book) |
| 216 | self.inner_ct = ContentType.objects.get_for_model(Inner) |
| 217 | |
| 218 | author = Author.objects.create(pk=1, name=u'The Author') |
| 219 | author.books.create(name=u'The inline Book') |
| 220 | holder = Holder(dummy=13) |
| 221 | holder.save() |
| 222 | Inner(dummy=42, holder=holder).save() |
| 223 | self.change_url = '/admin/admin_inlines/holder/%i/' % holder.id |
| 224 | |
| 225 | result = self.client.login(username='super', password='secret') |
| 226 | self.assertEqual(result, True) |
| 227 | |
| 228 | def tearDown(self): |
| 229 | self.client.logout() |
| 230 | |
| 231 | def test_inline_add_m2m_noperm(self): |
| 232 | user = self.user |
| 233 | permission = Permission.objects.get(codename='add_author', content_type=self.author_ct) |
| 234 | user.user_permissions.add(permission) |
| 235 | # Make sure the inline is removed |
| 236 | response = self.client.get('/admin/admin_inlines/author/add/') |
| 237 | # This would be a TabularInline |
| 238 | self.assertNotContains(response, '<h2>Author-book relationships</h2>') |
| 239 | self.assertNotContains(response, 'Add another Author-Book Relationship') |
| 240 | self.assertNotContains(response, 'id="id_Author_books-TOTAL_FORMS"') |
| 241 | |
| 242 | def test_inline_add_fk_noperm(self): |
| 243 | user = self.user |
| 244 | permission = Permission.objects.get(codename='add_holder', content_type=self.holder_ct) |
| 245 | user.user_permissions.add(permission) |
| 246 | response = self.client.get('/admin/admin_inlines/holder/add/') |
| 247 | # This would be a StackedInline |
| 248 | self.assertNotContains(response, '<h2>Inners</h2>') |
| 249 | self.assertNotContains(response, 'Add another Inner') |
| 250 | self.assertNotContains(response, 'id="id_inner_set-TOTAL_FORMS"') |
| 251 | |
| 252 | def test_inline_change_m2m_noperm(self): |
| 253 | user = self.user |
| 254 | permission = Permission.objects.get(codename='change_author', content_type=self.author_ct) |
| 255 | user.user_permissions.add(permission) |
| 256 | response = self.client.get('/admin/admin_inlines/author/1/') |
| 257 | self.assertNotContains(response, '<h2>Author-book relationships</h2>') |
| 258 | self.assertNotContains(response, 'Add another Author-Book Relationship') |
| 259 | self.assertNotContains(response, 'id="id_Author_books-TOTAL_FORMS"') |
| 260 | |
| 261 | def test_inline_change_fk_noperm(self): |
| 262 | user = self.user |
| 263 | permission = Permission.objects.get(codename='change_holder', content_type=self.holder_ct) |
| 264 | user.user_permissions.add(permission) |
| 265 | response = self.client.get(self.change_url) |
| 266 | self.assertNotContains(response, '<h2>Inners</h2>') |
| 267 | self.assertNotContains(response, 'Add another Inner') |
| 268 | self.assertNotContains(response, 'id="id_inner_set-TOTAL_FORMS"') |
| 269 | |
| 270 | def test_inline_add_m2m_add_perm(self): |
| 271 | user = self.user |
| 272 | permission = Permission.objects.get(codename='add_author', content_type=self.author_ct) |
| 273 | user.user_permissions.add(permission) |
| 274 | permission = Permission.objects.get(codename='add_book', content_type=self.book_ct) |
| 275 | user.user_permissions.add(permission) |
| 276 | response = self.client.get('/admin/admin_inlines/author/add/') |
| 277 | self.assertNotContains(response, '<h2>Author-book relationships</h2>') |
| 278 | self.assertNotContains(response, 'Add another Author-Book Relationship') |
| 279 | self.assertNotContains(response, 'id="id_Author_books-TOTAL_FORMS"') |
| 280 | |
| 281 | def test_inline_add_fk_add_perm(self): |
| 282 | user = self.user |
| 283 | permission = Permission.objects.get(codename='add_holder', content_type=self.holder_ct) |
| 284 | user.user_permissions.add(permission) |
| 285 | permission = Permission.objects.get(codename='add_inner', content_type=self.inner_ct) |
| 286 | user.user_permissions.add(permission) |
| 287 | response = self.client.get('/admin/admin_inlines/holder/add/') |
| 288 | self.assertContains(response, '<h2>Inners</h2>') |
| 289 | self.assertContains(response, 'Add another Inner') |
| 290 | self.assertContains(response, 'value="3" id="id_inner_set-TOTAL_FORMS"') |
| 291 | |
| 292 | def test_inline_change_m2m_add_perm(self): |
| 293 | # We need the change permission on the related model to make changes to the |
| 294 | # intermediate model. |
| 295 | user = self.user |
| 296 | permission = Permission.objects.get(codename='change_author', content_type=self.author_ct) |
| 297 | user.user_permissions.add(permission) |
| 298 | permission = Permission.objects.get(codename='add_book', content_type=self.book_ct) |
| 299 | user.user_permissions.add(permission) |
| 300 | response = self.client.get('/admin/admin_inlines/author/1/') |
| 301 | self.assertNotContains(response, '<h2>Author-book relationships</h2>') |
| 302 | self.assertNotContains(response, 'Add another Author-Book Relationship') |
| 303 | self.assertNotContains(response, 'id="id_Author_books-TOTAL_FORMS"') |
| 304 | self.assertNotContains(response, 'id="id_Author_books-0-DELETE"') |
| 305 | |
| 306 | def test_inline_change_m2m_change_perm(self): |
| 307 | # Editing the preexisting m2m relation as well as adding additional |
| 308 | # ones should be possible. |
| 309 | user = self.user |
| 310 | permission = Permission.objects.get(codename='change_author', content_type=self.author_ct) |
| 311 | user.user_permissions.add(permission) |
| 312 | permission = Permission.objects.get(codename='change_book', content_type=self.book_ct) |
| 313 | user.user_permissions.add(permission) |
| 314 | response = self.client.get('/admin/admin_inlines/author/1/') |
| 315 | self.assertContains(response, '<h2>Author-book relationships</h2>') |
| 316 | self.assertContains(response, 'Add another Author-Book Relationship') |
| 317 | self.assertContains(response, 'value="4" id="id_Author_books-TOTAL_FORMS"') |
| 318 | self.assertContains(response, '<input type="hidden" name="Author_books-0-id" value="1"') |
| 319 | self.assertContains(response, 'id="id_Author_books-0-DELETE"') |
| 320 | |
| 321 | def test_inline_change_fk_add_perm(self): |
| 322 | user = self.user |
| 323 | permission = Permission.objects.get(codename='change_holder', content_type=self.holder_ct) |
| 324 | user.user_permissions.add(permission) |
| 325 | permission = Permission.objects.get(codename='add_inner', content_type=self.inner_ct) |
| 326 | user.user_permissions.add(permission) |
| 327 | response = self.client.get(self.change_url) |
| 328 | self.assertContains(response, '<h2>Inners</h2>') |
| 329 | self.assertContains(response, 'Add another Inner') |
| 330 | self.assertContains(response, 'value="3" id="id_inner_set-TOTAL_FORMS"') |
| 331 | self.assertNotContains(response, '<input type="hidden" name="inner_set-0-id" value="1"') |
| 332 | |
| 333 | def test_inline_change_fk_change_perm(self): |
| 334 | user = self.user |
| 335 | permission = Permission.objects.get(codename='change_holder', content_type=self.holder_ct) |
| 336 | user.user_permissions.add(permission) |
| 337 | permission = Permission.objects.get(codename='change_inner', content_type=self.inner_ct) |
| 338 | user.user_permissions.add(permission) |
| 339 | response = self.client.get(self.change_url) |
| 340 | self.assertContains(response, '<h2>Inners</h2>') |
| 341 | self.assertContains(response, 'value="1" id="id_inner_set-TOTAL_FORMS"') |
| 342 | self.assertContains(response, '<input type="hidden" name="inner_set-0-id" value="1"') |
| 343 | |
| 344 | def test_inline_change_fk_add_change_perm(self): |
| 345 | user = self.user |
| 346 | permission = Permission.objects.get(codename='change_holder', content_type=self.holder_ct) |
| 347 | user.user_permissions.add(permission) |
| 348 | permission = Permission.objects.get(codename='add_inner', content_type=self.inner_ct) |
| 349 | user.user_permissions.add(permission) |
| 350 | permission = Permission.objects.get(codename='change_inner', content_type=self.inner_ct) |
| 351 | user.user_permissions.add(permission) |
| 352 | response = self.client.get(self.change_url) |
| 353 | self.assertContains(response, '<h2>Inners</h2>') |
| 354 | self.assertContains(response, 'value="4" id="id_inner_set-TOTAL_FORMS"') |
| 355 | self.assertContains(response, '<input type="hidden" name="inner_set-0-id" value="1"') |
| 356 | |
| 357 | def test_inline_change_fk_del_perm(self): |
| 358 | # The Author ForeignKey in the Book model does not allow NULL values, |
| 359 | # so we use different models this time. |
| 360 | user = self.user |
| 361 | collection = TitleCollection.objects.create(pk=1) |
| 362 | title = Title.objects.create(collection=collection, title1='foo', title2='foo') |
| 363 | collection_ct = ContentType.objects.get_for_model(TitleCollection) |
| 364 | title_ct = ContentType.objects.get_for_model(Title) |
| 365 | permission = Permission.objects.get(codename='change_titlecollection', content_type=collection_ct) |
| 366 | user.user_permissions.add(permission) |
| 367 | permission = Permission.objects.get(codename='change_title', content_type=title_ct) |
| 368 | user.user_permissions.add(permission) |
| 369 | permission = Permission.objects.get(codename='delete_title', content_type=title_ct) |
| 370 | user.user_permissions.add(permission) |
| 371 | response = self.client.get('/admin/admin_inlines/titlecollection/1/') |
| 372 | self.assertContains(response, '<h2>Titles</h2>') |
| 373 | self.assertContains(response, 'id="id_title_set-0-DELETE"') |