本文介绍基于Django的web开发原理与方法。
概述
Django 遵循MVT(Model View Template)设计模式,
- Model用于管理数据,常用于和数据库交互;
- Template是前端展示的用户界面,
- View用户处理用户的request,并返回response。
创建Django项目
用户认证
Dango内置用户认证系统(django.contrib.auth)
- Django自带一个用户认证系统,它处理用户账号、组、权限和基于cookie的用户会话。
- 使用
django.contrib.auth
应用,可以轻松实现用户的注册、登录和注销功能。
使用表单
- 注册:可以通过创建一个
UserCreationForm
(Django内置)或自定义的表单来注册新用户。这个表单通常会要求用户提供用户名、邮箱、密码等信息。 - 登录:使用
AuthenticationForm
(Django内置)或自定义表单来实现登录功能。
使用Django REST框架(DRF)
- 如果你的Django应用是一个API服务,Django REST框架提供了一套认证和权限的系统,用于管理RESTful API的用户认证。
- DRF支持多种认证方式,如Token认证、OAuth、JSON Web Token(JWT)等。
djangorestframework-simplejwt
是一个与Django REST框架配合使用的库,提供了JWT认证的实现。
第三方认证
django-allauth或
python-social-auth等第三方包
使用OAuth协议
数据库管理
Django 默认支持多种数据库系统,主要支持的数据库包括:
- SQLite: 默认配置的数据库,适用于开发和测试环境,因为它是基于文件的,不需要单独的数据库服务器。
- PostgreSQL: 推荐用于生产环境的数据库,因为它功能强大、稳定且支持高并发。
- MySQL: 另一种流行的选择,特别是在Web应用程序中。
- Oracle: 适用于需要在 Oracle 数据库环境下工作的企业级应用。
- MariaDB: MySQL的一个分支,完全兼容,是一个自由和开源的选择。
下面以sqlite为例,介绍默认数据库的使用方法。
sqlite在settings.py中的配置如下:
1 | DATABASES = { |
一般流程
定义模型
每个模型映射到数据库中的一个表,模型的每个属性对应表中的一个字段
1 | from django.db import models |
迁移数据库
创建迁移:运行
makemigrations
命令为模型的改变生成迁移文件。1
python manage.py makemigrations
应用迁移:运行
migrate
命令应用迁移到数据库,执行实际的数据库结构变更。1
python manage.py migrate appname
查看SQL命令
1
python manage.py sqlmigrate appname 0001
在admin中查看数据库
1
2
3
4
5
6
7
8# 创建超级用户
python manage.py createsuperuser
# 添加表单到admin
from django.contrib import admin
from .models import Article
admin.site.register(Article)
# 查看
http://127.0.0.1:8000/admin/
使用模型
创建和保存数据
1 | from myapp.models import Article |
查询数据
1 | # 获取所有Article对象 |
更新数据
1 | article = Article.objects.get(id=1) |
删除数据
1 | article = Article.objects.get(id=1) |
上传文件/图片
模型:
FileField
用于存储文件到文件系统,并在数据库中保存文件的路径。1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21from django.db import models
from django.contrib.auth.models import User
import os
from uuid import uuid4
def user_directory_path(instance, filename):
ext = filename.split('.')[-1]
# 为文件生成唯一的文件名
uuid_name = str(uuid4())
filename = f"{uuid_name}.{ext}"
# 文件将被上传到MEDIA_ROOT/user_<id>/<filename>
instance.file_uuid = uuid_name
return 'user_{0}/{1}'.format(instance.user.id, filename)
class UploadedFile(models.Model):
user = models.ForeignKey(User, on_delete=models.CASCADE)
single_file = models.FileField(upload_to=user_directory_path)
uploaded_at = models.DateTimeField(auto_now_add=True)
file_uuid = models.CharField(max_length=250, editable=False)
def __str__(self):
return os.path.basename(self.single_file.name)创建文件表单(使用ModelForm,因为创建的表单与数据库之间关联)
1
2
3
4
5
6
7from django import forms
from .models import UploadedFile
class UploadedFileForm(forms.ModelForm):
class Meta:
model = UploadedFile # 指定这个表单对应的模型是UploadedFile
fields = ['single_file'] # 指定表单中需要包含的字段,这里只需要上传的文件字段使用模型
1
2
3
4
5
6
7def post(self, request, *args, **kwargs):
form = UploadFileForm(request.POST, request.FILES)
if form.is_valid():
uploaded_file_instance = form.save(commit=False)
uploaded_file_instance.user = request.user
uploaded_file_instance.save()
return Response(status=status.HTTP_201_CREATED)前端设置
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58import React, { useState, useEffect } from 'react';
import axios from 'axios';
function Reference() {
const [uploadStatus, setUploadStatus] = useState('');
const [uploadedFiles, setUploadedFiles] = useState([]);
const textInputRef = useRef(null); // 添加一个引用来访问文本输入域
const fileInputRef = useRef(null); // 添加一个引用来访问文件输入域
const handleSubmit = async (e) => {
e.preventDefault();
setUploadStatus('Uploading...');
const formData = new FormData(e.target);
formData.append('user', user_name);
try {
const response = await axios.post('http://localhost:8000/postdata/upload/', formData);
if (response.status === 201) { // 假设状态码 200 表示上传成功
setUploadStatus('Upload successful!');
// 上传成功后清空输入内容
textInputRef.current.value = ""; // 清空文本输入域
fileInputRef.current.value = ""; // 清空文件输入域
} else {
// 处理非 200 响应状态码
setUploadStatus('Upload failed, please try again.');
}
} catch (error) {
setUploadStatus('Error occurred!');
console.error('Error:', error);
}
};
return (
<div className="info">
<form className="uploadForm" encType="multipart/form-data" onSubmit={handleSubmit}>
<div className="fields">
<label id="reference_text" htmlFor="text">Text:</label>
<textarea
ref={textInputRef}
id="text"
name="text"
placeholder="Text">
</textarea>
</div>
<div className="fields">
<label htmlFor="file"> File (pdf,txt,doc):</label>
<input
ref={fileInputRef}
type="file"
id="single_file"
name="single_file"
accept=".pdf, .txt, .docx"
/>
</div>
<button type="submit" id="submitUpload">Upload</button>
</form>
</div>
);
}
export default Reference;
问题汇总
划分APP的原则是什么?
- 划分APP的目的是为了重用(reuse)代码,因此在逻辑上功能不同的组建可以分开;
- 经验原则是:use one ‘core’ app until it hurts,尽量不要把代码切太碎。
表单(forms)有什么作用?
生成HTML表单代码,还可以用来验证提交的数据,简化数据的收集和验证过程
用法
定义表单
```python
from django.forms import ModelFormModelForm类适合直接与数据库模型绑定的表单,自动为模型中的字段生成表单字段
from .models import Contact
class ContactForm(ModelForm):
class Meta:
model = Contact
fields = [‘name’, ‘email’, ‘message’]1
2
3
4
5
6
7
8
9
2.
```python
from django import forms
# Form类适用于不直接与数据库模型关联的表单
class UploadFileForm(forms.Form):
title = forms.CharField(max_length=50)
file = forms.FileField()
使用表单
在views中使用,根据上面不同的方式使用方式不同,下面是使用第一种方式时(ModelForm),处理的方法:
from django.http import HttpResponseRedirect from django.shortcuts import render from .forms import ContactForm def contact(request): if request.method == 'POST': form = ContactForm(request.POST) if form.is_valid(): # 验证表单(可以自行定义) # 处理表单数据 return HttpResponseRedirect('/thanks/') else: form = ContactForm() return render(request, 'contact.html', {'form': form})
使用
{{ form }}
简单地渲染表单