| 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"') |