Django Basics

over 1 year

2023 04

How to Develop a Basic Django application

Intro

We will create a Demo Project with Django which uses a default base Template and integrate it with D3

📁 Create project folder
mkdir 42-portfolio-demo 
cd 42-portfolio-demo

🌏 Create and load a virtual environment
This will create a Pipfile as well

sudo pipenv shell

pipenv install django

django-admin startproject portfolio_suite
cd portfolio_suite

python manage.py startapp portfolio_app
python manage.py migrate
python manage.py runserver 3001

🏛️ Now let's define the Models, this is the Structure for our DataBase
portfolio_app/models.py
from django.db import models

class User(models.Model):
    intr_username = models.CharField(max_length=30)
    intra_id = models.IntegerField(unique=True, db_index=True)
    first_name = models.CharField(max_length=50)
    last_name = models.CharField(max_length=50)
    email = models.CharField(max_length=150)
    bio = models.TextField(max_length=800)
    updated_at = models.DateTimeField(auto_created=True, auto_now=True)

    def _str_(self):
        return "@" + self.intr_username

This will create a migration file where you can see all changes that will be done to the database.
python manage.py makemigrations portfolio_app
...
...
mistake on email field, should be EmailField

rm portfolio_app/migrations/0001_initial.py

The migrate command will make the database take effect upon the migrations on the migrations folder.
Change model to EmailField and make the migration
python manage.py makemigrations portfolio_app
python manage.py migrate portfolio_app

Add your User to the Admin module
portfolio_app/admin.py
from django.contrib import admin
from .models import User

class UserAdmin(admin.ModelAdmin):
    fields = ['intra_id', 'intra_username', 'first_name', 'last_name', 'email']
    list_display = ('intra_username', 'first_name', 'last_name', 'email', 'updated_at')


admin.site.register(User, UserAdmin)


🔑 Create a SuperUser to access the Admin
python manage.py createsuperuser
Username: test
Password: test1234
Or whatever you want (never put test1234 as a password on production ofcs...)

python manage.py runserver 3001

portfolio_app/views.py
from django.shortcuts import render
from django.http import HttpResponse

def usersIndex(request):
    return HttpResponse("Hello, world. You're at the users index.")

portfolio_suite/urls.py
from django.contrib import admin
from django.urls import path
from portfolio_app import views

urlpatterns = [
    path('admin/', admin.site.urls),
    path('users/', views.usersIndex, name="usersIndex")
]
🦪 To explore a minimalistic inner structured shell for your application you can run:
python manage.py shell

from portfolio_app.models import User
User.objects.all()

User.objects.get(id=1)
User.objects.get(intra_username="arosado")

🔗 Setting a urls module for the portfolio:
portfolio_app/urls.py
from django.urls import path

from . import views

urlpatterns = [
    path("users/", views.usersIndex, name="usersIndex"),
]

portfolio_suite/urls.py
from django.contrib import admin
from django.urls import path, include

urlpatterns = [
    path('admin/', admin.site.urls),
    path("", include("portfolio_app.urls")),
]

Try again the routes make sure it works!

Add one more route:
portfolio_app/urls.py
...
path("users/<str:intra_username>", views.userShow, name="userShow"),
That is a dynamic route, and you can access the value which will be a string input by the user on the url. Based on that value we will return the view with the username if it exists.

Add the method to the views:
portfolio_app/views.py
from django.shortcuts import render, get_object_or_404
from .models import User

...

def userShow(request, intra_username):
    return HttpResponse("Hello, world. You're at the user [%s] page." % intra_username)

As you can see, you can access the parameter on the views.

run the server and visit: users/your_param

Templates

In order to recycle code we can build templates and develop contexts so we can contain the application under the same template.
♻️ This way we avoid having a bunch of html files with headers and repetitive stuff.

mkdir -p mkdir portfolio_app/templates/users
touch portfolio_app/templates/users/show.html

portfolio_app/views.py
from django.shortcuts import render, get_object_or_404
from django.http import HttpResponse
from .models import User

...

def userShow(request, intra_username):
    user = get_object_or_404(User, intra_username=intra_username)
    return render(request, "users/show.html", {"user": user})
Beware, the parameters must be passed through a  dictionary  📚


portfolio_app/templates/users/show.html
{% load static %}
<link rel="stylesheet" href="{% static 'users/style.css' %}">

USER: {{ user.first_name }}

Now let's create that static cascade style sheet
mkdir -p portfolio_app/static/users
touch portfolio_app/static/users/style.css
touch portfolio_app/static/base.css

Now we need a base html template:
touch portfolio_app/templates/base.html

base.html
<!DOCTYPE html>
<html lang="en">
<head>
    {% load static %}
    <link rel="stylesheet" href="{% static 'base.css' %}">
    <title>{% block title %}42Portfolio{% endblock %}</title>
    {% block custom_style_sheets %}{% endblock %}
</head>

<body>
    <nav><h1>42 Portfolio</h1></nav>

    <div id="content">
        {% block content %}{% endblock %}
    </div>

    <footer>all rights reserved by 42 Lisboa</footer>
</body>
</html>

portfolio_app/static/base.css
@keyframes gradient {
  0% {
    background-position: 0% 50%;
  }
  50% {
    background-position: 100% 50%;
  }
  100% {
    background-position: 0% 50%;
  }
}

body {
	width: 100vw;
	min-height: 100vh;
	margin: 0;
	background: linear-gradient(-45deg, rgba(42, 42, 42, 0.1), rgba(42, 42, 42, 0.4), rgba(42, 42, 42, 0.3), rgba(42, 42, 42, 0.2));
	background-size: 200% 200%;
	animation: gradient 10s ease infinite;
	font-family: 'Nova Mono', monospace;
}

portfolio_app/static/users/style.css
b {
	color: pink;
}

portfolio/templates/users/show.html
{% extends "base.html" %}

{% block title %}{{ user.intra_username }}{% endblock %}

{% block custom_style_sheets %}
{% load static %}
<link rel="stylesheet" href="{% static 'users/style.css' %}">
{% endblock %}

{% block content %}
<h4>USER: <b>{{ user.first_name }}</b></h4>
{% endblock %}

Adding custom JavaScript

Add a block to your template's header:
(I also added the link to d3.min.js because I want to use it all over this template)

portfolio_app/templates/base.html
<script src="https://d3js.org/d3.v7.min.js"></script>
{% block custom_javascripts %}{% endblock %}

Now let's create a js file under the static folder
touch portfolio_app/static/users/portfolioCard.js

Load the custom js to the view where you want to use it:
portfolio_app/templates/users/show.html
{% block custom_javascripts %}
<script type="text/javascript" src="{% static 'users/portfolioCard.js' %}"></script>
{% endblock %}

Great it works! But how can I for example, use the User model with JS!?

Well we can serialize the Object and the convert to JSON and deserialize in the JS ;)

So let's try to do that:

portfolio_app/views.py
from django.shortcuts import render, get_object_or_404
from django.http import HttpResponse
from .models import User
from django.core.serializers import serialize
from django.core.serializers.json import DjangoJSONEncoder

def usersIndex(request):
    return HttpResponse("Hello, world. You're at the users index.")

def userShow(request, intra_username):
    user = get_object_or_404(User, intra_username=intra_username)
    serialized_user = serialize('json', [user], cls=DjangoJSONEncoder)
    return render(request, "users/show.html", {"user": user, "serialized_user": serialized_user[1:-1]})

portfolio_app/templates/users/show.html
...

{% block custom_javascripts %}
<script type="text/javascript" src="{% static 'users/portfolioCard.js' %}"
	defer
	data-user="{{ serialized_user }}">
</script>
{% endblock %}

...

defer: to load after all the document was loaded completely and we pass the serialized user through a data-set

Now we can access the data-set and see the object:

portfolio_app/static/users/portfolioCarsd.js
const data = document.currentScript.dataset;
const user = JSON.parse(data.user);

console.log(user)
Let's add a nice svg! :)

portfolio_app/static/users/portfolioCarsd.js
const svgHeight = 300, svgWidth = 600;
const data = document.currentScript.dataset;
const user = JSON.parse(data.user);

// console.log(user)

window.addEventListener("load", () => {
  d3.select( "#content" ).append( "svg" )

  d3.selectAll( "svg" )
    .attr( "width", svgWidth )
    .attr( "height", svgHeight );
});

Add some style to the base.css for the #content id

portfolio_app/static/base.css
body {
  ...
  text-align: center;
}

#content {
  display: flex;
  align-items: center;
  justify-items: center;
  flex-flow: column;
  gap: 1.5rem;
  margin: 1rem auto;
  padding-bottom: 1rem;
  max-width: 800px;
  background-image: url("https://i.pinimg.com/originals/70/72/c0/7072c003a01ab656c982542e9c38c475.jpg");
  background-size: cover;
  background-position: center;
}

portfolio_app/static/users/style.css
h4 {
  background-color: rgba(255, 255, 255, 0.8);
  padding: 0.25rem;
  border-radius: 0.25rem;
}

b {
  color: pink;
}

svg {
  margin: auto;
  display: inline-block;
  background-color: rgba(42, 42, 142, 0.9);
}

Now have fun, you can do this to see some drag interactions on your svg, just to test the d3 library and how powerful it can be!!!

portfolio_app/static/users/style.css
const svgHeight = 300, svgWidth = 600;
const data = document.currentScript.dataset;
const user = JSON.parse(data.user);

// console.log(user)

const drawCircles2 = (svg, circles) => {
  svg.selectAll( "circle" )
    .data( circles ).enter().append( "circle" )
    .attr( "r", d => d["r"] )
    .attr( "cx", d => d["cx"] )
    .attr( "cy", d => d["cy"] )
    .attr( "fill", d => d["color"] );
}

window.addEventListener("load", () => {
  const svg = d3.select( "#content" ).append( "svg" )
    .attr( "id", "drag-drop")

  d3.selectAll( "svg" )
    .attr( "width", svgWidth )
    .attr( "height", svgHeight );

  let circles = [
    {r: 20, color: "red", cx: 50, cy: 100},
    {r: 20, color: "green", cx: 250, cy: 150},
    {r: 20, color: "blue", cx: 350, cy: 100}
  ];
  drawCircles2( svg, circles );

  let color = undefined, widget = undefined;
  
  svg.selectAll( "circle" )
    .call( d3.drag()
        .on( "start", function () {
          color = d3.select( this ).attr( "fill" );
          widget = d3.select( this ).attr( "fill", "lime" );
        } )
        .on( "drag", function () {
          let pt = d3.pointer( event, this );
          widget.attr( "cx", pt[0] ).attr( "cy", pt[1] )
        } )
        .on( "end", function () {
          widget.attr( "fill", color );
          widget = undefined;
          color = undefined;
        } )
    );
});
Now we just need more data to actually create a nice visualization instead of this circles... You can use React for example and develop a REST API in Django!

Repo: https://github.com/pulgamecanica/42Portfolio-Demo/