0%

Web开发——基于Django

本文介绍基于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-allauthpython-social-auth等第三方包

使用OAuth协议

数据库管理

Django 默认支持多种数据库系统,主要支持的数据库包括:

  1. SQLite: 默认配置的数据库,适用于开发和测试环境,因为它是基于文件的,不需要单独的数据库服务器。
  2. PostgreSQL: 推荐用于生产环境的数据库,因为它功能强大、稳定且支持高并发。
  3. MySQL: 另一种流行的选择,特别是在Web应用程序中。
  4. Oracle: 适用于需要在 Oracle 数据库环境下工作的企业级应用。
  5. MariaDB: MySQL的一个分支,完全兼容,是一个自由和开源的选择。

下面以sqlite为例,介绍默认数据库的使用方法。

sqlite在settings.py中的配置如下:

1
2
3
4
5
6
DATABASES = {
'default': {
'ENGINE': 'django.db.backends.sqlite3',
'NAME': BASE_DIR / 'db.sqlite3',
}
}

一般流程

定义模型

每个模型映射到数据库中的一个表,模型的每个属性对应表中的一个字段

1
2
3
4
5
6
from django.db import models

class Article(models.Model):
title = models.CharField(max_length=100)
content = models.TextField()
pub_date = models.DateTimeField('date published')

迁移数据库

  • 创建迁移:运行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
2
3
4
5
from myapp.models import Article

# 创建一个新的Article实例并保存到数据库
article = Article(title='Hello Django', content='Django is cool', pub_date=timezone.now())
article.save()
查询数据
1
2
3
4
5
6
7
8
# 获取所有Article对象
articles = Article.objects.all()

# 获取单个对象
article = Article.objects.get(id=1)

# 使用过滤器
recent_articles = Article.objects.filter(pub_date__lte=timezone.now()).order_by('-pub_date')[:5]
更新数据
1
2
3
article = Article.objects.get(id=1)
article.title = 'New title'
article.save()
删除数据
1
2
article = Article.objects.get(id=1)
article.delete()

上传文件/图片

  • 模型:FileField用于存储文件到文件系统,并在数据库中保存文件的路径。

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    from 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
    7
    from 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
    7
    def 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
    58
    import 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的原则是什么?

  1. 划分APP的目的是为了重用(reuse)代码,因此在逻辑上功能不同的组建可以分开;
  2. 经验原则是:use one ‘core’ app until it hurts,尽量不要把代码切太碎。

表单(forms)有什么作用?

  1. 生成HTML表单代码,还可以用来验证提交的数据,简化数据的收集和验证过程

  2. 用法

    1. 定义表单

      1. ```python
        from django.forms import ModelForm

        ModelForm类适合直接与数据库模型绑定的表单,自动为模型中的字段生成表单字段

        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()
    2. 使用表单

      1. 在views中使用,根据上面不同的方式使用方式不同,下面是使用第一种方式时(ModelForm),处理的方法:

        1. 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})
          
        2. 使用{{ form }}简单地渲染表单

参考文献

  1. https://developer.mozilla.org/zh-CN/docs/Learn/Server-side/Django/Introduction
  2. https://www.w3schools.com/django/index.php