[Tut] How I Designed a Personal Portfolio Website with Django (Part 2) - Printable Version +- Sick Gaming (https://www.sickgaming.net) +-- Forum: Programming (https://www.sickgaming.net/forum-76.html) +--- Forum: Python (https://www.sickgaming.net/forum-83.html) +--- Thread: [Tut] How I Designed a Personal Portfolio Website with Django (Part 2) (/thread-101087.html) |
[Tut] How I Designed a Personal Portfolio Website with Django (Part 2) - xSicKxBot - 05-03-2023 How I Designed a Personal Portfolio Website with Django (Part 2) <div> <div class="kk-star-ratings kksr-auto kksr-align-left kksr-valign-top" data-payload='{"align":"left","id":"1332450","slug":"default","valign":"top","ignore":"","reference":"auto","class":"","count":"1","legendonly":"","readonly":"","score":"5","starsonly":"","best":"5","gap":"5","greet":"Rate this post","legend":"5\/5 - (1 vote)","size":"24","title":"How I Designed a Personal Portfolio Website with Django (Part 2)","width":"142.5","_legend":"{score}\/{best} - ({count} {votes})","font_factor":"1.25"}'> <div class="kksr-stars"> <div class="kksr-stars-inactive"> <div class="kksr-star" data-star="1" style="padding-right: 5px"> <div class="kksr-icon" style="width: 24px; height: 24px;"></div> </p></div> <div class="kksr-star" data-star="2" style="padding-right: 5px"> <div class="kksr-icon" style="width: 24px; height: 24px;"></div> </p></div> <div class="kksr-star" data-star="3" style="padding-right: 5px"> <div class="kksr-icon" style="width: 24px; height: 24px;"></div> </p></div> <div class="kksr-star" data-star="4" style="padding-right: 5px"> <div class="kksr-icon" style="width: 24px; height: 24px;"></div> </p></div> <div class="kksr-star" data-star="5" style="padding-right: 5px"> <div class="kksr-icon" style="width: 24px; height: 24px;"></div> </p></div> </p></div> <div class="kksr-stars-active" style="width: 142.5px;"> <div class="kksr-star" style="padding-right: 5px"> <div class="kksr-icon" style="width: 24px; height: 24px;"></div> </p></div> <div class="kksr-star" style="padding-right: 5px"> <div class="kksr-icon" style="width: 24px; height: 24px;"></div> </p></div> <div class="kksr-star" style="padding-right: 5px"> <div class="kksr-icon" style="width: 24px; height: 24px;"></div> </p></div> <div class="kksr-star" style="padding-right: 5px"> <div class="kksr-icon" style="width: 24px; height: 24px;"></div> </p></div> <div class="kksr-star" style="padding-right: 5px"> <div class="kksr-icon" style="width: 24px; height: 24px;"></div> </p></div> </p></div> </div> <div class="kksr-legend" style="font-size: 19.2px;"> 5/5 – (1 vote) </div> </p></div> <p>We are designing a personal portfolio website using the Django web framework. </p> <p>In the <a href="https://blog.finxter.com/how-i-designed-a-personal-portfolio-website-with-django/" data-type="post" data-id="1332381" target="_blank" rel="noreferrer noopener">first part of this series</a>, we learned how to create Django models for our database. We created an admin panel, and added ourselves as a superuser. I expect that by now you have used the admin panel to add your sample projects.</p> <p>You are required to go through the <a href="https://blog.finxter.com/how-i-designed-a-personal-portfolio-website-with-django/" data-type="post" data-id="1332381" target="_blank" rel="noreferrer noopener">first part</a> of this series for you to follow along with us if you haven’t already done so. In this series, we will create a view function using the sample projects. By the end of this series, we will have created a fully functioning personal portfolio website.</p> <h2 class="wp-block-heading">The View Function</h2> <div class="wp-block-image"> <figure class="aligncenter size-full"><img loading="lazy" decoding="async" width="779" height="519" src="https://blog.finxter.com/wp-content/uploads/2023/05/image-29.png" alt="" class="wp-image-1332468" srcset="https://blog.finxter.com/wp-content/uploads/2023/05/image-29.png 779w, https://blog.finxter.com/wp-content/uploads/2023/05/image-29-300x200.png 300w, https://blog.finxter.com/wp-content/uploads/2023/05/image-29-768x512.png 768w" sizes="(max-width: 779px) 100vw, 779px" /></figure> </div> <p>We can choose to use class-based views or function-based views, or both to create our portfolio website. </p> <p>If you use class-based views, you must subclass it with Django’s <code>ListView</code> class to list all your sample projects. For a full sample project description, you must create another class and subclass it with Django’s <code>DetailView</code> class.</p> <p>For our portfolio website, we will use function-based views so that you can learn how to query our database. Open the <code>views.py</code> file and write the following code:</p> <pre class="EnlighterJSRAW" data-enlighter-language="python" data-enlighter-theme="" data-enlighter-highlight="" data-enlighter-linenumbers="" data-enlighter-lineoffset="" data-enlighter-title="" data-enlighter-group="">from django.shortcuts import render from .models import Project # Create your views here. def project_list(request): projects = Project.objects.all() context = { 'projects': projects } return render(request, 'index.html', context) </pre> <p>We import the <code>Projects</code> class and perform a query to retrieve all the objects in the table. This is a simple example of querying a database. </p> <p>The <code>context</code> <a href="https://blog.finxter.com/python-dictionary/" data-type="post" data-id="5232" target="_blank" rel="noreferrer noopener">dictionary</a> that contains the object is sent to the template file using the <code>render()</code> function. The function also renders the template, <code>index.html</code>. This tells us that all information available in the <code>context</code> dictionary will be displayed in the given template file.</p> <p>This <code>view</code> function will only list all our sample projects. For a full description of the projects, we will have to create another view function.</p> <pre class="EnlighterJSRAW" data-enlighter-language="generic" data-enlighter-theme="" data-enlighter-highlight="" data-enlighter-linenumbers="" data-enlighter-lineoffset="" data-enlighter-title="" data-enlighter-group="">def full_view(request, pk): project = Project.objects.get(pk=pk) context = { 'project': project } return render(request, 'full_view.html', context) </pre> <p>This <code>view</code> function looks similar to the previous one only that it comes with another parameter, <code>pk</code>. We perform another query to get an object based on its primary key. I will soon explain what is meant by primary key.</p> <h2 class="wp-block-heading">Template Files</h2> <div class="wp-block-image"> <figure class="aligncenter size-full"><img decoding="async" loading="lazy" width="779" height="519" src="https://blog.finxter.com/wp-content/uploads/2023/05/image-28.png" alt="" class="wp-image-1332467" srcset="https://blog.finxter.com/wp-content/uploads/2023/05/image-28.png 779w, https://blog.finxter.com/wp-content/uploads/2023/05/image-28-300x200.png 300w, https://blog.finxter.com/wp-content/uploads/2023/05/image-28-768x512.png 768w" sizes="(max-width: 779px) 100vw, 779px" /></figure> </div> <p>We have to create two template files for our view functions. Then, a <code>base.html</code> file with Bootstrap added to make it look nice. The template files will inherit everything in the <code>base.html</code> file. Copy the following code and save it inside the templates folder as <code>base.html</code>.</p> <pre class="EnlighterJSRAW" data-enlighter-language="generic" data-enlighter-theme="" data-enlighter-highlight="" data-enlighter-linenumbers="" data-enlighter-lineoffset="" data-enlighter-title="" data-enlighter-group=""><link rel="stylesheet" href="https://stackpath.bootstrapcdn.com/bootstrap/4.1.3/css/bootstrap.min.css" integrity="sha384-MCw98/SFnGE8fJT3GXwEOngsV7Zt27NXFoaoApmYm81iuXoPkFOJwJ8ERdknLPMO" crossorigin="anonymous"> <nav class="navbar navbar-expand-lg navbar-light bg-light"> <div class="container"> <a class="navbar-brand" href="{% url 'index' %}">Personal Portfolio</a> <button class="navbar-toggler" type="button" data-toggle="collapse" data-target="#navbarSupportedContent" aria-controls="navbarSupportedContent" aria-expanded="false" aria-label="Toggle navigation"> <span class="navbar-toggler-icon"></span> </button> <div class="collapse navbar-collapse" id="navbarSupportedContent"> <ul class="navbar-nav mr-auto"> <li class="nav-item active"> <a class="nav-link" href="{% url 'index' %}">Home</a> </li> </ul> </div> </div> </nav> <div class="container"> {% block content %}{% endblock %} </div> <script src="https://code.jquery.com/jquery-3.3.1.slim.min.js" integrity="sha384-q8i/X+965DzO0rT7abK41JStQIAqVgRVzpbzo5smXKp4YfRvH+8abtTE1Pi6jizo" crossorigin="anonymous"></script> <script src="https://cdnjs.cloudflare.com/ajax/libs/popper.js/1.14.3/umd/popper.min.js" integrity="sha384-ZMP7rVo3mIykV+2+9J3UJ46jBk0WLaUAdn689aCwoqbBJiSnjAK/l8WvCWPIPm49" crossorigin="anonymous"></script> <script src="https://stackpath.bootstrapcdn.com/bootstrap/4.1.3/js/bootstrap.min.js" integrity="sha384-ChfqqxuZUCnJSK3+MXmPNIyE6ZbWh2IMqE241rYiqJxyMiZ6OW/JmZQ5stwEULTy" crossorigin="anonymous"></script> </pre> </p> <p>The <code>a</code> tag has a <code>{% ... %}</code> tag in its href attribute. This is Django’s way of linking to the <code>index.html</code> file. The <code>block content</code> and <code>endblock</code> tags inside the <code>div</code> tag is reserved for any template file inheriting from the <code>base.html</code> file. </p> <p>As we will see, any template file inheriting from the <code>base.html</code> file must include such tags.</p> <p>The Bootstrap files are beyond the scope of this article. Check the documentation to learn more.</p> <p>Create the <code>index.html</code> file inside the template folder as indicated in the view function. Then, write the following script:</p> <pre class="EnlighterJSRAW" data-enlighter-language="generic" data-enlighter-theme="" data-enlighter-highlight="" data-enlighter-linenumbers="" data-enlighter-lineoffset="" data-enlighter-title="" data-enlighter-group="">{% extends "base.html" %} {% load static %} {% block content %} <h1>Projects Completed</h1> <div class="row"> {% for project in projects %} <div class="col-md-4"> <div class="card mb-2"> <img class="card-img-top" src="{{ project.image.url }}"> <div class="card-body"> <h5 class="card-title">{{ project.title }}</h5> <p class="card-text">{{ project.description }}</p> <a href="{% url 'full_view' project.pk %}" class="btn btn-primary"> Read More </a> </div> </div> </div> {% endfor %} </div> {% endblock %} </pre> <p>The <code>index.html</code> file inherits the <code>base.html</code> as shown in the first line of the code. Imagine all the scripts we have to write to render hundreds or even thousands of sample projects! Once again, Django comes to the rescue with its template engine, for loops.</p> <div class="wp-block-image"> <figure class="aligncenter size-full"><img decoding="async" loading="lazy" width="713" height="401" src="https://blog.finxter.com/wp-content/uploads/2023/05/image-25.png" alt="" class="wp-image-1332460" srcset="https://blog.finxter.com/wp-content/uploads/2023/05/image-25.png 713w, https://blog.finxter.com/wp-content/uploads/2023/05/image-25-300x169.png 300w" sizes="(max-width: 713px) 100vw, 713px" /></figure> </div> <p>Using the <code>for</code> loop, we loop through all the projects (no matter how many they are) passed by the context dictionary. Each iteration renders the image (using the <code>img</code> tag), the title, the description and a link to get the full description of the project.</p> <p>Notice that the <code>a</code> tag is pointing to a given project represented as <code>project.pk</code>. This is the primary key passed as a parameter in the second view function. More on that soon.</p> <p>Again, notice the <code>block content</code> and the <code>endblock</code> tags. Since the <code>index.html</code> extends the <code>base.html</code> file, it will only display all that is found in the file. Any addition must be written inside the <code>block content</code> and <code>endblock</code> tags. Otherwise, it won’t be displayed.</p> <p>Finally, the <code>full_view.html</code> file:</p> <pre class="EnlighterJSRAW" data-enlighter-language="generic" data-enlighter-theme="" data-enlighter-highlight="" data-enlighter-linenumbers="" data-enlighter-lineoffset="" data-enlighter-title="" data-enlighter-group="">{% extends "base.html" %} {% load static %} {% block content %} <h1>{{ project.title }}</h1> <div class="row"> <div class="col-md-8"> <img src="{% project.image.url %}" alt="" width="100%"> </div> <div class="col-md-4"> <h5>About the project:</h5> <p>{{ project.description }}</p> <br> <h5>Technology used:</h5> <p>{{ project.technology }}</p> </div> </div> {% endblock %} </pre> <p>That’s all about our template files. Notice how everything is linked to our database model as we learned in the first part of this series. We retrieve data from the database, pass them to the view function, and render them in the template files.</p> <div class="wp-block-image"> <figure class="aligncenter size-full"><img decoding="async" loading="lazy" width="713" height="401" src="https://blog.finxter.com/wp-content/uploads/2023/05/image-26.png" alt="" class="wp-image-1332461" srcset="https://blog.finxter.com/wp-content/uploads/2023/05/image-26.png 713w, https://blog.finxter.com/wp-content/uploads/2023/05/image-26-300x169.png 300w" sizes="(max-width: 713px) 100vw, 713px" /></figure> </div> <p>We haven’t let Django know of an existing templates folder. Go to the <code>settings.py</code> file, under the <code>TEMPLATES</code> section. Register the <code>templates</code> folder there.</p> <pre class="EnlighterJSRAW" data-enlighter-language="python" data-enlighter-theme="" data-enlighter-highlight="" data-enlighter-linenumbers="" data-enlighter-lineoffset="" data-enlighter-title="" data-enlighter-group="">TEMPLATES = [ { 'BACKEND': 'django.template.backends.django.DjangoTemplates', 'DIRS': [os.path.join(BASE_DIR, 'templates')], #add these 'APP_DIRS': True, 'OPTIONS': { 'context_processors': [ 'django.template.context_processors.debug', 'django.template.context_processors.request', 'django.contrib.auth.context_processors.auth', 'django.contrib.messages.context_processors.messages', ], }, }, ] </pre> <h2 class="wp-block-heading">Registering the URLs</h2> <div class="wp-block-image"> <figure class="aligncenter size-full"><img decoding="async" loading="lazy" width="779" height="519" src="https://blog.finxter.com/wp-content/uploads/2023/05/image-27.png" alt="" class="wp-image-1332465" srcset="https://blog.finxter.com/wp-content/uploads/2023/05/image-27.png 779w, https://blog.finxter.com/wp-content/uploads/2023/05/image-27-300x200.png 300w, https://blog.finxter.com/wp-content/uploads/2023/05/image-27-768x512.png 768w" sizes="(max-width: 779px) 100vw, 779px" /></figure> </div> <p>We need to hook up our view functions to URLs. First is the project-level URL. Open the <code>urls.py</code> in the project folder, and add these:</p> <pre class="EnlighterJSRAW" data-enlighter-language="python" data-enlighter-theme="" data-enlighter-highlight="" data-enlighter-linenumbers="" data-enlighter-lineoffset="" data-enlighter-title="" data-enlighter-group="">from django.contrib import admin from django.urls import path, include from django.conf import settings from django.conf.urls.static import static urlpatterns = [ path('admin/', admin.site.urls), path('', include('portfolio.urls')), ] if settings.DEBUG: urlpatterns += static(settings.MEDIA_URL, document_root=settings.MEDIA_ROOT) </pre> <p>We register the file using the <code>include()</code> method. So, once we start the local server, and go to <code>http://127.0.0.1:8000</code>, we will see our home page which is the <code>index.html</code> file. Then, in the <code>if</code> statement, we tell Django where to find the user-uploaded images.</p> <p>Next are the app-level URLs. Create a <code>urls.py</code> file in the <code>portfolio</code> folder.</p> <pre class="EnlighterJSRAW" data-enlighter-language="python" data-enlighter-theme="" data-enlighter-highlight="" data-enlighter-linenumbers="" data-enlighter-lineoffset="" data-enlighter-title="" data-enlighter-group="">from django.urls import path from .views import project_list, full_view urlpatterns = [ path('', project_list, name='index'), path('<int:pk>/', full_view, name='full_view'), ] </pre> <p>The <code>full_view</code> URL is hooked up with a primary key. It is the same primary key in the templates file and in the second view function. This is an integer representing the number of each project. For the first project you added in the previous series, the URL will be <code>http://127.0.0.1:8000/1</code></p> <p>Hoping that everything is set, start the local server, and you will see everything displayed.</p> <h2 class="wp-block-heading">Conclusion</h2> <p>We have successfully come to the end of the second series of this project. The first series is here:</p> <p class="has-base-2-background-color has-background"><img src="https://s.w.org/images/core/emoji/14.0.0/72x72/1f4a1.png" alt="?" class="wp-smiley" style="height: 1em; max-height: 1em;" /> <strong>First Part</strong>: <a href="https://blog.finxter.com/how-i-designed-a-personal-portfolio-website-with-django/" data-type="URL" data-id="https://blog.finxter.com/how-i-designed-a-personal-portfolio-website-with-django/" target="_blank" rel="noreferrer noopener">How I Designed a Personal Portfolio Website with Django</a></p> <p>If you encounter errors, be sure to check the <a href="https://github.com/finxter/portfolio_website" data-type="URL" data-id="https://github.com/finxter/portfolio_website" target="_blank" rel="noreferrer noopener">full code on my GitHub</a> page. Our app is now running in the local server. In the final series of this project, we will see how we can deploy this to a production server.</p></p> </div> https://www.sickgaming.net/blog/2023/05/02/how-i-designed-a-personal-portfolio-website-with-django-part-2/ |