ウェブサイトについて知る
- デフォルトで書き込まれる静的エフェクト
- 動的:Webフレームワークの機能が必要です。
現在、データ ストレージに使用できるものは次のとおりです。 - txtファイル
- エクセルファイル
- プロフェッショナル ソフトウェア: データベース管理システム
MySQL/Oracle/SQLServer/DB2/Access...
7 MysQL
7.1 MySQL のインストール
MySQL は本質的にはソフトウェアです。
- 8.x
- 5.x
わずかに
7.2 MySQLの起動
7.3 MySQLコマンド
7.3.1 データベース管理(フォルダー)
7.3.2 データシート(ファイル)の管理
やや
チャー、速い。varchar、スペースを節約します。
7.4 例: 従業員管理
7.4.2 MySQLを操作するためのPython
接続を作成し、カーソルを作成し、カーソルを実行して SQL ステートメントを実行してから切断します。
import pymysql
# 1.连接MySQL
conn = pymysql.connect(host="127.0.0.1", port=3306, user="root", passwd="xxxx", charset="utf8", db='unicom')
cursor = conn.cursor(cursor=pymysql.cursors.DictCursor)
# 2.发送指令
cursor.execute("insert into admin(username, password, mobile) values('张三', 'qwe123', '155555')")
conn.commit()
# 3.关闭连接
cursor.close()
conn.close()
SQL を動的に実行します。SQL スプライシングを行うために文字列フォーマットを使用しないでください。セキュリティ リスクが発生します。 SQL インジェクション
sql = "insert into admin(username, password, mobile) values(%s, %s, %s)"
cursor.execute(sql, ["李四", "qwe123", "1999999"])
sql = "insert into admin(username, password, mobile) values(%(n1)s, %(n2)s, %(n3)s)"
cursor.execute(sql, {
"n1": "王五", "n2": "qwe123", "n3": "1999999"})
conn.commit()
- data
fetchall をクエリしてすべてのデータを取得します。
fetchone で条件を満たす最初のデータを取得します。
import pymysql
# 1.连接MySQL
conn = pymysql.connect(host="127.0.0.1", port=3306, user="root", passwd="xxxxx, charset="utf8", db='unicom')
cursor = conn.cursor(cursor=pymysql.cursors.DictCursor)
# 2.发送指令
sql = "select * from admin where id > 2 "
cursor.execute(sql)
datalist = cursor.fetchall()
for row_dict in datalist:
print(row_dict)
# 3.关闭连接
cursor.close()
conn.close()
8. ケース: Flask+MySQL
from flask import Flask, render_template, request
import pymysql
app = Flask(__name__)
@app.route("/add/user", methods=["GET", "POST"])
def add_user():
if request.method == "GET":
return render_template("add_user.html")
username = request.form.get("user")
password = request.form.get("pwd")
mobile = request.form.get("mobile")
# 1.连接MySQL
conn = pymysql.connect(host="127.0.0.1", port=3306, user="root", password="xxxx", charset="utf8", db="unicom")
cursor = conn.cursor(cursor=pymysql.cursors.DictCursor)
# 2.执行SQL
sql = "insert into admin(username, password, mobile) values(%s, %s, %s)"
cursor.execute(sql, [username, password, mobile])
conn.commit()
# 3.关闭连接
cursor.close()
conn.close()
return "添加成功"
@app.route("/show/user")
def show_user():
# 1.连接MySQL
conn = pymysql.connect(host="127.0.0.1", port=3306, user="root", password="xxxx", charset="utf8", db="unicom")
cursor = conn.cursor(cursor=pymysql.cursors.DictCursor)
# 2.执行SQL
sql = "select * from admin"
cursor.execute(sql)
data_list = cursor.fetchall()
# 3.关闭连接
cursor.close()
conn.close()
print(data_list)
return render_template("show_user.html", data_list=data_list)
if __name__ == '__main__':
app.run()
9 イニシャルジャンゴ
9.1 ジャンゴをインストールする
インストール後、django-amin.exe と django モジュールが作成されます。
9.2 プロジェクトの作成
9.2.1 ターミナル内
9.2.2 Pycharm
わずかに
9.3 アプリ
9.4 クイックスタート
9.4.1 別のページを書く
9.4.2 テンプレート
9.4.3 静的ファイル
9.5 テンプレートの構文
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Title</title>
</head>
<body>
<h1>模板语法的学习</h1>
<div>{
{ n1 }}</div>
<div>{
{ n2 }}</div>
<div>{
{ n2.0 }}</div>
<div>{
{ n2.1 }}</div>
<div>{
{ n2.2 }}</div>
<div>
{% for item in n2 %}
<span>{
{ item }}</span>
{% endfor %}
</div>
<hr/>
<div>{
{ n3 }}</div>
<div>{
{ n3.name }}</div>
<div>{
{ n3.salary }}</div>
<div>{
{ n3.role }}</div>
<div>
{% for k, v in n3.items %}
<li>{
{ k }} = {
{ v }}</li>
{% endfor %}
</div>
<hr>
{
{ n4.1 }}
{
{ n4.1.name }}
{% for item in n4 %}
<div>{
{ item.name }}{
{ item.salary }}</div>
{% endfor %}
<hr>
{% if n1 == "韩超" %}
<h1>哒哒哒</h1>
{% else %}
<h1>嘟嘟嘟</h1>
{% endif %}
{% if n1 == "韩超" %}
<h1>哒哒哒</h1>
{% elif n1 == "xxx" %}
<h1>bibibi</h1>
{% else %}
<h1>嘟嘟嘟</h1>
{% endif %}
</body>
</html>
事例:擬似ユニコムニュースセンター
クローラーが動画报错:Expecting value: line 1 column 1 (char 0)
を参照できる場合
def news(request):
import requests
# 请求头添加浏览器信息
headers = {
'User-Agent': "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/114.0.0.0 Safari/537.36"
}
url = "http://www.chinaunicom.com.cn/api/article/NewsByIndex/2/2023/06/news"
res = requests.get(url, headers=headers)
data_list = res.json()
print(data_list)
return render(request, "news.html", {
"news_list": data_list})
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Title</title>
</head>
<body>
<h1>联通新闻中心</h1>
<ul>
{% for item in news_list %}
<li>{
{ item.news_title }} 时间: {
{ item.post_time }} </li>
{% endfor %}
</ul>
</body>
</html>
9.6 リクエストとレスポンス
from django.shortcuts import render, HttpResponse, redirect
def index(request):
return HttpResponse("欢迎使用")
def user_list(request):
return render(request, "user_list.html")
def user_add(request):
return render(request, "user_add.html")
def tpl(request):
name = "韩超发方法"
roles = ["管理员", "CEO", "保安"]
user_info = {
"name": "张三", "salary": 10000, "role": "CTO"}
data_list = [
{
"name": "张三", "salary": 10000, "role": "CTO"},
{
"name": "李四", "salary": 10000, "role": "CTO"},
{
"name": "王五", "salary": 10000, "role": "CTO"},
]
return render(request, "tpl.html", {
"n1": name, "n2": roles, "n3": user_info, "n4": data_list})
def news(request):
import requests
# 请求头添加浏览器信息
headers = {
'User-Agent': "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/114.0.0.0 Safari/537.36"
}
url = "http://www.chinaunicom.com.cn/api/article/NewsByIndex/2/2023/06/news"
res = requests.get(url, headers=headers)
data_list = res.json()
print(data_list)
return render(request, "news.html", {
"news_list": data_list})
def something(request):
# request是一个对象,封装了用户通过发送过来的所有请求相关数据
# 1.获取请求方式 GET/POST
print(request.method)
# 2.在URL上传递值 /something/?n1=123&n2=999
print(request.GET)
# 3.在请求体中提交数据
print(request.POST)
# 4.【响应】HttpResponse("返回内容"),将字符串内容返回给请求者
# return HttpResponse("返回内容")
# 5.【响应】读取HTML内容+渲染(替换)-> 字符串,返回给用户浏览器。
# return render(request, 'something.html', {"title": "来了"})
# 6.【响应】让浏览器重定向到其他页面
return redirect("https://www.baidu.com")
リダイレクトについて: Web フレームワークはリダイレクトされた Web サイトをブラウザーに返し、ブラウザーは独自にその Web サイトを要求します。
ケース: ユーザーログイン
def login(request):
if request.method == "GET":
return render(request, "login.html")
# 如果是POST请求,获取用户提交的数据
print(request.POST)
username = request.POST.get("user")
password = request.POST.get("pwd")
if username == "root" and password == "123":
# return HttpResponse("登陆成功")
return redirect("https://www.baidu.com/")
# return HttpResponse("登陆失败")
return render(request, 'login.html', {
"error_msg": "用户名或密码错误"})
9.7 データベースの操作
Django はデータベースの開発と運用が容易であり、ORM フレームワークが内部で提供されています。
9.7.1 サードパーティモジュールのインストール
pip install mysqlclient
9.7.2 ORM
DATABASES = {
'default': {
'ENGINE': 'django.db.backends.mysql',
'NAME':'dbname',
'USER': 'root',
'PASSWORD': 'xxx',
'HOST': '',
'PORT': '',
}
}
9.7.3 ジャンゴアクションテーブル
# 生成数据库同步脚本
python manage.py makemigrations
# 迁移处理表
python manage.py migrate
9.7.4 テーブル内のデータ
from app01.models import Department, UserInfo
def orm(request):
# 测试ORM操作表中的数据
### 1.新建
# Department.objects.create(title="销售部")
# Department.objects.create(title="IT部")
# Department.objects.create(title="运营部")
# UserInfo.objects.create(name="张三", password="123", age=19)
# UserInfo.objects.create(name="李四", password="666", age=29)
### 2.删除
# UserInfo.objects.filter(id=3).delete()
# Department.objects.all().delete()
### 3.获取数据
# data_list = [对象-行, 对象-行, 对象-行] QuerySet类型
# data_list = UserInfo.objects.all()
# for obj in data_list:
# print(obj.id, obj.name, obj.password, obj.age)
# data_list = UserInfo.objects.filter(id=4)
# print(data_list)
# row_obj = UserInfo.objects.filter(id=4).first()
# print(row_obj.id, row_obj.name, row_obj.password, row_obj.age)
### 4.更新数据
# UserInfo.objects.all().update(password=999)
UserInfo.objects.filter(id=4).update(age=999)
return HttpResponse("成功")
事例: ユーザー管理
他の人の Web サイトにリダイレクトするには、完全な URL を記述する必要があります。自分のサイトへのリダイレクトは必要ありません。
10 ジャンゴ開発
テーマ: 従業員管理システム
10.1 新しいプロジェクト
設定の「DIRS」の内容を削除します。テンプレートフォルダーを削除します。
10.2 アプリの作成
アプリを作成する
アプリを登録する
10.3 テーブル構造の設計(django)
10.4 MySQL でのテーブルの作成
10.5 静的ファイル管理
10.6 部門管理
10.6.1 部門リスト
期待される効果:
コードの詳細はビデオで説明されています
10.7 テンプレートの継承
10.8 ユーザー管理
テンプレート構文では括弧は使用できません。括弧を追加する必要がある場所を読み取ると、括弧が自動的に追加されます。
テンプレートの構文は Python の構文とは少し異なります。
10.8.1 Form について初めて知る
views.py
10.8.3 モデルフォーム
モデル.py
from django.db import models
# Create your models here.
class Department(models.Model):
"""部门表"""
title = models.CharField(verbose_name="", max_length=32)
def __str__(self):
return self.title
class UserInfo(models.Model):
"""员工表"""
name = models.CharField(verbose_name="姓名", max_length=16)
password = models.CharField(verbose_name="密码", max_length=64)
age = models.IntegerField(verbose_name="年龄")
account = models.DecimalField(verbose_name="账户余额", max_digits=10, decimal_places=2, default=0)
create_time = models.DateTimeField(verbose_name="入职时间")
depart = models.ForeignKey(to="Department", to_field="id", on_delete=models.CASCADE)
gender_choices = (
(1, "男"),
(2, "女"),
)
gender = models.SmallIntegerField(verbose_name="性别", choices=gender_choices)
views.py
from django import forms
class UserModelForm(forms.ModelForm):
class Meta:
model = models.UserInfo
# fields = ["name", "password", "age", "account", "create_time"]
fields = "__all__"
def user_model_form_add(request):
form = UserModelForm()
return render(request, "user_model_form_add.html", {
"form": form})
モデルフォーム内のDepartmentテーブルをフェッチする場合、まずmodels.Department.objects.all()を通じてクエリセットをフェッチし、次にその中のオブジェクトを走査して出力します。
ただし、この出力の結果は、Department オブジェクトです。
したがって、部門を書き直す必要があります
- スタイルを追加する方法
class UserModelForm(forms.ModelForm):
class Meta:
model = models.UserInfo
# fields = ["name", "password", "age", "account", "create_time"]
fields = "__all__"
def __init__(self, *args, **kwargs):
super().__init__(*args, **kwargs)
for name, field in self.fields.items():
field.widget.attrs = {
"class": "form-control", "placeholder": field.label}
def user_model_form_add(request):
if request.method == "GET":
form = UserModelForm()
return render(request, "user_model_form_add.html", {
"form": form})
form = UserModelForm(data=request.POST)
if form.is_valid():
# 数据合法,保存到数据库
# {'name': '123', 'password': '123', 'age': 123, 'account': Decimal('0'), 'create_time': datetime.datetime(2011, 11, 11, 0, 0, tzinfo=zoneinfo.ZoneInfo(key='UTC')), 'depart': <Department: 营销部>, 'gender': 1}
form.save()
return redirect('/user/list/')
print(form.cleaned_data)
# 校验失败(在页面上显示错误信息)
return render(request, "user_model_form_add.html", {
"form": form})
設定のLANGUAGE_CODEを「zh-hans」に変更すると、プロンプト情報が中国語になります。
10.8.4 ユーザーの編集
10.8.5 削除
わずかに
10.9 エリート番号管理
10.9.1 テーブル構造
class PrettyNumber(models.Model):
"""靓号表"""
mobile = models.CharField(verbose_name="手机号", max_length=11)
price = models.IntegerField(verbose_name="价格", default=0)
level_choices = (
(1, "1级"),
(2, "2级"),
(3, "3级"),
(4, "4级"),
)
level = models.SmallIntegerField(verbose_name="级别", choices=level_choices, default=1)
status_choices = (
(1, "已占用"),
(2, "未使用")
)
status = models.SmallIntegerField(verbose_name="状态", choices=status_choices, default=2)
データベースシミュレーションでデータを自分で作成する
10.9.2 プレミアム番号のリスト
10.9.3 新しいアカウントの作成
10.9.3.1 認証
方法 1: 正規表現
from django.core.validators import RegexValidator
class PrettyModelForm(forms.ModelForm):
mobile = forms.CharField(
label="手机号",
validators=[RegexValidator(r'^1\d{10}', '手机号格式错误')]
)
class Meta:
model = models.PrettyNumber
fields = "__all__"
def __init__(self, *args, **kwargs):
super().__init__(*args, **kwargs)
for name, field in self.fields.items():
field.widget.attrs = {
"class": "form-control", "placeholder": field.label}
方法 2: フック方法
from django.core.exceptions import ValidationError
class PrettyModelForm(forms.ModelForm):
class Meta:
model = models.PrettyNumber
fields = "__all__"
def __init__(self, *args, **kwargs):
super().__init__(*args, **kwargs)
for name, field in self.fields.items():
field.widget.attrs = {
"class": "form-control", "placeholder": field.label}
def clean_mobile(self):
txt_mobile = self.cleaned_data['mobile']
if len(txt_mobile) != 11:
raise ValidationError("格式错误")
return txt_mobile
10.9.4 プリティナンバーの編集
class PrettyEditModelForm(forms.ModelForm):
# mobile = forms.CharField(disabled=True, label="手机号")
class Meta:
model = models.PrettyNumber
fields = ["mobile", "price", "level", "status"]
def __init__(self, *args, **kwargs):
super().__init__(*args, **kwargs)
for name, field in self.fields.items():
field.widget.attrs = {
"class": "form-control", "placeholder": field.label}
def pretty_edit(request, nid):
"""编辑靓号"""
row_object = models.PrettyNumber.objects.filter(id=nid).first()
if request.method == "GET":
form = PrettyEditModelForm(instance=row_object)
return render(request, "pretty_edit.html", {
"form": form})
form = PrettyEditModelForm(data=request.POST, instance=row_object)
if form.is_valid():
form.save()
return redirect("/pretty/list/")
return render(request, "pretty_edit.html", {
"form": form})
プリティナンバー削除
def pretty_delete(request, nid):
models.PrettyNumber.objects.filter(id=nid).delete()
return redirect("/pretty/list/")
10.9.5 携帯電話番号の検索
コメントは真ん中の二重アンダースコアです
def pretty_list(request):
data_dict = {
}
search_data = request.GET.get("q", "")
if search_data:
data_dict["mobile__contains"] = search_data
# 排序,id表示asc,-id表示desc
queryset = models.PrettyNumber.objects.filter(**data_dict).order_by("-level")
return render(request, "pretty_list.html", {
"queryset": queryset, "search_data": search_data})
<div style="margin-bottom: 10px" class="clearfix">
<a class="btn btn-success" href="/pretty/add/">
<span class="glyphicon glyphicon-plus-sign" aria-hidden="true"></span>
新建靓号
</a>
<div style="float: right; width: 300px">
<form method="get">
<div class="input-group">
<input type="text" name="q" class="form-control" placeholder="Search for..." value="{
{ search_data }}">
<span class="input-group-btn">
<button class="btn btn-default" type="submit">
<span class="glyphicon glyphicon-search" aria-hidden="true"></span>
</button>
</span>
</div>
</form>
</div>
</div>
10.9.6 ページング
ユーザーがアクセスしたいページ番号に基づいて開始位置と終了位置を計算します。
page = int(request.GET.get('page', 1))
page_size = 10
start = (page-1) * page_size
end = page * page_size
# 排序,id表示asc,-id表示desc
queryset = models.PrettyNumber.objects.filter(**data_dict).order_by("-level")[start:end]
from django.utils.safestring import mark_safe
Python バックエンドが HTML 文字列を書き込み、それをフロントエンド HTML に渡したい場合は、 make_safe を使用して page_str_list 文字列をラップする必要があります。
def pretty_list(request):
data_dict = {
}
search_data = request.GET.get("q", "")
if search_data:
data_dict["mobile__contains"] = search_data
page = int(request.GET.get('page', 1))
page_size = 10
start = (page-1) * page_size
end = page * page_size
# 排序,id表示asc,-id表示desc
total_count = models.PrettyNumber.objects.filter(**data_dict).order_by("-level").count()
queryset = models.PrettyNumber.objects.filter(**data_dict).order_by("-level")[start:end]
total_page_count, div = divmod(total_count, page_size)
if div:
total_page_count += 1
# 计算出,显示当前页的前5页,后5页
plus = 5
if total_page_count <= 2 * plus:
# 数据库中数据比较少
start_page = 1
end_page = total_page_count
else:
# 数据库数据比较多
if page <= plus:
start_page = 1
end_page = 2 * plus + 1
elif (page + plus) > total_page_count:
start_page = total_page_count - 2 * plus
end_page = total_page_count
else:
start_page = page - plus
end_page = page + plus
# 页码
page_str_list = []
page_str_list.append('<li><a href="?page={}">首页</a></li>'.format(1))
# 上一页
if page > 1:
prev = '<li><a href="?page={}">上一页</a></li>'.format(page - 1)
else:
prev = '<li><a href="?page={}">上一页</a></li>'.format(1)
page_str_list.append(prev)
for i in range(start_page, end_page + 1):
if i == page:
ele = '<li class="active"><a href="?page={}">{}</a></li>'.format(i, i)
else:
ele = '<li><a href="?page={}">{}</a></li>'.format(i, i)
page_str_list.append(ele)
# 下一页
if page < total_page_count:
next = '<li><a href="?page={}">下一页</a></li>'.format(page + 1)
else:
next = '<li><a href="?page={}">下一页</a></li>'.format(total_page_count)
page_str_list.append(next)
# 尾页
page_str_list.append('<li><a href="?page={}">尾页</a></li>'.format(total_page_count))
page_string = mark_safe("".join(page_str_list))
return render(request, "pretty_list.html", {
"queryset": queryset, "search_data": search_data, 'page_string': page_string})
- パッケージのページネーション クラス
"""
自定义的分页组件,以后如果想要使用这个分页组件,你需要做如下几件事:
在视图函数中:
def pretty_list(request):
# 1.根据情况筛选数据
queryset = models.PrettyNumber.objects.all()
# 2.实例化分页对象
page_object = Pagination(request, queryset)
context = {
"queryset": page_object.page_queryset, # 分完页的数据
'page_string': page_object.html() # 生成的页码
}
return render(request, "pretty_list.html", context)
在HTML页面中:
{% for obj in queryset %}
{
{obj.xx}}
{% endfor %}
<ul class="pagination">
{
{ page_string }}
</ul>
"""
from django.utils.safestring import mark_safe
class Pagination(object):
def __init__(self, request, queryset, page_size=10, page_param="page", plus=5):
"""
:param request: 请求的对象
:param queryset: 符合条件的数据(根据这个数据进行分页处理)
:param page_size: 每页显示多少条数据
:param page_param: 在URL中传递的获取分页的参数
:param plus: 显示当前页的 前或后几页(页码)
"""
page = request.GET.get(page_param, "1")
if page.isdecimal():
page = int(page)
else:
page = 1
self.page = page
self.page_size = page_size
self.start = (page - 1) * page_size
self.end = page * page_size
self.page_queryset = queryset[self.start:self.end]
# 排序,id表示asc,-id表示desc
total_count = queryset.count()
total_page_count, div = divmod(total_count, page_size)
if div:
total_page_count += 1
self.total_page_count = total_page_count
self.plus = plus
def html(self):
if self.total_page_count <= 2 * self.plus:
# 数据库中数据比较少
start_page = 1
end_page = self.total_page_count
else:
# 数据库数据比较多
if self.page <= self.plus:
start_page = 1
end_page = 2 * self.plus + 1
elif (self.page + self.plus) > self.total_page_count:
start_page = self.total_page_count - 2 * self.plus
end_page = self.total_page_count
else:
start_page = self.page - self.plus
end_page = self.page + self.plus
# 页码
page_str_list = []
page_str_list.append('<li><a href="?page={}">首页</a></li>'.format(1))
# 上一页
if self.page > 1:
prev = '<li><a href="?page={}">上一页</a></li>'.format(self.page - 1)
else:
prev = '<li><a href="?page={}">上一页</a></li>'.format(1)
page_str_list.append(prev)
for i in range(start_page, end_page + 1):
if i == self.page:
ele = '<li class="active"><a href="?page={}">{}</a></li>'.format(i, i)
else:
ele = '<li><a href="?page={}">{}</a></li>'.format(i, i)
page_str_list.append(ele)
# 下一页
if self.page < self.total_page_count:
next = '<li><a href="?page={}">下一页</a></li>'.format(self.page + 1)
else:
next = '<li><a href="?page={}">下一页</a></li>'.format(self.total_page_count)
page_str_list.append(next)
# 尾页
page_str_list.append('<li><a href="?page={}">尾页</a></li>'.format(self.total_page_count))
search_sting = """
<li>
<form style="float: left; margin-left: -1px" method="get">
<div class="input-group" style="width: 200px">
<input type="text" name="page" class="form-control" placeholder="页码">
<span class="input-group-btn">
<button class="btn btn-default" type="submit">跳转</button>
</span>
</div>
</form>
</li>
"""
page_str_list.append(search_sting)
page_string = mark_safe("".join(page_str_list))
return page_string
request.GET のディープ コピー urlencode() を使用して、以前の条件を取得します。
import copy
query_dict = copy.deepcopy(request.GET)
query_dict._mutable = True
self.query_dict = query_dict
self.page_param = page_param
10.9.7 時間プラグイン
わずかに
10.10 モデルフォームとブートストラップ
事業者が多い場合はビュー機能を分割可能
10.11 管理者の操作
class Admin(models.Model):
"""管理员"""
username = models.CharField(verbose_name="用户名", max_length=32)
password = models.CharField(verbose_name="密码", max_length=64)
インポート時:
モジュールを作成するときは、通常、組み込みモジュールが最初に作成され、次にサードパーティ モジュール、最後にカスタム モジュールが作成されます。短いモジュールが
上にあります。
- パスワードの整合性チェック
django-clean: 参考: https://pythonjishu.com/django-clean/
class AdminModelForm(BootStrapModelForm):
confirm_password = forms.CharField(
label="确认密码",
widget=forms.PasswordInput
)
class Meta:
model = models.Admin
fields = ["username", "password", "confirm_password"]
widgets = {
"password": forms.PasswordInput
}
def clean_confirm_password(self):
print(self.cleaned_data)
pwd = self.cleaned_data.get("password")
confirm = self.cleaned_data.get("confirm_password")
if confirm != pwd:
raise ValidationError("密码不一致,请重新输入")
# 返回什么,此字段以后保存到数据库就是什么
return confirm
def admin_add(request):
""" 添加管理员 """
title = "新建管理员"
if request.method == "GET":
form = AdminModelForm()
return render(request, 'change.html', {
"form": form, "title": title})
form = AdminModelForm(data=request.POST)
if form.is_valid():
form.save()
return redirect('/admin/list')
return render(request, "change.html", {
'form': form, "title": title})
render_value=True をプラグインに追加すると、エラーが報告された後も元の値が保持されます。
- パスワードのMD5暗号化
import hashlib
def md5(data_string):
salt = 'xxxxxxxx'
obj = hashlib.md5(salt.encode('utf-8'))
obj.update(data_string.encdoe('utf-8'))
return obj.hexdigest()
設定に SECRET_KEY の値があり、ソルトとして使用されます
class AdminModelForm(BootStrapModelForm):
confirm_password = forms.CharField(
label="确认密码",
widget=forms.PasswordInput(render_value=True)
)
class Meta:
model = models.Admin
fields = ["username", "password", "confirm_password"]
widgets = {
"password": forms.PasswordInput(render_value=True)
}
def clean_password(self):
pwd = self.cleaned_data.get("password")
return md5(pwd)
def clean_confirm_password(self):
pwd = self.cleaned_data.get("password")
confirm = md5(self.cleaned_data.get("confirm_password"))
if confirm != pwd:
raise ValidationError("密码不一致,请重新输入")
# 返回什么,此字段以后保存到数据库就是什么
return confirm
10.12 ユーザーログイン
10.12.1 ユーザー認証 - Cookie とセッション
- http プロトコルの特徴
ステートレス & ショート リンク
- クッキーとセッションとは何ですか?
Django はデフォルトでセッションをデータベースに置きます
from django.shortcuts import render, HttpResponse, redirect
from django import forms
from app01 import models
from app01.utils.encrypt import md5
class LoginForm(forms.Form):
username = forms.CharField(
label="用户名",
widget=forms.TextInput,
required=True
)
password = forms.CharField(
label="密码",
widget=forms.PasswordInput(render_value=True)
)
def __init__(self, *args, **kwargs):
super().__init__(*args, **kwargs)
for name, field in self.fields.items():
if field.widget.attrs:
field.widget.attrs["class"] = "form-control"
field.widget.attrs['placeholder'] = field.label
else:
field.widget.attrs = {
"class": "form-control",
"placeholder": field.label
}
def clean_password(self):
pwd = self.cleaned_data.get("password")
return md5(pwd)
def login(request):
if request.method == 'GET':
form = LoginForm()
return render(request, 'login.html', {
'form': form})
form = LoginForm(data=request.POST)
if form.is_valid():
print(form.cleaned_data)
# 校验用户名和密码
admin_object = models.Admin.objects.filter(**form.cleaned_data).first()
if not admin_object:
form.add_error("password", "用户名或密码错误")
return render(request, 'login.html', {
'form': form})
# 用户名和密码正确
# 网站要生成一个字符串;写到用户浏览器的cookie中,再写入到session中
request.session["info"] = {
'id': admin_object.id, 'name': admin_object.username}
return redirect('/admin/list/')
return render(request, 'login.html', {
'form': form})
10.12.2 ログイン
ログインしていないユーザーは、URL 経由で管理者リスト ページにアクセスできません。
10.12.3 ミドルウェアの経験
ビュー機能にアクセスするにはミドルウェアを経由する必要があり、戻るにはミドルウェアを経由する必要があります。
ミドルウェアを通過できない場合は、直接戻ってください。
- ミドルウェアの定義
from django.utils.deprecation import MiddlewareMixin
from django.shortcuts import HttpResponse
class M1(MiddlewareMixin):
"""中间件1"""
def process_request(self, request):
print("M1.进来了")
return HttpResponse("无权访问")
def process_response(self, request, response):
print("M1.走了")
return response
class M2(MiddlewareMixin):
"""中间件1"""
def process_request(self, request):
print("M2.进来了")
def process_response(self, request, response):
print("M2.走了")
return response
- ミドルウェアの適用
settings.pyにミドルウェアを登録する
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",
'app01.middleware.auth.M1',
'app01.middleware.auth.M2',
]
- ミドルウェアの process_request メソッド
process_request メソッドに戻り値がない場合 (return None)、そのまま続行できます。
戻り値がある場合、戻り値は一般にビュー関数の戻り値と同様です
10.12.4 ミドルウェアによるログイン検証の実装
- ミドルウェアを書く
from django.utils.deprecation import MiddlewareMixin
from django.shortcuts import HttpResponse, redirect
class AuthMiddleware(MiddlewareMixin):
"""中间件1"""
def process_request(self, request):
# 0.排除那些不需要登录就能访问的页面
# request.path_info获取当前用户请求的URL /login/
if request.path_info == "/login/":
return
# 1.读取当前访问用户的session信息,如果能读到,说明登陆过:
info_dict = request.session.get("info")
print(info_dict)
if info_dict:
return
# 2.如果没有登陆过,重新回到登录页面
return redirect('/login/')
- アプリケーションミドルウェア
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",
'app01.middleware.auth.AuthMiddleware',
]
10.12.5 ログアウト
def logout(request):
""" 注销 """
request.session.clear()
return redirect('/login/')
10.13 画像検証コード
10.13.1 Python を使用して画像を生成する
pip install pillow
参考文献:
https://www.cnblogs.com/wupeiqi/articles/5812291.html
10.14 Ajaxリクエスト
10.14.1 GETリクエスト
# html中
$.ajax({
url: "/task/ajax/",
type: "get",
data:{
n1: 123,
n2: 456
},
success: function(res){
console.log(res);
}
})
def task_ajax(request):
print(request.GET)
return HttpResponse("成功了")
10.14.2 POSTリクエスト
投稿リクエストを送信するときに使用できます免除csrft oken
# html中
$.ajax({
url: "/task/ajax/",
type: "post",
data:{
n1: 123,
n2: 456
},
success: function(res){
console.log(res);
}
})
from django.shortcuts import render, redirect, HttpResponse
from django.views.decorators.csrf import csrf_exempt
@csrf_exempt
def task_ajax(request):
print(request.POST)
return HttpResponse("成功了")
10.14.3 バインディングイベント
{% extends 'layout.html' %}
{% block content %}
<div class="container">
<h1>任务管理</h1>
<h3>示例1</h3>
<input id="btn1" type="button" class="btn btn-primary" value="点击" onclick="clickMe();"/>
</div>
{% endblock %}
{% block js %}
<script type="text/javascript">
$(function () {
// 页面框架加载完成之后代码自动执行
bindBtn1Event();
})
function bindBtn1Event() {
$("#btn1").click(function () {
$.ajax({
url: "/task/ajax/",
type: "post",
data: {
n1: 123,
n2: 456
},
success: function (res) {
console.log(res);
}
})
})
}
</script>
{% endblock %}
10.14.4 ajaxリクエストの戻り値
- フロントエンド
{% extends 'layout.html' %}
{% block content %}
<div class="container">
<h1>任务管理</h1>
<h3>示例1</h3>
<input id="btn1" type="button" class="btn btn-primary" value="点击"/>
</div>
{% endblock %}
{% block js %}
<script type="text/javascript">
$(function () {
// 页面框架加载完成之后代码自动执行
bindBtn1Event();
})
function bindBtn1Event() {
$("#btn1").click(function () {
$.ajax({
url: "/task/ajax/",
type: "post",
data: {
n1: 123,
n2: 456
},
dataType: "JSON",
success: function (res) {
console.log(res);
console.log(res.status);
console.log(res.data);
}
})
})
}
</script>
{% endblock %}
- バックエンドの戻り値は
通常、シリアル化する必要がある json 辞書です。
import json
from django.shortcuts import render, redirect, HttpResponse
from django.views.decorators.csrf import csrf_exempt
@csrf_exempt
def task_ajax(request):
print(request.POST)
data_dict = {
"status": True, "data": [11, 22, 33, 44]}
json_string = json.dumps(data_dict)
return HttpResponse(json_string)
Django はシリアル化メソッド JsonResponse も提供します。
{
% extends 'layout.html' %}
{
% block content %}
<div class="container">
<h1>Ajax学习</h1>
<h3>示例1</h3>
<input id="btn1" type="button" class="btn btn-primary" value="点击1"/>
<h3>示例2</h3>
<input type="text" id="txtUser" placeholder="用户名"/>
<input type="text" id="txtAge" placeholder="年龄"/>
<input id="btn2" type="button" class="btn btn-primary" value="点击2"/>
<h3>示例3</h3>
<form id="form3">
<input type="text" name="user" placeholder="用户名"/>
<input type="text" name="age" placeholder="年龄"/>
<input type="text" name="email" placeholder="邮箱"/>
<input type="text" name="more" placeholder="介绍"/>
</form>
<input id="btn3" type="button" class="btn btn-primary" value="点击3"/>
</div>
{
% endblock %}
{
% block js %}
<script type="text/javascript">
$(function () {
// 页面框架加载完成之后代码自动执行
bindBtn1Event();
bindBtn2Event();
bindBtn3Event();
})
function bindBtn1Event() {
$("#btn1").click(function () {
$.ajax({
url: "/task/ajax/",
type: "post",
data: {
n1: 123,
n2: 456
},
dataType: "JSON",
success: function (res) {
console.log(res);
console.log(res.status);
console.log(res.data);
}
})
})
}
function bindBtn2Event() {
$("#btn2").click(function () {
$.ajax({
url: "/task/ajax/",
type: "post",
data: {
name: $("#txtUser").val(),
age: $("#txtAge").val()
},
dataType: "JSON",
success: function (res) {
console.log(res);
console.log(res.status);
console.log(res.data);
}
})
})
}
function bindBtn3Event() {
$("#btn3").click(function () {
$.ajax({
url: "/task/ajax/",
type: "post",
data: $("#form3").serialize(),
dataType: "JSON",
success: function (res) {
console.log(res);
console.log(res.status);
console.log(res.data);
}
})
})
}
</script>
{
% endblock %}
10.14.5 ケース
{% extends 'layout.html' %}
{% block content %}
<div class="container">
<div class="panel panel-default">
<div class="panel-heading">表单</div>
<div class="panel-body">
<form id="formAdd" novalidate>
<div class="clearfix">
{% for field in form %}
<div class="col-xs-6">
<div class="form-group" style="position: relative; margin-bottom: 20px;">
<label>{
{ field.label }}</label>
{
{ field }}
<span class="error-msg" style="color:red;position: absolute;"></span>
</div>
</div>
{% endfor %}
</div>
<div class="col-xs-12">
<button id="btnAdd" type="button" class="btn btn-primary">提 交</button>
</div>
</form>
</div>
</div>
<hr/>
<h1>Ajax学习</h1>
<h3>示例1</h3>
<input id="btn1" type="button" class="btn btn-primary" value="点击1"/>
<h3>示例2</h3>
<input type="text" id="txtUser" placeholder="用户名"/>
<input type="text" id="txtAge" placeholder="年龄"/>
<input id="btn2" type="button" class="btn btn-primary" value="点击2"/>
<h3>示例3</h3>
<form id="form3">
<input type="text" name="user" placeholder="用户名"/>
<input type="text" name="age" placeholder="年龄"/>
<input type="text" name="email" placeholder="邮箱"/>
<input type="text" name="more" placeholder="介绍"/>
</form>
<input id="btn3" type="button" class="btn btn-primary" value="点击3"/>
</div>
{% endblock %}
{% block js %}
<script type="text/javascript">
$(function () {
// 页面框架加载完成之后代码自动执行
bindBtn1Event();
bindBtn2Event();
bindBtn3Event();
bindBtnAddEvent();
})
function bindBtn1Event() {
$("#btn1").click(function () {
$.ajax({
url: "/task/ajax/",
type: "post",
data: {
n1: 123,
n2: 456
},
dataType: "JSON",
success: function (res) {
console.log(res);
console.log(res.status);
console.log(res.data);
}
})
})
}
function bindBtn2Event() {
$("#btn2").click(function () {
$.ajax({
url: "/task/ajax/",
type: "post",
data: {
name: $("#txtUser").val(),
age: $("#txtAge").val()
},
dataType: "JSON",
success: function (res) {
console.log(res);
console.log(res.status);
console.log(res.data);
}
})
})
}
function bindBtn3Event() {
$("#btn3").click(function () {
$.ajax({
url: "/task/ajax/",
type: "post",
data: $("#form3").serialize(),
dataType: "JSON",
success: function (res) {
console.log(res);
console.log(res.status);
console.log(res.data);
}
})
})
}
function bindBtnAddEvent() {
$("#btnAdd").click(function () {
$(".error-msg").empty();
$.ajax({
url: "/task/add/",
type: "post",
data: $("#formAdd").serialize(),
dataType: "JSON",
success: function (res) {
if (res.status) {
alert("添加成功");
} else {
{
#console.log(res.error);#}
$.each(res.error, function(name, data){
// console.log(name, data);
$("#id_" + name).next().text(data[0])
})
}
}
})
})
}
</script>
{% endblock %}
10.15 Django 開発 - コンテンツのレビュー
10.16 注文
テーブル構造
class Order(models.Model):
""" 订单 """
oid = models.CharField(verbose_name="订单号", max_length=64)
title = models.CharField(verbose_name="名称", max_length=32)
price = models.IntegerField(verbose_name="价格")
status_choices = (
(1, "待支付"),
(2, "已支付")
)
status = models.SmallIntegerField(verbose_name="状态", choices=status_choices, default=1)
admin = models.ForeignKey(verbose_name="管理员", to="Admin", on_delete=models.CASCADE)
form.instance.oid=...
ユーザーが入力していない値を追加することによって
import random
from datetime import datetime
from django.shortcuts import render, redirect, HttpResponse
from django.views.decorators.csrf import csrf_exempt
from django.http import JsonResponse
from app01.utils.bootstrap import BootStrapModelForm
from app01 import models
class OrderModelForm(BootStrapModelForm):
class Meta:
model = models.Order
# fields = "__all__"
exclude = ["oid"]
def order_list(request):
form = OrderModelForm
return render(request, 'order_list.html', {
'form': form})
@csrf_exempt
def order_add(request):
form = OrderModelForm(data=request.POST)
if form.is_valid():
# title=? price=? status=? admin_id?
# print(form.cleaned_data)
form.instance.oid = datetime.now().strftime("%Y%m%d%H%M%S") + str(random.randint(1000, 9999))
form.save()
return JsonResponse({
"status": True})
return JsonResponse({
"status": False, 'error': form.errors})
$("#formAdd")
は jQuery オブジェクトと$("#formAdd")[0]
DOM オブジェクトであり、reset() メソッドがあります。
10.17 チャート
- 海外のハイチャート。
- eチャート、国内。
10.18 ファイルのアップロード
10.18.1 基本操作
{% extends 'layout.html' %}
{% block content %}
<div class="container">
<form method="post" enctype="multipart/form-data">
{% csrf_token %}
<input type="text" name="username">
<input type="file" name="avatar">
<input type="submit" value="提交">
</form>
</div>
{% endblock %}
from django.shortcuts import render, HttpResponse
def upload_list(request):
if request.method == "GET":
return render(request, 'upload_list.html')
# print(request.POST) # 请求体传递过来的数据
# print(request.FILES) # 发过来的文件
file_object = request.FILES.get("avatar")
# print(file_object.name)
f = open(file_object.name, mode='wb')
for chunk in file_object.chunks():
f.write(chunk)
f.close()
return HttpResponse("...")
10.18.2 ケース: データの一括アップロード
<form method="post" enctype="multipart/form-data" action="/depart/multi/">
{% csrf_token %}
<div class="form-group">
<input type="file" name="exc">
</div>
<input type="submit" value="上传" class="btn btn-info btn-sm">
</form>
def depart_multi(request):
"""批量上传"""
# 1.获取用户上传的文件对象
file_object = request.FILES.get("exc")
# 2.对象传递给openpyxl, 由openpyxl读取文件的内容
wb = load_workbook(file_object)
sheet = wb.worksheets[0]
# 3.循环获取每一行数据
for row in sheet.iter_rows(min_row=2):
text = row[0].value
exists = models.Department.objects.filter(title=text).exists()
if not exists:
models.Department.objects.create(title=text)
return redirect("/depart/list/")
10.18.3 ケース: 混合データ (フォーム)
ページを送信するとき: ユーザーはデータとファイルを入力します (入力を空にすることはできず、エラーが報告されます)。
- フォームは HTML タグを生成できます: type=file
- フォームの検証
- form.cleaned_data はデータ + ファイル オブジェクトを取得します
{% extends "layout.html" %}
{% block content %}
<div class="container">
<div class="panel panel-default">
<div class="panel-heading">
<h3 class="panel-title">{
{ title }}</h3>
</div>
<div class="panel-body">
<form method="post" enctype="multipart/form-data" novalidate>
{% csrf_token %}
{% for field in form %}
<div class="form-group">
<label>{
{ field.label }}</label>
{
{ field }}
<span style="color: red">{
{ field.errors.0 }}</span>
</div>
{% endfor %}
<button type="submit" class="btn btn-primary">提 交</button>
</form>
</div>
</div>
</div>
{% endblock %}
from django.shortcuts import render, HttpResponse
import os
from app01 import models
def upload_list(request):
if request.method == "GET":
return render(request, 'upload_list.html')
# print(request.POST) # 请求体传递过来的数据
# print(request.FILES) # 发过来的文件
file_object = request.FILES.get("avatar")
# print(file_object.name)
f = open(file_object.name, mode='wb')
for chunk in file_object.chunks():
f.write(chunk)
f.close()
return HttpResponse("...")
from django import forms
from app01.utils.bootstrap import BootStrapForm
class UpForm(BootStrapForm):
bootstrap_exclude_fields = ['img']
name = forms.CharField(label="姓名")
age = forms.IntegerField(label="年龄")
img = forms.FileField(label="头像")
def upload_form(request):
title = "Form上传"
if request.method == "GET":
form = UpForm()
return render(request, 'upload_form.html', {
"title": title, "form": form})
form = UpForm(data=request.POST, files=request.FILES)
if form.is_valid():
print(form.cleaned_data)
# 读取到内容, 自己处理每个字段的内容
# 1.读取图片内容,写入到文件夹中,并获取文件的路径
image_object = form.cleaned_data.get('img')
db_file_path = os.path.join("static", "img", image_object.name)
file_path = os.path.join("app01", "static", "img", image_object.name)
f = open(file_path, mode='wb')
for chunk in image_object.chunks():
f.write(chunk)
f.close()
# 将图片的文件路径写入到数据库
models.Boss.objects.create(
name=form.cleaned_data['name'],
age=form.cleaned_data['age'],
img=db_file_path,
)
return HttpResponse("...")
return render(request, 'upload_form.html', {
"title": title, "form": form})
10.19.4 メディアを有効にする
urls.py で設定します。
from django.urls import path, re_path
from django.views.static import serve
from django.conf import settings
urlpatterns = {
re_path(r'^media/(?P<path>.*)', serve, {
'document_root': settings.MEDIA_ROOT}, name='media'),
}
settings.py で設定します。
import os
MEDIA_ROOT = os.path.join(BASE_DIR, "media")
MEDIA_URL = "/media/"
10.19.5 ケース: 混合データ (フォーム)
import os
from django.shortcuts import render, HttpResponse
from django.conf import settings
from app01 import models
def upload_list(request):
if request.method == "GET":
return render(request, 'upload_list.html')
# print(request.POST) # 请求体传递过来的数据
# print(request.FILES) # 发过来的文件
file_object = request.FILES.get("avatar")
# print(file_object.name)
f = open(file_object.name, mode='wb')
for chunk in file_object.chunks():
f.write(chunk)
f.close()
return HttpResponse("...")
from django import forms
from app01.utils.bootstrap import BootStrapForm
class UpForm(BootStrapForm):
bootstrap_exclude_fields = ['img']
name = forms.CharField(label="姓名")
age = forms.IntegerField(label="年龄")
img = forms.FileField(label="头像")
def upload_form(request):
title = "Form上传"
if request.method == "GET":
form = UpForm()
return render(request, 'upload_form.html', {
"title": title, "form": form})
form = UpForm(data=request.POST, files=request.FILES)
if form.is_valid():
print(form.cleaned_data)
# 读取到内容, 自己处理每个字段的内容
# 1.读取图片内容,写入到文件夹中,并获取文件的路径
image_object = form.cleaned_data.get('img')
# media_path = os.path.join(settings.MEDIA_ROOT, image_object.name)
media_path = os.path.join("media", image_object.name)
f = open(media_path, mode='wb')
for chunk in image_object.chunks():
f.write(chunk)
f.close()
# 将图片的文件路径写入到数据库
models.Boss.objects.create(
name=form.cleaned_data['name'],
age=form.cleaned_data['age'],
img=media_path,
)
return HttpResponse("...")
return render(request, 'upload_form.html', {
"title": title, "form": form})
ケース: 混合データ (ModalForm)
models.py 内
class City(models.Model):
name = models.CharField(verbose_name="名称", max_length=32)
age = models.IntegerField(verbose_name="人口")
# 本质上数据也是CharField,帮你自动保存数据,支持upload_to
img = models.FileField(verbose_name="Logo", max_length=128, upload_to='city/')
モデルフォームの定義
class UpModelForm(BootStrapModelForm):
bootstrap_exclude_fields = ['img']
class Meta:
model = models.City
fields = "__all__"
ビュー
def upload_modal_form(request):
"""基于modelform"""
title = "ModelForm上传文件"
if request.method == 'GET':
form = UpModelForm()
return render(request, 'upload_form.html', {
"form": form, "title": title})
form = UpModelForm(data=request.POST, files=request.FILES)
if form.is_valid():
# 对于文件,自动保存:
# 字段 上传的路径写入到数据库
form.save()
return HttpResponse("上传成功")
return render(request, 'upload_form.html', {
"form": form, "title": title})