需求分析:
实现网站搭建的过程:首先进行网站的需求性分析
网站可分为前台系统和后台系统,由不同的功能拆分为不同的模块
如下是一个电商网站可以拆分出的模块:
在编写代码前,我们要先对网站进行架构,通过分层设计网站的不同组件,软件开发人员可以选择修改和添加应用层,而非全局作出更改
如下是经典的三层结构:
接入层:提供静态内容的web前端服务器和部分动态缓存内容。
逻辑层:生成动态内容的应用服务器。
存储层:提供数据存储的服务器
TIPS:
为什么之前在用Django的时候直接runserver就好了呢?因为Django的开发服务器(runserver)是一个轻量级服务器,能够处理HTTP请求和提供静态资源服务,但主要应用于调试阶段,由于单线程且没有负载均衡SSL加密等功能,在生产环境中我们需要专业的接入层,像是Nginx
模块引入:
为了确保功能的实现,我们可以引入所需的功能模块
其中,installed_app这个设置告诉用户在该项目中安装了哪些程序,可以是第三方或自定义的,也可以是django自带的
middleware这个设置定义了处理请求的中间件和响应件,中间件是处理请求和响应的钩子,可用于请求到达视图/响应返回客户段前执行特定的操作。
在构建网站的过程中,我们要把不同需求拆解为功能模块的同时为其添加数据库表结构
首先,我们来进行用户模块的构建:
INSTALLED_APPS = [
# 其他已安装的应用...
'django.contrib.auth', # 用户验证框架和模型
'django.contrib.contenttypes', # 权限管理
]
MIDDLEWARE = [
# 其他中间件...
'django.contrib.sessions.middleware.SessionMiddleware', # 会话管理系统
'django.contrib.auth.middleware.AuthenticationMiddleware', # 用户验证模型
]
这是利用了Django自带的用户模块,配置当中的.就是/的意思,暗含代码路径在django/contrib/auth/moddles.py,模型里面包含这些内容
让我们先来了解一下用户模块:
django.contrib.auth 模块为我们提供了用户认证系统的核心功能,包括用户模型、表单、视图、URL 配置等,这些功能已经预先定义好,可以直接使用或根据需要进行扩展。它是一个完整的用户认证框架,旨在帮助开发者快速实现用户注册、登录、权限管理等功能,而无需从头开始编写这些代码。
Django 自带用户认证模块提供的功能
以下是 Django 自带用户认证模块(django.contrib.auth)为你预先定义好的内容:
----
1. 用户模型(User)
Django 提供了一个默认的用户模型 User,定义在 django.contrib.auth.models 中。它包含以下字段:
• username:用户名
• password:密码(加密存储)
• email:邮箱
• first_name 和 last_name:用户姓名
• is_active:是否激活
• is_staff:是否是管理员
• is_superuser:是否是超级用户
• groups 和 user_permissions:用户组和权限
你可以直接使用这个默认的用户模型,或者通过继承 AbstractUser 或 AbstractBaseUser 来扩展它。
----
2. 表单(forms)
Django 提供了一些内置的表单类,用于用户注册、登录、密码修改等功能:
• UserCreationForm:用户注册表单
• AuthenticationForm:用户登录表单
• PasswordChangeForm:密码修改表单
• SetPasswordForm:设置密码表单
• PasswordResetForm:密码重置表单
这些表单类已经预定义了字段和验证逻辑,你可以直接使用,也可以通过继承它们来自定义字段或验证规则。
----
3. 视图(views)
Django 提供了一些内置的视图函数,用于处理用户认证相关的操作:
• login:用户登录
• logout:用户登出
• password_change:密码修改
• password_reset:密码重置
• password_reset_done:密码重置完成
• password_reset_confirm:密码重置确认
• password_reset_complete:密码重置完成提示
这些视图函数可以直接在 URL 配置中使用,或者通过继承 View 类来自定义视图逻辑。
----
4. URL 配置(urls)
Django 提供了一个 auth 应用的 URL 配置模块 django.contrib.auth.urls,它包含了用户认证相关的 URL 路由,例如:
from django.contrib.auth import views as auth_views
urlpatterns = [
path('login/', auth_views.LoginView.as_view(), name='login'),
path('logout/', auth_views.LogoutView.as_view(), name='logout'),
path('password_change/', auth_views.PasswordChangeView.as_view(), name='password_change'),
path('password_change/done/', auth_views.PasswordChangeDoneView.as_view(), name='password_change_done'),
path('password_reset/', auth_views.PasswordResetView.as_view(), name='password_reset'),
path('password_reset/done/', auth_views.PasswordResetDoneView.as_view(), name='password_reset_done'),
path('reset/<uidb64>/<token>/', auth_views.PasswordResetConfirmView.as_view(), name='password_reset_confirm'),
path('reset/done/', auth_views.PasswordResetCompleteView.as_view(), name='password_reset_complete'),
]
你可以直接在项目的 urls.py 文件中包含这些 URL 路由,或者根据需要自定义 URL 路径和视图参数。
----
5. 中间件和会话管理
Django 的认证系统还提供了中间件(如 AuthenticationMiddleware)和会话管理功能,用于在请求中自动处理用户认证信息,并将用户对象绑定到 request.user。
----
扩展:
在很多时候我们都需要对Django自带的模型进行相应的拓展
关联表:
这个时候可以使用关联表
用如下的onetoonefield方法创建:
from django.db import models
from django.contrib.auth.models import User
class Profile(models.Model):
GENDER_CHOICES = (
('M', 'Male'), # 男性
('F', 'Female'), # 女性
)
user = models.OneToOneField(User, on_delete=models.CASCADE)
gender = models.CharField(max_length=1, choices=GENDER_CHOICES) # 性别
birth_date = models.DateField(null=True, blank=True) # 出生日期
模型字段解释
1. GENDER_CHOICES:
• 这是一个元组列表,用于定义性别字段的选项。每个元组包含两个元素:第一个是存储在数据库中的值,第二个是显示给用户的值。
2. user:
• 这是一个 OneToOneField 字段,它建立了 Profile 模型和 User 模型之间的一对一关系。
• User 是 Django 内置的用户模型,通常包含用户名、密码、电子邮件等基本信息。
• on_delete=models.CASCADE 参数指定了当关联的 User 实例被删除时,相关的 Profile 实例也会被级联删除。
3. gender:
• 这是一个 CharField 字段,用于存储用户的性别。
• max_length=1 指定了性别字段的最大长度为1个字符。
• choices=GENDER_CHOICES 参数将性别字段的值限制为在 GENDER_CHOICES 中定义的选项。
4. birth_date:
• 这是一个 DateField 字段,用于存储用户的出生日期。
• null=True 允许该字段在数据库中存储为 NULL,这意味着它可以为空。
• blank=True 允许在 Django 管理后台或表单中不填写该字段。
关联表的使用
在 Django 中,当你使用 OneToOneField 时,Django 会在数据库中创建一个新的表来存储 Profile 模型的数据。这个表会包含一个额外的字段(通常是 user_id),这个字段是一个外键,指向 User 表的主键。
例如,如果你的 User 表的主键是 id,那么 Profile 表可能会包含以下字段:
• id:Profile 表的主键。
• user_id:外键,指向 User 表的 id 字段。
• gender:存储性别的字段。
• birth_date:存储出生日期的字段。
这种设计允许每个 User 实例都有一个对应的 Profile 实例,而每个 Profile 实例只能属于一个 User 实例。这种一对一的关系在数据库中通过外键约束来实现,确保了数据的一致性和完整性。
总结
这段代码通过 OneToOneField 创建了一个 Profile 模型,它与 User 模型建立了一对一的关系。这种关系通过在 Profile 表中添加一个外键字段来实现,该字段指向 User 表的主键。这种设计使得每个用户都有一个相关的个人资料,而每个个人资料只属于一个用户。
信号定义:
在创建用户过程中多次会用到创建用户的代码,为了避免重复创建,我们将定义一个信号,在创建用户时自动创建档案:
from django.apps import AppConfig
class MyAppConfig(AppConfig):
name = 'myapp'
def ready(self):
import myapp.signals
该方法使用要在myapp下创建一个signals.py文件并且在app.py文件当中重写ready方法来连接信号,以及更新installed_apps
INSTALLED_APPS = [
...
'myapp.apps.MyAppConfig',
...
]
TIPS:当然,signals的内容也可以直接写在models里面
然后我们在更改完models之后再去更改视图和模版文件
模板:
<h2>{{ user.get_full_name }}</h2>
<ul>
<li>Username: {{ user.username }}</li> <!-- 显示用户名 -->
<li>Location: {{ user.profile.gender }}</li> <!-- 显示性别 -->
<li>Birth Date: {{ user.profile.birth_date }}</li> <!-- 显示出生日期 -->
</ul>
视图:
def update_profile(request, user_id):
user = User.objects.get(pk=user_id) # 获取用户对象
user.profile.gender = 'M' # 设置性别
user.save() # 保存
视图函数当中反应的是对于用户档案的更新:在更新用户档案时,浏览器会先上传一个表单,然后由服务器来处理这个表单,处理的方法就是视图函数当中的先获取数据对象,调用先前定义的模型进行修改,然后再调用save方法进行更新
注意!!:这里储存的是模型的字段,因此不能直接储存到数据库,我们需要使用forms.ModelForm类创建表单
from django import forms
class UserForm(forms.ModelForm):
class Meta:
model = User # 关联User模型
fields = ('first_name', 'last_name', 'email') # 表单字段
class ProfileForm(forms.ModelForm):
class Meta:
model = Profile # 关联Profile模型
fields = ('url', 'gender', 'birth_date') # 表单字段
然后再编写对应用户登陆的视图
from django.contrib import messages
from django.shortcuts import redirect, render
from django.db import transaction
from .forms import UserForm, ProfileForm
@login_required # 验证登录
@transaction.atomic
def update_profile(request):
if request.method == 'POST': # POST请求
user_form = UserForm(request.POST, instance=request.user) # 获取用户表单信息
profile_form = ProfileForm(request.POST, instance=request.user.profile) # 获取表单信息
if user_form.is_valid() and profile_form.is_valid(): # 验证通过则保存信息
user_form.save()
profile_form.save()
messages.success(request, _('Your profile was successfully updated!'))
return redirect('settings:profile')
else: # 验证不通过则显示错误信息
messages.error(request, _('Please correct the error below.'))
else: # 其他请求,一般是GET请求
user_form = UserForm(instance=request.user)
profile_form = ProfileForm(instance=request.user.profile)
# 返回表单页面
return render(request, 'profiles/profile.html', {
'user_form': user_form,
'profile_form': profile_form
})
如上代码当中,用户如果用的是post方法,则获取表单信息与之对照,如果是get方式则返回表单页面,并将内容填入表单,(对应的,我们需要更改其模版文件)
<form method="post">
{% csrf_token %}
{{ user_form.as_p }} <!-- 渲染用户表单 -->
{{ profile_form.as_p }} <!-- 渲染Profile表单 -->
<button type="submit">Save changes</button> <!-- 提交按钮 -->
</form>
继承:
如果我们的用户系统完全不需要某一字段,也可以使用继承的方式(AbstractBaseUser)
from __future__ import unicode_literals
from django.db import models
from django.core.mail import send_mail
from django.contrib.auth.models import PermissionsMixin
from django.contrib.auth.base_user import AbstractBaseUser
from django.utils.translation import ugettext_lazy as _
from .managers import UserManager
class User(AbstractBaseUser, PermissionsMixin):
GENDER_CHOICES = (
('M', 'Male'), # 男性
('F', 'Female'), # 女性
)
gender = models.CharField(max_length=1, choices=GENDER_CHOICES) # 性别
birth_date = models.DateField(null=True, blank=True) # 出生年月
email = models.EmailField(_('email address'), unique=True) # 电子邮件
first_name = models.CharField(_('first name'), max_length=30, blank=True)
last_name = models.CharField(_('last name'), max_length=30, blank=True)
date_joined = models.DateTimeField(_('date joined'), auto_now_add=True) # 注册时间
is_active = models.BooleanField(_('active'), default=True) # 是否活跃
avatar = models.ImageField(upload_to='avatars/', null=True, blank=True) # 用户头像
objects = UserManager()
USERNAME_FIELD = 'email'
REQUIRED_FIELDS = []
class Meta:
verbose_name = _('user')
verbose_name_plural = _('users')
def get_full_name(self): # 获取用户全名
full_name = '%s %s' % (self.first_name, self.last_name)
return full_name.strip()
def get_short_name(self):
return self.first_name
def email_user(self, subject, message, from_email=None, **kwargs): # 向用户发送电子邮件
send_mail(subject, message, from_email, [self.email], **kwargs)
在使用继承AbstractBaseUser时,有很多相关的限制(建议直接重新自己写)
如果要添加某一些字段,可以采用继承AbstractUser方法,运营此法可对于原有的类进行更改
from django.db import models
from django.contrib.auth.models import AbstractUser
class User(AbstractUser):
GENDER_CHOICES = (
('M', 'Male'), # 男性
('F', 'Female'), # 女性
)
gender = models.CharField(max_length=1, choices=GENDER_CHOICES) # 性别
birth_date = models.DateField(null=True, blank=True) # 出生年月
模块设计的大概步骤:
然后我们再来进行商品库模块的设计:
首先我们设计了类别模型、商品模型以及类别和商品的多对多的模型
from django.db import models
class Category(models.Model):
name = models.CharField('Name', max_length=255, db_index=True) # 类别名称
description = models.TextField('Description', blank=True) # 类别描述
products = models.ManyToManyField('Product') # 多对多关系
class Product(models.Model):
title = models.CharField('Title') # 商品名称
description = models.TextField('Description', blank=True) # 商品描述
attributes = models.TextField('Attribute', blank=True) # 商品附属信息
date_created = models.DateTimeField() # 商品创建时间
在上述代码中,可以把manytomany理解为一个声明,表示两个表要建联,而ProductCategory则可以视作声明的具体内容
from django.http import HttpResponse
from .models import Product, Category
def get_product_detail(request, product_id): # 获取商品详情
return Product.objects.get(pk=product_id)
def get_all_products(request, category_id): # 通过类别获取商品列表
return Category.objects.get(pk=category_id).products.all()
定义相应的视图函数(根据搜索返回商品的过程,实际搜索当中要比这复杂的多因为用户的描述和实际商品的名称并非完全相同,此时也要给出最合理的结果)
{% for product in productions %}
<p>{{ product.title }}</p> <!-- 商品的名称 -->
<p>{{ product.description }}</p> <!-- 商品的描述 -->
{% endfor %}
并给出相应的模版文件(这里用了for标签来渲染列表)
总结:
总之,网站的制作就是先进行需求分析,根据需求分析得到各个功能模块,然后对每一个模块进行编写,编写的顺序为:模型文件再到视图函数及其对应的模版文件,urls.py和settings.py的两个视图函数的编写可以放在这一步之前也可以是之后,但是为了方便,编写settings.py之前我们最好还是要明确自己要用到的所有models和middleware