本文档为 "使用 Python3.6 + Django2.1 搭建个人博客" 系列文档的第一篇,主要讲述如何创建 Django 项目、配置 MySQL 数据库、自定义用户模型等。

创建虚拟环境

  • 创建虚拟环境
[ allenlideMacBook-Pro:~ allen ]$ mkvirtualenv opcoder
  • 进入虚拟环境并安装依赖包
(opcoder) allenlideMacBook-Pro:~ allen$ pip3 install django==2.1.10
  • 查看当前虚拟环境下已安装的依赖包
(opcoder) allenlideMacBook-Pro:~ allen$ pip3 list

Package    Version
---------- -------
Django     2.1.10 
pip        19.2.1 
pytz       2019.2 
setuptools 41.0.1 
wheel      0.33.4 

创建 MySQL 数据库

opcoder2

MySQL 数据库字符集选择 utf8mb4 可支持 emoji

创建 Django 项目

  • 利用 Pycharm 工具创建 Django 项目

python-opcoderA01

配置数据库连接

  • 安装依赖包
(opcoder) allenlideMacBook-Pro:~ allen$ pip3 install pymysql
  • opcoder/setting.py 配置文件中,配置数据库信息
DATABASES = {
    'default': {
        'ENGINE': 'django.db.backends.mysql',  
        'NAME': 'opcoder',
        'USER': 'root',
        'PASSWORD': 'password',
        'HOST': '127.0.0.1',
        'PORT': 3306,
        'OPTIONS': {'charset': 'utf8mb4', }
    }
}
  • opcoder/__init__.py 文件中,更改默认数据库连接
import pymysql
pymysql.install_as_MySQLdb()

配置静态文件路径

  • opcoder/setting.py 配置文件中,配置静态文件路径
STATICFILES_DIRS = (
    os.path.join(BASE_DIR, 'static'),
)

更改时区

  • opcoder/setting.py 配置文件中,配置时区
LANGUAGE_CODE = 'zh-hans'

TIME_ZONE = 'Asia/Shanghai'

USE_I18N = True

USE_L10N = True

USE_TZ = False      # 关闭国际时间,不然数据库报错

自定义用户模型

  • 安装 图片/文件 处理依赖包
(opcoder) allenlideMacBook-Pro:~ allen$ pip3 install Pillow 

(opcoder) allenlideMacBook-Pro:~ allen$ pip3 install django-imagekit

创建 users 应用

(opcoder) allenlideMacBook-Pro:blog allen$ python3 manage.py startapp users
  • opcoder/setting.py 配置文件中,添加 app
INSTALLED_APPS = [
    ...,
    'imagekit',     
    'users',
]

python-opcoderA02

  • users/models.py 文件中,创建 UserProfile 数据模型
from django.db import models
from django.contrib.auth.models import AbstractUser

from imagekit.models import ProcessedImageField
from imagekit.processors import ResizeToFill

from datetime import datetime


class UserProfile(AbstractUser):
    link = models.URLField('个人网址', blank=True, help_text='提示:网址必须填写以 http 开头的完整形式')
    nick_name = models.CharField(max_length=20, verbose_name='昵称', null=True, blank=True)
    mobile = models.CharField(max_length=11, verbose_name='手机', null=True, blank=True)
    address = models.CharField(max_length=200, verbose_name='地址', null=True, blank=True)
    avatar = ProcessedImageField(
        upload_to='avatar/%Y/%m/%d',
        default='avatar/default.png',
        verbose_name='头像',
        processors=[ResizeToFill(80, 80)]
    )

    class Meta:
        verbose_name = '用户信息'
        verbose_name_plural = verbose_name
        ordering = ['-id']

    def __str__(self):
        return self.username

imagekit 使用说明

  • opcoder/settings.py 文件中为 AUTH_USER_MODEL 设置一个值来重写默认的用户表
INSTALLED_APPS = [
    ...,
    'users',
    'imagekit',
]
AUTH_USER_MODEL = 'users.UserProfile'

配置图片/文件上传

因为数据模型中使用了 ImageField 类型, 所以我们要为文件/图片配置上传目录

  • 在项目根目录下创建 media 目录树并维护默认图片

python-opcoderA20

  • opcoder/settings.py 文件中配置 文件/图片 上传路径
# 用户 文件/图片 上传配置
MEDIA_URL = '/media/'
MEDIA_ROOT = os.path.join(BASE_DIR, 'media')
  • opcoder/settings.py 文件中配置添加以下信息
TEMPLATES = [
    {
        ...,
        'OPTIONS': {
            'context_processors': [
                ...,
                'django.template.context_processors.media',
            ],
        },
    },
]

python-opcoderA04

  • opcoder/urls.py 文件中配置处理上传文件访问的 URL
from django.contrib import admin
from django.conf.urls import url
from django.views.static import serve

from opcoder.settings import MEDIA_ROOT


urlpatterns = [
    url(r'admin/', admin.site.urls),
    url(r'^media/(?P<path>.*)$', serve, {"document_root": MEDIA_ROOT}),
]

生成数据库

  • 执行数据库生成语句
(opcoder) allenlideMacBook-Pro:blog allen$ python3 manage.py makemigrations

(opcoder) allenlideMacBook-Pro:blog allen$ python3 manage.py migrate

opcoder_migrate

  • 查看生成的表结构

python-opcoderA05

创建超级用户

  • 创建超级用户
(opcoder) allenlideMacBook-Pro:blog allen$ python3 manage.py createsuperuser

用户名: admin
电子邮件地址: 
Password: 
Password (again): 
Superuser created successfully.
  • 使用超级用户登录后台管理页面
(opcoder) allenlideMacBook-Pro:blog allen$ python manage.py runserver

python-opcoderA06

注册 admin

为了让 admin 界面管理某个数据模型,我们需要先注册该数据模型到 admin。

普通注册

  • 查看原始页面

python-opcoderA11

  • users/admin.py 文件中添加如下信息
from django.contrib import admin

from .models import UserProfile

class UserProfileAdmin(admin.ModelAdmin):
    list_display = ('username', 'email', 'is_staff', 'is_active', 'date_joined')
    list_filter = ('is_staff', 'is_superuser', 'is_active', 'groups')

admin.site.register(UserProfile, UserProfileAdmin)
  • 查看注册数据模型后的页面

python-opcoderA12

装饰器注册

  • users/admin.py 文件中添加如下信息
from django.contrib import admin

from .models import UserProfile


@admin.register(UserProfile)
class UserProfileAdmin(admin.ModelAdmin):
    # 显示字段
    list_display = ('username', 'email', 'is_staff', 'is_active', 'date_joined')
    # 过滤字段
    list_filter = ('is_staff', 'is_superuser', 'is_active', 'groups')
    fieldsets = (
        ('基础信息', {'fields': (('username', 'password'), ('email', 'mobile'),)}),
        ('权限信息', {'fields': (('is_active', 'is_staff', 'is_superuser'), 'groups', 'user_permissions')}),
        ('重要日期', {'fields': (('last_login', 'date_joined'),)}),
    )
    # 搜索字段
    search_fields = ('username', 'email')

python-opcoderA13

Django 后台管理

   Django 自带的后台管理是 Django 明显的特色之一,可以让我们快速便捷的管理数据,后台管理可以在各个 app 的 admin.py 文件中进行控制。

修改登录界面名称

  • 先找到 admin 的页面文件
(opcoder) allenlideMacBook-Pro:admin allen$ pwd
/Users/allen/.VirtualEnvs/opcoder/lib/python3.6/site-packages/django/contrib/admin/templates/admin
  • 初始登录界面

python-opcoderA07

  • 找到 base_site.html 来修改名称

python-opcoderA08

  • 修改名称

python-opcoderA09

  • 重启服务并检查修改是否成功

python-opcoderA10

修改应用名称

  • users/apps.py 文件中添加如下信息
from django.apps import AppConfig


class UsersConfig(AppConfig): 
       name = 'users' 
       verbose_name = "用户信息" 
  • users/__init__.py 文件中添加如下信息
default_app_config = 'users.apps.UsersConfig'
  • 查看应用的显示名称,已经由 USERS 改为了 用户信息

python-opcoderA14

明文密码转密文

   使用自定义用户模型后,在 Django 后台会使用明文密码,通过定制认证功能,可以实现密码的密文存储。

  • 在后台页面新增一个用户,我们可以看到其密码是明文存储的

python-opcoder15

  • users/admin.py 修改为如下内容
from django.contrib import admin
from django import forms
from django.contrib.auth.admin import UserAdmin as BaseUserAdmin
from django.contrib.auth.forms import ReadOnlyPasswordHashField

from .models import UserProfile


class UserCreationForm(forms.ModelForm):
    password1 = forms.CharField(label='密码', widget=forms.PasswordInput)
    password2 = forms.CharField(label='密码确认', widget=forms.PasswordInput)

    class Meta:
        model = UserProfile
        fields = ('username', 'email', )

    def clean_password2(self):
        password1 = self.cleaned_data.get("password1")
        password2 = self.cleaned_data.get("password2")
        if password1 and password2 and password1 != password2:
            raise forms.ValidationError("Passwords don't match")
        return password2

    def save(self, commit=True):
        user = super().save(commit=False)
        user.set_password(self.cleaned_data["password1"])
        if commit:
            user.save()
        return user


class UserChangeForm(forms.ModelForm):
    password = ReadOnlyPasswordHashField()

    class Meta:
        model = UserProfile
        fields = ('username', 'email', 'password', 'is_active')

    def clean_password(self):
        return self.initial["password"]


class UserAdmin(BaseUserAdmin):
    form = UserChangeForm
    add_form = UserCreationForm

    list_display = ('username', 'email', 'is_staff', 'is_active', 'date_joined')
    list_filter = ('is_staff', 'is_superuser', 'is_active', 'groups')
    fieldsets = (
        ('基础信息', {'fields': (('username', 'password'), ('email', 'mobile'), )}),
        ('权限信息', {'fields': (('is_active', 'is_staff', 'is_superuser'), 'groups', 'user_permissions')}),
        ('重要日期', {'fields': (('last_login', 'date_joined'), )}),
    )
    add_fieldsets = (
        (None, {
            'classes': ('wide',),
            'fields': (('username', 'email'), ('password1', 'password2'), )}
        ),
    )
    search_fields = ('username', 'email')
    ordering = ('username', 'email',)
    filter_horizontal = ()


class UserProfileAdmin(admin.ModelAdmin):
    list_display = ('username', 'email', 'is_staff', 'is_active', 'date_joined')
    list_filter = ('is_staff', 'is_superuser', 'is_active', 'groups')
    fieldsets = (
        ('基础信息', {'fields': (('username', 'password'), ('email', 'mobile'), )}),
        ('权限信息', {'fields': (('is_active', 'is_staff', 'is_superuser'), 'groups', 'user_permissions')}),
        ('重要日期', {'fields': (('last_login', 'date_joined'), )}),
    )
    search_fields = ('username', 'email')

admin.site.register(UserProfile, UserAdmin)

python-opcoderA16

美化后台管理页面

  • 安装依赖包
(opcoder) allenlideMacBook-Pro:blog allen$ pip3 install bootstrap-admin
  • 添加至 opcoder/settings.py 文件的 INSTALLED_APPS 中
INSTALLED_APPS = [
    'bootstrap_admin',   # 这个必须放在所有app的前面
    ...,
]    

python-opcoder17

  如果在安装 bootstrap-admin 后,后台页面显示有问题,可以使用 Ctrl+F5 强制刷新一下而在 MAC 下需使用 command+shift+r 强制刷新。

  • 查看美化后的后台管理页面

python-opcoder18

原创文章,转载请注明出处:http://www.opcoder.cn/article/30/