Ref
Lecture 3 - CS50's Web Programming with Python and JavaScript
Django 개발 환경 세팅하기 - Web 개발 학습하기 | MDN
Django 문서 | Django 문서 | 장고(Django) (Django official)
Django
Web Applications
> Static: Every single time we open the web page, it looks exactly the same.
- For large sites, it would be unreasonable for employees to have to manually edit a large HTML file every time a change is made.
> Dynamic: A dynamic website is one that takes advantage of a programming language to dynamically generate HTML and CSS files.
HTTP
> HTTP(HyperText Transfer Protocol): A widely-accepted protocol for how messages are transferred back and forth across the internet. Typically, information online is passed between a client and a server.
- The client will send a request to the server.
- 'GET' is a simply a type of request. "I would like to get a particular page."
- The '/' typically indicates that we're looking for the website's home page, and the three dots indicate that we could be passing in more information as well.
- In this case, I want to get the slash page, just denoting the root of the website, usually the default page for the website.
- HTTP 1.1 is the version of HTTP that we're using.
- The host is what URL we're trying to access the web page for.
- After receiving a request, a server will then send back an HTTP response.
- Such a response will include the HTTP version, a status code, a description of the content, and then some additional information.
- On the second line, there is Content-Type text.html, which just means the format of the data that's coming back in this response is the HTML data. It's HTML data that the user's web browser on the client should then render as HTML, for example.
- 401: Happens when you're trying to access a page you're not supposed to have access to.
Django
> Django: A Python-based web framework that will allow us to write Python code that dynamically generates HTML and CSS.
- Install Django, also have to install pip.
- Once you have Pip installed, you can run 'pip3 install django' in your terminal to install Django.
> Install Django: Using Django inside a Python virtual environment
- (Windows) Install the virtual environment software
1. py -3 -m pip install virtualenvwrapper-win
2. mkvirtualenv my_django_environment
- 'deactivate': Exit out of the current Python virtual environment
- 'workon': List available virtual environments
- 'workon NAME_OF_ENVIRONMENT': Activate the specified Python virtual environment
- 'rmvirtualenv NAME_OF_ENVIRONMENT': Remove the specified environment
- 'django-admin --version': 5.1.3
> After installing Django, we can create a new Django project.
1. Run 'django-admin startproject PROJECT_NAME' to create a number of starter files for your project.
- django-admin startproject cs50lec3
2. Run 'cd PROJECT_NAME' to navigate into your new project's directory.
- cd cs50lec3
3. Open the directory in your text editor of choice.(cmd: 'code .') Some files have been created.
- 'manage.py' is what we use to execute commands on our terminal. We won't have to edit it, but we'll use if often.
- 'settings.py' contains some important configuration settings for our new project.
- 'urls.py' contains directions for where users should be routed after navigating to a certain URL.
4. Start the project by running 'python manage.py runserver'. This will open a development server, which you can access by visiting the URL provided. This development server is being run locally on your machine, meaning other people cannot access your website. This should bring you to a default landing page.
> Starting development server at http://127.0.0.1:8000/
- 127.0.0.1 is an IP address that just refers to my local computer.
- 8000 is a port number and it refers to what type of service is being run.
* Pressing Ctrl+C to exit, stop running the server.
5. We'll have to create an application. Django projects are split into one or more applications. Most of our projects will only require one application, but larger sites can make use of this ability to split a site into multiple apps.
Run 'manage.py startapp APP_NAME' to create an application. This will create some additional directories and files, including 'views.py'.
6. Now, we have to install our new app. Go to 'settings.py', scroll down to the list of 'INSTALLED_APPS', and add the name of our new application to this list.
> Run 'manage.py startapp hello' to create an application 'hello', then go to settings.py-INSTALLED_APPS and add 'hello', to the list.
테스트 해보기>
1. cmd에서 장고앱 저장할 부모폴더로 이동
2. 테스트 사이트용 폴더 생성하고 그 폴더로 이동 mkdir, cd
3. django-admin > manage.py에서 개발용웹서버 실행
4. 사이트 만들어짐, django skeleton app homepage
Routes
1. 'Views.py' will contain a number of different views. Each view is something that the user might want to see. To create a first view, write function that takes in a request.
> 'HttpResponse(Includes a response code of 200 and a string of text that can be displayed in a web browser) is a special class created by Django.
- In order to do this, we have include 'from django.http import HttpResponse'.
#views.py
from django.shortcuts import render
from django.http import HttpResponse
def index(request):
request HttpResponse("Hello, world!")
2. We need to somehow associate this view we have just created with a specific URL. To do this, create another file 'urls.py' in the same directory as views.py. We already have a urls.py file for the whole project, but it is best to have a separate one for each individual app.
3. Inside our new urls.py, we'll create a list of url patterns that a user might visit while using our website. 'urls.py' needs to define a variable called urlpatterns, which will be a list of all of the allowable URLs that can be accessed for this particular app.
1. Make some imports
- 'from django.urls import path': Gives us the ability to reroute URLS
- 'from . import views': Import any functions we've created in 'views.py'.
2. Create a list called 'urlpatterns'.
3. For each desired URL, add an item to the 'urlpatterns' list that contains a call to the 'path' function with two or three arguments: A string representing the URL path, a function from 'views.py' that we wish to call when that URL is visited, and (optionally) a name for that path, in the format 'name="something"'.
- 1st arg "": The empty string means no additional argument. Nothing at the end of the route.
- 2nd arg: What views should be rendered when this URL is visited. If I want to render my index view that in 'views.py'. Then what I want to render when someone visits this URL, is going to be views.index. 'views' represents 'views.py', that file where I've defined all of my views. 'index' is the name of the function that I want to call when someone visits this URL, for example.
- 3rd arg: You can optionally provide a path with a string name.
#'hello' - 'urls.py'
from django.urls import path
from . import views #dot means 'from the current directory' #views.py and urls.py are located in the same directory. '. import views' means import all of that into this particular file.
urlpatterns = [path("", views.index, name="index")]
4. We've created a 'urls.py', and we'll edit it for the entire project. In this file, there's already a path called 'admin'. To add another path for the new app, we'll add an item to the 'urlpatterns' list. This follows the same pattern as our earlier paths, except instead of adding a function from 'views.py' as our second argument, we want to be able to include all of the paths from the 'urls.py' file within our application. To do this, write: 'include("APP_NAME.urls")' where 'include' is a function we gain access to by also importing 'include from django.urls' as shown in the 'urls.py' below.
#cs50lec3 - urls.py
from django.contrib import admin
from django.urls import path, include
urlpatterns=[
path('admin/', admin.site.urls),
path('hello/', include("hello.urls"))] #Inside of the hello module, get at the urls file there.
5. By doing this, we've specified that when a user visits our site, and then in the search bar adds '/hello' to the URL, they'll be redirected to the paths inside of our new application.
<Overview>
I have a Django project called cs50lec3. Inside of cs50lec3, there is a URLs file that decides what URLs I can access. I can access /admin, which takes me to the admin application which is created by Django. And I can go to /hello to go to the hello application. When do that, look at the urls.py inside of the hello directory to figure out what additional URLs I can get to from there. So this is one master urls.py file that might connect to multiple different other URL configurations that exist as well. Then inside of the urls.py for the hello app, when someone visits the default route on this particular application, go ahead and just run the index function that is one of my views.
> Start my application
- Using 'python manage.py runserver' and visit the url provided.
- This is because we have only defined the URL 'localhost:8000/hello', but we haven't defined the URL 'localhost:8000' with nothing added to the end.
- So, add '/hello' to the URL in my search bar:
- I can have as many views as I want, also I can create additional functions that each return different responses.
- What happened?
1. When we accessed the URL 'localhost:8000/hello/', Django looked at what came after the base URL(localhost:8000/) and went to our project's urls.py file and searched for a pattern that matched 'hello'.
2. It found that extension because we defined it, and saw that when met with that extension, it should 'include' our 'urls.py' file from within our application.
3. Then, Django ignored the parts of the URL it has already used in rerouting '(localhost:8000/hello/', or all of it) and looked inside our other 'urls.py' file for a pattern that matches the remaining part of the URL.
4. It found that our only path so far ("") matched what was left of the URL, and so it directed us to the function from 'views.py' associated with that path.
5. Finally, Django ran that function within 'views.py', and returned the result (HttpResponse("Hello, world!")) to our web browser.
- We can change the 'index' function within 'views.py' to return anything we want it to. We could even keep track of variables and do calculations within the function before eventually returning something.
- How we can add more than one view to our application:
#views.py
from django.shortcuts import render
from django.http import HttpResponse
def index(request):
return HttpResponse("Hello, world!")
def yj(request):
return HttpResponse("Hello, YJ!") #This function is going to return HttpResponse "Hello, YJ!"
- I need to associate this new view that I've created with the URL.
#urls.py(within our application)
from django.urls import path
from . import views
urlpatterns=[
path("", views.index, name="index), #A default route represented by the empty string
path("yj", views.yj, name="yj")] #Add a new path, type yj into the URL instead, that will load the yj function, give that a name "yj".
- When we add 'yj' to the URL, we get different page.
> Many sites are parameterized by items included in the URL.
- It seems impossible that sites like GitHub and Twitter(X) would have an individual URL path for each of its users.
- How we could make a path that's a bit more flexible?
#'views.py' -
def greet(request, name):
return HttpResponse(f"Hello, {name}!") #f: formatted string
- This function takes in not only a request, but also an additional argument of a user's name. And then returns a custom HTTP Response based on that name.
- Rather than prescribing exactly what the URL should look like, like nothing after the end of the route, or NAME at the end of the route, this line is saying this route code be any string that we're going to give a variable name of name to.
- Next, we have to create a more flexible path in 'urls.py'.
#'urls.py' -
path("<str:name>", views.greet, name="greet")
- By augmenting the 'greet' function to utilize Python's 'capitalize' function that capitalizes a string, it looks a little bit nicer.
#'views.py'-
def greet(request, name):
return HttpResponse(f"Hello, {name.capitalize()}!")
- Django allow me to incorporate it into the response that I'm giving back. So I'm using Python to take the name, capitalize it, and use that in the response that gets sent back to the user.
Templates
> We can include any HTML elements we want to.
- Return a blue header instead of just the text in our 'index' function:
#views.py - index
def index(request):
return HttpResponse("<h1 style=\"color:blue\">Hello, world!</h1>")
- I would get very tedious to write an entire HTML page within 'views.py'. It would also constitute bad design, as we want to keep separate parts of our project in separate files whenever possible.
> Django's templates
- This will allow us to write HTML and CSS in separate files and render those files using Django. The syntax we'll use for rendering a template looks like this:
#Template
def index(request):
return render(request, "hello/index.html")
- To create that template, create a folder called 'templates' inside our app, then create a folder called 'hello'(or whatever) within that, and then add a file called 'index.html'.
- Next, add whatever we want to that new file:
#
<!DOCTYPE html>
<html lang="en">
<head>
<title>Hello</title>
</head>
<body>
<h1>Hello, World!</h1>
</body>
</html>
- Now, when we visit the main page of our application, we can see the header and title have been updated.
- HTML is a markup language, which means it doesn't have support for things like a variable to represent someone's name.
> Django's templating language
- Django has added its own templating language on top of the existing HTML.
- Use this to change the content of our HTML files based on the URL visited.
- I can take advantage of that to be able to render an HTML page that actually has a variable, condition or a loop inside of it.
#def greet
def greet(request, name):
return render(request, "hello/greet.html", {
"name": name.capitalize()
})
- We passed a third argument into the 'render' function here, one that is known as the context. In this context, we can provide information that we would like to have available within our HTML files. This context takes the form of a Python dictionary.
#greet.html
...<h1>Hello, {{name}}!</h1>...
- Double curly brackets: Allows us to access variables that we've provided in the 'context' argument.
- We've seen how we can modify our HTML templates based on the context we provide.
Conditionals
> Is it New Year's?
- Create a new app
1. Run 'python manage.py startapp newyear' in the terminal
2. Edit 'settings.py', awodding "newyear" as one of our 'INSTALLED_APPS'
3. Edit our project's 'urls.py' file, and include a path similar to the one we created for the 'hello' app: #path('newyear/', include("newyear.urls.))
- Create another 'urls.py' file within our new app's directory, and update it to include a path similar to the index path in 'hello':
#urls.py
from django.urls import path
from . import views
urlpatterns=[path("", views.index, name="index"),] #A single empty route that loads the index function
- Create an index function in 'views.py'
- To check whether or not it's New Year's Day, we can import Python's datetime module. To get a sense for how this module works, we can look at the documentation, and then test it outside of Django using the Python interpreter.
-- Python interpreter: A tool we can use to test out small chunks of Python code. Run 'python' in your terminal, and then you'll be able to type and run Python code within your terminal. Run 'exit()' to leave.
-- We can use this to construct a boolean expression that will evaluate to True if and only if today is New Year's Day: 'now.day==1 and now.month==1'
-- Now that we have an expression we can use to evaluate whether or not it's New Year's Day, we can update our index function in 'views.py'
#views.py
import datetime
from django.shortcuts import render
def index(request):
now=datetime.datetime.now()
return render(request, "newyear/index.html", {
"newyear":now.month==1 and now.day==1}) #Need access to the current date. If now.month==1 and now.day==1, "newyear"'s value is going to be true.
- Create 'index.html' template. We'll have to again create a new folder called 'templates', a folder within that called 'newyear', and a file within that called 'index.html'. Inside that file, we'll write something like this:
(Folder newyear - Folder templates - Folder newyear - File index.html)
#index.html
<!DOCTYPE html>
<html lang="en">
<head>
<title>Is it New Year's?</title>
</head>
<body>
{% if newyear %}
<h1>YES</h1>
{% if else %}
<h1>NO</h1>
{% endif %}
</body>
</html>
- {% %} as opening and closing tags around logical statements
- Django's formatting language requires you to include an ending tag indicating that we are done with our 'if-else' block.
- The HTML is actually being sent to your web browser includes only the NO header, meaning that Django is using the HTML template we wrote to create a new HTML file. And then sending it to our web browser.
- If we cheat a little bit and make sure that our condition is always true, we see that the opposite case is filled:
#
def index(request):
now = datetime.datetime.now()
return render(request, "newyear/index.html", {
"newyear": True
})
Styling
- If we want to add a CSS file, which is awork static file because it doesn't change, we'll first create a folder called 'static', then create a 'newyear' folder within that, and then a 'styles.css' file within that. (Folder static - Folder newyear - file styles.css)
- In this file, we can add any styling.
#styles.css
h1{
font-family: sans-serif;
font-size: 90px;
text-align: center;
}
- To include this styling in our HTML file, we add the line {% load static %} to the top of the HTML template, which signals to Django that we wish to have access to the files in our 'static' folder.
- Then, rather than hard-coding the link to a stylesheet as we did before, we'll use some Django-specific syntax:
#
<link rel="stylesheet" href="{% static 'newyear/styles.css' %}">
-> Restart the server, the styling has changed!
Tasks
Mini Project: TODO list
- Create a new app
1. Run 'python manage.py startapp tasks' in the terminal
2. Edit 'settings.py', adding "tasks" as one of our 'INSTALLED_APPS'
3. Edit our project's 'urls.py' file, and include a path similar to the one we created for the 'hello' app:
#urls.py
path('tasks/', include("tasks.urls"))
4. Create another 'urls.py' file within our new app's directory, and update it to include a path similar to the index path in 'hello'
#urls.py
from django.urls import path
from . import views
urlpatterns = [path("", views.index, name="index"),]
5. Create an index function in 'views.py'
- Create a list of a tasks and then display them to a page. Create a Python list at the top of 'views.py' where we'll store our tasks. Then, we can update our 'index' function to render a template, and provide our newly-created list as context.
#views.py
from django.shortcuts import render
tasks = ["foo", "bar", "baz"]
def index(request):
return render(request, "tasks/index.html", {
"tasks": tasks
})
- index.html needs access to all of my tasks. Tasks are inside of variable tasks
- The key distinction is, whatever is here on the right after the colon, this is the variable takes on. On the left, that is the name of the variable that the HTML template will have access to when Django is rendering in.
- So Django has access to this variable name on the left that has this value on the right.
(Folder tasks - Folder templates - Folder tasks - file index.html): The template that I'm rendering is tasks/index.html(Check views.py)
#the template HTML file
<!DOCTYPE html>
<html lang="en">
<head>
<title>Tasks</title>
</head>
<body>
<ul>
{% for task in tasks %}
<li>{{ task }}</li>
{% endfor %}
</ul>
</body>
</html>
- Looping over all of the tasks inside of "tasks" and creating a list item for each one of those tasks.
- Just as in before where we could use if inside of curly braces and percent signs to use a condition, likewise we can use 'for' to say something.
- I would like to display a list item. Using double curly braces, I'm saying plug in the task here. In between these list item tasks, I would like to plug in whatever the value of the task variable is.
Forms
- To be able to add some new tasks, we'll start taking a look at using forms to update a web page.
- Add another function to 'views.py' that will render a page with a form for adding a new task:
#views.py
def add(request):
return render(request, "tasks/add.html") #the function will render add.html
- Add another path to 'urls.py'
#urls.py
path("add", views.add, name="add") #add function inside of views
- Create our 'add.html' file, which is fairly similar to 'index.html', except that in the body we'll include a form rather than a list:
#add.html
...
<body>
<h1>Add Task:</h1>
<form action="">
<input type="text" name="task">
<input type="submit">
</form>
</body>
...
- However, what we've just isn't necessarily the best design, as we've just repeated the bulk of that HTML in two different files. Django's templating language gives us a way to eliminate this design: Template inheritance.
- I'm going to define an HTML file called the layout, just some file that the other files add.html and index.html are going to inherit from. They're going to inherit from my layout all of the structure of the page that's the same on both of the pages.
- All I need to write is what differs between the pages.
#templates/tasks/layout.html
...
<body>
{% block body %} #this block called body
{% endblock %}
</body>
...
- This block might change depending on which file we're using, add.html or index.html.
- The rest of the structure won't change, but the content of this block called body might change.
- We've again used {% ... %} to denote some sort of non-HTML logic, and in this case, we're telling Django to fill this "block" with some text from another file. Now, we can alter our other two HTML files to look like:
#index.html
{% extends "tasks/layout.html" %} #This html page extends tasks/layout.html.
{% block body %}
<h1>Tasks:</h1>
<ul>
{% for task in tasks %}
<li>{{ task }}</li>
{% endfor %}
</ul>
{% endblock %}
- I'm inheriting from the layout.html template, basically saying use the layout.html template, except inside of block body, I would like to include all of this content.
#add.html
{% extends "tasks/layout.html" %}
{% block body %}
<h1>Add Task:</h1>
<form action="">
<input type="text" name="task">
<input type="submit">
</form>
{% endblock %}
- Notice how we can now get rid of much of the repeated code by extending our layout file. Now, our index page remains the same, and we now have an add page as well:
- It's not ideal to have to type "/add" in the URL any time we want to add a new task, so we'll add some links between pages. Instead of hard-coding links though, we can use the 'name' variable we assigned to each path in 'urls.py', and create a link that looks like this:
#urls.py
<a href="{% url 'add' %}">Add a New Task</a>
- Django will find a URL whose name is add and link me directly to that route.
- Where 'add' is the name of that path. We can do a similar thing in our 'add.html'
#add.html
<a href="% url 'index' %}">View Tasks</a>
- This could potentially create a problem though, as we have a few routes named 'index' throughout our different apps(tasks, newyears). We can solve this by going into each of our app's 'urls.py' file, and adding an 'app_name' variable, so that the files now look something like this:
#urls.py
from django.urls import path
from . import views
app_name="tasks"
urlpatterns=[path("", views.index, name="index"), path("add", views.add, name="add")]
- We can then change our links from simply 'index' and 'add' to ;tasks:index' and 'tasks:add'.
#add.html
<a href="{% url 'tasks:index' %}">View Tasks</a>
#index.html
<a href="{% url 'tasks:add' %}">Add a New Task</a>
- We can add an action to a form to be able to take that form and submit it somewhere. And when I submit the form, I'll go ahead and submit it back to the URL for tasks:add. I send it back to that add URL when I submit the form.
- And I'm going to give this form a specific request method.
#add.html
<form action="{% url 'tasks:add' %}" method="post">
- This means that once the form is submitted, we'll be routed back to the 'add' URL. We'll be using a 'post' method rather than a 'get' method, which is typically what we'll use any time a form could alter the state of that web page.
- 'Post' is generally used for submitting form data, it doesn't include parameters inside the URL the way a get request does.
- The post ability send data via a different request method to my add route.
- Django requires a token to prevent Cross-Site Request Forgery(CSRF) Attack: This is an attack where a malicious user attempts to send a request to your server from somewhere other than your site.
- For example, that a banking website has a form for one user to transfer money to another one. Someone could submit a transfer from outside of the bank's website.
- To solve this problem, when Django sends a response rendering a template, it also provides a CSRF token that is unique with each new session on the site.
- When a request is submitted, Django checks to make sure the CSRF token associated with the request matches one that it has recently provide.
- If a malicious user on another site attempted to submit a request, they would be blocked due to an invalid CSRF token.
- This CSRF validation is built into the Django Middleware framework, which can intervene in the request-response processing of a Django app.
- Inside of 'settings.py', there's a whole bunch of middleware that's installed by default inside of Django application in terms of making sure that we have various different features hooked into this request response processing. And one of those is the CSRF view middleware, this feature of Django that allows it to make sure that our requests, whenever we're submitting data via post, something that has the potential to change the state of the application in some way, that we need to have CSRF validation. We need to add some sort of token to our form, to make sure that Django is able to authenticate the validity of this form to make sure they know the form actually came from the web application itself.
- To incorporate this technology into our code, add a line to the form in 'add.html'
#add.html
...
<form action="{% url 'tasks:add' %}" method="post">
{% csrf_token %}
<input type="text" name="task">
<input type="submit">
</form>
...
- This line adds a hidden input field with the CSRF token provided by Django, such that when we reload the page, it looks as though nothing has changed. However, if we inspect element, we'll notice that a new input field has been added:
Django Forms
Django Forms: An easier way to collect information from a user.
- Add the following to the top of 'views.py' to import the 'forms' module:
from django import forms
- Now, we can create a new form within 'views.py' creating a Python class called 'NewTaskForm':
class NewTaskForm(forms.Form):
task = forms.CharField(label="New Task")
> What's going on in this class:
- Inside the parentheses after 'NewTaskForm', there is 'forms.Form'. This is because our new form inherits from a class called 'Form' that is included in the 'forms' module. And this is another example of how inheritance is used to take a more general description (the 'forms.Form' class) and narrow it down to what we want (new Form).
- Inside this class, we can specify what information we would like to collect from the user. (In this case, the name of a task)
- We specify that this should be a textual input by writing 'forms.CharField', but there are many other input fields included in Django's form module that we can choose from.
- Within this 'CharField', we specify a 'label', which will appear to the user when they load the page. A 'label' is just one of many arguments we can pass into a form field.
>Now we've created a 'NewTaskForm' class, we can include it in the context while rendering the 'add' page.
#Add a new task
def add(request):
return render(request, "tasks/add.html",{
"form": NewTaskForm()
})
> Within 'add.html', we can replace our input field with the form we just created:
<form action="{% url 'tasks:add' %}" method="post">
{% csrf_token %}
{{ form }}
<input type="submit">
</form>
- Django will generate the necessary HTML to make that form work.
> To specify a number indicating the priority, I could additionally give this form access to a priority variable, which is an integer field whose label is priority.
- I can even add constraints on this. To be able to ensure that it's valid data, I can give it a min value and a max value.
class NewTaskForm(forms.Form):
task = forms.CharField(label="New Task")
priority = forms.IntegerField(label="Priority", min_value=1, max_value=10)
> There are several advantages to using the 'forms' module rather than manually writing an HTML form:
- If we want to add new fields to the form, we can simply add them in 'views.py' without typing additional HTML.
- Django automatically performs client-side validation, or validation local to the user's machine. Meaning it will now allow a user to submit their form if it is incomplete.
- Django provides simple server-side validation, or validation that occurs once form data has reached the server.
> Now that we have a form set up. What happens when a user clicks the submit button?
- When a user navigates to the add page by clicking a link or typing in the URL, they submit a 'GET' request to the server, which we've already handled in our 'add' function.
- When a user submits a form though, they send a 'POST' request to the server, which at the moment is not handled in the 'add' function.
- We can handle a 'POST' method by adding a condition based on the 'request' argument our function takes in.
> In order to redirect the user after a successful submission, we need a few more imports
#views.py
from django.shortcuts import render
from django import forms
from django.urls import reverse
from django.http import HttpResponseRedirect
tasks = ["foo", "bar", "baz"]
class NewTaskForm(forms.Form):
task = forms.CharField(label="New Task")
priority = forms.IntegerField(label="Priority", min_value=1, max_value=10)
# Create your views here.
def index(request):
return render(request, "tasks/index.html", {
"tasks": tasks
})
#Add a new task
def add(request):
#Check if metod is POST
if request.method == "POST":
#Take in the data the user submitted and save it as form
form = NewTaskForm(request.POST)
#Check if form data is valid(server-side)
if form.is_valid():
#Isolate the task from the 'cleaned' version of form data
task = form.cleaned_data["task"]
#Add the new task to our list of tasks
tasks.append(task)
#Redirect user to list of tasks
return HttpResponseRedirect(reverse("tasks:index"))
else:
#If the form is invalid, re-render the page with existing info
return render(request, "tasks/add.html",{
"form":form
})
return render(request, "tasks/add.html",{
"form": NewTaskForm()
})
- NewTaskForm creates a blank form. But you can also populate that form with some data. And if I populate it with request.POST, request.POST contains all of the data that the user submitted when they submitted the form.
- Call 'if form.is_valid', and inside this if statement use some logic using the cleaned data as a result of using this form. Inside of this variable, that will give me access to all of the data the user submitted. If I want to get what task they submitted, I had a variable called task inside class NewTaskForm, => 'task=form.cleaned_data["task"]'
- And then add this task to my list of tasks. => tasks.append(task)
- If the form is valid, we take the data from the form, get the task, add it to my growing list.
- But else, if the form is not valid, I should return the add.html file again. Instead of providing the form back to them, a new form back to them, I'm going to send back the existing form data back to them. So we can display information about any errors that might have come up as well.
Sessions
- We've successfully built an application that allows us to add tasks to a growing list. However, it may be a problem that we store these tasks as a global variable, as it means that all of the users who visit the page see the exact same list.
- In order to solve this problem, we're going to employ a tool known as sessions.
- Session: A way to store unique data on the server side for a each new visit to a website.
- To use session in our application, we'll first delete our global 'tasks' variable, then alter our 'index' function, and finally make sure that anywhere else we had used the variable 'tasks', we replace it with 'request.session["tasks"]'.
#views.py
# Create your views here.
def index(request):
#Check if there already exists a "tasks" key in our session
if "tasks" not in request.session:
#if not, create a new list
request.session["tasks"] = []
return render(request, "tasks/index.html", {
"tasks": request.session["tasks"]
})
#Add a new task
def add(request):
#Check if metod is POST
if request.method == "POST":
#Take in the data the user submitted and save it as form
form = NewTaskForm(request.POST)
#Check if form data is valid(server-side)
if form.is_valid():
#Isolate the task from the 'cleaned' version of form data
task = form.cleaned_data["task"]
#Add the new task to our list of tasks
request.session["tasks"] += [task]
#Redirect user to list of tasks
return HttpResponseRedirect(reverse("tasks:index"))
else:
#If the form is invalid, re-render the page with existing info
return render(request, "tasks/add.html",{
"form":form
})
return render(request, "tasks/add.html",{
"form": NewTaskForm()
})
#if "tasks" not in request.session: request.session["tasks"]="": If I look inside the session, and if tasks is not in that session, let me add to request.session["tasks"] and set that equal to the empty list. If the user doesn't already have a list of tasks, give them an empty list of tasks.
#return~"tasks": request.session["tasks"]: Instead of rendering the variable tasks which no longer exists, I'll render request.session["tasks"] to pass in that list of tasks to this particular template.
#if request.method==""POST": If the user submitted some form data
#form=NewTaskForm(request.POST): Then we figure out all the data they submitted and save it inside this form variable.
#if form.is_valid(): We check to see if the form is valid. 'Did they actually provide a task, Are they providing all the necessary data in the right format?'
#task=form.cleaned_data["task"]: If so, then we get the task and add it to the list of tasks.
#request.session["tasks"] += [task]: We append that to the list of tasks that's already stored inside of the session before redirecting the user back.
#return HttpResponseRedirect(reverse("tasks:index")): Figure out what the URL of the index URL for the tasks app is, and use that URL as the one that we ultimately redirect to when we return this HttpResponseredirect. In order to use this, we need to import 'from django.http import HttpResponse' and 'from django.urls import reverse'. After I submit a new task and add it to my list of tasks, I'm going to redirected back to the index page of my tasks application.
#else~: Otherwise, if the form is not valid, then we go ahead and render that same add.html file back to them,
#"form":form: But we pass in the form that they submitted so that they can see all of the errors they made. They can make modifications to their own form submission if they'd like to.
#return~: And then otherwise, if the request method wasn't POST at all, if the user just tried to get the page rather than submit data to it, then we're just going to render to them an empty form.
- So by maintaining was global variable called tasks and updating it anytime I submit the form, I've been able to dynamically grow this list of tasks inside of my application and display all of those tasks here inside of my HTML page.
> No such table: django_session ERROR: Django tends to store data inside of tables, but we haven't yet gotten to what that ultimately means or how to manipulate or interact with data stored inside of tables. But Django stores data about sessions that would happen inside of a table by default. You can change to have Django store data about sessions elsewhere.
- The way to give Django access to the table that it wants to create, that it has been waiting to create but it hasn't yet, is to run this command below inside the terminal.
- Finally, before Django will be able to store this data, we must run 'python manage.py migrate' in the terminal.
> I have a for loop inside of a Django template, I can also add an empty condition to say, if I run the for loop but it doesn't run at all because the sequence is empty, just say 'no tasks'.
#index.html
<ul>
{% for task in tasks %}
<li>{{ task }}</li>
{% empty %}
<li>No tasks</li>
{% endfor %}
</ul>
+
> Django
1. Python을 기반으로 한 웹 프레임워크
2. Model, Template, View로 구성된 MTV 패턴 사용
3. SQL을 사용하지 않고 Database에서 사용하는 테이블을 자동으로 대응해주는 ORM을 통해 코드를 작성할 수 있다.
4. 많은 보안 기능을 내장하고 있어 보안성이 우수
5. 다양한 타사 패키지 및 플러그인을 지원
6. 23년 Laravel, Ruby on Rails에 이어 3위. 인기 많은 프레임워크
7. Instagram, JetBrains, Spotify, Drop Box
8. 설치가 간편
9. 수많은 개발자 커뮤니티를 가지고 있음
10. 강력하고 많은 라이브러리 사용 가능 -> 많은 코딩 없이 프로그래밍
11. IDE(통합개발환경)가 훌륭 (PyCharm, Visual Studio Code)
12. 프로젝트 만들면 별도의 개발 없이 자동으로 관리자 화면 제공
13. App단위로 구성되어있어 독립적으로 작업할 때 매우 쉬움
14. ORM을 제공하기 때문에 쿼리 없이 데이터 관리 가능
15. 웹 서버를 포함하고 있어 개발과정에 별도로 웹서버가 없어도 됨
16. 개발과정에서 소스가 수정되면, 서버를 다시 시작하지 않아도 바로 적용이 됨
17. Python을 기본으로 개발한다면 Python에서 제공하는 모든 기능을 활용할 수 있음
18. (단)소형 프로젝트에는 사용하기 무겁고 기능이 많아 부적합
19. (단)내부에 구현된 기능이 많으면 자유 코딩에 한계
20. (단)ORM을 사용하면, 복잡한 데이터 구조나 프로시져를 많이 사용한 곳엔 부적합
21. (단)Python의 단점과 같음. 인터프리터 언어(속도느림)



댓글 없음:
댓글 쓰기