vkk.users.models
A collection of models classes and function associated with user management.
1""" 2A collection of models classes and function associated with user management. 3""" 4 5import secrets 6from smtplib import SMTPException 7from django.db import models 8from django.utils import timezone 9from django.conf import settings 10from django.contrib.auth.models import AbstractBaseUser, BaseUserManager 11from django.contrib.auth.hashers import make_password 12from django.utils.translation import gettext_lazy as _ 13from django.core.exceptions import ValidationError 14from django.core.signing import Signer 15from django.core.mail import send_mail 16from django.template.loader import get_template 17from django.urls import reverse 18 19SALT = 'W3VIvFmvhhvePbCmC_hcixstdpNRrjB5QqleEwvE2Uh00Mbi9VYfU-dixweZ3n1-e9JpkcHIw6XzCIZ_nefLqg' 20 21# Helper functions for User creation notifications 22 23 24def signed_url(new_user): 25 """ 26 Returns a url to a page which allows the user can confirm their registration. 27 """ 28 return reverse( 29 'vkk:users:registration_confirm', 30 args=new_user.get_signed_token().split('/') 31 ) 32 33 34def send_registration_mail(user): 35 """ 36 Notifies the User of their registration. A new NewUser instance is derived accordingly. 37 """ 38 new_user = NewUser.objects.create(user=user) 39 context = {'user': user, 'new_user': new_user, 40 'signed_url': signed_url(new_user)} 41 subject = get_template('vkk/users/registration_subject.txt') 42 message = get_template('vkk/users/registration_email.txt') 43 try: 44 send_mail( 45 subject=subject.render(context=context), 46 message=message.render(context=context), 47 from_email=None, 48 recipient_list=[user.email] 49 ) 50 except SMTPException: 51 new_user.delete() 52 raise 53 54 55def send_renew_mail(new_user): 56 """ 57 Renews a NewUser instance and sends a notification to them. 58 """ 59 user = new_user.user 60 new_user.renew_token() 61 context = {'user': user, 'new_user': new_user, 62 'signed_url': signed_url(new_user)} 63 subject = get_template('vkk/users/renew_subject.txt') 64 message = get_template('vkk/users/renew_email.txt') 65 send_mail( 66 subject=subject.render(context=context), 67 message=message.render(context=context), 68 from_email=None, 69 recipient_list=[user.email] 70 ) 71 72# Create your models here. 73 74 75class UserManager(BaseUserManager): 76 """ 77 A manager class for the User model. 78 """ 79 80 def _create_user(self, email, first_name, password, **extra_fields): 81 """ 82 Creates and saves a user with the given email, password and first_name. 83 (last_name as well, if provided.) 84 """ 85 email = self.normalize_email(email) 86 if not email or email == '': 87 raise ValueError('An email address must be provided') 88 user = self.model(email=email, first_name=first_name, **extra_fields) 89 user.password = make_password(password) 90 user.full_clean() 91 user.save(using=self._db) 92 return user 93 94 def create_user(self, email, first_name, password=None, **extra_fields): 95 """ 96 Creates and saves a typical user. 97 """ 98 extra_fields.setdefault('is_accountant', False) 99 extra_fields.setdefault('is_active', False) 100 return self._create_user(email, first_name, password, **extra_fields) 101 102 def create_superuser(self, email, first_name, password=None, **extra_fields): 103 """ 104 Creates and saves a user with accounting permissions. 105 """ 106 extra_fields.setdefault('is_accountant', True) 107 extra_fields.setdefault('is_active', False) 108 if extra_fields.get('is_accountant') is not True: 109 raise ValueError('Accountants must have is_accounting=True') 110 return self._create_user(email, first_name, password, **extra_fields) 111 112 113class User(AbstractBaseUser): 114 """ 115 An option user model similar to AbstractUser stripped to the needs of this app. 116 """ 117 first_name = models.CharField(_('first name'), max_length=150) 118 last_name = models.CharField(_('last name'), max_length=150, blank=True) 119 email = models.EmailField(_('email address'), unique=True) 120 is_accountant = models.BooleanField( 121 verbose_name=_('accountant status'), 122 default=False, 123 help_text=_( 124 'Designates whether the user can access features reserved for accounting.'), 125 ) 126 is_active = models.BooleanField( 127 verbose_name=_('active'), 128 default=True, 129 help_text=_( 130 'Designates whether this user should be treated as active. ' 131 'Unselect this instead of deleting accounts.' 132 ), 133 ) 134 135 EMAIL_FIELD = 'email' 136 USERNAME_FIELD = 'email' 137 REQUIRED_FIELDS = ['first_name'] 138 139 objects = UserManager() 140 141 class Meta: 142 """ 143 A meta class for configuration. 144 """ 145 verbose_name = _('user') 146 verbose_name_plural = _('users') 147 default_permissions = () 148 149 def get_full_name(self): 150 """ 151 Return the first_name plus the last_name, with a space in between. 152 """ 153 full_name = '%s %s' % (self.first_name, self.last_name) 154 return full_name.strip() 155 156 def get_reverse_full_name(self): 157 """ 158 Return the last_name plus the first_name, with a space in between. 159 """ 160 full_name = '%s, %s' % (self.last_name, self.first_name) 161 return full_name.strip() 162 163 def get_short_name(self): 164 """ 165 Return the short name for the user. 166 """ 167 return self.last_name 168 169 def clean(self): 170 super().clean() 171 self.email = self.__class__.objects.normalize_email(self.email) 172 173 174class NewUserManager(models.Manager): 175 """ 176 A manager class for managing the `NewUser` model. 177 """ 178 179 def create(self, **kwargs): 180 """ 181 Creates a `NewUser` objects. 182 """ 183 token = secrets.token_urlsafe(32) 184 while (self.get_queryset().filter(token=token).exists()): 185 token = secrets.token_urlsafe(32) 186 kwargs.setdefault('token', token) 187 kwargs.setdefault( 188 'expiration_date', 189 timezone.now().date() + timezone.timedelta(days=21) 190 ) 191 return super().get_queryset().create(**kwargs) 192 193 # raises django.core.signing.BadSignature 194 def get_with_signed_token(self, signed_token): 195 """ 196 Return New User with signature check for token. 197 """ 198 signer = Signer(sep='/', salt=SALT) 199 token = signer.unsign(signed_token) 200 return self.get_queryset().get(token=token) 201 202 203class NewUser(models.Model): 204 """ 205 Model to keep track of new users who have not yet confirmed their credentials. 206 """ 207 user = models.OneToOneField( 208 to=settings.AUTH_USER_MODEL, 209 verbose_name=_('user'), 210 on_delete=models.CASCADE 211 ) 212 token = models.SlugField( 213 verbose_name=_('token'), 214 unique=True 215 ) 216 expiration_date = models.DateField( 217 verbose_name=_('expiration date'), 218 help_text="Date after which the generated token will be expired." 219 ) 220 221 def renew_token(self): 222 """ 223 Renews the token and sets the expiration date accordingly. 224 """ 225 self.token = secrets.token_urlsafe(32) 226 227 # making sure no duplicate token is generated and assigned 228 retry = True 229 while (retry): 230 retry = False 231 try: 232 self.validate_unique() 233 except ValidationError as exception: 234 if 'token' in exception.message_dict: 235 self.token = secrets.token_urlsafe(32) 236 retry = True 237 return self 238 239 def get_signed_token(self): 240 """ 241 Return the token as a url-safe signed string. 242 """ 243 return Signer(sep='/', salt=SALT).sign(self.token) 244 245 def is_expired(self): 246 """ 247 Returns whether given token is expired. 248 """ 249 return timezone.now().date() > self.expiration_date 250 251 objects = NewUserManager() 252 253 class Meta: 254 """ 255 A meta class for configuration. 256 """ 257 verbose_name = _('new user') 258 verbose_name_plural = _('new users') 259 default_permissions = ()
25def signed_url(new_user): 26 """ 27 Returns a url to a page which allows the user can confirm their registration. 28 """ 29 return reverse( 30 'vkk:users:registration_confirm', 31 args=new_user.get_signed_token().split('/') 32 )
Returns a url to a page which allows the user can confirm their registration.
35def send_registration_mail(user): 36 """ 37 Notifies the User of their registration. A new NewUser instance is derived accordingly. 38 """ 39 new_user = NewUser.objects.create(user=user) 40 context = {'user': user, 'new_user': new_user, 41 'signed_url': signed_url(new_user)} 42 subject = get_template('vkk/users/registration_subject.txt') 43 message = get_template('vkk/users/registration_email.txt') 44 try: 45 send_mail( 46 subject=subject.render(context=context), 47 message=message.render(context=context), 48 from_email=None, 49 recipient_list=[user.email] 50 ) 51 except SMTPException: 52 new_user.delete() 53 raise
Notifies the User of their registration. A new NewUser instance is derived accordingly.
56def send_renew_mail(new_user): 57 """ 58 Renews a NewUser instance and sends a notification to them. 59 """ 60 user = new_user.user 61 new_user.renew_token() 62 context = {'user': user, 'new_user': new_user, 63 'signed_url': signed_url(new_user)} 64 subject = get_template('vkk/users/renew_subject.txt') 65 message = get_template('vkk/users/renew_email.txt') 66 send_mail( 67 subject=subject.render(context=context), 68 message=message.render(context=context), 69 from_email=None, 70 recipient_list=[user.email] 71 )
Renews a NewUser instance and sends a notification to them.
76class UserManager(BaseUserManager): 77 """ 78 A manager class for the User model. 79 """ 80 81 def _create_user(self, email, first_name, password, **extra_fields): 82 """ 83 Creates and saves a user with the given email, password and first_name. 84 (last_name as well, if provided.) 85 """ 86 email = self.normalize_email(email) 87 if not email or email == '': 88 raise ValueError('An email address must be provided') 89 user = self.model(email=email, first_name=first_name, **extra_fields) 90 user.password = make_password(password) 91 user.full_clean() 92 user.save(using=self._db) 93 return user 94 95 def create_user(self, email, first_name, password=None, **extra_fields): 96 """ 97 Creates and saves a typical user. 98 """ 99 extra_fields.setdefault('is_accountant', False) 100 extra_fields.setdefault('is_active', False) 101 return self._create_user(email, first_name, password, **extra_fields) 102 103 def create_superuser(self, email, first_name, password=None, **extra_fields): 104 """ 105 Creates and saves a user with accounting permissions. 106 """ 107 extra_fields.setdefault('is_accountant', True) 108 extra_fields.setdefault('is_active', False) 109 if extra_fields.get('is_accountant') is not True: 110 raise ValueError('Accountants must have is_accounting=True') 111 return self._create_user(email, first_name, password, **extra_fields)
A manager class for the User model.
95 def create_user(self, email, first_name, password=None, **extra_fields): 96 """ 97 Creates and saves a typical user. 98 """ 99 extra_fields.setdefault('is_accountant', False) 100 extra_fields.setdefault('is_active', False) 101 return self._create_user(email, first_name, password, **extra_fields)
Creates and saves a typical user.
103 def create_superuser(self, email, first_name, password=None, **extra_fields): 104 """ 105 Creates and saves a user with accounting permissions. 106 """ 107 extra_fields.setdefault('is_accountant', True) 108 extra_fields.setdefault('is_active', False) 109 if extra_fields.get('is_accountant') is not True: 110 raise ValueError('Accountants must have is_accounting=True') 111 return self._create_user(email, first_name, password, **extra_fields)
Creates and saves a user with accounting permissions.
Inherited Members
- django.db.models.manager.BaseManager
- creation_counter
- auto_created
- use_in_migrations
- model
- name
- deconstruct
- check
- from_queryset
- contribute_to_class
- db_manager
- db
- get_queryset
- all
- django.contrib.auth.base_user.BaseUserManager
- normalize_email
- make_random_password
- get_by_natural_key
- django.db.models.manager.BaseManagerFromQuerySet
- aaggregate
- abulk_create
- abulk_update
- acontains
- acount
- acreate
- aearliest
- aexists
- aexplain
- afirst
- aget
- aget_or_create
- aggregate
- ain_bulk
- aiterator
- alast
- alatest
- alias
- annotate
- aupdate
- aupdate_or_create
- bulk_create
- bulk_update
- complex_filter
- contains
- count
- create
- dates
- datetimes
- defer
- difference
- distinct
- earliest
- exclude
- exists
- explain
- extra
- filter
- first
- get
- get_or_create
- in_bulk
- intersection
- iterator
- last
- latest
- none
- only
- order_by
- raw
- reverse
- select_for_update
- union
- update
- update_or_create
- using
- values
- values_list
114class User(AbstractBaseUser): 115 """ 116 An option user model similar to AbstractUser stripped to the needs of this app. 117 """ 118 first_name = models.CharField(_('first name'), max_length=150) 119 last_name = models.CharField(_('last name'), max_length=150, blank=True) 120 email = models.EmailField(_('email address'), unique=True) 121 is_accountant = models.BooleanField( 122 verbose_name=_('accountant status'), 123 default=False, 124 help_text=_( 125 'Designates whether the user can access features reserved for accounting.'), 126 ) 127 is_active = models.BooleanField( 128 verbose_name=_('active'), 129 default=True, 130 help_text=_( 131 'Designates whether this user should be treated as active. ' 132 'Unselect this instead of deleting accounts.' 133 ), 134 ) 135 136 EMAIL_FIELD = 'email' 137 USERNAME_FIELD = 'email' 138 REQUIRED_FIELDS = ['first_name'] 139 140 objects = UserManager() 141 142 class Meta: 143 """ 144 A meta class for configuration. 145 """ 146 verbose_name = _('user') 147 verbose_name_plural = _('users') 148 default_permissions = () 149 150 def get_full_name(self): 151 """ 152 Return the first_name plus the last_name, with a space in between. 153 """ 154 full_name = '%s %s' % (self.first_name, self.last_name) 155 return full_name.strip() 156 157 def get_reverse_full_name(self): 158 """ 159 Return the last_name plus the first_name, with a space in between. 160 """ 161 full_name = '%s, %s' % (self.last_name, self.first_name) 162 return full_name.strip() 163 164 def get_short_name(self): 165 """ 166 Return the short name for the user. 167 """ 168 return self.last_name 169 170 def clean(self): 171 super().clean() 172 self.email = self.__class__.objects.normalize_email(self.email)
An option user model similar to AbstractUser stripped to the needs of this app.
A wrapper for a deferred-loading field. When the value is read from this object the first time, the query is executed.
A wrapper for a deferred-loading field. When the value is read from this object the first time, the query is executed.
A wrapper for a deferred-loading field. When the value is read from this object the first time, the query is executed.
A wrapper for a deferred-loading field. When the value is read from this object the first time, the query is executed.
A wrapper for a deferred-loading field. When the value is read from this object the first time, the query is executed.
150 def get_full_name(self): 151 """ 152 Return the first_name plus the last_name, with a space in between. 153 """ 154 full_name = '%s %s' % (self.first_name, self.last_name) 155 return full_name.strip()
Return the first_name plus the last_name, with a space in between.
157 def get_reverse_full_name(self): 158 """ 159 Return the last_name plus the first_name, with a space in between. 160 """ 161 full_name = '%s, %s' % (self.last_name, self.first_name) 162 return full_name.strip()
Return the last_name plus the first_name, with a space in between.
164 def get_short_name(self): 165 """ 166 Return the short name for the user. 167 """ 168 return self.last_name
Return the short name for the user.
170 def clean(self): 171 super().clean() 172 self.email = self.__class__.objects.normalize_email(self.email)
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.
A wrapper for a deferred-loading field. When the value is read from this object the first time, the query is executed.
A wrapper for a deferred-loading field. When the value is read from this object the first time, the query is executed.
A wrapper for a deferred-loading field. When the value is read from this object the first time, the query is executed.
Accessor to the related object on the reverse side of a one-to-one relation.
In the example::
class Restaurant(Model):
place = OneToOneField(Place, related_name='restaurant')
Place.restaurant
is a ReverseOneToOneDescriptor
instance.
Accessor to the related objects manager on the reverse side of a many-to-one relation.
In the example::
class Child(Model):
parent = ForeignKey(Parent, related_name='children')
Parent.children
is a ReverseManyToOneDescriptor
instance.
Most of the implementation is delegated to a dynamically defined manager
class built by create_forward_many_to_many_manager()
defined below.
Accessor to the related objects manager on the reverse side of a many-to-one relation.
In the example::
class Child(Model):
parent = ForeignKey(Parent, related_name='children')
Parent.children
is a ReverseManyToOneDescriptor
instance.
Most of the implementation is delegated to a dynamically defined manager
class built by create_forward_many_to_many_manager()
defined below.
Inherited Members
- django.db.models.base.Model
- Model
- from_db
- pk
- get_deferred_fields
- refresh_from_db
- arefresh_from_db
- serializable_value
- asave
- save_base
- delete
- adelete
- prepare_database_save
- validate_unique
- date_error_message
- unique_error_message
- get_constraints
- validate_constraints
- full_clean
- clean_fields
- check
- django.contrib.auth.base_user.AbstractBaseUser
- Meta
- save
- get_username
- natural_key
- is_anonymous
- is_authenticated
- set_password
- check_password
- set_unusable_password
- has_usable_password
- get_session_auth_hash
- get_session_auth_fallback_hash
- get_email_field_name
- normalize_username
The requested object does not exist
Inherited Members
- builtins.Exception
- Exception
- django.core.exceptions.ObjectDoesNotExist
- silent_variable_failure
- builtins.BaseException
- with_traceback
- add_note
- args
The query returned multiple objects when only one was expected.
Inherited Members
- builtins.Exception
- Exception
- builtins.BaseException
- with_traceback
- add_note
- args
175class NewUserManager(models.Manager): 176 """ 177 A manager class for managing the `NewUser` model. 178 """ 179 180 def create(self, **kwargs): 181 """ 182 Creates a `NewUser` objects. 183 """ 184 token = secrets.token_urlsafe(32) 185 while (self.get_queryset().filter(token=token).exists()): 186 token = secrets.token_urlsafe(32) 187 kwargs.setdefault('token', token) 188 kwargs.setdefault( 189 'expiration_date', 190 timezone.now().date() + timezone.timedelta(days=21) 191 ) 192 return super().get_queryset().create(**kwargs) 193 194 # raises django.core.signing.BadSignature 195 def get_with_signed_token(self, signed_token): 196 """ 197 Return New User with signature check for token. 198 """ 199 signer = Signer(sep='/', salt=SALT) 200 token = signer.unsign(signed_token) 201 return self.get_queryset().get(token=token)
A manager class for managing the NewUser
model.
180 def create(self, **kwargs): 181 """ 182 Creates a `NewUser` objects. 183 """ 184 token = secrets.token_urlsafe(32) 185 while (self.get_queryset().filter(token=token).exists()): 186 token = secrets.token_urlsafe(32) 187 kwargs.setdefault('token', token) 188 kwargs.setdefault( 189 'expiration_date', 190 timezone.now().date() + timezone.timedelta(days=21) 191 ) 192 return super().get_queryset().create(**kwargs)
Creates a NewUser
objects.
195 def get_with_signed_token(self, signed_token): 196 """ 197 Return New User with signature check for token. 198 """ 199 signer = Signer(sep='/', salt=SALT) 200 token = signer.unsign(signed_token) 201 return self.get_queryset().get(token=token)
Return New User with signature check for token.
Inherited Members
- django.db.models.manager.BaseManager
- creation_counter
- auto_created
- use_in_migrations
- model
- name
- deconstruct
- check
- from_queryset
- contribute_to_class
- db_manager
- db
- get_queryset
- all
- django.db.models.manager.BaseManagerFromQuerySet
- aaggregate
- abulk_create
- abulk_update
- acontains
- acount
- acreate
- aearliest
- aexists
- aexplain
- afirst
- aget
- aget_or_create
- aggregate
- ain_bulk
- aiterator
- alast
- alatest
- alias
- annotate
- aupdate
- aupdate_or_create
- bulk_create
- bulk_update
- complex_filter
- contains
- count
- dates
- datetimes
- defer
- difference
- distinct
- earliest
- exclude
- exists
- explain
- extra
- filter
- first
- get
- get_or_create
- in_bulk
- intersection
- iterator
- last
- latest
- none
- only
- order_by
- raw
- reverse
- select_for_update
- union
- update
- update_or_create
- using
- values
- values_list
204class NewUser(models.Model): 205 """ 206 Model to keep track of new users who have not yet confirmed their credentials. 207 """ 208 user = models.OneToOneField( 209 to=settings.AUTH_USER_MODEL, 210 verbose_name=_('user'), 211 on_delete=models.CASCADE 212 ) 213 token = models.SlugField( 214 verbose_name=_('token'), 215 unique=True 216 ) 217 expiration_date = models.DateField( 218 verbose_name=_('expiration date'), 219 help_text="Date after which the generated token will be expired." 220 ) 221 222 def renew_token(self): 223 """ 224 Renews the token and sets the expiration date accordingly. 225 """ 226 self.token = secrets.token_urlsafe(32) 227 228 # making sure no duplicate token is generated and assigned 229 retry = True 230 while (retry): 231 retry = False 232 try: 233 self.validate_unique() 234 except ValidationError as exception: 235 if 'token' in exception.message_dict: 236 self.token = secrets.token_urlsafe(32) 237 retry = True 238 return self 239 240 def get_signed_token(self): 241 """ 242 Return the token as a url-safe signed string. 243 """ 244 return Signer(sep='/', salt=SALT).sign(self.token) 245 246 def is_expired(self): 247 """ 248 Returns whether given token is expired. 249 """ 250 return timezone.now().date() > self.expiration_date 251 252 objects = NewUserManager() 253 254 class Meta: 255 """ 256 A meta class for configuration. 257 """ 258 verbose_name = _('new user') 259 verbose_name_plural = _('new users') 260 default_permissions = ()
Model to keep track of new users who have not yet confirmed their credentials.
Accessor to the related object on the forward side of a one-to-one relation.
In the example::
class Restaurant(Model):
place = OneToOneField(Place, related_name='restaurant')
Restaurant.place
is a ForwardOneToOneDescriptor
instance.
A wrapper for a deferred-loading field. When the value is read from this object the first time, the query is executed.
A wrapper for a deferred-loading field. When the value is read from this object the first time, the query is executed.
222 def renew_token(self): 223 """ 224 Renews the token and sets the expiration date accordingly. 225 """ 226 self.token = secrets.token_urlsafe(32) 227 228 # making sure no duplicate token is generated and assigned 229 retry = True 230 while (retry): 231 retry = False 232 try: 233 self.validate_unique() 234 except ValidationError as exception: 235 if 'token' in exception.message_dict: 236 self.token = secrets.token_urlsafe(32) 237 retry = True 238 return self
Renews the token and sets the expiration date accordingly.
240 def get_signed_token(self): 241 """ 242 Return the token as a url-safe signed string. 243 """ 244 return Signer(sep='/', salt=SALT).sign(self.token)
Return the token as a url-safe signed string.
246 def is_expired(self): 247 """ 248 Returns whether given token is expired. 249 """ 250 return timezone.now().date() > self.expiration_date
Returns whether given token is expired.
Method descriptor with partial application of the given arguments and keywords.
Supports wrapping existing descriptors and handles non-descriptor callables as instance methods.
Method descriptor with partial application of the given arguments and keywords.
Supports wrapping existing descriptors and handles non-descriptor callables as instance methods.
A wrapper for a deferred-loading field. When the value is read from this object the first time, the query is executed.
Inherited Members
- django.db.models.base.Model
- Model
- from_db
- pk
- get_deferred_fields
- refresh_from_db
- arefresh_from_db
- serializable_value
- save
- asave
- save_base
- delete
- adelete
- prepare_database_save
- clean
- validate_unique
- date_error_message
- unique_error_message
- get_constraints
- validate_constraints
- full_clean
- clean_fields
- check
The requested object does not exist
Inherited Members
- builtins.Exception
- Exception
- django.core.exceptions.ObjectDoesNotExist
- silent_variable_failure
- builtins.BaseException
- with_traceback
- add_note
- args
The query returned multiple objects when only one was expected.
Inherited Members
- builtins.Exception
- Exception
- builtins.BaseException
- with_traceback
- add_note
- args