With this blog post, I will get started with the code. The code for the project can be found in the repository:
Besides the
usual Django project setup, for now the environment variables such as
SECRET_KEY have been defined in a file env.py file which is importing in settings.py
file. The variables that need to be defined are specified in env_example.py
file. There are definitely more elegant ways to do this, but that can come
later when I am about to deploy a first version.
Though I
intend to only collect the email of a user as required info, I thought it best
to define a custom user model rather than use the User model that is present in
django.contrib.auth.models. This is done with:
class User(AbstractUser):
'''User model for authentication and authorization'''
def save(self, *args, **kwargs):
'''Username validation during save to db'''
try:
validate_email(self.username)
except:
raise ValidationError('Username must be a valid email')
else:
super().save(*args, **kwargs)
def clean_fields(self, exclude=None):
'''Ensure that username is a valid email in Admin dashboard.'''
try:
validate_email(self.username)
except:
raise ValidationError('Username must be a valid email')
The custom User model being defined imports AbstractUser model from django.contrib.auth.models. This provides all the working features of the in-built User model without creating a database table. For now, all I wish to do is enforce the requirement that the username should be a valid email. For that, I overrode two methods – clean_fields and save. The clean_fields method works when a form is filled such as in the Admin dashboard. Since this will be a REST API, users will be created using User.objects.create() method, and this method does not call the clean_fields model method. So, it is necessary to override the save() model method as well. Having only the save() method and omitting the clean_fields() method is possible, but when using the Admin dashboard, this will result in an exception that breaks the server rather than a nice error message in the form field.
I wrote a test for this right away in the tests.py file:
def test_username_only_email():
'''Test that the username can only be a valid email'''
# Passing test with valid email
user1 = User(username='someuser@domain.com')
user1.set_password('somepasswordfortest')
user1.save()
users_in_db = User.objects.all().count()
assert users_in_db == 1
# Failing test with normal text instead of email
with pytest.raises(Exception):
user2 = User(username='someuser')
user2.set_password('someotherpassword')
user2.save()
users_in_db = User.objects.all().count()
assert users_in_db == 1
The test creates a user with the username being a valid email and verifies that there is now one user created in the database. The test then tries to create a user with a username not being an email address and verifies that an exception is raised.
No comments:
Post a Comment