| Total Complexity | 53 |
| Total Lines | 289 |
| Duplicated Lines | 0 % |
Complex classes like RegistrationAdmin often do a lot of different things. To break such a class down, we need to identify a cohesive component within that class. A common approach to find such a component is to look for fields/methods that share the same prefixes, or suffixes.
Once you have determined the fields that belong together, you can apply the Extract Class refactoring. If the component makes sense as a sub-class, Extract Subclass is also a candidate, and is often faster.
| 1 | # -*- coding: utf-8 -*- |
||
| 161 | class RegistrationAdmin(admin.ModelAdmin): |
||
| 162 | """Admin class of RegistrationProfile |
||
| 163 | |||
| 164 | Admin users can accept/reject registration and activate user in Django |
||
| 165 | Admin page. |
||
| 166 | |||
| 167 | If ``REGISTRATION_SUPPLEMENT_CLASS`` is specified, admin users can see the |
||
| 168 | summary of the supplemental information in list view and detail of it in |
||
| 169 | change view. |
||
| 170 | |||
| 171 | ``RegistrationProfile`` is not assumed to handle by hand thus |
||
| 172 | adding/changing/deleting is not accepted even in Admin page. |
||
| 173 | ``RegistrationProfile`` only can be accepted/rejected or activated. |
||
| 174 | To prevent these disallowed functions, the special AdminForm called |
||
| 175 | ``RegistrationAdminForm`` is used. |
||
| 176 | Its ``save`` method is overridden and it actually does not save the |
||
| 177 | instance. |
||
| 178 | It just call ``accept``, ``reject`` or ``activate`` method of current |
||
| 179 | registration backend. So you don't want to override the ``save`` method of |
||
| 180 | the form. |
||
| 181 | |||
| 182 | """ |
||
| 183 | list_display = ('user', 'get_status_display', |
||
| 184 | 'activation_key_expired', 'display_supplement_summary') |
||
| 185 | raw_id_fields = ['user'] |
||
| 186 | search_fields = ('user__username', 'user__first_name', 'user__last_name') |
||
| 187 | list_filter = ('_status', ) |
||
| 188 | |||
| 189 | form = RegistrationAdminForm |
||
| 190 | backend = get_backend() |
||
| 191 | |||
| 192 | readonly_fields = ('user', '_status') |
||
| 193 | |||
| 194 | actions = ( |
||
| 195 | 'accept_users', |
||
| 196 | 'reject_users', |
||
| 197 | 'force_activate_users', |
||
| 198 | 'resend_acceptance_email' |
||
| 199 | ) |
||
| 200 | |||
| 201 | def __init__(self, model, admin_site): |
||
| 202 | super(RegistrationAdmin, self).__init__(model, admin_site) |
||
| 203 | if not hasattr(super(RegistrationAdmin, self), 'get_inline_instances'): |
||
| 204 | # Django 1.3 doesn't have ``get_inline_instances`` method but |
||
| 205 | # ``inline_instances`` was generated in ``__init__`` thus |
||
| 206 | # update the attribute |
||
| 207 | self.inline_instances = self.get_inline_instances(None) |
||
| 208 | |||
| 209 | def has_add_permission(self, request): |
||
| 210 | """registration profile should not be created by hand""" |
||
| 211 | return False |
||
| 212 | |||
| 213 | def has_delete_permission(self, request, obj=None): |
||
| 214 | """registration profile should not be created by hand""" |
||
| 215 | return False |
||
| 216 | |||
| 217 | def has_accept_permission(self, request, obj): |
||
| 218 | """whether the user has accept permission""" |
||
| 219 | if not settings.REGISTRATION_USE_OBJECT_PERMISSION: |
||
| 220 | obj = None |
||
| 221 | return request.user.has_perm('registration.accept_registration', obj) |
||
| 222 | |||
| 223 | def has_reject_permission(self, request, obj): |
||
| 224 | """whether the user has reject permission""" |
||
| 225 | if not settings.REGISTRATION_USE_OBJECT_PERMISSION: |
||
| 226 | obj = None |
||
| 227 | return request.user.has_perm('registration.reject_registration', obj) |
||
| 228 | |||
| 229 | def has_activate_permission(self, request, obj): |
||
| 230 | """whether the user has activate permission""" |
||
| 231 | if not settings.REGISTRATION_USE_OBJECT_PERMISSION: |
||
| 232 | obj = None |
||
| 233 | return request.user.has_perm('registration.activate_user', obj) |
||
| 234 | |||
| 235 | def get_actions(self, request): |
||
| 236 | """get actions displaied in admin site |
||
| 237 | |||
| 238 | RegistrationProfile should not be deleted in admin site thus |
||
| 239 | 'delete_selected' is disabled in default. |
||
| 240 | |||
| 241 | Each actions has permissions thus delete the action if the accessed |
||
| 242 | user doesn't have appropriate permission. |
||
| 243 | |||
| 244 | """ |
||
| 245 | actions = super(RegistrationAdmin, self).get_actions(request) |
||
| 246 | if 'delete_selected' in actions: |
||
| 247 | del actions['delete_selected'] |
||
| 248 | if not request.user.has_perm('registration.accept_registration'): |
||
| 249 | del actions['accept_users'] |
||
| 250 | if not request.user.has_perm('registration.reject_registration'): |
||
| 251 | del actions['reject_users'] |
||
| 252 | if not request.user.has_perm('registration.accept_registration') or \ |
||
| 253 | not request.user.has_perm('registration.activate_user'): |
||
| 254 | del actions['force_activate_users'] |
||
| 255 | return actions |
||
| 256 | |||
| 257 | def accept_users(self, request, queryset): |
||
| 258 | """Accept the selected users, if they are not already accepted""" |
||
| 259 | for profile in queryset: |
||
| 260 | self.backend.accept(profile, request=request, force=True) |
||
| 261 | accept_users.short_description = _( |
||
| 262 | "(Re)Accept registrations of selected users" |
||
| 263 | ) |
||
| 264 | |||
| 265 | def reject_users(self, request, queryset): |
||
| 266 | """Reject the selected users, if they are not already accepted""" |
||
| 267 | for profile in queryset: |
||
| 268 | self.backend.reject(profile, request=request) |
||
| 269 | reject_users.short_description = _( |
||
| 270 | "Reject registrations of selected users" |
||
| 271 | ) |
||
| 272 | |||
| 273 | def force_activate_users(self, request, queryset): |
||
| 274 | """Activates the selected users, if they are not already activated""" |
||
| 275 | for profile in queryset: |
||
| 276 | self.backend.accept(profile, request=request, send_email=False) |
||
| 277 | self.backend.activate(profile.activation_key, request=request) |
||
| 278 | force_activate_users.short_description = _( |
||
| 279 | "Activate selected users forcibly" |
||
| 280 | ) |
||
| 281 | |||
| 282 | def resend_acceptance_email(self, request, queryset): |
||
| 283 | """Re-sends acceptance emails for the selected users |
||
| 284 | |||
| 285 | Note that this will *only* send acceptance emails for users |
||
| 286 | who are eligible to activate; emails will not be sent to users |
||
| 287 | whose activation keys have expired or who have already |
||
| 288 | activated or rejected. |
||
| 289 | |||
| 290 | """ |
||
| 291 | site = get_site(request) |
||
| 292 | for profile in queryset: |
||
| 293 | if not profile.activation_key_expired(): |
||
| 294 | if profile.status != 'rejected': |
||
| 295 | profile.send_acceptance_email(site=site) |
||
| 296 | resend_acceptance_email.short_description = _( |
||
| 297 | "Re-send acceptance emails to selected users" |
||
| 298 | ) |
||
| 299 | |||
| 300 | def display_supplement_summary(self, obj): |
||
| 301 | """Display supplement summary |
||
| 302 | |||
| 303 | Display ``__unicode__`` method result of |
||
| 304 | ``REGISTRATION_SUPPLEMENT_CLASS`` |
||
| 305 | ``Not available`` when ``REGISTRATION_SUPPLEMENT_CLASS`` is not |
||
| 306 | specified |
||
| 307 | |||
| 308 | """ |
||
| 309 | if obj.supplement: |
||
| 310 | return force_text(obj.supplement) |
||
| 311 | return _('Not available') |
||
| 312 | display_supplement_summary.short_description = _( |
||
| 313 | 'A summary of supplemental information' |
||
| 314 | ) |
||
| 315 | |||
| 316 | def display_activation_key(self, obj): |
||
| 317 | """Display activation key with link |
||
| 318 | |||
| 319 | Note that displaying activation key is not recommended in security |
||
| 320 | reason. |
||
| 321 | If you really want to use this method, create your own subclass and |
||
| 322 | re-register to admin.site |
||
| 323 | |||
| 324 | Even this is a little bit risky, it is really useful for developping |
||
| 325 | (without checking email, you can activate any user you want) thus |
||
| 326 | I created but turned off in default :-p |
||
| 327 | |||
| 328 | """ |
||
| 329 | if obj.status == 'accepted': |
||
| 330 | activation_url = reverse('registration_activate', |
||
| 331 | kwargs={ |
||
| 332 | 'activation_key': obj.activation_key}) |
||
| 333 | return mark_safe('<a href="%s">%s</a>' % ( |
||
| 334 | activation_url, obj.activation_key)) |
||
| 335 | return _('Not available') |
||
| 336 | display_activation_key.short_description = _('Activation key') |
||
| 337 | display_activation_key.allow_tags = True |
||
| 338 | |||
| 339 | def get_inline_instances(self, request, obj=None): |
||
| 340 | """ |
||
| 341 | return inline instances with registration supplement inline instance |
||
| 342 | """ |
||
| 343 | inline_instances = [] |
||
| 344 | supplement_class = self.backend.get_supplement_class() |
||
| 345 | if supplement_class: |
||
| 346 | kwargs = { |
||
| 347 | 'extra': 1, |
||
| 348 | 'max_num': 1, |
||
| 349 | 'can_delete': False, |
||
| 350 | 'model': supplement_class, |
||
| 351 | } |
||
| 352 | inline_base = get_supplement_admin_inline_base_class() |
||
| 353 | inline_form = type( |
||
| 354 | str("RegistrationSupplementInlineAdmin"), |
||
| 355 | (inline_base,), kwargs |
||
| 356 | ) |
||
| 357 | inline_instances = [inline_form(self.model, self.admin_site)] |
||
| 358 | supercls = super(RegistrationAdmin, self) |
||
| 359 | if hasattr(supercls, 'get_inline_instances'): |
||
| 360 | if django.VERSION >= (1, 5): |
||
| 361 | inline_instances.extend(supercls.get_inline_instances( |
||
| 362 | request, obj |
||
| 363 | )) |
||
| 364 | else: |
||
| 365 | # Django 1.4 cannot handle obj |
||
| 366 | inline_instances.extend(supercls.get_inline_instances( |
||
| 367 | request, |
||
| 368 | )) |
||
| 369 | else: |
||
| 370 | # Django 1.3 |
||
| 371 | for inline_class in self.inlines: |
||
| 372 | inline_instance = inline_class(self.model, self.admin_site) |
||
| 373 | inline_instances.append(inline_instance) |
||
| 374 | return inline_instances |
||
| 375 | |||
| 376 | def get_object(self, request, object_id, from_field=None): |
||
| 377 | """add ``request`` instance to model instance and return |
||
| 378 | |||
| 379 | To get ``request`` instance in form, ``request`` instance is stored |
||
| 380 | in the model instance. |
||
| 381 | |||
| 382 | """ |
||
| 383 | if django.VERSION < (1, 8, 0): |
||
| 384 | obj = super(RegistrationAdmin, self).get_object(request, object_id) |
||
| 385 | else: |
||
| 386 | # Note: |
||
| 387 | # from_field was introduced from django 1.8 |
||
| 388 | obj = super(RegistrationAdmin, self).get_object(request, |
||
| 389 | object_id, |
||
| 390 | from_field) |
||
| 391 | if obj: |
||
| 392 | attr_name = settings._REGISTRATION_ADMIN_REQ_ATTR_NAME_IN_MODEL_INS |
||
| 393 | setattr(obj, attr_name, request) |
||
| 394 | return obj |
||
| 395 | |||
| 396 | @csrf_protect_m |
||
| 397 | @transaction_atomic |
||
| 398 | def change_view(self, request, object_id, form_url='', extra_context=None): |
||
| 399 | """called for change view |
||
| 400 | |||
| 401 | Check permissions of the admin user for ``POST`` request depends on |
||
| 402 | what action is requested and raise PermissionDenied if the action is |
||
| 403 | not accepted for the admin user. |
||
| 404 | |||
| 405 | """ |
||
| 406 | obj = self.get_object(request, unquote(object_id)) |
||
| 407 | |||
| 408 | # Permissin check |
||
| 409 | if request.method == 'POST': |
||
| 410 | # |
||
| 411 | # Note: |
||
| 412 | # actions will be treated in form.save() method. |
||
| 413 | # in general, activate action will remove the profile because |
||
| 414 | # the profile is no longer required after the activation |
||
| 415 | # but if I remove the profile in form.save() method, django admin |
||
| 416 | # will raise IndexError thus I passed `no_profile_delete = True` |
||
| 417 | # to activate with backend in form.save() method. |
||
| 418 | # |
||
| 419 | action_name = request.POST.get('action_name') |
||
| 420 | if (action_name == 'accept' and |
||
| 421 | not self.has_accept_permission(request, obj)): |
||
| 422 | raise PermissionDenied |
||
| 423 | elif (action_name == 'reject' and |
||
| 424 | not self.has_reject_permission(request, obj)): |
||
| 425 | raise PermissionDenied |
||
| 426 | elif (action_name == 'activate' and |
||
| 427 | not self.has_activate_permission(request, obj)): |
||
| 428 | raise PermissionDenied |
||
| 429 | elif action_name == 'force_activate' and ( |
||
| 430 | not self.has_accept_permission(request, obj) or |
||
| 431 | not self.has_activate_permission(request, obj)): |
||
| 432 | raise PermissionDenied |
||
| 433 | |||
| 434 | if django.VERSION < (1, 4,): |
||
| 435 | response = super(RegistrationAdmin, self).change_view( |
||
| 436 | request, object_id, extra_context |
||
| 437 | ) |
||
| 438 | else: |
||
| 439 | response = super(RegistrationAdmin, self).change_view( |
||
| 440 | request, object_id, form_url, extra_context |
||
| 441 | ) |
||
| 442 | |||
| 443 | if (request.method == 'POST' and |
||
| 444 | action_name in ('activate', 'force_activate')): |
||
| 445 | # if the requested data is valid then response will be an instance |
||
| 446 | # of ``HttpResponseRedirect`` otherwise ``TemplateResponse`` |
||
| 447 | if isinstance(response, HttpResponseRedirect): |
||
| 448 | obj.delete() |
||
| 449 | return response |
||
| 450 | admin.site.register(RegistrationProfile, RegistrationAdmin) |
||
| 451 |