原生django详解
基于📕django官方文档解析,用于了解django框架原理与ORM数据库操作概念
相关文档:
1.虚拟环境创建
为保证项目的版本统一,应全程使用虚拟环境进行开发
虚拟环境将创建在对应项目文件夹下 ps:vscode可可视化创建 Venv
虚拟环境使项目不被其余环境干扰,且易于部署
创建环境:python -m venv "name"
激活环境:environment\Scripts\activate
取消激活:deactivate
2.项目与接口创建
2.1 创建项目
1.项目创建
django-admin startproject <projectName>
2.项目测试(仅适用于开发环境)
python manage.py runserver
项目初始结构
<projectName>/
manage.py 管理 Django 项目的命令行工具
mysite/ 纯 Python 包,用于配置项目
__init__.py
settings.py Django 项目的配置文件
urls.py URL 声明应用
asgi.py 配置asgi服务器的入口
wsgi.py 配置wsgi服务器的入口
注意:1.在修改setting前需要先将TIME_ZONE设置为 'Asia/Shanghai'(本地时区)
2.对于完全自定义接口的项目,将INSTALLED_APPS中默认引入应用删除(引入应用会在 migrate
命令执行时,在数据库中创建表)
2.2 创建应用
1.创建app
python manage.py startapp <appName>
2.初始结构
app/
__init__.py
admin.py
apps.py
migrations/
__init__.py
models.py
tests.py
views.py
urls.py 自己创建
2.3 创建视图
1.在views.py里创建视图代码(接口逻辑)
from django.http import HttpResponse
def index(request):
return HttpResponse("Hello, world. You're at the polls index.")
2.定义应用内url映射配置视图
在app目录内,创建该app的URLconf urls.py
from django.urls import path
from . import views
urlpatterns = [
path("", views.index, name="index"),
]
3.在项目全局中配置urlconf
from django.contrib import admin
from django.urls import include, path
urlpatterns = [
path("myapp/", include("myapp.urls")),
path("admin/", admin.site.urls),
]
include() 运行引用其余URLconfs
4.即可测试运行接口
python manage.py runserver
3.数据库配置
3.1 基础配置
django默认采用sqlite作为数据库,可在setting中切换
seeting
->DATABASES
->default
字段
DATABASES = {
'default': {
'ENGINE': 'django.db.backends.sqlite3',
'NAME': BASE_DIR / 'db.sqlite3',
}
}
ENGINE
-- 可选值有'django.db.backends.sqlite3'
,'django.db.backends.postgresql'
,'django.db.backends.mysql'
,'django.db.backends.oracle'
其它 可用后端NAME
-- 数据库的名称。如果你使用 SQLite,数据库将是你电脑上的一个文件,在这种情况下,NAME
应该是此文件完整的绝对路径,包括文件名。默认值BASE_DIR / 'db.sqlite3'
将把数据库文件储存在项目的根目录。
如果不使用 SQLite,则必须添加一些额外设置,比如 USER
、 PASSWORD
、 HOST
等等。想了解更多数据库设置方面的内容,请看文档:DATABASES
3.2 创建数据模型
数据模型对于数据库表的每个字段,用于在python中便捷操作数据库、避免sql注入风险并提高数据库更换高效性
模型可以直接在数据库中创建表(如果有权限)
每个字段对应数据库表中的一个字段
应用models.py
from django.db import models
class Question(models.Model):
question_text = models.CharField(max_length=200)
pub_date = models.DateTimeField("date published")
def __str__(self):
return self.question_text # 用于对每个数据对象进行展示
class Choice(models.Model):
question = models.ForeignKey(Question, on_delete=models.CASCADE)
choice_text = models.CharField(max_length=200)
votes = models.IntegerField(default=0
def __str__(self):
return self.choice_text)
未设置主键,会自动创建主键id,并在每次添加数据时自动填写
3.3 激活模型
- 为这个应用创建数据库 schema(生成
CREATE TABLE
语句)。 - 创建可以与
Question
和Choice
对象进行交互的 Python 数据库 API 将app注册到项目里
将对应app的apps.py里的Config类注册到setting里
INSTALLED_APPS = [
"myapp.apps.MyappConfig",
]
2.模型迁移
在python中进行数据模型操作
python manage.py makemigrations myapp
3.数据库迁移
基于已有内容对数据库进行迁移
python manage.py migrate
3.4 更改数据模型的三步
- 编辑
models.py
文件,改变模型。 - 运行
python manage.py makemigrations
为模型的改变生成迁移文件。 - 运行
python manage.py migrate
来应用数据库迁移
3.5 数据api的操作
1.举例:
from polls.models import Choice, Question
# 替换datetime.datetime.now(),已设置好时区.
>>> from django.utils import timezone
>>> q = Question(question_text="What's new?", pub_date=timezone.now())
# q是是个数据模型类的实例,代表数据库表中的一行数据
# 将数据对象保存到数据库内.
>>> q.save()
# 完成写入后,即可获取到数据内的该对象ID.
>>> q.id
1
>>> q.question_text
"What's new?
>>> q.pub_date
datetime.datetime(2012, 2, 26, 13, 0, 0, 775217, tzinfo=datetime.timezone.utc)
# 修改该对象所对应数据库中的值
>>> q.question_text = "What's up?"
>>> q.save()
# objects.all() 显示数据库中的所有question数据量.
>>> Question.objects.all()
<QuerySet [<Question: What's up?>]> # 数据对象所显示的名称是我们在数据结构中所定义的str
3.6 常用数据API汇总
创建对象(插入数据)
本质:
INSERT
SQL 语句
from blog.models import Blog
b = Blog(name="Beatles Blog", tagline="All the latest Beatles news.")
b.save()
修改对象(修改数据)
本质:
UPDATE
SQL 语句
# b5是一个已经保存到数据库中的 Blog 数据实例
b5.name = "New name"
b5.save()
检索对象(提取数据)
本质:
SELECT
语句①检索全部对象
方法 all() 返回了一个包含数据库中所有对象的 QuerySet 对象
all_entries = Entry.objects.all()
②通过过滤器检索指定对象
filter(**kwargs)
返回一个新的 QuerySet
,包含的对象满足给定查询参数;(若不存在,则返回一个空查询集)
空值判断:
if objects.exists():
...
else:
...
exclude(**kwargs)
返回一个新的 QuerySet
,包含的对象 _不_ 满足给定查询参数
查询关键词:field__lookuptype=value
(使用双下划线)分割 条件字段和查询条件
注:
1.可以将细化操作链接在一起
Entry.objects.filter(headline__startswith="What").exclude(
... pub_date__gte=datetime.date.today()
... ).filter(pub_date__gte=datetime.date(2005, 1, 30))
2.每个 QuerySet
都是唯一的,且前后无关联,可独立存储、使用、复用
q1 = Entry.objects.filter(headline__startswith="What")
q2 = q1.exclude(pub_date__gte=datetime.date.today())
q3 = q1.filter(pub_date__gte=datetime.date.today())
3.创建 QuerySet
的过程不涉及任何数据库活动。你可以一直堆叠过滤条件
q = Entry.objects.filter(headline__startswith="What")
q = q.filter(pub_date__lte=datetime.date.today())
q = q.exclude(body_text__icontains="food")
print(q)
③ get()
检索单个对象
查询条件表达式与query相同;(若不存在,则抛出DoseNotExist异常)
空值判断:
try:
obj = Model.objects.get...
except Model.DoseNotExist:
...
空值快捷抛出404:
get_object_or_404()
from django.shortcuts import get_object_or_404, render
from .models import Question
# ...
def detail(request, question_id):
question = get_object_or_404(Question, pk=question_id)
return render(request, "polls/detail.html", {"question": question})
获取数据对象所有字段字典数据的方法(包括自动生成的ID字段):
# 获取所有字段名称
fields = [field.name for field in user_obj._meta.fields]
# 获取所有字段数据生成字典
user_obj_data = {field: getattr(user_obj, field) for field in fields}
④限制 QuerySet
条目数
采用python的切片语法实现(不支持负索引)
Entry.objects.all()[5:10]
等价于sql:OFFSET 5 LIMIT 5
要检索 _单个_ 对象而不是列表(例如,SELECT foo FROM bar LIMIT 1
),请使用索引而不是切片
Entry.objects.order_by("headline")[0]
⑤字段查询(where条件)
field__lookuptype=value
(使用双下划线)
Entry.objects.filter(pub_date__lte="2006-01-01")
等价于
SELECT * FROM blog_entry WHERE pub_date <= '2006-01-01';
3.7 旧数据库接入(不创建新数据库)
设置default数据库
mysql配置
DATABASES = {
"default": {
"ENGINE": "django.db.backends.mysql",
"NAME": "mydatabase",
"USER": "mydatabaseuser",
"PASSWORD": "mypassword",
"HOST": "127.0.0.1",
"PORT": "5432",
}
}
根据数据库自动生成数据模型
使用Django自带inspectdb实现,导出为指定文件
python manage.py inspectdb > models.py
- 从models.py里复制对应的表数据模型到对应的app的models.py里
4 多样化视图
4.1 接收参数的视图
- view.py 需要接受参数的视图
def detail(request, question_id):
return HttpResponse("You're looking at question %s." % question_id)
def results(request, question_id):
response = "You're looking at the results of question %s."
return HttpResponse(response % question_id)
def vote(request, question_id):
return HttpResponse("You're voting on question %s." % question_id)
app 里的urls.py
from django.urls import path
from . import views
urlpatterns = [
# ex: /polls/
path("", views.index, name="index"),
# ex: /polls/5/
path("<int:question_id>/", views.detail, name="detail"),
# ex: /polls/5/results/
path("<int:question_id>/results/", views.results, name="results"),
# ex: /polls/5/vote/
path("<int:question_id>/vote/", views.vote, name="vote"),
]
2.普通链接,通过request.GET和request.POST来获取传递的参数
GET
request.GET.get[param]
POST(json数据解析)
data = json.load(request.body)
param = data['param']
4.2 view常用返回
json数据返回
默认返回状态码为200,可自定义返回状态码
from django.http import HttpResponse, JsonResponse
def ...:
...
return JsonResponse({
"status": "success"
}, status=200)
5 测试
6.JWT
6.1 自定义token生成与装饰器(简单)
- 安装PyJWT
pip install PyJWT
创建生成和验证 JWT 的函数
在项目根目录下创建common文件夹,用于存放被全局app可调用的utils.py
common/
__init__.py
utils.py # 公共的实用函数
utils.py
# utils.py
import jwt
from django.conf import settings
from datetime import datetime, timedelta, timezone
def generate_jwt(user):
payload = {
'openid': user.openid,
'exp': datetime.now(timezone.utc) + timedelta(minutes=5),
'iat': datetime.now(timezone.utc),
}
token = jwt.encode(payload, settings.SECRET_KEY, algorithm='HS256')
return f'Bearer {token}'
def decode_jwt(auth_header):
if not auth_header.startswith('Bearer '):
return None
token = auth_header.split(' ')[1]
try:
payload = jwt.decode(token, settings.SECRET_KEY, algorithms=['HS256'])
return payload
except jwt.ExpiredSignatureError:
return None
except jwt.InvalidTokenError:
return None
创建一个装饰器来保护视图(接口装饰器)
1.请求头中名称“'AUTHORIZATION”,在获取时,需要加上HTTP_
# decorators.py
from django.http import JsonResponse
from common.utils import decode_jwt
def jwt_required(view_func):
def _wrapped_view(request, *args, **kwargs):
auth_header = request.META.get('HTTP_AUTHORIZATION')
if not auth_header or not auth_header.startswith('Bearer '):
return JsonResponse({'error': 'Token missing'}, status=401)
payload = decode_jwt(auth_header)
if payload is None:
return JsonResponse({'error': 'Invalid or expired token'}, status=401)
request.openid = payload['openid']
return view_func(request, *args, **kwargs)
return _wrapped_view
- 创建登录接口用于获取token
# views.py
from django.http import JsonResponse
from common.utils import generate_jwt
# 登录测试接口
@require_http_methods(["POST"])
def login(request):
data = json.loads(request.body)
openid = data['openid']
try:
user = User.objects.get(openid=openid)
token = generate_jwt(user)
return JsonResponse({
"status": "success",
"token": token
})
except User.DoesNotExist:
return JsonResponse({
"status": "error",
"msg": "请先注册"
}, status = 403)
- 创建保护视图(使用装饰器)
# views.py
from django.http import HttpResponse, JsonResponse
from django.views.decorators.http import require_http_methods
# 数据模型
from user.models import User
from common.utils import generate_jwt
from common.decorators import jwt_required
# 用户昵称、头像信息修改接口
@require_http_methods(["POST"])
@jwt_required
def userinfo_update(request):
openid = request.openid
data = json.loads(request.body)
avatar = data['avatar']
username = data['username']
user_obj = User.objects.get(openid=openid)
msg = ""
if (avatar != "") & (avatar is not None) & (avatar != user_obj.avatar):
user_obj.avatar = avatar
user_obj.save()
msg += '头像更新成功'
if (username != "") & (username is not None) & (username != user_obj.username):
user_obj.username = username
user_obj.save()
msg += '昵称更新成功'
return JsonResponse({
"status": "success",
"msg": msg
})
6.2 自定义token生成、装饰与存储校验(含数据库)
- 安装JWT
- 创建token存储模型
- 创建生成和验证 JWT 的函数
- 创建装饰器
- 创建登录获取token接口
- 创建保护接口
7.跨域请求
- 使用
django-cors-headers
,可以配置运行的来源请求
安装 django-cors-headers
首先,安装 django-cors-headers
:
pip install django-cors-headers
配置 Django 设置
接下来,在 Django 项目的 settings.py
中进行以下配置:
- 添加
corsheaders
到INSTALLED_APPS
:
INSTALLED_APPS = [ ... 'corsheaders', ...]
- 添加
corsheaders.middleware.CorsMiddleware
到中间件列表,并确保它位于其他中间件之前:
MIDDLEWARE = [ 'corsheaders.middleware.CorsMiddleware', 'django.middleware.common.CommonMiddleware', ...]
- 配置 CORS 设置:
您可以根据需要配置不同的 CORS 设置。以下是一些常见的设置选项:
允许所有来源:
CORS_ALLOW_ALL_ORIGINS = True
只允许特定来源:
CORS_ALLOWED_ORIGINS = [ 'https://example.com', 'https://sub.example.com',]
允许所有方法:
CORS_ALLOW_METHODS = [ 'DELETE', 'GET', 'OPTIONS', 'PATCH', 'POST', 'PUT',]
允许所有头部:
CORS_ALLOW_HEADERS = [ 'accept', 'accept-encoding', 'authorization', 'content-type', 'dnt', 'origin', 'user-agent', 'x-csrftoken', 'x-requested-with',]
- 使用 django的中间件和装饰器
# setting.py
MIDDLEWARE = [
'django.middleware.csrf.CsrdView,iddleware',
]
from django.view.decorators.csrf import csrf_exempt
# 在接口前修饰,即可豁免该接口的所有CSRF检查
@csrf_exempt
def ...