Creating My Website from the Ground Up with Django

Creating a website from the ground up is an exciting journey, and in this article, I'll walk you through the main features I realized in my website using Django framework. You'll receive vital insights into the technical complexities, design decisions, and lessons I've learned along the way.

Before we get into the nitty-gritty, it's worth mentioning that I won't be covering the basics of Django installation or creating a new project. There are various excellent courses available, including an amazing video series by Corey Schafer. Instead, I'll concentrate on the most important components of the project, throwing light on the main steps and decisions that brought this idea to reality.

Let's embark on this exciting journey through the development of my website, where every decision and code snippet plays a significant role in shaping the final product.

Understand your requirements: 

Every project starts with a spark of inspiration, which is usually based on a specific need. It's similar to having a "Aha!" moment when you realize you could use something that doesn't yet exist. Following that, it's critical to take a step back and identify the goals of your project. These objectives serve as your North Star, leading you on your trip. Once you've established your objectives, you may begin to shape the scope of your project. With a clear scope in place, it's time to consider the exciting features and functions that will bring your project to life and address the demands that spurred the original idea. My "Aha!" moment in my instance came when I realized I needed an internet presence for my résumé and portfolio. It wasn't just about displaying my thoughts and writings; it was also about honing my programming abilities. So, my endeavor isn't only about meeting a need; it's also about making room for my personal and professional development. It's like creating a welcoming online home for my abilities and thoughts!😊

Database and ORM:

One of the fundamental aspects of any Django project is the design of the database. Django provides an Object-Relational Mapping (ORM) that allows developers to work with databases using Python objects rather than writing raw SQL queries. This ORM simplifies the process of interacting with the database, making it more intuitive and efficient.

By default, Django uses SQLite3, which is a serverless relational database. While practical for development, it may not be the best choice for production. In a production environment, it's essential to use a more reliable database that supports concurrent connections, scalability, and provides better security measures. There are many flavors of databases tailored to different applications, with two broad categories being SQL (relational) and NoSQL databases.

Relational databases, categorized as SQL databases, offer the advantage of handling complex operations using SQL queries. On the other hand, NoSQL databases excel in horizontal scalability, a crucial aspect for applications with high demands.

The choice of the right database depends on the specific requirements of your project, whether it's a complex data-intensive application with intricate operations or a high-traffic application that needs to scale seamlessly. Understanding the strengths and weaknesses of both SQL and NoSQL databases is crucial for making an informed decision.

Given the project's limited scope, I chose PostgreSQL, a commonly used database system for Django. to install PostgreSQL If you are using Ubuntu :

sudo apt-get update
sudo apt-get install postgresql

On Windows, you can download the installer from the official PostgreSQL website.

psycopg2 is a PostgreSQL adapter for Python. You can install it using pip, Python's package manager.

pip install psycopg2

Access the PostgreSQL command line, for Ubuntu:

sudo -u postgres psql

For windows open the command prompt or PowerShell as an administrator.

Log in to the PostgreSQL command line as the superuser (postgres) by running :

psql -U postgres

Next step is to create a new user and grant all privileges to the user on the database:

CREATE DATABASE djangoDB;
CREATE USER moh WITH PASSWORD 'yourpassword';
ALTER ROLE youruser SET client_encoding TO 'utf8';
ALTER ROLE youruser SET default_transaction_isolation TO 'read committed';
ALTER ROLE youruser SET timezone TO 'UTC';
GRANT ALL PRIVILEGES ON DATABASE djangoDB TO youruser;
\q

In your django project's settings, replace this :

'default': {
        'ENGINE': 'django.db.backends.sqlite3',
        'NAME': BASE_DIR / 'db.sqlite3',
    }

by this :

DATABASES = {
    'default': {
        'ENGINE': 'django.db.backends.postgresql',
        'NAME': 'djangoDB',
        'USER': 'youruser',
        'PASSWORD': 'yourpassword',
        'HOST': 'localhost',
        'PORT': '5432',  # Default PostgreSQL port
    }
}

to create the initial database schema and tables in PostgreSQL:

python manage.py makemigrations
python manage.py migrate

Create an admin superuser to access the Django admin page

python manage.py createsuperuser

run the development server :

python manage.py runserver

In your app's model.py file you can find models that define your database's schema all the model classes should inherit form models.Model in many cases you might have some fields that are associated with media objects an files for example models.FileField or models.ImageField when a new file is uploaded the filed will point to the new file, leaving the old file in your storage occupying space. Here's a way to automatically delete old files:

 

class workExperience(models.Model):

    job_title = models.CharField(max_length= 75)
    company_name = models.CharField(max_length= 50)
    job_location = models.CharField(max_length= 50)
    job_description = RichTextField()
    skills = RichTextField()
    starting_date = models.DateField()
    end_date = models.DateField(blank= True, null= True)
    company_thumbnail = models.ImageField(upload_to="Job/companies_logos/")
#here I want to automatically delete old Images when the Image is Updated 

    def __str__(self):
        return self.job_title
    
    def save(self,*args,**kwargs):#over writing the old save method
        self.delete_old_assets()
        super().save(*args, **kwargs)

    def delete(self,*args,**kwargs):#over write the old delete method
        if self.company_thumbnail and os.path.isfile(self.company_thumbnail.path):
            os.remove(self.company_thumbnail.path)
        super().delete(*args,**kwargs)

    def delete_old_assets(self):#custum method to delete the image file
        if self.pk:
            old_instance = workExperience.objects.get(pk=self.pk) 
            if old_instance.company_thumbnail  and os.path.isfile(old_instance.company_thumbnail.path):
                if old_instance.company_thumbnail != self.company_thumbnail or not self.company_thumbnail :
                    os.remove(old_instance.company_thumbnail.path)

 

Multi-resoultion Video Player :

One of the standout features I integrated into my project is a multi-resolution video player. To ensure the smoothest experience for users, I delved into the realm of video processing and discovered an ingenious approach: storing the same video with multiple qualities. This way, regardless of a user's internet speed or device capability, they can enjoy the content hassle-free.

To execute this, I leveraged the power of FFmpeg and the efficiency of multithreading. This combination not only streamlined the video processing but also eliminated the need to sit around waiting for the video to be processed. It's all about providing users with instant access to the content they want, in the quality they need, and at a speed that suits them.

In a nutshell, the multi-resolution video player feature ensures that whether a user is on a high-speed connection or a slower one, they can relish the content without buffering or lag. It's all about creating a seamless, enjoyable viewing experience for everyone.


class scaleVideoThread(threading.Thread):

    def __init__(self,input_path,output_path,output_resolution,bitrate = None):

        self.input_path = input_path
        self.output_path = output_path
        self.output_resolution = output_resolution
        self.bitrate = bitrate

        threading.Thread.__init__(self)

    def run(self):
        try:
            # Convert the input_path to an absolute path
            input_path = os.path.abspath(self.input_path)

            # Open the input video clip
            clip = VideoFileClip(self.input_path)

            # Resize the clip to the specified resolution
            resized_clip = clip.resize(self.output_resolution)

            # Set a specific bitrate if provided
            if self.bitrate:
                resized_clip = resized_clip.set_bitrate(self.bitrate)

            # Write the resized video to the output path
            resized_clip.write_videofile(self.output_path, codec='libx264', audio=True)

            # Close the video clip resources
            clip.close()
            resized_clip.close()

            return True
        
        except Exception as e:
            print(f"Failed to process video: {e}")
            
            return False

Background 3D animation

Finally here is the code I use for my background 3d animation.


const canvas = document.getElementById('scene-canvas');
const scene = new THREE.Scene();
const camera = new THREE.PerspectiveCamera(75, window.innerWidth / window.innerHeight, 0.1, 1000);
const renderer = new THREE.WebGLRenderer({ canvas });
renderer.setSize(window.innerWidth, window.innerHeight);

// Set the background color to white
renderer.setClearColor(0xeeeeee);

// Create wireframe cube and wireframe torus
const cubeGeometry = new THREE.BoxGeometry();
const torusGeometry = new THREE.TorusGeometry(1, 0.4, 16, 100); // Customize torus parameters as needed
const material = new THREE.LineBasicMaterial({ color: 0xbf92ff, transparent: true, opacity: 0.5, linewidth: 3 }); // Thicker lines (set linewidth to your desired value)

const cube = new THREE.LineSegments(new THREE.WireframeGeometry(cubeGeometry), material);
const torus = new THREE.LineSegments(new THREE.WireframeGeometry(torusGeometry), material);



let mouseX = 0;
let mouseY = 0;

console.log(mouseX,mouseY)
scene.add(cube);
scene.add(torus);

cube.scale.set(5, 5, 5);
torus.scale.set(5, 5, 5);
torus.visible = false;

// Position the camera
camera.position.z = 5;

// Create an animation loop
const animate = () => {
    requestAnimationFrame(animate);

    // Alternate between cube and torus every 30 seconds
    const time = performance.now() * 0.001;
    const period = 15;
    const shouldShowCube = Math.floor(time / period) % 2 === 0;

    cube.visible = shouldShowCube;
    torus.visible = !shouldShowCube;

    // Rotate the visible shape
    if (shouldShowCube) {
        cube.rotation.x += 0.01;
        cube.rotation.y += 0.01;
    } else {
        torus.rotation.x += 0.01;
        torus.rotation.y += 0.01;
    }

    // Render the scene
    renderer.render(scene, camera);
};

// Start the animation
animate();

I trust you found this both enjoyable and informative. I'm open to any questions you may have or further discussions. Your curiosity and engagement are always welcome.

Share Now