FOR WINDOWS
type cmd and open on pc, then run
python --version
- if python is not installed, download latest python from python.org and install after it is downloaded tick all boxes and true to whatever pop ups while installing it
- open cmd again and confirm if its installed by checking python version
pip --version
pip install virtualenv
N:B it is good practice to always create a virtual environment in python projects, it helps to install the packages needed for that particular project, without affecting other project(e.g if project a was built with python version 3, and my new project is built with python version 12, with the help of virtual environment, i builds like a compartment for indiviual projects in such that one doesnt affect the other and the code doesnt break)(to avoid conflict of packages)
- run on project terminal in vscode, you can call your virtual environment any name but its recommended to mostly use 'env' or 'venv' because git .ignore already automatically use env but if you use adifferent name, youll havve to manually add it youself
python -m venv env - Activate virtual environment (N:B: you replace the env with your virtual environment name if you used a different name from env), run on terminal for windows
env\scripts\activate
run on mac/linuxsource env/bin/activate - if it throws an error after about it being disabled, it is the window security that is preventing it from running, just switch the terminal from powershell to command prompt and run the code again, it will work.
- Then install the necessary packages needed for your project, N:B make sure youre installing in your project that has env in terminal e.g ((env) PS C:\Users\user\OneDrive\Desktop\PYTHON> pip install django)
- You need to always reactivate the virtual environment anytime you come back to the project by running
env\scripts\activate
- run
pip install djangoN:B: ignore the pip upgrade notice - then create django project, run
django-admin startproject projectname .N:B: the "." after the project name is important because without it, it'll create a nested project folder for us - Do not modify anything in the manage.py file
single-line # a single line comment, multiple-line """this is for multiple line comments/docstring"""
- run
python manage.py runserverand open the port on browser N:B: Always change the default 'admin/' path in the urls.py file to a diff name to avoid hackurlpatterns = [ path('anynameyouwant/', admin.site.urls), ] - To stop server, click ctrl + c
- then create django app, run
django-admin startapp projectname .or runpython manage.py startapp projectname
- Go to the project folder, open the settings.py file, then add all your created apps app into the 'INSTALLED_APPS' code block e.g
INSTALLED_APPS = [ 'django.contrib.admin', 'django.contrib.auth', 'django.contrib.contenttypes', 'django.contrib.sessions', 'django.contrib.messages', 'django.contrib.staticfiles', 'apptest' ]- Then create urls.py file in created app folder, then type
from django.urls import path - type
urlpatterns = [ ] - In project folder, urls.py file, import include e.g
from django.urls import path, include - then put a path for each app that you want your requests to be directed to e.g all requests will go to this url
path('', include('apptest.urls'))and all request with apptest/ will go to this urlpath('apptest/', include('apptest.urls'))
- Go to project, the settings.py file, the 'TEMPLATES' code block, just put somethings showing where our template is located in the
'DIRS': []configuraton or the better approach is in each app folder you create, create a new folder called templates in them so each app has their template folder - then create html file in the created template folder
- then navigate to views.py file. N:B: There is Function based views(easier to learn but with more code blocks) and Class based views(bit complex to learn but with more lesser code blocks)
- define function that takes a request argument and return with the template page html e.g in the views.py file, type
def homepage(request): return render(request, 'home.html')
- then navigate to urls.py file, import your function from the app views e.g
from apptest.views import homepage - and then add the path inside the 'urlpatterns' codeblock e.g
urlpatterns = [ path('', homepage) ] - create other pages in template folder
- then in views.py file define function that returns the page e.g
def aboutpage(request): return render(request, 'about.html') - Then in urls.py file, add the path e.g
path('about', aboutpage)
- you give the url name in url.py file e.g
path('', homepage, name='home')and in the page html, for the anchor tag href, you put{% url 'urlname' %}to link the page e.g<a href="{% url 'about' %}">About Her</a>
- We create a block with a title name so we can dynamically change based on the active page... that is any page that extends from this page can provide their own title, or content unique to them (if our app contains something common to all pages of the app like a nav bar, we create a file that will contain the content so the other pages can just inherit it instead of repeating the codes and the block will be for the part that will change.) e.g
<title>{% block titlename %} {% endblock titlename %}</title>- in the other pages(home.html,about.html), no need to rewrite the doctype and other informations already in base.html, just include the new information that is different on the new page and extend the base.html data into i.e
{% extends 'base.html' %}this code automatically imports all thats in base html. - Then to add new contents on the page like home.html page, you put your contents within the starting and closing of the block tag e.g
{% block title %} Homepage {% endblock title %}, it updates the page title and this contains the body content e.g{% block main %} <h1>A girl named Fola</h1> <a href="{% url 'about' %}">About Her</a> {% endblock main%} - You can include a page in another page(e.g each pages should show in homepage in sections) with
{% include 'nameofpage.html' %}e.g{% include 'testimony.html' %}the testimonial will show in te home page.
- for form submission, when you use get method, it shows all form details in the url, best method is post but it'll show csrf error, so add
{% csrf_token %}in the form so django will automaticcally generate random token for us and the data will be hidden - in views.py, we can retrieve the form details the user is sending to us by extracting it from the request code in the function e.g in contact function of view.py
def contactpage(request): if request.method == "POST": fullname = request.POST["fullname"] print(f'User submitted name {fullname}') return render(request, 'contact.html') return render(request, 'contact.html')- But the better approach is to add
.getto extract because if a key name is not there using only POST to extract, it will throw an error but if we use.getto extract, if it is not there, it will ignore it and check for others e.gdef contactpage(request): if request.method == "POST": fullname = request.POST.get("fullname") email = request.POST.get("email") about = request.POST.get("about") print(f'User submitted name {fullname}, about {about}')
- But the better approach is to add
- django has built in message app found inside INSTALLED_APPS = [], project settings.py file, which is used to send message from our backend to frontend. So we import it in views.py i.e
from django.contrib import messagesand use it in our function e.gdef contactpage(request): if request.method == "POST": fullname = request.POST.get("fullname") email = request.POST.get("email") about = request.POST.get("about") if not fullname: messages.error(request, "Please provide your full name") return render(request, 'contact.html') if not email or not about: messages.error(request, "All fields are required") return render(request, 'contact.html') if len(fullname) <br 5: messages.error(request, "Name is too short") return render(request, 'contact.html') messages.success(request, "Details submitted")
N:B: Django uses JINJA Template just like React uses JSX - We display the backend error message on the page by looping through the message inside the base.html file because all page inherits from it and so our error message can show wherever page we are
<div class="error_container"> {% for msg in messages %} <p>{{msg}}</p> {% endfor %}</div>
remember to close the for loop withendfor
- Import
from django.shortcuts import render, redirect, resolve_url, then redirect after all requirements are filled to the page function you want to redirect to and/or to resolve if the page function is in a different file e.greturn redirect(homepage)orreturn redirect(resolve_url('home'))
- import in views.py file,
from django.views import View. The first letter of classname must be capitalized or PascalCasing e.gclass Contactview(View): def get(self, request): return render(request, 'contact.html') def post(self, request): return render(request, 'contact.html') - import your Class from the app views e.g
from apptest.views import homepage, aboutpage, contactpage, testimonialpage, ContactView - to add the path inside the 'urlpatterns' codeblock, you add
.as_view()to the class name e.gpath('contact', ContactView.as_view(), name='contact')
- run
python manage.py check
(not 100% valid, might not tell you some error)
- navigate to models.py, create models by creating a class but with
models.Modele.gclass ContactMessage(models.Model): - N:B: django automatically gives each model an id value
- run
python manage.py makemigrationsto create migration file for our models - then run
python manage.py migrate, this will run all our migration file against our database to create the tables and it clears the error message"You have 19 unapplied migration(s). Your project may not work properly until you apply the migrations for app(s): admin, auth, contenttypes, sessions." - N:B anytime we modify our models or create new ones, we need to run the two migrations command
- in views.py, we import the models from app e.g
from app.models import ModelName - then put the details in database when form is successfully submitted by
ModelName.objects.create(databaseName=createdVariable)e.gContactMessage.objects.create(fullname = fullname, email = email, about = about )
- go to project urls.py file and check the admin path, the open it on browser
- There are 3 users in django {SuperUser, StaffUser, NormalUser}
- We create superuser account by running
python manage.py createsuperuser, it'll ask for username, email, password(it won't show on terminal when you're typing it in), then create the account successfully. - N:B if you encOunter password not strong error and ask you to bypass it, type "y" to bypass it
- Then login with the details on the admin dashboard in browser
- A model by default is not registered on the admin panel so in admin.py file, we import our models e.g
from app.models import ModelName - Then we regsiter our model by
admin.site.register(ModelName). Then go reload Admin panel in browser - to make the data more readable in the db, we add a method in the class model
def __str__(self): return self.valuename
so the data will store the persons name as header in database e.gclass ContactMessage(models.Model): fullname = models.CharField(max_length=250) email = models.EmailField() def __str__(self): return f'Name: {self.fullname} Email: {self.email}'- Create a new app for authentication, do the necessary steps for app creation, create a signup.html in the new app templates and write your code, create class based view in view.py
- Then import
from django.contrib.auth.models import Userin views.py, then write a check to see if a user data already exists in databaseif User.objects.filter(variable=variable).exists()but convert the value to lowercase first so it doesn't escape the checkvariable = variable.lower()e.g
- if we don't want the models id to be predictable, in models.py file we
import uuidthen inputid = models.UUIDField(default=uuid.uuid4, primary_key=True, editable=False)e.gfrom django.db import models import uuid class Product(models.Model): id = models.UUIDField(default=uuid.uuid4, primary_key=True, editable=False) name = models.CharField(max_length=250) description = models.TextField() price = models.PositiveBigIntegerField() quantity = models.PositiveIntegerField() image = models.ImageField() - if we want to link another table e.g if products belongs and was posted by different users, import
from django.contrib.auth.models import Userand then useuser = models.ForeignKey(User, on_delete= models.CASCADE)in models.py file
def get(self, request): return render(request, 'signup.html')
def post(self, request): username = request.POST.get("username") email = request.POST.get("email") firstname = request.POST.get("firstname") lastname = request.POST.get("lastname") password = request.POST.get("password") if not username or not email or not firstname or not lastname or not password: messages.error(request, "All fields are required") return render(request, 'signup.html') if len(password) < 8: messages.error(request, "Password is too short") return render(request, 'signup.html')
username = username.lower()
email =email.lower()
if User.objects.filter(username=username).exists():
messages.error(request, "Username is taken")
return render(request, 'signup.html')
if User.objects.filter(email=email).exists():
messages.error(request, "Email already exists")
return render(request, 'signup.html')```
-
then save the details
user = User.objects.create(username=username, email=email, first_name=firstname, last_name=lastname) -
N:B check out the user model (ctrl + click User) to see how the databaseName is written e.g first_name also don't put password together because it is always hashed and it'll be tried to save as plain text there, put it separate.
-
then set the password
user.set_password(password)and then save ituser.save() -
then inurls.py, import view e.g
from authz.views import SignupViewand in urlpath codeblock, input class based view path e.gpath('signup', SignupView.as_view(), name='signup') -
Then add the signup to navigation in base.html
-
- Create login.html in templates and put the necessary code in it, then in the views.py, create a login view function with the necessary checks for form submission e.g
def Loginview(request): if request.method == "POST": username = request.POST.get("username") password = request.POST.get("password") if not username or not password: messages.error(request, "All fields are required") return render(request, 'login.html') username = username.lower() username_exists = User.objects.filter(username = username).first() if not username_exists: messages.error(request, "Invalid login credentials") return render(request, 'login.html') return render(request, 'login.html')
- Create login.html in templates and put the necessary code in it, then in the views.py, create a login view function with the necessary checks for form submission e.g
-
import
from django.contrib.auth import login, logout, authenticatein views.py -
then we authenticate to see if password match for the account created
user = authenticate(username = username, password=password) -
then we allow login if all is correct with
login(request, user)e.glogin(request, user) messages.success(request, "Successful Login") return redirect(resolve_url('home')) -
then add the path in urls.py
-
go to views.py and create a logout function
def Logoutview(request): logout(request) return render(request, 'login.html') -
then add the path in urls.py, also add it to navigation in base.html header
-
in the main page to do condition so logout only displays when user is logged in
{% if user.is_authenticated %}e.g -
{% if user.is_authenticated %} <li><a href="{% url 'logout' %}">Logout</a></li> {% else %} <li><a href="{% url 'signup' %}">Signup</a></li> <li><a href="{% url 'login' %}">Login</a></li> {% endif %}
-
in app view.py, import
from django.contrib.auth.decorators import login_required, then we put this decorator@login_requiredat the top of any function we want accessed only on login e.g@login_required def homepage(request): return render(request, 'home.html') -
Then anywhere inside settings.py file, we input
LOGIN_URL = 'authz/login' -
go to views.py, in the function, after the post request, put
next_page = request.GET.get('next')this put the right url when you try accessing a page andor get redirected to login before accessing the page e.gdef Loginview(request): if request.method == "POST": next_page = request.GET.get('next') username = request.POST.get("username") password = request.POST.get("password") if not username or not password: messages.error(request, "All fields are required") return render(request, "login.html")
- At the root level, create folder called 'static', you can put css, js, images in here
- in the meta of the template pages, link the stylesheet
<link rel="stylesheet" href="{% static 'css/style.css' %}">but for it to work, at the top of the page before DOCTYPE, put{% load static %} - them
import osafter the "from pathlib" in settings.py file, and put this after the "static_url" at the bottomSTATICFILES_DIRS = (os.path.join(BASE_DIR, 'static'),) - for different styling in each pages, create a block style in the base page and the use the block in each pages and put the link, also load static below "extends" e.g
{% extends 'base.html' %} {% load static %} - add text e.g
<img src="{% static 'assets/6.png' %}" alt="contact image" width="250px" >, load static at the head of page below extend
- create a folder at root level and add the url variable at bottom in setting.py file e.g
MEDIA_URL = '/media/'and then add the directory variable to save our uploads toMEDIA_ROOT = os.path.join(BASE_DIR, 'media') - N:B:
image = models.ImageField(upload_to="product/")the "upload to" means it will create a product folder inside our media folder and save there, this is better because uploading can come from different part of the app so to keep things organized - to show the recently uploaded first i.e when created
created_at = models.DateTimeField(auto_now_add=True) - to show when updated
updated_at = models.DateTimeField(auto_now=True) - N:B: Anytime we want to add image/file field in Models, we need to have a library called "Pillow" installed using
pip install pillow
- create a .gitignore file, go to gitignore.io, search for the software you're using on it (Django, React), copy and paste everything in the gitignore
- Go to: Render Dashboard
- Click: New + → Web Service
- Connect your GitHub repo:
- Fill manually: Runtime → Python
- On terminal install gunicorn
pip install gunicorn, Render needs it. - then Generate requirements.txt with
pip freeze > requirements.txt, ensure you're at the root folder - Push to GitHub
git add . git commit -m "Added requirements.txt" git push - Open the settings.py and change the 'ALLOWED_HOSTS = []' to
ALLOWED_HOSTS = [ "django-python-test.onrender.com", "localhost", "127.0.0.1", ] - Also change: 'DEBUG = True' to
DEBUG = False-then Push to GitHubgit add . git commit -m "Configured ALLOWED_HOSTS for Render" git push
- ref to class Products in views.py, we create a context dictionary that takes key value pair and send it to frontend, the dict can be created outside the request and parsed in e.g
class Products(LoginRequiredMixin, View): def get(self, request): all_products = Product.objects.all().order_by('-created_at') context = { 'all_productskey': all_products } return render(request, 'products.html', context) - then we write a for loop that picks the product one after the other in frontend, ref to products.html file e.g
<div class="all_products">
{% for prod in all_productskey %}
<div class="product-card">
<h3>Name: {{prod.name}}</h3>
<img src="{{prod.image.url}}" alt="{{prod.name}}">
<p>Description: {{prod.description}}</p>
<p>Quantity: {{prod.quantity}}</p>
<p>Seller: {{prod.user.username}}</p>
</div>
{% endfor %}
</div>
- N:B: The image will not display, so we go to project url.py and add a configuration. import settings and static i.e
from django.conf import settingsandfrom django.conf.urls.static import static - then add to urlpatterns
urlpatterns += static(settings.MEDIA_URL, document_root=settings.MEDIA_ROOT)
- N:B we must include
enctype="multipart/form-datain our form so that our uploaded files can be sent
-
ref to product urls.py, the path will have
<>for dynamic input of the product id e.gpath('edit-product/<str:product_id>', EditProduct.as_view(), name='edit-product') -
in product views.py, for the edirproduct view make checks to see it is the seller trying to update the product e.g
-
`class EditProduct(LoginRequiredMixin, View):
def get(self, request, product_id): product = Product.objects.filter(id=product_id).first() if not product: return redirect(resolve_url("products")) if product.user != request.user: return redirect(resolve_url("products")) context = {"product": product} return render(request, 'edit_product.html', context) def post(self, request, product_id): product = Product.objects.filter(id=product_id).first() if not product: return redirect(resolve_url("products")) if product.user != request.user: return redirect(resolve_url("products"))name = request.POST.get("name") description = request.POST.get("description") quantity = request.POST.get("quantity") price = request.POST.get("price") image = request.FILES.get("image") product.name = name or product.name product.description = description or product.description product.price = price or product.price product.quantity = quantity or product.quantity product.image = image or product.image product.save() messages.success(request, "Product successfully updated") return redirect(resolve_url("products"))` -
then go to the editproduct html and the input boxes there put value to populate with the already existing informations of the product e.g
<label for="">Name</label></br>
<input type="text" name="name" required value="{{product.name}}"></br>
<label for="">Description</label></br>
<textarea name="description">{{product.description}}</textarea></br>