feat(projects): 新增Django综合案例示例代码及素材

This commit is contained in:
100gle
2022-08-10 16:15:39 +08:00
parent b4c2257308
commit 3bf65b2755
65 changed files with 1496 additions and 0 deletions

View File

@@ -0,0 +1,17 @@
class Proxy:
def __init__(self, delegate):
self.delegate = delegate
def fetch(self):
print(f"fetching {self.delegate.url} by proxy...")
class Request:
def __init__(self, url, proxy=False):
self.url = url
self.proxy = Proxy(self) if proxy else None
if __name__ == "__main__":
req = Request("https://sspai.com", proxy=True)
req.proxy.fetch()

View File

@@ -0,0 +1,28 @@
from dataclasses import dataclass
from pprint import pprint
@dataclass
class User:
name: str
age: int
email: str
telephone: str
table = [
User(
name="John",
age=25,
email="example.john@sspai.com",
telephone="021-12345678",
),
User(
name="Steve",
age=30,
email="example.steve@sspai.com",
telephone="000-1111-1111",
),
]
pprint(table)

View File

@@ -0,0 +1,22 @@
#!/usr/bin/env python
"""Django's command-line utility for administrative tasks."""
import os
import sys
def main():
"""Run administrative tasks."""
os.environ.setdefault('DJANGO_SETTINGS_MODULE', 'quickstart.settings')
try:
from django.core.management import execute_from_command_line
except ImportError as exc:
raise ImportError(
"Couldn't import Django. Are you sure it's installed and "
"available on your PYTHONPATH environment variable? Did you "
"forget to activate a virtual environment?"
) from exc
execute_from_command_line(sys.argv)
if __name__ == '__main__':
main()

View File

@@ -0,0 +1,3 @@
from django.contrib import admin
# Register your models here.

View File

@@ -0,0 +1,6 @@
from django.apps import AppConfig
class MyappConfig(AppConfig):
default_auto_field = 'django.db.models.BigAutoField'
name = 'myapp'

View File

@@ -0,0 +1,3 @@
from django.db import models
# Create your models here.

View File

@@ -0,0 +1,3 @@
from django.test import TestCase
# Create your tests here.

View File

@@ -0,0 +1,61 @@
from django.http import HttpResponse
def poem(request):
content = """
<style>
figure {
display: block;
margin: auto;
width: 40%;
}
figure > figcaption {
text-align: center;
}
blockquote {
margin: 0;
}
blockquote p {
padding: 15px;
line-height: 1cm;
background: #eee;
border-radius: 5px;
}
</style>
<figure>
<blockquote>
<i>
<p>
Beautiful is better than ugly.<br />
Explicit is better than implicit.<br />
Simple is better than complex.<br />
Complex is better than complicated.<br />
Flat is better than nested.<br />
Sparse is better than dense.<br />
Readability counts.<br />
Special cases aren't special enough to break the rules.<br />
Although practicality beats purity.<br />
Errors should never pass silently.<br />
Unless explicitly silenced.<br />
In the face of ambiguity, refuse the temptation to guess.<br />
There should be one-- and preferably only one --obvious way to do
it.<br />
Although that way may not be obvious at first unless you're
Dutch.<br />
Now is better than never.<br />
Although never is often better than *right* now.<br />
If the implementation is hard to explain, it's a bad idea.<br />
If the implementation is easy to explain, it may be a good idea.<br />
Namespaces are one honking great idea -- let's do more of those!<br />
</p>
</i>
</blockquote>
<figcaption>
——Tim Peters, <cite><b>The Zen of Python</b></cite>
</figcaption>
</figure>
"""
return HttpResponse(content)

View File

@@ -0,0 +1,3 @@
from django.contrib import admin
# Register your models here.

View File

@@ -0,0 +1,6 @@
from django.apps import AppConfig
class OrmConfig(AppConfig):
default_auto_field = 'django.db.models.BigAutoField'
name = 'orm'

View File

@@ -0,0 +1,26 @@
# Generated by Django 4.0.4 on 2022-06-25 05:27
from django.db import migrations, models
class Migration(migrations.Migration):
initial = True
dependencies = [
]
operations = [
migrations.CreateModel(
name='User',
fields=[
('id', models.AutoField(primary_key=True, serialize=False, verbose_name='用户ID')),
('name', models.CharField(max_length=10, verbose_name='用户名')),
('age', models.IntegerField(verbose_name='年龄')),
('gender', models.BooleanField(blank=True, choices=[(0, ''), (1, '')], default='', verbose_name='性别')),
('email', models.EmailField(max_length=50, verbose_name='邮箱')),
('telephone', models.CharField(blank=True, max_length=11, verbose_name='联系电话')),
('register_at', models.DateTimeField(auto_now_add=True, verbose_name='注册时间')),
],
),
]

View File

@@ -0,0 +1,35 @@
from django.db import models
class User(models.Model):
gender_choices = [
(0, ""),
(1, ""),
]
id = models.AutoField(
primary_key=True,
verbose_name="用户ID",
)
name = models.CharField(
max_length=10,
verbose_name="用户名",
)
age = models.IntegerField(verbose_name="年龄")
gender = models.BooleanField(
choices=gender_choices,
blank=True,
default="",
verbose_name="性别",
)
email = models.EmailField(max_length=50, verbose_name="邮箱")
telephone = models.CharField(
max_length=11,
blank=True,
verbose_name="联系电话",
)
register_at = models.DateTimeField(auto_now_add=True, verbose_name="注册时间")
def __repr__(self):
return f"User<id:{self.id}, name:{self.name}>"

View File

@@ -0,0 +1,3 @@
from django.test import TestCase
# Create your tests here.

View File

@@ -0,0 +1,7 @@
from django.urls import path
from .views import index
urlpatterns = [
path("", index, name="orm.index"),
]

View File

@@ -0,0 +1,5 @@
from django.http import HttpResponse
def index(request):
return HttpResponse("ORM page here.")

View File

@@ -0,0 +1,16 @@
"""
ASGI config for quickstart project.
It exposes the ASGI callable as a module-level variable named ``application``.
For more information on this file, see
https://docs.djangoproject.com/en/4.0/howto/deployment/asgi/
"""
import os
from django.core.asgi import get_asgi_application
os.environ.setdefault('DJANGO_SETTINGS_MODULE', 'quickstart.settings')
application = get_asgi_application()

View File

@@ -0,0 +1,125 @@
"""
Django settings for quickstart project.
Generated by 'django-admin startproject' using Django 4.0.4.
For more information on this file, see
https://docs.djangoproject.com/en/4.0/topics/settings/
For the full list of settings and their values, see
https://docs.djangoproject.com/en/4.0/ref/settings/
"""
from pathlib import Path
# Build paths inside the project like this: BASE_DIR / 'subdir'.
BASE_DIR = Path(__file__).resolve().parent.parent
# Quick-start development settings - unsuitable for production
# See https://docs.djangoproject.com/en/4.0/howto/deployment/checklist/
# SECURITY WARNING: keep the secret key used in production secret!
SECRET_KEY = "django-insecure-)8usuw-fz#s1wq7u2_$u!g&=llu!%j5gl5)$a(avyh5h20_ssq"
# SECURITY WARNING: don't run with debug turned on in production!
DEBUG = True
ALLOWED_HOSTS = []
# Application definition
INSTALLED_APPS = [
"django.contrib.admin",
"django.contrib.auth",
"django.contrib.contenttypes",
"django.contrib.sessions",
"django.contrib.messages",
"django.contrib.staticfiles",
# internal apps
"orm",
]
MIDDLEWARE = [
"django.middleware.security.SecurityMiddleware",
"django.contrib.sessions.middleware.SessionMiddleware",
"django.middleware.common.CommonMiddleware",
"django.middleware.csrf.CsrfViewMiddleware",
"django.contrib.auth.middleware.AuthenticationMiddleware",
"django.contrib.messages.middleware.MessageMiddleware",
"django.middleware.clickjacking.XFrameOptionsMiddleware",
]
ROOT_URLCONF = "quickstart.urls"
TEMPLATES = [
{
"BACKEND": "django.template.backends.django.DjangoTemplates",
"DIRS": [BASE_DIR.joinpath("templates")],
"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",
],
},
},
]
WSGI_APPLICATION = "quickstart.wsgi.application"
# Database
# https://docs.djangoproject.com/en/4.0/ref/settings/#databases
DATABASES = {
"default": {
"ENGINE": "django.db.backends.sqlite3",
"NAME": BASE_DIR / "db.sqlite3",
}
}
# Password validation
# https://docs.djangoproject.com/en/4.0/ref/settings/#auth-password-validators
AUTH_PASSWORD_VALIDATORS = [
{
"NAME": "django.contrib.auth.password_validation.UserAttributeSimilarityValidator",
},
{
"NAME": "django.contrib.auth.password_validation.MinimumLengthValidator",
},
{
"NAME": "django.contrib.auth.password_validation.CommonPasswordValidator",
},
{
"NAME": "django.contrib.auth.password_validation.NumericPasswordValidator",
},
]
# Internationalization
# https://docs.djangoproject.com/en/4.0/topics/i18n/
LANGUAGE_CODE = "zh-Hans"
TIME_ZONE = "Asia/Shanghai"
USE_I18N = True
USE_TZ = True
# Static files (CSS, JavaScript, Images)
# https://docs.djangoproject.com/en/4.0/howto/static-files/
STATIC_URL = "static/"
# Default primary key field type
# https://docs.djangoproject.com/en/4.0/ref/settings/#default-auto-field
DEFAULT_AUTO_FIELD = "django.db.models.BigAutoField"

View File

@@ -0,0 +1,26 @@
"""quickstart URL Configuration
The `urlpatterns` list routes URLs to views. For more information please see:
https://docs.djangoproject.com/en/4.0/topics/http/urls/
Examples:
Function views
1. Add an import: from my_app import views
2. Add a URL to urlpatterns: path('', views.home, name='home')
Class-based views
1. Add an import: from other_app.views import Home
2. Add a URL to urlpatterns: path('', Home.as_view(), name='home')
Including another URLconf
1. Import the include() function: from django.urls import include, path
2. Add a URL to urlpatterns: path('blog/', include('blog.urls'))
"""
from django.contrib import admin
from django.urls import include, path
from myapp import views
urlpatterns = [
path("admin/", admin.site.urls),
path("poem/", views.poem, name="poem"),
path("view/", include("view.urls")),
path("tmpl/", include("tmpl.urls")),
path("orm/", include("orm.urls")),
]

View File

@@ -0,0 +1,16 @@
"""
WSGI config for quickstart project.
It exposes the WSGI callable as a module-level variable named ``application``.
For more information on this file, see
https://docs.djangoproject.com/en/4.0/howto/deployment/wsgi/
"""
import os
from django.core.wsgi import get_wsgi_application
os.environ.setdefault('DJANGO_SETTINGS_MODULE', 'quickstart.settings')
application = get_wsgi_application()

View File

@@ -0,0 +1,43 @@
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8" />
<meta http-equiv="X-UA-Compatible" content="IE=edge" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title>The Zen of Python</title>
<style>
figure {
display: block;
margin: auto;
width: 40%;
}
figure > figcaption {
text-align: center;
}
blockquote {
margin: 0;
}
blockquote p {
padding: 15px;
line-height: 1cm;
background: #eee;
border-radius: 5px;
}
</style>
</head>
<body>
{% csrf_token %}
<figure>
<blockquote>
<p>
{% for line in lines %} <i>{{ line }}</i><br />
{% endfor %}
</p>
</blockquote>
<figcaption>
——{{ author }}, <cite><b>{{ source }}</b></cite>
</figcaption>
</figure>
</body>
</html>

View File

@@ -0,0 +1,3 @@
from django.contrib import admin
# Register your models here.

View File

@@ -0,0 +1,6 @@
from django.apps import AppConfig
class TmplConfig(AppConfig):
default_auto_field = 'django.db.models.BigAutoField'
name = 'tmpl'

View File

@@ -0,0 +1,3 @@
from django.db import models
# Create your models here.

View File

@@ -0,0 +1,3 @@
from django.test import TestCase
# Create your tests here.

View File

@@ -0,0 +1,7 @@
from django.urls import path
from .views import index
urlpatterns = [
path("", index, name="tmpl_index"),
]

View File

@@ -0,0 +1,36 @@
from django.shortcuts import render
def index(request):
poem = """\
Beautiful is better than ugly.
Explicit is better than implicit.
Simple is better than complex.
Complex is better than complicated.
Flat is better than nested.
Sparse is better than dense.
Readability counts.
Special cases aren't special enough to break the rules.
Although practicality beats purity.
Errors should never pass silently.
Unless explicitly silenced.
In the face of ambiguity, refuse the temptation to guess.
There should be one-- and preferably only one --obvious way to do it.
Although that way may not be obvious at first unless you're Dutch.
Now is better than never.
Although never is often better than *right* now.
If the implementation is hard to explain, it's a bad idea.
If the implementation is easy to explain, it may be a good idea.
Namespaces are one honking great idea -- let's do more of those!
"""
lines = poem.strip().split("\n")
author = "Tim Peters"
source = "The Zen of Python"
context = dict(
lines=lines,
author=author,
source=source,
)
return render(request, "poem.html", context=context)

View File

@@ -0,0 +1,3 @@
from django.contrib import admin
# Register your models here.

View File

@@ -0,0 +1,6 @@
from django.apps import AppConfig
class ViewConfig(AppConfig):
default_auto_field = 'django.db.models.BigAutoField'
name = 'view'

View File

@@ -0,0 +1,3 @@
from django.db import models
# Create your models here.

View File

@@ -0,0 +1,3 @@
from django.test import TestCase
# Create your tests here.

View File

@@ -0,0 +1,8 @@
from django.urls import path
from .views import IndexView, index
urlpatterns = [
path("", index),
path("class/", IndexView.as_view()),
]

View File

@@ -0,0 +1,59 @@
from django.http import HttpResponse, HttpResponseNotAllowed
from django.utils.decorators import method_decorator
from django.views import View
from django.views.decorators.csrf import csrf_exempt
@csrf_exempt
def index(request):
"""index page"""
method = request.method
if method == "GET":
return HttpResponseNotAllowed(["POST"])
elif method == "POST":
return HttpResponse(f"You has got this page by POST method.")
@method_decorator(csrf_exempt, name="dispatch")
class IndexView(View):
template = """\
<p>You has got this page by {method} method, the following steps are: <br />
{content}
</p>
"""
def get(self, request):
"""index page"""
steps = [
"1. handle GET request",
"2. log request and other info",
"3. query something from database",
]
response = self.template.format(
method="GET",
content=r"<br />".join(steps),
)
return HttpResponse(response)
def post(self, request):
steps = [
"1. handle POST request",
"2. log request and other info",
"3. get form or parameters from request",
"4. parse form or parameters",
"5. query something from database",
"6. return response",
]
response = self.template.format(
method="POST",
content=r"<br />".join(steps),
)
return HttpResponse(response)

View File

@@ -0,0 +1,49 @@
# server.py
import logging
import socketserver
import sys
logging.basicConfig(
level=logging.INFO,
format="[{asctime}] [{levelname}] [{module}] - {message}",
style="{",
)
log = logging.getLogger("server")
class TCPHandler(socketserver.BaseRequestHandler):
def handle(self) -> None:
request = self.request.recv(1024).decode().strip()
log.info(f"Get request:\n{request}")
header = "HTTP/1.0 200 OK\r\n\n"
content = """
<div style="text-align: center;">
<h1>Hello, World</h1>
<p>You got it!</p>
</div>
""".strip()
response = header + content
self.request.sendall(response.encode())
def main():
HOST, PORT = "127.0.0.1", 8000
srv = socketserver.TCPServer(
server_address=(HOST, PORT),
RequestHandlerClass=TCPHandler,
)
log.info(f"Listening on http://{HOST}:{PORT}...")
try:
srv.serve_forever()
except KeyboardInterrupt:
log.info("Server stopped")
srv.server_close()
srv.shutdown()
log.info("Bye...")
sys.exit(0)
if __name__ == '__main__':
main()

View File

@@ -0,0 +1,22 @@
#!/usr/bin/env python
"""Django's command-line utility for administrative tasks."""
import os
import sys
def main():
"""Run administrative tasks."""
os.environ.setdefault('DJANGO_SETTINGS_MODULE', 'todolist.settings')
try:
from django.core.management import execute_from_command_line
except ImportError as exc:
raise ImportError(
"Couldn't import Django. Are you sure it's installed and "
"available on your PYTHONPATH environment variable? Did you "
"forget to activate a virtual environment?"
) from exc
execute_from_command_line(sys.argv)
if __name__ == '__main__':
main()

View File

@@ -0,0 +1,3 @@
from django.contrib import admin
# Register your models here.

View File

@@ -0,0 +1,6 @@
from django.apps import AppConfig
class PopupConfig(AppConfig):
default_auto_field = 'django.db.models.BigAutoField'
name = 'popup'

View File

@@ -0,0 +1,35 @@
# Generated by Django 4.0.4 on 2022-06-28 13:48
from django.db import migrations, models
import django.db.models.deletion
class Migration(migrations.Migration):
initial = True
dependencies = [
]
operations = [
migrations.CreateModel(
name='Group',
fields=[
('id', models.BigAutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
('name', models.CharField(default='收集箱', max_length=100, unique=True, verbose_name='分类名称')),
],
),
migrations.CreateModel(
name='Task',
fields=[
('id', models.BigAutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
('name', models.CharField(max_length=100, verbose_name='任务名称')),
('priority', models.IntegerField(choices=[(0, '一般'), (1, '优先'), (3, '紧急')], default=0, verbose_name='任务优先级')),
('description', models.TextField(blank=True, max_length=500, null=True, verbose_name='任务描述')),
('created_at', models.DateTimeField(auto_now_add=True, verbose_name='创建时间')),
('updated_at', models.DateTimeField(auto_now=True, verbose_name='更新时间')),
('is_done', models.BooleanField(choices=[(False, '未完成'), (True, '已完成')], default=False, verbose_name='是否完成')),
('group', models.ForeignKey(default=0, on_delete=django.db.models.deletion.DO_NOTHING, to='popup.group', verbose_name='所属分类')),
],
),
]

View File

@@ -0,0 +1,47 @@
from django.db import models
class Task(models.Model):
"""the task record in todolist"""
priority_choices = (
(0, "一般"),
(1, "优先"),
(3, "紧急"),
)
is_done_choices = (
(False, "未完成"),
(True, "已完成"),
)
name = models.CharField(max_length=100, verbose_name="任务名称")
priority = models.IntegerField(
choices=priority_choices, default=0, verbose_name="任务优先级"
)
description = models.TextField(
max_length=500, verbose_name="任务描述", blank=True, null=True
)
created_at = models.DateTimeField(auto_now_add=True, verbose_name="创建时间")
updated_at = models.DateTimeField(auto_now=True, verbose_name="更新时间")
is_done = models.BooleanField(
default=False, verbose_name="是否完成", choices=is_done_choices
)
group = models.ForeignKey(
"Group", on_delete=models.DO_NOTHING, default=0, verbose_name="所属分类"
)
def __repr__(self):
return f"<Task {self.name}>"
class Group(models.Model):
name = models.CharField(
max_length=100,
default="收集箱",
unique=True,
verbose_name="分类名称",
)
def __repr__(self):
return f"<Group '{self.name}'>"

View File

@@ -0,0 +1,3 @@
from django.test import TestCase
# Create your tests here.

View File

@@ -0,0 +1,12 @@
from django.urls import path
from .views import about, create_task, delete_task, index, query_all_tasks, update_task
urlpatterns = [
path("", index, name="index"),
path("about/", about, name="about"),
path("tasks/", query_all_tasks, name="tasks"),
path("tasks/create/", create_task, name="create_task"),
path("tasks/update/<int:task_id>/", update_task, name="update_task"),
path("tasks/delete/<int:task_id>/", delete_task, name="delete_task"),
]

View File

@@ -0,0 +1,112 @@
from django.http.response import HttpResponseNotAllowed
from django.shortcuts import redirect, render
from django.utils.html import format_html
from .models import Group, Task
def index(request):
welcome = "欢迎来到 Todolist 练习项目 Popup 应用主页!"
index_content = format_html(
"""
这是<a href="https://sspai.com/series/271" style="text-decoration: none;">《100 小时后请叫我程序员》</a>课程用于进行 Django 实践练习的网站,主要包括了以下几部分:
<ul>
<li><b>少数派 Logo</b>:你可以点击跳转至少数派首页;</li>
<li><b>Home</b>:即当前页面;</li>
<li><b>Task</b>:用于实践的待办清单页面,你可以在里面创建、更新以及删除任务;</li>
<li><b>About</b>:对个人或网站信息进行简要说明的介绍页。</li>
</ul>
"""
)
ctx = dict(
title="home",
welcome=welcome,
index_content=index_content,
)
return render(request, "index.html", context=ctx)
def about(request):
title = "About"
header = f"{title} Me"
about_content = """
这是一段简单的About Me 页面。
它可以用来展示关于个人的一些信息,或者是简单的介绍。
例如:
""".strip().splitlines()
items = [
("昵称", "100gle"),
("职业", "少数派作者"),
("编程语言", "Python、Golang、JavaScript 等等"),
]
ctx = dict(
title=title,
header=header,
about_content=about_content,
items=items,
)
return render(request, "about.html", context=ctx)
def query_all_tasks(request):
tasks = Task.objects.all()
fields = ["序号", "任务名称", "优先级", "任务描述", "是否完成", "分组"]
if Group.objects.count() == 0:
groups = [
Group(name="收集箱"),
Group(name="生活"),
Group(name="工作"),
]
Group.objects.bulk_create(groups)
groups = Group.objects.all()
ctx = dict(
title="Task",
tasks=tasks,
fields=fields,
groups=groups,
)
return render(request, "tasks.html", context=ctx)
def create_task(request):
"""create a task"""
if request.method == "POST":
name = request.POST.get("taskName")
priority = request.POST.get("taskPriority")
description = request.POST.get("taskDescription")
group_id = request.POST.get("taskGroup")
group = Group.objects.get(id=group_id)
Task.objects.create(
name=name, priority=priority, description=description, group=group
)
return redirect("/tasks/")
else:
return HttpResponseNotAllowed(["POST"])
def update_task(request, task_id):
"""update a task"""
task = Task.objects.get(id=task_id)
task.is_done = False if task.is_done else True
task.save()
return redirect("/tasks/")
def delete_task(request, task_id):
"""delete a task"""
task = Task.objects.get(id=task_id)
task.delete()
return redirect("/tasks/")

File diff suppressed because one or more lines are too long

View File

@@ -0,0 +1,149 @@
/* base layout */
.container {
margin: 0;
padding: 0;
box-sizing: border-box;
}
h1 {
margin-left: 15px;
}
p {
margin: 10px;
word-spacing: 1.5px;
}
ul {
list-style: none;
}
/* navbar */
.navbar ul {
display: flex;
align-items: center;
justify-content: space-between;
text-align: center;
list-style: none;
padding-left: 0;
}
.navbar a {
display: flex;
justify-content: center;
align-items: center;
text-decoration: none;
border: none;
font-size: 20px;
font-weight: bold;
}
.navbar a:hover {
color: goldenrod !important;
}
.content,
.about {
margin: 0 10px;
}
.content li::before,
.about li::before {
content: "\1F449";
padding-right: 5px;
}
/* data table */
.data-table {
display: table;
table-layout: fixed;
width: 100%;
border-collapse: collapse;
}
.data-table th,
.data-table td {
text-align: center;
word-wrap: break-word;
overflow-wrap: break-word;
}
.data-table th:first-child {
width: 5%;
}
.data-table th:nth-child(2) {
width: 15%;
}
.data-table th:nth-child(4) {
width: 30%;
}
.data-table td[class="ops"] {
display: flex;
flex-direction: column;
justify-content: center;
align-items: center;
}
.data-table .ops span {
display: block;
color: var(--accent);
cursor: pointer;
}
.btn-add-task {
display: flex;
flex-direction: row-reverse;
}
.btn-add-task > button {
display: block;
width: 50px;
height: 50px;
border-radius: 50%;
font-size: 20px;
box-shadow: 2px 2px 2px 1px rgba(0, 0, 0, 0.2);
}
.btn-add-task > button:hover {
background-color: rgb(204, 77, 77);
}
.task-form {
display: none;
flex-direction: column;
align-items: center;
}
.form {
display: block;
width: 80%;
}
.form > div {
display: flex;
flex-direction: row;
justify-content: space-between;
vertical-align: middle;
}
.form .btn-submit {
text-align: center;
transition: 0.1ms;
}
.form .btn-submit > input {
display: block;
width: 100%;
font-size: 20px;
transition: 0.1ms;
}
#task-textarea {
width: 60%;
resize: none;
}
/* footer */
#footer {
text-align: center;
}

View File

@@ -0,0 +1,84 @@
<?xml version="1.0" encoding="utf-8"?>
<svg version="1.1" id="图层_1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" width="123" height="44" viewBox="0 0 123 44" style="enable-background:new 0 0 123 44;" xml:space="preserve">
<style type="text/css">
.st0{fill:#292525;}
.st1{fill:#DA282A;}
.st2{fill:#FFFFFF;}
</style>
<path class="st0" d="M59.8,31.7C59.8,31.7,59.7,31.7,59.8,31.7c-0.1,0-0.2-0.1-0.2-0.1l-0.9-1.3c0,0,0-0.1,0-0.1c0,0,0-0.1,0-0.1
c0,0,0-0.1,0.1-0.1c0,0,0.1,0,0.1,0c6.6-0.6,11.8-2.9,17-7.5c0,0,0,0,0.1,0c0,0,0.1,0,0.1,0c0,0,0.1,0,0.1,0c0,0,0,0,0.1,0.1
l0.9,1.1c0,0,0.1,0.1,0,0.2c0,0.1,0,0.1-0.1,0.1C71.6,28.6,66.6,30.8,59.8,31.7L59.8,31.7z" />
<path class="st0" d="M69.2,12.2h-1.5c-0.1,0-0.2,0.1-0.2,0.2v12.4c0,0.1,0.1,0.2,0.2,0.2h1.5c0.1,0,0.2-0.1,0.2-0.2V12.5
C69.4,12.3,69.3,12.2,69.2,12.2z" />
<path class="st0" d="M60.7,23.5H59c0,0-0.1,0-0.1,0c0,0-0.1,0-0.1-0.1c0,0,0-0.1,0-0.1c0,0,0-0.1,0-0.1l3.6-7.9c0,0,0-0.1,0.1-0.1
c0,0,0.1,0,0.1,0h1.7c0,0,0.1,0,0.1,0c0,0,0.1,0,0.1,0.1c0,0,0,0.1,0,0.1c0,0,0,0.1,0,0.1l-3.6,7.9c0,0,0,0.1-0.1,0.1
C60.8,23.5,60.7,23.5,60.7,23.5z" />
<path class="st0" d="M75.6,21c0,0-0.1,0-0.1,0c0,0-0.1,0-0.1-0.1l-3.1-5.4c0,0,0-0.1,0-0.1c0,0,0-0.1,0-0.1c0,0,0-0.1,0.1-0.1
c0,0,0.1,0,0.1,0h1.8c0,0,0.1,0,0.1,0c0,0,0.1,0,0.1,0.1l3.1,5.4c0,0,0,0.1,0,0.1c0,0,0,0.1,0,0.1c0,0,0,0.1-0.1,0.1
c0,0-0.1,0-0.1,0H75.6L75.6,21z" />
<path class="st0" d="M108.5,31.6C108.5,31.6,108.4,31.6,108.5,31.6l-1.4-0.5c-0.1,0-0.1-0.1-0.1-0.1c0,0,0-0.1,0-0.1
c0,0,0-0.1,0-0.1l0.8-2c0.3-0.8,0.6-1.6,0.8-2.5c0.3-1.3,0.4-2.5,0.4-3.8V14c0-0.1,0-0.1,0.1-0.2c0,0,0.1-0.1,0.2-0.1
c5.3,0.1,7.5-0.5,10.3-1.3l0.7-0.2c0,0,0.1,0,0.1,0s0.1,0,0.1,0.1l0.8,1.2c0,0,0,0.1,0,0.1c0,0,0,0.1,0,0.1c0,0,0,0.1-0.1,0.1
c0,0-0.1,0-0.1,0l-0.3,0.1c-2.9,0.9-5,1.5-9.8,1.6c-0.1,0-0.1,0-0.1,0.1c0,0-0.1,0.1-0.1,0.1v6.9c0,1.4-0.2,2.7-0.5,4.1l-0.1,0.3
c-0.2,0.8-0.4,1.7-0.8,2.5l-0.8,2C108.7,31.5,108.6,31.6,108.5,31.6C108.6,31.6,108.5,31.6,108.5,31.6z" />
<path class="st0" d="M105.6,22c0,0-0.1,0-0.1,0c0,0-0.1,0-0.1-0.1L103,18c0,0,0,0,0-0.1c0,0,0-0.1,0-0.1c0,0,0-0.1,0-0.1
c0,0,0,0,0.1-0.1l1.8-1.1c0,0,0.1,0,0.1-0.1c0,0,0-0.1,0-0.1c0,0,0-0.1,0-0.1c0,0,0-0.1,0-0.1l-1.9-2.9c0,0,0-0.1,0-0.1
c0,0,0-0.1,0-0.1c0,0,0-0.1,0-0.1c0,0,0,0,0.1-0.1l1.3-0.8c0,0,0.1,0,0.2,0c0.1,0,0.1,0,0.1,0.1l2.3,3.6c0,0,0,0,0,0.1l0.6,0.9
c0,0,0,0.1,0,0.1c0,0,0,0.1,0,0.1c0,0,0,0.1,0,0.1c0,0,0,0-0.1,0.1l-1.8,1.1c0,0-0.1,0-0.1,0.1c0,0,0,0.1,0,0.1c0,0,0,0.1,0,0.1
c0,0,0,0.1,0,0.1l1.4,2.2c0,0,0,0.1,0,0.1c0,0,0,0.1,0,0.1c0,0.1,0,0.1-0.1,0.1l-1.3,0.8C105.7,22,105.6,22,105.6,22z" />
<path class="st0" d="M104.4,31.8C104.4,31.8,104.4,31.8,104.4,31.8l-1.5-0.6c0,0-0.1,0-0.1,0c0,0,0,0,0-0.1c0,0,0-0.1,0-0.1
c0,0,0-0.1,0-0.1l3.3-8.3c0,0,0-0.1,0-0.1c0,0,0,0,0.1,0s0.1,0,0.1,0c0,0,0.1,0,0.1,0l1.4,0.6c0,0,0.1,0,0.1,0c0,0,0,0,0,0.1
c0,0,0,0.1,0,0.1c0,0,0,0.1,0,0.1l-3.3,8.3c0,0,0,0,0,0.1C104.6,31.7,104.6,31.7,104.4,31.8C104.5,31.8,104.5,31.8,104.4,31.8z" />
<path class="st0" d="M120.8,31.7C120.8,31.7,120.8,31.7,120.8,31.7c-0.1,0-0.2-0.1-0.2-0.1l-1.6-2.3c-0.3-0.4-0.6-0.8-0.8-1.3
l-0.8-1.6c-0.3-0.6-0.6-1.3-0.8-2l-1.5-5.7c0,0,0-0.1-0.1-0.1c0,0-0.1,0-0.1,0c-0.4,0-0.7,0-1.1,0.1c0,0-0.1,0-0.1,0
c0,0,0,0.1,0,0.1v10.9c0,0.1,0,0.1,0.1,0.1c0,0,0.1,0.1,0.1,0.1h2.6c0.1,0,0.1,0,0.2,0.1c0,0,0.1,0.1,0.1,0.2v1.4
c0,0.1,0,0.1-0.1,0.2c0,0-0.1,0.1-0.2,0.1h-4.3c-0.1,0-0.1,0-0.2-0.1c0,0-0.1-0.1-0.1-0.2V17.6c0,0,0,0,0-0.1V17
c0-0.1,0-0.1,0.1-0.2c0,0,0,0,0.1,0c0,0,0.1,0,0.1,0c2.4,0,4.7-0.3,7-0.8l0.9-0.2c0,0,0.1,0,0.1,0c0,0,0.1,0.1,0.1,0.1l0.7,1.3
c0,0,0,0.1,0,0.1c0,0,0,0.1,0,0.1c0,0,0,0.1-0.1,0.1c0,0-0.1,0-0.1,0l-0.9,0.2c-1,0.2-1.9,0.4-2.9,0.6c0,0,0,0-0.1,0c0,0,0,0,0,0
c0,0,0,0,0,0.1c0,0,0,0,0,0.1l0.9,3.5c0,0,0,0.1,0,0.1c0,0,0,0,0.1,0.1c0,0,0.1,0,0.1,0c0,0,0.1,0,0.1,0l2.9-1.2c0,0,0.1,0,0.1,0
c0,0,0.1,0,0.1,0c0,0,0.1,0,0.1,0c0,0,0,0,0,0.1l0.5,1.3c0,0.1,0,0.1,0,0.2c0,0.1-0.1,0.1-0.1,0.1l-3.2,1.3c0,0-0.1,0-0.1,0.1
c0,0,0,0.1,0,0.1c0.2,0.6,0.4,1.1,0.6,1.6l0.7,1.3c0.2,0.4,0.4,0.8,0.7,1.2l1.9,2.7c0,0,0,0.1,0,0.1c0,0,0,0.1,0,0.1
c0,0,0,0.1,0,0.1c0,0-0.1,0-0.1,0.1L120.8,31.7C120.9,31.7,120.9,31.7,120.8,31.7z" />
<path class="st0" d="M86.8,22.3h-1.5c-0.1,0-0.1,0-0.2-0.1c0,0-0.1-0.1-0.1-0.2v-4c0,0,0-0.1-0.1-0.1c0,0-0.1-0.1-0.1-0.1h-4
c-0.1,0-0.1,0-0.2-0.1c0,0-0.1-0.1-0.1-0.2v-1.4c0-0.1,0-0.1,0.1-0.2c0,0,0.1-0.1,0.2-0.1h4c0,0,0.1,0,0.1-0.1c0,0,0.1-0.1,0.1-0.1
v-3.5c0-0.1,0-0.1,0.1-0.2c0,0,0.1-0.1,0.2-0.1h1.5c0.1,0,0.1,0,0.2,0.1c0,0,0.1,0.1,0.1,0.2V16c0,0,0,0.1,0.1,0.1
c0,0,0.1,0.1,0.1,0.1h4c0.1,0,0.1,0,0.2,0.1c0,0,0.1,0.1,0.1,0.2v1.4c0,0.1,0,0.1-0.1,0.2c0,0-0.1,0.1-0.2,0.1h-4c0,0,0,0-0.1,0
c0,0,0,0-0.1,0c0,0,0,0,0,0.1c0,0,0,0,0,0.1v4c0,0.1,0,0.1-0.1,0.2C86.9,22.3,86.9,22.3,86.8,22.3z" />
<path class="st0" d="M83,16.2c0,0-0.1,0-0.1,0c0,0-0.1,0-0.1-0.1l-1.5-2.8c0-0.1,0-0.1,0-0.2c0-0.1,0.1-0.1,0.1-0.1l1.4-0.7
c0.1,0,0.1,0,0.2,0c0.1,0,0.1,0.1,0.1,0.1l1.5,2.8c0,0,0,0.1,0,0.1c0,0,0,0.1,0,0.1c0,0.1-0.1,0.1-0.1,0.1L83,16.2
C83,16.2,83,16.2,83,16.2z" />
<path class="st0" d="M89,16.2c0,0-0.1,0-0.1,0l-1.2-1c0,0-0.1-0.1-0.1-0.1c0-0.1,0-0.1,0-0.2l2.1-2.5c0,0,0.1-0.1,0.1-0.1
c0.1,0,0.1,0,0.2,0l1.2,1c0,0,0.1,0.1,0.1,0.1c0,0.1,0,0.1,0,0.2l-2.1,2.5C89.1,16.1,89.1,16.2,89,16.2C89.1,16.2,89,16.2,89,16.2z" />
<path class="st0" d="M82.2,22c-0.1,0-0.1,0-0.1-0.1l-1.2-1c0,0,0,0-0.1-0.1c0,0,0-0.1,0-0.1c0-0.1,0-0.1,0.1-0.2l2.2-2.6
c0,0,0,0,0.1-0.1c0,0,0.1,0,0.1,0c0,0,0.1,0,0.1,0c0,0,0.1,0,0.1,0l1.2,1c0,0,0,0,0.1,0.1c0,0,0,0.1,0,0.1c0,0,0,0.1,0,0.1
c0,0,0,0.1,0,0.1l-2.2,2.6C82.4,21.9,82.3,21.9,82.2,22C82.3,22,82.3,22,82.2,22z" />
<path class="st0" d="M89.8,21.9C89.7,21.9,89.7,21.8,89.8,21.9c-0.1,0-0.2-0.1-0.2-0.1l-1.9-2.6c0,0,0,0,0-0.1c0,0,0-0.1,0-0.1
c0,0,0-0.1,0-0.1c0,0,0,0,0.1-0.1L89,18c0,0,0,0,0.1,0c0,0,0.1,0,0.1,0c0,0,0.1,0,0.1,0c0,0,0,0,0.1,0.1l1.9,2.6c0,0,0,0.1,0,0.2
c0,0.1,0,0.1-0.1,0.1l-1.2,0.9C89.8,21.8,89.8,21.9,89.8,21.9z" />
<path class="st0" d="M91.6,30.2C91.6,30.2,91.7,30.2,91.6,30.2c0.1,0.1,0.1,0.1,0.1,0.2c0,0,0,0.1,0,0.1c0,0,0,0.1-0.1,0.1l-1.2,1
c-0.1,0.1-0.1,0.1-0.2,0.1c-0.1,0-0.2,0-0.3,0l-3.6-1.7c0,0-0.1,0-0.1,0s-0.1,0-0.1,0l-1.4,0.6c-0.6,0.3-1.3,0.5-1.9,0.7l-1.7,0.5
c-0.1,0-0.1,0-0.2,0c-0.1,0-0.1-0.1-0.1-0.1l-0.3-1.1c0-0.1,0-0.1,0-0.2c0-0.1,0-0.1,0-0.2c0-0.1,0.1-0.1,0.1-0.1
c0,0,0.1-0.1,0.2-0.1l1.8-0.6c0.3-0.1,0.7-0.2,1-0.4l0.4-0.2l-0.8-0.4l-1.8-0.8c0,0-0.1,0-0.1-0.1c0,0,0,0,0-0.1c0,0,0-0.1,0-0.1
c0,0,0-0.1,0-0.1l1-1.8c0,0,0-0.1,0-0.1c0,0,0-0.1,0-0.1c0,0-0.1-0.1-0.1-0.1c0,0-0.1,0-0.1,0h-1.1c-0.1,0-0.2,0-0.3-0.1
c-0.1-0.1-0.1-0.2-0.1-0.3v-0.9c0-0.1,0-0.2,0.1-0.3c0.1-0.1,0.2-0.1,0.3-0.1h2.5l0.3-0.5c0,0,0-0.1,0.1-0.1c0,0,0.1,0,0.1,0H86
c0,0,0.1,0,0.1,0c0,0,0.1,0,0.1,0.1c0,0,0,0.1,0,0.1c0,0,0,0.1,0,0.1L86,23.3h5.2c0.1,0,0.2,0,0.3,0.1c0.1,0.1,0.1,0.2,0.1,0.3v1.6
c0,0.1,0,0.2,0,0.2c0,0.1-0.1,0.1-0.1,0.2l-1,1c-0.7,0.7-1.4,1.3-2.2,1.8L91.6,30.2z M84.4,26.9l1.8,0.8c0,0,0.1,0,0.1,0
c0,0,0.1,0,0.1,0l0.9-0.6c0.6-0.4,1.1-0.9,1.7-1.4l0.6-0.6h-4.4c-0.1,0-0.2,0-0.2,0.1c-0.1,0-0.1,0.1-0.2,0.2l-0.6,1
c0,0,0,0.1-0.1,0.2c0,0.1,0,0.1,0,0.2c0,0.1,0,0.1,0.1,0.1C84.3,26.9,84.3,26.9,84.4,26.9z" />
<path class="st0" d="M98,28l2.1,2.3c0,0,0.1,0.1,0.1,0.2c0,0.1,0,0.1-0.1,0.2l-1.1,1c0,0,0,0-0.1,0c0,0-0.1,0-0.1,0
c-0.1,0-0.1,0-0.2-0.1L96.2,29c0,0-0.1,0-0.1-0.1c0,0-0.1,0-0.1,0c0,0-0.1,0-0.1,0c0,0-0.1,0-0.1,0.1l-2.5,2.7c0,0-0.1,0.1-0.2,0.1
c-0.1,0-0.1,0-0.2-0.1l-1.1-1c0,0,0,0,0-0.1c0,0,0-0.1,0-0.1c0,0,0-0.1,0-0.1c0,0,0-0.1,0-0.1l2-2.1l0.8-0.9c0,0,0.1-0.1,0.1-0.1
c0-0.1,0-0.1-0.1-0.1l-0.2-0.3c-0.8-1-1.3-2.2-1.6-3.5l-0.2-0.7c-0.1-0.7-0.2-1.3-0.2-2v-4c0-0.1,0-0.1,0.1-0.2c0,0,0,0,0.1,0
c0,0,0.1,0,0.1,0h1.5c0.1,0,0.1,0,0.2,0.1c0,0,0.1,0.1,0.1,0.2v3.8c0,0.6,0.1,1.3,0.2,1.9l0.1,0.3c0.2,1,0.6,2,1.2,2.9
c0,0,0,0,0.1,0c0,0,0,0,0.1,0c0,0,0,0,0.1,0c0,0,0,0,0.1,0c0.6-0.9,1-1.9,1.3-3l0-0.1c0.2-0.7,0.2-1.3,0.2-2v-4.4c0,0,0-0.1,0-0.1
c0,0-0.1,0-0.1,0h-4.9c-0.1,0-0.1,0-0.2-0.1c0,0-0.1-0.1-0.1-0.2v-2.9c0-0.1,0-0.1,0.1-0.2c0,0,0.1-0.1,0.2-0.1h1.5
c0.1,0,0.1,0,0.2,0.1c0,0,0.1,0.1,0.1,0.2v1.3h5c0.1,0,0.1,0,0.2,0.1c0,0,0.1,0.1,0.1,0.2v1.4v5c0,0.7-0.1,1.4-0.2,2.2l-0.2,0.7
c-0.3,1.2-0.8,2.4-1.6,3.4L97.4,27c0,0-0.1,0.1-0.1,0.2c0,0.1,0,0.1,0.1,0.2L98,28z" />
<path class="st1" d="M22,44c12.2,0,22-9.8,22-22C44,9.8,34.2,0,22,0C9.8,0,0,9.8,0,22C0,34.2,9.8,44,22,44z" />
<path class="st2" d="M32,11.3l-21.2,3.6c-0.2,0-0.4,0.1-0.6,0.3c-0.1,0.2-0.2,0.4-0.3,0.6l-0.2,1.8c0,0.2,0,0.3,0,0.4
c0.1,0.1,0.1,0.3,0.2,0.4c0.1,0.1,0.2,0.2,0.4,0.2c0.1,0,0.3,0.1,0.4,0l4.1-0.7v14.7c0,0.2,0,0.4,0.1,0.5c0.1,0.2,0.2,0.3,0.4,0.4
c0.2,0.1,0.3,0.1,0.5,0.1c0.2,0,0.4-0.1,0.5-0.2l1.6-1c0.2-0.1,0.3-0.3,0.4-0.5c0.1-0.2,0.2-0.4,0.2-0.6v-14l5.7-0.9v10.3
c0,0.6,0.2,1.3,0.5,1.8l2.5,4.7c0.1,0.1,0.2,0.3,0.3,0.3c0.1,0.1,0.3,0.1,0.4,0.1h2.2c0.1,0,0.3,0,0.4-0.1c0.1-0.1,0.2-0.2,0.3-0.3
c0.1-0.1,0.1-0.3,0.1-0.4c0-0.1,0-0.3-0.1-0.4l-2.5-4.9C28.2,27,28,26.4,28,25.7V15.7l4-0.7c0.2,0,0.4-0.1,0.5-0.3
c0.1-0.2,0.2-0.4,0.3-0.6l0.2-1.8c0-0.2,0-0.3,0-0.5s-0.1-0.3-0.2-0.4s-0.2-0.2-0.4-0.2C32.3,11.3,32.1,11.2,32,11.3z" />
</svg>

After

Width:  |  Height:  |  Size: 8.7 KiB

View File

@@ -0,0 +1,16 @@
const addTaskButton = document.querySelector(".btn-add-task");
const formDiv = document.querySelector(".task-form");
const taskStatus = document.querySelector(".task-status");
const updateOp = document.querySelector(".update-op");
// show task form
if (addTaskButton) {
addTaskButton.addEventListener("click", () => {
// use inline style to override css property.
if (formDiv.style.display === "") {
formDiv.style.display = "none";
}
formDiv.style.display =
formDiv.style.display == "none" ? "flex" : "none";
});
}

View File

@@ -0,0 +1,24 @@
{% extends "base.html" %}
{% block title %}
{{ title }}
{% endblock %}
{% block content %}
<article class="about">
<h1>{{ header }}</h1>
<section>
<p>
{% for content in about_content %}
{{ content }} <br />
{% endfor %}
</p>
<ul>
{% for item in items %}
<li>{{ item.0 }}{{ item.1 }}</li>
{% endfor %}
</ul>
</section>
</article>
{% endblock %}

View File

@@ -0,0 +1,44 @@
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>
{% if title == "home" %}
Todolist Web App
{% else %}
Todolist Web App - {% block title %}{% endblock %}
{% endif %}
</title>
{% load static %}
<link rel="stylesheet" href={% static "css/simple-v1.min.css" %}>
<link rel="stylesheet" href={% static "css/style.css" %}>
</head>
<body>
<div class="container">
<nav class="navbar">
<ul>
<li>
<a href="https://sspai.com" class="logo">
<img src={% static "images/sspai-logo-light.svg" %} alt="logo">
</a>
</li>
<li><a href={% url "index" %}>Home</a></li>
<li><a href={% url "tasks" %}>Task</a></li>
<li><a href={% url "about" %}>About</a></li>
</ul>
</nav>
<hr />
<main>
{% block content %}
{% endblock %}
</main>
<footer id="footer">
<p class="copyright">&copy; 2022-Present 100gle & 少数派</p>
</footer>
</div>
<script type="text/javascript" src={% static "js/main.js" %}></script>
</body>
</html>

View File

@@ -0,0 +1,19 @@
{% extends "base.html" %}
{% block title %}
{{ title }}
{% endblock %}
{% block content%}
<article class="content">
<h1>🗣 <i>Hello, world</i></h1>
<section>
<p>{{ welcome }}</p>
<p>
{{ index_content }}
</p>
</section>
</article>
{% endblock%}

View File

@@ -0,0 +1,96 @@
{% extends "base.html" %}
{% block title%}
{{ title }}
{% endblock %}
{% block content %}
<div class="data-table">
<h1>Tasks</h1>
{% if not tasks %}
<p>🤔 Whoops……似乎任务都做完了那么可以点击下方的按钮添加任务</p>
{% else %}
<table>
<thead>
<tr>
{% for field in fields %}
<th>{{ field }}</th>
{% endfor%}
<th>操作</th>
</tr>
</thead>
<tbody>
{% for task in tasks %}
<tr>
<td>{{ task.id }}</td>
<td>{{ task.name }}</td>
<td>
{% if task.priority == 1 %}
{{ "一般" }}
{% elif task.priority == 2 %}
{{ "优先" }}
{% else %}
{{ "紧急" }}
{% endif %}
</td>
<td class="task-decs">{{ task.description }}</td>
<td class="task-status">
{% if task.is_done %}
{{ "已完成" }}
{% else %}
{{ "未完成" }}
{% endif %}
</td>
<td>{{ task.group.name }}</td>
<td>
<div class="ops">
<span>
<a href="/tasks/update/{{task.id}}">更新</a>
</span>
<span>
<a href="/tasks/delete/{{task.id}}">删除</a>
</span>
</div>
</td>
</tr>
{% endfor %}
</tbody>
</table>
</div>
{% endif %}
<div class="btn-add-task">
<button>+</button>
</div>
<div class="task-form">
<form action={% url "create_task" %} method="post" class="form">
{% csrf_token %}
<div>
<label for="taskName">任务名称:</label>
<input type="text" name="taskName" id="taskName" placeholder="请输入待办事项名称" />
</div>
<div>
<label for="taskPriority">优先级:</label>
<select name="taskPriority" id="taskPriority">
<option value="1" selected>一般</option>
<option value="2">优先</option>
<option value="3">紧急</option>
</select>
</div>
<div>
<label for="taskGroup"> 分组: </label>
<select name="taskGroup" id="taskGroup">
{% for group in groups %}
<option value="{{ group.id }}">{{ group.name }}</option>
{% endfor %}
</select>
</div>
<div>
<label for="task-textarea">任务备注:</label>
<textarea name="taskDescription" id="task-textarea" rows="5" placeholder="备注内容(可选)"></textarea>
</div>
<div class="btn-submit">
<input type="submit" value="添加" />
</div>
</form>
</div>
{% endblock %}

View File

@@ -0,0 +1,16 @@
"""
ASGI config for todolist project.
It exposes the ASGI callable as a module-level variable named ``application``.
For more information on this file, see
https://docs.djangoproject.com/en/4.0/howto/deployment/asgi/
"""
import os
from django.core.asgi import get_asgi_application
os.environ.setdefault('DJANGO_SETTINGS_MODULE', 'todolist.settings')
application = get_asgi_application()

View File

@@ -0,0 +1,126 @@
"""
Django settings for todolist project.
Generated by 'django-admin startproject' using Django 4.0.4.
For more information on this file, see
https://docs.djangoproject.com/en/4.0/topics/settings/
For the full list of settings and their values, see
https://docs.djangoproject.com/en/4.0/ref/settings/
"""
from pathlib import Path
# Build paths inside the project like this: BASE_DIR / 'subdir'.
BASE_DIR = Path(__file__).resolve().parent.parent
# Quick-start development settings - unsuitable for production
# See https://docs.djangoproject.com/en/4.0/howto/deployment/checklist/
# SECURITY WARNING: keep the secret key used in production secret!
SECRET_KEY = "django-insecure-h$0(p$ca027=i@rg(!3ut8zm@((lfkvjdc%=g$f6gecl#5c)ly"
# SECURITY WARNING: don't run with debug turned on in production!
DEBUG = True
ALLOWED_HOSTS = []
# Application definition
INSTALLED_APPS = [
"django.contrib.admin",
"django.contrib.auth",
"django.contrib.contenttypes",
"django.contrib.sessions",
"django.contrib.messages",
"django.contrib.staticfiles",
"popup",
]
MIDDLEWARE = [
"django.middleware.security.SecurityMiddleware",
"django.contrib.sessions.middleware.SessionMiddleware",
"django.middleware.common.CommonMiddleware",
"django.middleware.csrf.CsrfViewMiddleware",
"django.contrib.auth.middleware.AuthenticationMiddleware",
"django.contrib.messages.middleware.MessageMiddleware",
"django.middleware.clickjacking.XFrameOptionsMiddleware",
]
ROOT_URLCONF = "todolist.urls"
TEMPLATES = [
{
"BACKEND": "django.template.backends.django.DjangoTemplates",
"DIRS": [BASE_DIR / "templates"],
"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",
],
},
},
]
WSGI_APPLICATION = "todolist.wsgi.application"
# Database
# https://docs.djangoproject.com/en/4.0/ref/settings/#databases
DATABASES = {
"default": {
"ENGINE": "django.db.backends.sqlite3",
"NAME": BASE_DIR / "db.sqlite3",
}
}
# Password validation
# https://docs.djangoproject.com/en/4.0/ref/settings/#auth-password-validators
AUTH_PASSWORD_VALIDATORS = [
{
"NAME": "django.contrib.auth.password_validation.UserAttributeSimilarityValidator",
},
{
"NAME": "django.contrib.auth.password_validation.MinimumLengthValidator",
},
{
"NAME": "django.contrib.auth.password_validation.CommonPasswordValidator",
},
{
"NAME": "django.contrib.auth.password_validation.NumericPasswordValidator",
},
]
# Internationalization
# https://docs.djangoproject.com/en/4.0/topics/i18n/
LANGUAGE_CODE = "zh-Hans"
TIME_ZONE = "Asia/Shanghai"
USE_I18N = True
USE_TZ = True
# Static files (CSS, JavaScript, Images)
# https://docs.djangoproject.com/en/4.0/howto/static-files/
STATIC_URL = "static/"
STATICFILES_DIRS = [
BASE_DIR / "static",
]
# Default primary key field type
# https://docs.djangoproject.com/en/4.0/ref/settings/#default-auto-field
DEFAULT_AUTO_FIELD = "django.db.models.BigAutoField"

View File

@@ -0,0 +1,22 @@
"""todolist URL Configuration
The `urlpatterns` list routes URLs to views. For more information please see:
https://docs.djangoproject.com/en/4.0/topics/http/urls/
Examples:
Function views
1. Add an import: from my_app import views
2. Add a URL to urlpatterns: path('', views.home, name='home')
Class-based views
1. Add an import: from other_app.views import Home
2. Add a URL to urlpatterns: path('', Home.as_view(), name='home')
Including another URLconf
1. Import the include() function: from django.urls import include, path
2. Add a URL to urlpatterns: path('blog/', include('blog.urls'))
"""
from django.contrib import admin
from django.urls import include, path
urlpatterns = [
path("admin/", admin.site.urls),
path("", include("popup.urls")),
]

View File

@@ -0,0 +1,16 @@
"""
WSGI config for todolist project.
It exposes the WSGI callable as a module-level variable named ``application``.
For more information on this file, see
https://docs.djangoproject.com/en/4.0/howto/deployment/wsgi/
"""
import os
from django.core.wsgi import get_wsgi_application
os.environ.setdefault('DJANGO_SETTINGS_MODULE', 'todolist.settings')
application = get_wsgi_application()