Draw the plan, get to work
~ Laptop and the studious Lady
Table of Content
- Introduction
- What is a model?
- Django Model fields types
- Building the CV/Resume models in Django
- Uploading image via Django Admin site
Introduction
Recall our Database Entity-Relation diagram in Part 1, we shall be using the design to create our models in Django project named CVeate which we setup in Part 2.
Fresher:
In cv app, we can see the folder structure
|--- cveate
|--- cv
|--- __init__.py
|--- admin.py
|--- apps.py
|--- models.py
|--- tests.py
|--- views.py
From the ER-diagram, we would create the following models in cv/models.py:
- Resume houses the basic resume information such as the user it belongs to.
- EmploymentHistory stores employment information including Internships and Apprenticeships.
- EducationHistory stores education information.
- Qualification stores certification ( or testimonial) acquired.
- Skill stores technical and non-technical skills.
- Referee stores referee which is usually expected on resumes.
- WebLink stores social media links such as LinkedIn URL.
- ResumeTemplate stores the various HTML templates.
When we think of a resume, we think of a first name, last name email, job title, and professional summary or career objectives. Those are key fields that cannot be left out when filling a typical resume.
To begin, we first need a top-down understanding of how models work.
What is a model?
A model can be imagined as a 'sample' of how things should be represented. Let's imagine you visit a clothing store, one usually sees a mannequin which is dressed in a sample of clothing. This is done to help you visualize the same clothing on yourself or someone else with a similar body type.
Model gives you a glimpse of what to expect. Same as actual models who walk at fashion shows.
In Django and back-end development in general, models act as a guide to what type of data is stored and what Django (or whichever programming language) should expect as inputs.
In Django, models have different data types as expected inputs, from characters to date to boolean (true/false) to URL input types.
Consider a spreadsheet named "Resume" that has columns with different names and different types of inputs,
In each record (row), one cannot only put a value that agrees with that specific field when data validation is enabled. If you try to put a number in a text field, you will be told that cannot be accepted.
The above is a visual example of what models do. There can be different constraints (rules) that each field can have. For example, one can make sure that a field cannot be left empty. Hence, a value must be provided by the user, in Django models, you can specify the same thing by setting blank=False
although Django makes sure a value is provided by default.
Similarly, one can choose to allow users to skip an entry on a spreadsheet. This can be demonstrated by adding blank=True
on a field name in Django. However, for that to take effect, one needs to make sure that the database itself is aware of that change.
By this, one must either provide a default value which will be sent along with other records to the database or set null=True
which sends a value defined as null
to the database to show no entry in that field.
Therefore, the database becomes aware that the particular field can have no value if set null=True
in the model. And the database is given a standard or default value if default=<a standard value>
is set.
Django Model Field Types
We usually use different types of model fields for different data types. For example to represent primitive data types, Django models have the following:
- Short to medium length strings -
models.CharField(max_length=<add a number>)
- Long text -
models.TextField()
- Numbers -
models.IntegerField()
- Decimal number -
models.DecimalField(decimal_place=<add a decimal placement>)
- Boolean -
models.BooleanField()
- Floating point number -
models.FloatField()
Some other frequently used field types are:
- Email -
models.EmailField()
- URL -
models.URLField()
- IP Address -
models.IPAddressField()
- Image -
models.ImageField()
- File -
models.FileField()
- Date and Time -
models.DateTimeField()
- Date only -
models.DateField()
Model types used to establish relationships between other models are:
- One-to-one relationship -
models.OneToOne()
enforces uniqueness on the field name it is applied to. - Foreign relationship -
models.ForeignKey()
is almost the same as one-to-one, the only difference is foreign relationship is NOT unique. - Many-to-many relationship -
models.ManyToMany()
is a two-way relationship.
You can go through django documentation to learn more. In summary, model fields are mapping to real-world data types.
Building the CV/Resume models in Django
Since we need to identify the user who owns a resume, we establish a foreign relationship to Django's default User
model.
This means that a user can have more than one resume.
from django.contrib.auth.models import User
class Resume(models.Model):
'''Resume model'''
user = models.ForeignKey(User, on_delete=models.CASCADE, related_name='resumes')
job_title = models.CharField(max_length=200)
first_name = models.CharField(max_length=200)
last_name = models.CharField(max_length=200)
email = models.CharField(max_length=200)
professional_summary = models.TextField()
resume_template = models.ForeignKey(
ResumeTemplate,
on_delete=models.CASCADE
)
The above code block looks similar to the spreadsheet image showed previously.
Notice the additions to models.ForeignKey
for resume_template
field. The on_delete
answers the question what should happen to this field when the model it points to gets deleted?.
Possible answers are;
- models.CASCADE: wherever the referenced model instance is mentioned, delete it.
- models.SET_NULL: do not delete, instead make the field nullable. Note that this requires the
null=True
set on that field to prevent SQL constraint kicking in. - models.DO_NOTHING: does nothing.
- models.SET_DEFAULT: falls back to the default value set on that field. Similar to SET_NULL, this requires
default=<some default value>
set. - models.PROTECT: prevent deletion of the referenced object.
related_name
creates a reverse relation between the referenced model instance and the current model. For example in the Resume model, the User model can reference the Resume model without doing something like this; Resume.objects.filter(user=<a user ID>)
. Using related_name
, it can be done this way; user.resumes.all()
.
One can note that the Resume Template is a foreign key. We need to create the Resume Template model above the newly created Resume model.
For the ResumeTemplate
model, we shall give each template a name and a sample image which shall be uploaded by the admin via the admin site.
This is to allow users to have the option of selecting the desired template for their resume. Hence, the main idea behind a CV/Resume builder.
class ResumeTemplate(models.Model):
'''
This is used as the template options for Resume model.
'''
name = models.CharField(max_length=300)
template_image = models.ImageField(upload_to='resume_samples/')
def __str__(self):
return self.name
class Resume(models.Model):
....
upload_to
field in ImageField
answers the question "Where do you want the files that will be uploaded in the field to be stored?"
ImageField
requires Pillow, a python package, to be installed to utilize the ImageField
model data type. Run the following command:
>> pip install pillow
We run migrations to see how far we've gone and try interacting with the models from our admin site.
>> python manage.py makemigrations
The above command creates a migration file in cveate/cv/migrations/ folder. If you observe from the admin site, no changes have been seen. Now run:
>> python manage.py migrate
Visting localhost:8000/admin after providing your log in details, you will still see no difference. This is because you have not registered it on the admin site. To register, navigate to cveate/cv/admin.py
from django.contrib import admin
from cv import models # new
# Register your models here.
admin.site.register(models.ResumeTemplate) # new
admin.site.register(models.Resume) # new
Now, visit the admin site. Your dashboard should look similar to this:
Click on Resume Templates on the dashboard and create a new resume template named "casual". After creation, your Resume Template list should display this:
Add a resume that belongs to the admin (your account) via the admin dashboard. Upon creation, your Resume list should display this:
Notice resume shows "Resume object(1)" whereas resume template shows the name "casual".
This is because of the __str__
method included in ResumeTemplate
in cveate/cv/models.py file.
The method converts the model's object to a human-readable form depending on the return
statement.
To make resume human readable, let's add __str__
method in our Resume
model:
class Resume(models.Model):
'''Resume model'''
user = models.ForeignKey(User, on_delete=models.CASCADE, related_name='resumes')
job_title = models.CharField(max_length=200)
first_name = models.CharField(max_length=200)
last_name = models.CharField(max_length=200)
email = models.EmailField()
professional_summary = models.TextField()
resume_template = models.ForeignKey(
ResumeTemplate,
on_delete=models.CASCADE
)
def __str__(self): # new
return self.first_name # new
Refresh your admin site, you should no longer see "Resume object(1)".
Uploading Images via the Django Admin site
Upload a sample image you have on your computer to the resume template via the admin site. Mine is named "preview.jpg", .
You should see a link that holds the currently uploaded content, mine is "resume_samples/preview.jpg":
Try clicking on the link, you will be redirected to the dashboard with a warning message similar to the image below;
This is a result of how Django stores uploaded content. Uploaded contents are stored in a folder in the base called Media. To set up the above, we shall add it to our cveate/cveate/settings.py and cveate/cveate/urls.py files.
In cveate/cveate/settings.py,
...
STATIC_URL = '/static/'
MEDIA_URL = '/media/' # new
MEDIA_ROOT = os.path.join(BASE_DIR, 'media/') # new
In cveate/cveate/urls.py,
from django.conf import settings # <--- new
from django.conf.urls.static import static # new
from django.contrib import admin
from django.urls import path, include
urlpatterns = [
path('admin/', admin.site.urls),
path('resume', include('cv.urls')),
] + static(settings.MEDIA_URL, document_root=settings.MEDIA_ROOT) # new
Try clicking on the currently uploaded template file from the resume template, you should still see something similar to this error:
The above error simply says that the uploaded file is not currently stored in the media directory. Hence, we need to re-upload the file again to have it stored under media content.
Again, click on the currently uploaded template file for the resume template named casual. You should see a photo of the file you uploaded.
Other Models
Other models follow the same setup. Using the ER-diagram to design our models, we have the following in cveate/cv app:
What next?
This wraps up model creation for our CV/Resume builder. In the next part, we shall begin work on views, serializers, and routes๐.