devilry_account — User account models and views

The devilry_account module takes care of autentication, user profile editing, user profile creation, etc.

About the devilry User model

Devilry uses a completely custom Django user model:

  • It does not have the permission framework.

  • Only superusers get access to the Django admin UI.

  • We use full names instead of separate first_name and last_name fields, but we store the last name to make it possible to sort on that.

  • We support multiple usernames for each user (only used when the authentication backend uses usernames).

  • We support multiple email addresses per user.

  • We do not use a boolean field for is_active. Instead we handle this via a suspended_datetime field, and we have a field where reason for suspension can be set.

  • We have a field for storing the preferred language of the user.

Datamodel API

The data models was introduced by issue 780.

class devilry.devilry_account.models.UserQuerySet(model=None, query=None, using=None, hints=None)

Use this if need to get efficient access to the primary UserEmail.

This will add the notification_useremail_objects attribute to each returned User. notification_useremail_objects is a list that you can use if you need access to the UserEmail objects. Use User.notification_emails() to access the emails as a list of strings.

Use this if need to get efficient access to the primary UserEmail.

This will add the primary_useremail_objects attribute to each returned User. primary_useremail_objects is a list, and you should not use it directly. Use User.primary_useremail_object() or User.primary_email() to access the primary email.

Use this if need to get efficient access to the primary UserName.

This will add the primary_username_objects attribute to each returned User. primary_username_objects is a list, and you should not use it directly. Use User.primary_username_object() or User.primary_username() to access the primary username.

filter_by_emails(emails)

Filter the queryset to only include users with email address in the emails iterable.

filter_by_usernames(usernames)

Filter the queryset to only include users with username in the usernames iterable.

class devilry.devilry_account.models.UserManager(*args, **kwargs)

Manager for User.

get_queryset()

Return a new QuerySet object. Subclasses can override this method to customize the behavior of the Manager.

See UserQuerySet.prefetch_related_notification_emails().

See UserQuerySet.prefetch_related_primary_email().

See UserQuerySet.prefetch_related_primary_username().

filter_by_emails(emails)

See UserQuerySet.filter_by_emails().

filter_by_usernames(usernames)

See UserQuerySet.filter_by_usernames().

user_is_basenodeadmin(user, *basenode_modelsclasses)

Check if the given user is admin on any of the given basenode_modelsclasses.

Parameters:

basenode_modelsclasses – Model classes. They must have an admins one-to-many relationship with User.

user_is_admin(user)

Check if the given user is admin on any subject or period.

user_is_admin_or_superuser(user)

Return True if user.is_superuser, and fall back to calling user_is_admin() if not.

user_is_examiner(user)

Returns True if the given user is examiner on any AssignmentGroup.

user_is_student(user)

Returns True if the given user is candidate on any AssignmentGroup.

create_user(username='', email='', password=None, **kwargs)

Create a new user.

Requires username or email, and both can be supplied. If username is supplied, we create a UserName object with is_primary=True, and if email is supplied, we create a UserEmail object with use_for_notifications=True.

If password is supplied, we set the password, otherwise we set an unusable password.

Other than that, you can provide any User fields except shortname. shortname is created from username or email (in that order).

create_superuser(password=None, **kwargs)

Create a new superuser.

get_by_email(email)

Get a user by any of their emails.

Raises:

User.DoesNotExist – If no UserEmail with the given email is found.

Returns:

The user object.

Return type:

User

get_by_username(username)

Get a user by any of their username.

Raises:

User.DoesNotExist – If no UserName with the given username is found.

Returns:

The user object.

Return type:

User

bulk_create_from_emails(emails)

Bulk create users for all the emails in the given emails iterator.

All users is created with unusable password.

We create a UserEmail object for each of the created users. This UserEmail object has is_primary set to True.

Raises:
  • devilry_account.exceptions.IllegalOperationError – If the

  • CRADMIN_LEGACY_USE_EMAIL_AUTH_BACKEND`-setting is False

Returns:

A (created_users, excluded_emails)-tuple.

created_users is a queryset with the created users.

excluded_emails is a set of the emails that already existed.

bulk_create_from_usernames(usernames)

Bulk create users for all the usernames in the given usernames iterator.

All users is created with unusable password.

We create a UserName object for each of the created users. This UserName object has is_primary set to True.

Raises:
  • devilry_account.exceptions.IllegalOperationError – If the

  • CRADMIN_LEGACY_USE_EMAIL_AUTH_BACKEND`-setting is True

Returns:

A (created_users, excluded_usernames)-tuple.

created_users is a queryset with the created users.

excluded_usernames is a set of the usernames that already existed.

class devilry.devilry_account.models.User(*args, **kwargs)

User model for Devilry.

is_superuser

Is this user a superuser?

shortname

Short name for the user. This will be set to the primary email address or to the primary username depending on the auth backend. Must be unique.

fullname

Full name of the user. Optional.

lastname

The last name of the user. Optional. Used to sort by last name.

datetime_joined

The datetime the user was created.

suspended_datetime

Datetime when this account was suspended.

suspended_reason

Reason why the account is suspended.

languagecode

The language code for the preferred language for the user.

property is_active

bool(x) -> bool

Returns True when the argument x is true, False otherwise. The builtins True and False are the only two instances of the class bool. The class bool is a subclass of the class int, and cannot be subclassed.

get_full_name()

Get the fullname, falling back to shortname if fullname is not set.

get_short_name()

Get the short name for the user.

get_displayname()

Get as much of the name as possible. If we have only shortname, return that, if we have both shortname and fullname, return <fullname> (<shortname>).

get_initials()

Get the initials of the users name.

clean()

Hook for doing any extra model-wide validation after clean() has been called on every field by self.clean_fields. Any ValidationError raised by this method will not be associated with a particular field; it will have a special-case association with the field defined by NON_FIELD_ERRORS.

property notification_emails

Get the notification emails as a list of strings.

Only works if you have used UserQuerySet.prefetch_related_notification_emails() on the queryset.

property primary_useremail_object

Get the primary email address of the user as a UserEmail object.

Only works if you have used UserQuerySet.prefetch_related_primary_email() on the queryset.

Returns None if we have no primary email.

property primary_email

Get the primary email address of the user as a string.

Only works if you have used UserQuerySet.prefetch_related_primary_email() on the queryset.

Returns None if we have no primary email.

property primary_username_object

Get the primary username of the user as a UserName object.

Only works if you have used UserQuerySet.prefetch_related_primary_username() on the queryset.

Returns None if we have no primary username.

property primary_username

Get the primary username of the user as a string.

Only works if you have used UserQuerySet.prefetch_related_primary_username() on the queryset.

Returns None if we have no primary username.

exception DoesNotExist
exception MultipleObjectsReturned
class devilry.devilry_account.models.MergedUser(*args, **kwargs)

A merge user is created when two users are merged by devilry.devilry_account.user_merger.UserMerger.

source_user

The user we merged from. This user will have lost most of their data after the merge, but some foreign keys like created_by etc. will still remain.

target_user

The target of the merge. Most foreign keys previously pointing to MergedUser.source_user will have been moved to this user. This is all documented in MergedUser.summary_json.

summary_json

Summary of the merge as JSON.

Documents what changes was made during the merge.

exception DoesNotExist
exception MultipleObjectsReturned
class devilry.devilry_account.models.AbstractUserIdentity(*args, **kwargs)

Base class for UserEmail and UserName.

user

Foreign key to the user owning this email address.

created_datetime

The datetime when this was created.

last_updated_datetime

The datetime when this was last updated.

clean()

Hook for doing any extra model-wide validation after clean() has been called on every field by self.clean_fields. Any ValidationError raised by this method will not be associated with a particular field; it will have a special-case association with the field defined by NON_FIELD_ERRORS.

class devilry.devilry_account.models.UserEmail(*args, **kwargs)

Stores a single email address for a User.

email

The email address of the user. Must be unique.

use_for_notifications

Is this a notification email for the user? A user can have multiple notification emails.

is_primary

Is this the primary email for the user? Valid values are: None and True, and only one UserEmail per user can have is_primary=True.

clean()

Hook for doing any extra model-wide validation after clean() has been called on every field by self.clean_fields. Any ValidationError raised by this method will not be associated with a particular field; it will have a special-case association with the field defined by NON_FIELD_ERRORS.

exception DoesNotExist
exception MultipleObjectsReturned
created_datetime

The datetime when this was created.

last_updated_datetime

The datetime when this was last updated.

user

Foreign key to the user owning this email address.

class devilry.devilry_account.models.UserName(*args, **kwargs)

Stores a single username for a User.

The username is used for login, and the primary username is synced into User.shortname.

username

The username of the user. Must be unique.

is_primary

Is this the primary username for the user? Valid values are: None and True, and only one UserName per user can have is_primary=True.

clean()

Hook for doing any extra model-wide validation after clean() has been called on every field by self.clean_fields. Any ValidationError raised by this method will not be associated with a particular field; it will have a special-case association with the field defined by NON_FIELD_ERRORS.

exception DoesNotExist
exception MultipleObjectsReturned
created_datetime

The datetime when this was created.

last_updated_datetime

The datetime when this was last updated.

user

Foreign key to the user owning this email address.

class devilry.devilry_account.models.PermissionGroupUser(*args, **kwargs)

Defines the many-to-many relationship between User and PermissionGroup.

permissiongroup

The group.

user

The user.

exception DoesNotExist
exception MultipleObjectsReturned
class devilry.devilry_account.models.PermissionGroupQuerySet(model=None, query=None, using=None, hints=None)
class devilry.devilry_account.models.PermissionGroup(*args, **kwargs)

Permission group data model.

Each group has a grouptype which determines the type of objects it can be added to.

GROUPTYPE_DEPARTMENTADMIN = 'departmentadmin'

The value for grouptype that identifies the group as a departmentadmin permission group.

GROUPTYPE_SUBJECTADMIN = 'subjectadmin'

The value for grouptype that identifies the group as a subjectadmin permission group.

GROUPTYPE_PERIODADMIN = 'periodadmin'

The value for grouptype that identifies the group as a periodadmin permission group.

GROUPTYPE_CHOICES = (('departmentadmin', 'Department administrator group'), ('subjectadmin', 'Course administrator group'), ('periodadmin', 'Semester administrator group'))

Choices for grouptype.

name

The name of the group. Must be unique.

created_datetime

The time this group was created.

updated_datetime

Last time this group was updated.

syncsystem_update_datetime

Last time this group was updated from a third party sync system.

grouptype

The grouptype determines what kind of object a group can be added to:

is_custom_manageable

Is this group manageable by normal admins?

Only superusers can edit the group if this is False. Use cases for setting this to False:

  • Superusers want to create groups that they have full control over.

  • Groups imported from a third party sync system.

If grouptype is departmentadmin, this can not be True.

users

Users belonging to this group.

clean()

Hook for doing any extra model-wide validation after clean() has been called on every field by self.clean_fields. Any ValidationError raised by this method will not be associated with a particular field; it will have a special-case association with the field defined by NON_FIELD_ERRORS.

exception DoesNotExist
exception MultipleObjectsReturned
class devilry.devilry_account.models.PeriodPermissionGroupQuerySet(model=None, query=None, using=None, hints=None)

QuerySet for PeriodPermission.

user_is_periodadmin_for_period(user, period)

Find out if the given user is "periodadmin" on the given period.

You normally do not use this directly, but use get_devilryrole_for_user_on_period().

Parameters:
Returns:

True if the given user is periodadmin on the given period, otherwise it returns False.

Return type:

bool

get_devilryrole_for_user_on_period(user, period)

Get the devilryrole for the given user on the given period.

See also

If you need to find the same information for a subject, use SubjectPermissionGroupQuerySet.get_devilryrole_for_user_on_subject().

Parameters:
Returns:

One of the following looked up in the listed order:

Return type:

str

get_custom_managable_periodpermissiongroup_for_period(period)

Get the PeriodPermissionGroup where PermissionGroup.is_custom_manageable is True for the given period.

Each devilry.apps.core.models.Period has exactly one custom managable PermissionGroup, which is the permission group where admins added by non-superusers via the admin UI are added.

Note

The queryset used does select_related('permissiongroup'), so looking up permissiongroup for the returned PeriodPermissionGroup does not require an extra database query.

Parameters:

period – A devilry.apps.core.models.Period object.

Raises:

PeriodPermissionGroup.DoesNotExist – If no custom managable PermissionGroup exists for the given period.

class devilry.devilry_account.models.PeriodPermissionGroup(*args, **kwargs)

Defines the many-to-many relationship between devilry.apps.core.Period and PermissionGroup.

permissiongroup

The group.

period

The devilry.apps.core.Period.

clean()

Hook for doing any extra model-wide validation after clean() has been called on every field by self.clean_fields. Any ValidationError raised by this method will not be associated with a particular field; it will have a special-case association with the field defined by NON_FIELD_ERRORS.

exception DoesNotExist
exception MultipleObjectsReturned
class devilry.devilry_account.models.SubjectPermissionGroupQuerySet(model=None, query=None, using=None, hints=None)

QuerySet for SubjectPermission.

user_is_departmentadmin_for_subject(user, subject)

Find out if the given user is "departmentadmin" on the given subject.

You normally do not use this directly, but use get_devilryrole_for_user_on_subject().

Parameters:
Returns:

True if the given user is departmentadmin on the given subject, otherwise it returns False.

Return type:

bool

user_is_subjectadmin_for_subject(user, subject)

Find out if the given user is "subjectadmin" on the given subject.

This does not take higher permissions into consideration, so you should check if the user is departmentadmin with user_is_departmentadmin_for_subject() if you want to find the highest permission for the user.

You normally do not use this directly, but use get_devilryrole_for_user_on_subject().

Parameters:
Returns:

True if the given user is subjectadmin on the given subject, otherwise it returns False.

Return type:

bool

get_devilryrole_for_user_on_subject(user, subject)

Get the devilryrole for the given user on the given subject.

See also

If you need to find the same information for a period, use PeriodPermissionGroupQuerySet.get_devilryrole_for_user_on_period().

Parameters:
Returns:

One of the following looked up in the listed order:

Return type:

str

get_custom_managable_subjectpermissiongroup_for_subject(subject)

Get the SubjectPermissionGroup where PermissionGroup.is_custom_manageable is True for the given subject.

Each devilry.apps.core.models.Subject has exactly one custom managable PermissionGroup, which is the permission group where admins added by non-superusers via the admin UI are added.

Note

The queryset used does select_related('permissiongroup'), so looking up permissiongroup for the returned SubjectPermissionGroup does not require an extra database query.

Parameters:

subject – A devilry.apps.core.models.Subject object.

Raises:

SubjectPermissionGroup.DoesNotExist – If no custom managable PermissionGroup exists for the given subject.

class devilry.devilry_account.models.SubjectPermissionGroup(*args, **kwargs)

Defines the many-to-many relationship between devilry.apps.core.Subject and PermissionGroup.

permissiongroup

The permissiongroup.

subject

The devilry.apps.core.Subject.

clean()

Hook for doing any extra model-wide validation after clean() has been called on every field by self.clean_fields. Any ValidationError raised by this method will not be associated with a particular field; it will have a special-case association with the field defined by NON_FIELD_ERRORS.

exception DoesNotExist
exception MultipleObjectsReturned
exception devilry.devilry_account.models.AssignmentGuidelinesLookupError
class devilry.devilry_account.models.PeriodUserGuidelineAcceptanceQuerySet(model=None, query=None, using=None, hints=None)
mark_guidelines_as_accepted(user: User, period: Period, devilryrole: str) bool

Mark guidelines as accepted unless they already are for the given user, period and devilryrole.

Returns:

True if a new PeriodUserGuidelineAcceptance instance was created, otherwise return False.

Return type:

bool

class devilry.devilry_account.models.PeriodUserGuidelineAcceptance(id, user, period, devilryrole, accepted_datetime, guidelines_version, matched_regex)
exception DoesNotExist
exception MultipleObjectsReturned