01-新建一个名为"mall_backend"的Project
命令如下:
bash
CD E:\Python_project\P_001\myshop-test
E:
django-admin startproject mall_backend
02-新建应用并注册应用
执行下面条命令依次创建需要的应用:
bash
CD E:\Python_project\P_001\myshop-test\mall_backend\
E:
python manage.py startapp users
名叫users的应用创建好后,还需要在全局配置文件中对应在用进行注册,具体方法如下:
打开""E:\Python_project\P_001\myshop-test\mall_backend\mall_backend\settings.py""文件,找到名叫"INSTALLED_APPS"的列表(list),在其中加入应用名,如下:
python
INSTALLED_APPS = [
'django.contrib.admin',
'django.contrib.auth',
'django.contrib.contenttypes',
'django.contrib.sessions',
'django.contrib.messages',
'django.contrib.staticfiles',
'users', # 这是自己创建的应用,需要手动注册
]
03-实现"用户注册"模块
"用户注册"模块使用如下技术来开发。
使用Form表单方式进行表单数据的验证。
通过继承AbstractUser类来简化用户权限认证。
03-01-创建数据库模型类
打开文件 E:\Python_project\P_001\myshop-test\mall_backend\users\models.py 写入下面的代码:
python
from django.db import models
from datetime import datetime
from django.contrib.auth.models import AbstractUser, Group, Permission
class MyUser(AbstractUser):
SEX = (
(0, '男'),
(1, '女'),
)
LEVEL = (
(1, '寂寞卡会员'),
(2, '钻石卡会员'),
(3, '金卡会员'),
(4, '银卡会员'),
)
STATUS = (
(0, '正常'),
(1, '异常'),
)
groups = models.ManyToManyField(
Group,
verbose_name='groups',
blank=True,
help_text='The groups this user belongs to.',
related_name='user_groups' # 设置不同的 related_name
)
user_permissions = models.ManyToManyField(
Permission,
verbose_name='user permissions',
blank=True,
help_text='Specific permissions for this user.',
related_name='user_permissions' # 设置不同的 related_name
)
nickname = models.CharField('qq', blank=True, max_length=50)
truename = models.CharField('真实姓名', blank=True, max_length=50)
mobile = models.CharField('手机号码', max_length=11, default="")
sex = models.IntegerField(default=0, choices=SEX)
birthday = models.DateField(blank=True, null=True)
user_img = models.ImageField("头像", upload_to="user_img", default="")
level = models.IntegerField(default=4, choices=LEVEL)
status = models.IntegerField(default=0, choices=STATUS)
create_time = models.DateTimeField(default=datetime.now, verbose_name='创建时间')
update_time = models.DateTimeField(default=datetime.now, verbose_name="更新时间")
def __str__(self):
return self.username
class Meta(AbstractUser.Meta):
permissions = (
['check_myuser', '审核用户信息'],
)
问:在整个项目中,还用到了字段password、is_staff、is_superuser,那这些字段为什么不定义呢?原因是在基类AbstractUser中已经定义了。详情见:https://blog.csdn.net/wenhao_ir/article/details/131594115
03-02-创建表单类及表单类代码详解
在目录 E:\Python_project\P_001\myshop-test\mall_backend\users 下新建文件 forms.py
然后写入下面的代码A:
python
from users.models import *
from django import forms
from django.core.exceptions import ValidationError
import re
def mobile_validate(value):
mobile_re = re.compile(
r'^(13[0-9]|15[012356789]|17[678]|18[0-9]|14[57])[0-9]{8}$')
if not mobile_re.match(value):
raise ValidationError('手机号码格式错误')
class UserRegForm(forms.Form):
username = forms.CharField(label="用户名", min_length=6,
widget=forms.widgets.TextInput(
# 其中class样式为form-control,这是bootstrap的样式
attrs={'class': 'form-control', 'placeholder': "请输入用户名"}),
error_messages={
'required': '用户姓名不能为空',
'min_length': '长度最少6位',
})
password = forms.CharField(label="密码", min_length=6, max_length=10,
widget=forms.widgets.PasswordInput(
# render_value=True,页面校验不通过后,页面上该值还存在
attrs={"class": "form-control"}, render_value=True),
error_messages={
'max_length': '密码最长10位',
'required': '密码不能为空',
'min_length': '密码最少6位'
})
re_password = forms.CharField(label="确认密码", min_length=6, max_length=10,
widget=forms.widgets.PasswordInput(
# render_value=True,页面校验不通过后,页面上该值还存在
attrs={"class": "form-control"}, render_value=True),
error_messages={
'max_length': '密码最长10位',
'required': '密码不能为空',
'min_length': '密码最少6位'
})
nickname = forms.CharField(label="昵称", max_length=20, required=False,
widget=forms.widgets.TextInput(
# 其中class样式为form-control,这是bootstrap的样式
attrs={'class': 'form-control', 'placeholder': "请输入用户昵称"}),
error_messages={
'required': '用户昵称不能为空',
'max_length': '昵称长度不能超过20位',
})
email = forms.EmailField(label="邮箱",
widget=forms.widgets.EmailInput(
attrs={'class': 'form-control', }),
error_messages={
'required': '邮箱不能为空',
'invalid': '邮箱格式不对',
})
mobile = forms.CharField(label="手机号码", validators=[mobile_validate],
widget=forms.widgets.TextInput(
attrs={'class': 'form-control', }),
error_messages={
'required': '手机号码不能为空',
})
user_img = forms.ImageField(label="用户头像", required=False, widget=forms.widgets.FileInput(
attrs={'class': 'form-control'}))
# 全局钩子函数
def clean(self):
password = self.cleaned_data.get("password")
re_password = self.cleaned_data.get("re_password")
print(password)
if password != re_password:
# raise forms.ValidationError("二次密码输入不一致")
self.add_error("re_password", ValidationError("二次密码输入不一致"))
①类ValidationError有什么作用?
答:在Django中,ValidationError
类是一个异常类,它属于django.core.exceptions
模块。当数据验证失败时,可以通过引发ValidationError
异常来表示验证错误。
②-①re.compile()方法有什么用?
请参考博文:https://blog.csdn.net/wenhao_ir/article/details/132026895
②-2 有没有关于正则表达式 r'^(13[0-9]|15[012356789]|17[678]|18[0-9]|14[57])[0-9]{8}$'
的详细理解?
请参考博文 https://blog.csdn.net/wenhao_ir/article/details/132027668 的第4点、第5点和第6点。
③能不能介绍下Django中的表单(forms)模块中的类forms.CharField?
请参考博文:https://blog.csdn.net/wenhao_ir/article/details/132029002
④能不能介绍下什么是Django表单中的全局钩子函数?
在Django表单中,全局钩子函数(Global Form Validation Hook)是指在表单类中定义的特殊方法,它允许开发者对表单中的多个字段进行综合性的验证和处理。全局钩子函数会在表单的所有字段个别验证完成后被调用,用于执行那些需要考虑多个字段相互关系的验证逻辑或处理逻辑。
通常情况下,Django表单中的字段验证是在各个字段的 clean_<field_name>
方法中进行的。例如,对于一个字段名为 username
的表单字段,其验证逻辑会放在 clean_username()
方法中。这样的验证方法称为字段级别的验证。
但是,有些情况下需要对多个字段的数据进行联合验证或验证它们之间的关系。这时就可以使用全局钩子函数来进行全局性的表单验证。全局钩子函数的名称是 clean
,它没有特定的字段名称前缀,而是直接在表单类中定义。
全局钩子函数的工作方式如下:
-
先执行各个字段的单独验证方法(如
clean_username()
,clean_password()
等)来验证各个字段的合法性。 -
在所有字段单独验证通过后,Django会自动调用全局钩子函数
clean()
(如果表单类中有定义)。 -
在
clean()
方法中,您可以对多个字段的数据进行综合验证和处理。如果需要在多个字段之间执行联合验证,这里是合适的位置。 -
如果全局钩子函数
clean()
验证失败(即调用self.add_error()
方法添加了错误信息),则表单将被认为是无效的,验证失败的错误信息将在表单的非特定字段下方显示。
总结:全局钩子函数是在Django表单中定义的用于对多个字段进行综合性验证和处理的特殊方法。它在所有字段个别验证通过后被自动调用,允许开发者执行联合验证或处理多个字段之间的关系。
⑤能不能介绍下代码password = self.cleaned_data.get("password")
?
当用户提交表单后,self.cleaned_data
是一个字典,其中包含经过表单字段验证后的干净数据。每个表单字段的值,如果有效,都会存储在这个字典中,以其相应的字段名为键。
在代码中的这一行 password = self.cleaned_data.get("password")
,self.cleaned_data.get("password")
用于从字典中获取经过验证的 "password" 字段的干净值。
下面是对这一行代码的详细解释:
-
self
:在 Django 表单的上下文中,self
是表单实例自身的引用。它是封装表单数据和逻辑的对象。 -
cleaned_data
:cleaned_data
是表单实例(self
)的属性,它保存用户提交的经过验证和清理后的数据。这个字典中的数据根据表单类中定义的字段类型和验证器进行了清理和格式化。 -
.get("password")
:Python 字典的get()
方法用于根据给定的键获取与之关联的值。在这里,键是 "password",对应着 "password" 表单字段的名字。
self.cleaned_data.get("password")
这一行代码从表单的 cleaned_data
字典中获取 "password" 字段的干净值。如果表单有效且 "password" 字段存在并且通过了验证,它会返回 "password" 字段的干净值。如果 "password" 字段无效或未包含在提交的数据中,它将返回 None
。
通过使用这种方法来访问清理后的数据,表单的作者可以安全地获取用户的输入,无需担心潜在的验证错误或缺失的字段。这是 Django 表单中常用的访问经过验证数据的方式。
⑥能不能介绍下代码 self.add_error("re_password", ValidationError("二次密码输入不一致"))
?
当表单数据在全局钩子函数 clean
中进行综合校验时,如果发现特定的校验规则未通过,开发者可以通过 self.add_error(field, error)
方法来向表单字段添加错误信息。
在代码A中的这一行 self.add_error("re_password", ValidationError("二次密码输入不一致"))
,self.add_error()
方法用于向表单字段添加错误信息,以告知用户二次密码输入不一致。
下面是对这行代码的详细解释:
-
self
:在 Django 表单的上下文中,self
是表单实例自身的引用。它是封装表单数据和逻辑的对象。 -
add_error()
:add_error()
是表单实例的方法,用于向表单字段添加错误信息。它接收两个参数:- 第一个参数是表单字段的名称(field),这里是 "re_password",即确认密码字段的名称。
- 第二个参数是一个 ValidationError 对象,它用于指定具体的错误信息。
-
"re_password":这是表单字段的名称,即确认密码字段的名称。表单字段的名称是在定义表单类时给定的。
-
ValidationError("二次密码输入不一致")
:这是一个 ValidationError 对象,用于指定错误信息。在这里,它表示 "二次密码输入不一致" 是校验失败的原因。
在全局钩子函数 clean
中,当表单数据不满足某些特定的条件时(例如,密码和确认密码不一致),开发者可以使用 self.add_error()
方法来添加错误信息,然后这些错误信息将在渲染表单时显示给用户,帮助用户找到并纠正表单中的错误。
问:为什么add_error()要有第一个参数,即表单字段的名称呢?我感觉没有用呀。
您提出的疑问很合理。实际上,在全局钩子函数 clean
中使用 self.add_error()
方法时,第一个参数,即表单字段的名称,是非常有用的。它有两个重要的作用:
-
指定出现错误的字段: 在一个表单中,可能有多个字段需要进行全局校验,而不仅仅是一个确认密码字段。通过指定字段的名称作为第一个参数,
self.add_error()
方法可以告诉 Django 哪个字段出现了错误,从而确保错误信息被添加到正确的字段中。 -
显示错误信息: 表单字段的名称不仅指定了出现错误的字段,还在渲染表单时用于显示错误信息。当
self.add_error()
方法被调用后,错误信息将与相应字段一起传递给前端页面。这样,用户在查看表单时可以清楚地看到哪些字段存在错误以及错误的具体信息。
例如,在代码A中,self.add_error("re_password", ValidationError("二次密码输入不一致"))
会向 "re_password" 字段添加一个 "二次密码输入不一致" 的错误信息。如果用户在确认密码时输入的密码与之前输入的密码不一致,就会触发这个错误,并在确认密码字段下方显示 "二次密码输入不一致" 的错误提示,帮助用户及时纠正错误。
总结:表单字段的名称作为 self.add_error()
方法的第一个参数是为了将错误信息与相应字段关联起来,并在前端页面上正确显示错误信息,提高用户体验。
04-创建视图函数并编写视图函数
04-1-视图函数views.py里的代码
打开文件 E:\Python_project\P_001\myshop-test\mall_backend\users\views.py 写入下面的代码B:
python
from django.shortcuts import render
from users.models import MyUser
from users.forms import *
# Create your views here.
def user_reg(request):
if request.method == "GET":
form_obj = UserRegForm()
return render(request, 'shop/user_reg.html', {"form_obj": form_obj})
if request.method == "POST":
form_obj = UserRegForm(request.POST, request.FILES)
if form_obj.is_valid():
uname = request.POST.get("username", '')
users = MyUser.objects.filter(username=uname)
if users:
for user in users:
user_img = user.user_img
info = '用户已经存在'
else:
form_obj.cleaned_data.pop("re_password")
form_obj.cleaned_data["is_staff"] = 1
form_obj.cleaned_data["is_superuser"] = 0 # 非管理员
# 接收页面传递过来的参数,进行用户新增
user = MyUser.objects.create_user(**form_obj.cleaned_data)
user_img = user.user_img
info = '注册成功,请登陆'
return render(request, 'shop/user_reg.html', {"form_obj": form_obj, "info": info, "user_img": user_img})
else:
errors = form_obj.errors
print(errors)
return render(request, "shop/user_reg.html", {'form_obj': form_obj, 'errors': errors})
return render(request, 'shop/user_reg.html', {"form_obj": form_obj})
04-2-视图函数views.py代码详解
①注意:user_reg()并不是一个类,而是一个函数。
这个函数的参数为request,参数request代表了客户端发起的请求。它包含了关于请求的各种信息,例如用户提交的表单数据、HTTP方法等。
②语句 form_obj = forms.UserRegForm() 是什么意思?
注意:UserRegForm是一个类,这个类是在自己前面新建的文件 "E:\Python_project\P_001\myshop-test\mall_backend\users\forms.py" 中自定义的类,这个类继承自Django的forms.Form类。
所以这句代码是创建类UserRegForm的实例化对象form_obj 。
③详细解释下语句: return render(request, 'shop/user_reg.html', {"form_obj": form_obj}。
答:当你调用render
函数时,它用于将一个模板渲染成HTML响应并返回给客户端。这个函数的第一个参数是request
对象,第二个参数是模板的路径,第三个参数是一个字典,用于向模板传递上下文变量。在这里,第三个参数不太好理解,详情见我的另一篇博文,链接:https://blog.csdn.net/wenhao_ir/article/details/132501337
在代码中,render
函数的目的是渲染名为 'shop/user_reg.html'
的模板,同时传递一个名为 form_obj
的变量给模板。让我详细解释一下这段代码:
-
request
: 这是视图函数的第一个参数,代表了客户端发起的请求。它包含了关于请求的各种信息,例如用户提交的表单数据、HTTP方法等。 -
'shop/user_reg.html'
: 这是模板的路径,告诉Django要使用哪个模板来生成HTML响应。 -
{"form_obj": form_obj}
: 这是一个字典,用于传递变量给模板。在这里,form_obj
是一个表单实例,通过这个字典将这个表单实例传递给了模板。在模板中,你可以通过键名form_obj
来访问这个表单实例,以便在模板中渲染表单的各个字段、错误信息等。
总之,render
函数的第三个参数是用于向模板传递上下文变量的,让模板能够使用这些变量来动态生成HTML内容。在这里,你传递了一个名为 form_obj
的变量,这个变量在模板中可以被用来渲染注册表单的各个部分和数据。
④能否详细解释下代码B中的代码:
form_obj = forms.UserRegForm(request.POST, request.FILES)
咱们把03-02中的表单类的代码记为代码A,把视图函数里的代码记为代码B。
当你在Django中使用表单时,你需要在视图函数中处理用户提交的数据。这个数据通常包含在HTTP请求中的POST数据中,可能包含用户在表单中输入的文本、上传的文件等。为了处理这些数据,你需要实例化一个表单对象,并将这些数据传递给它进行验证和处理。
在你提到的代码中,form_obj = forms.UserRegForm(request.POST, request.FILES)
这一行代码完成了以下操作:
-
forms.UserRegForm
: 这是代码A中定义的注册表单的类。通过调用这个类,你可以创建一个用于处理用户提交数据的表单对象。 -
request.POST
: 这是一个包含用户在表单中提交的文本数据的字典。当用户通过POST方法提交表单时,表单中的文本字段的数据会包含在这个字典中。这个参数将这些POST数据传递给表单对象。 -
request.FILES
: 这是一个包含用户在表单中上传的文件的字典。当用户通过POST方法提交包含文件上传的表单时,上传的文件数据会包含在这个字典中。这个参数将这些文件数据传递给表单对象。
request.FILES
中包含的是用户通过表单上传的文件的数据,但这些文件并没有直接保存在这个字典中。实际上,这些上传的文件数据会被保存在服务器的临时文件夹中,然后在需要的时候通过文件句柄进行访问和处理。当用户通过表单上传文件时,这些文件数据会被存储在服务器上的一个临时目录中,通常位于 Django 项目的根目录下的
media
文件夹或者根据你在 Django 设置中配置的MEDIA_ROOT
设置的路径。临时文件会在请求处理完成后自动被清理掉。在你的代码中,通过
request.FILES
参数传递给表单对象后,你可以在表单类中的对应字段中访问这些文件数据。比如,在你的UserRegForm
类中,user_img
字段是一个forms.ImageField
类型的字段,它会处理用户上传的头像文件数据。总之,
request.FILES
中包含的是上传文件的数据的引用,而文件本身会被存储在服务器的临时目录中,可以在表单处理过程中进行访问和处理,然后根据需要进行持久化保存。
所以,form_obj = forms.UserRegForm(request.POST, request.FILES)
这行代码创建了一个 UserRegForm
类的实例,用于处理用户通过POST方法提交的数据,包括文本字段数据和上传的文件数据。这个实例可以用于验证用户提交的数据,并执行相关的处理操作,比如将数据保存到数据库中。
⑤能否详细解释下代码B中的代码: if form_obj.is_valid():
当你处理用户提交的表单数据时,其中一个关键的步骤是验证这些数据是否合法和有效。在Django中,你可以使用 is_valid()
方法来检查表单数据是否通过了验证规则,以及是否符合字段定义和自定义验证逻辑。
在你提到的代码中,form_obj.is_valid()
这一行代码的含义是检查用户提交的数据是否有效。这个方法会运行表单类中的所有验证规则,包括字段的定义验证(比如最小长度、最大长度等)、自定义验证方法以及其他内置的验证规则。
如果所有的验证规则都通过了,is_valid()
方法会返回 True
,表示用户提交的数据是有效的。如果任何一个验证规则失败,is_valid()
方法会返回 False
,并且表单对象会保存验证失败的信息,包括错误消息和错误类型。
因此,在你的代码中,这个条件 if form_obj.is_valid():
意味着如果用户提交的数据经过验证是有效的,就执行接下来的逻辑,比如保存数据到数据库。如果验证失败,则可以根据表单对象中的错误信息,回显错误消息给用户,让用户进行修正。
总之,form_obj.is_valid()
是用来判断用户提交的表单数据是否合法的关键方法,它决定了是否继续处理表单数据或者显示错误消息给用户。
问:is_valid()方法是怎么知道哪些函数或方法是验证规则的呢?
在Django中,is_valid()
方法通过以下方式来确定哪些函数或方法是验证规则:
-
字段类型和属性:每个字段在表单类中都有相应的字段类型,比如
CharField
、EmailField
、IntegerField
等。这些字段类型定义了默认的验证规则,例如长度限制、数据类型等。你可以在字段的构造函数中传递参数来自定义这些规则。 -
内置验证器:Django 提供了一些内置的验证器,你可以在字段的构造函数中使用
validators
参数来添加。这些验证器可以是函数、类方法或自定义的验证函数,它们会在is_valid()
方法执行时被调用。 -
自定义验证方法:你可以在表单类中定义自己的验证方法,这些方法的命名规则为
clean_<field_name>
,例如clean_username()
。这些方法会在is_valid()
方法执行时被自动调用。 -
clean()
方法:你可以在表单类中定义一个全局的clean()
方法,用于对整个表单的数据进行自定义的综合性验证。这个方法会在is_valid()
方法执行时被调用。
当你调用 is_valid()
方法时,Django 会遍历表单类中的所有字段,执行这些字段类型定义的默认验证规则,调用与字段相关的内置验证器,以及执行自定义的验证方法和全局的 clean()
方法。如果任何一个验证规则失败,is_valid()
方法会设置表单对象的 errors
属性,记录错误信息。
总之,Django 使用一系列约定和机制来确定验证规则,包括字段类型、内置验证器、自定义验证方法和全局的 clean()
方法。在调用 is_valid()
方法时,这些规则会被按顺序执行,以判断表单数据的有效性。
⑥代码:uname = request.POST.get("username", ''),为什么第2个参数要填一个空字符串?
在代码B中,uname = request.POST.get("username", '')
这行代码的作用是从request.POST
字典中获取键为 "username"
的值。如果这个键存在,就返回对应的值;如果这个键不存在,就返回一个默认值,这里是空字符串 ''
。
这种做法的目的是为了防止在访问字典中不存在的键时出现错误。如果使用request.POST["username"]
来获取值,当 "username"
这个键不存在时,会抛出 KeyError
异常,导致程序终止执行。而使用 .get()
方法,并且传递一个默认值作为第二个参数,可以在键不存在时提供一个安全的默认值,避免程序崩溃。
在这个具体情境中,uname
变量用于存储从用户提交的表单数据中获取的用户名。如果用户在表单中没有输入用户名,那么通过 request.POST.get("username", '')
可以确保 uname
变量始终有一个值,即空字符串,而不会引发异常。
总之,使用 .get()
方法并提供一个默认值是一种良好的编程习惯,可以保证在处理字典时不会因为键不存在而导致程序崩溃。
⑦代码:users = MyUser.objects.filter(username=uname) 是什么意思?
在代码B中,users = MyUser.objects.filter(username=uname)
这行代码的作用是从数据库中查询满足特定条件的用户数据。
具体解释如下:
-
MyUser
是在数据库模型文件中定义的一个类,它对应着数据库中的一张表。这个类包含了与用户相关的字段(比如用户名、密码、邮箱等)以及方法。 -
MyUser.objects
是一个与数据库交互的管理器(Manager)对象。通过这个管理器,你可以执行数据库查询操作。 -
.filter(username=uname)
是一个查询过滤条件,它的意思是从MyUser
表中筛选出字段username
值等于uname
变量值的记录。 -
将查询结果赋值给变量
users
。这个变量将包含所有满足条件的用户记录,以查询集(QuerySet)的形式返回。
所以,users = MyUser.objects.filter(username=uname)
这行代码的目的是从数据库中检索所有用户名等于 uname
变量值的用户记录,并将这些记录存储在 users
变量中,以供后续处理和使用。
⑧代码:form_obj.cleaned_data.pop("re_password")是什么意思?
在代码B中,form_obj.cleaned_data.pop("re_password")
这行代码的作用是从表单的清理数据(cleaned_data)中移除一个特定的字段。
解释如下:
-
form_obj
: 这是一个表单对象,它包含了用户提交的数据,并经过了验证和清理处理。 -
.cleaned_data
: 这是一个字典,包含了已经通过了验证并被清理过的数据。这些数据可以用于后续的处理,比如保存到数据库。 -
.pop("re_password")
: 这是对字典的操作,用于从字典中移除一个特定的键(key)及其对应的值(value)。在这里,"re_password"
是一个键,表示确认密码字段,这行代码的目的是将确认密码字段从cleaned_data
字典中移除。
通常情况下,确认密码字段不会被保存到数据库,所以在将表单数据保存到数据库之前,可能会需要将确认密码字段从 cleaned_data
中移除,以避免将这个字段保存到数据库中。
总之,form_obj.cleaned_data.pop("re_password")
这行代码的作用是从表单的清理数据中移除确认密码字段,以准备将其余数据保存到数据库中。
⑨代码:user = MyUser.objects.create_user(**form_obj.cleaned_data)
中的参数form_obj.cleaned_data前面的两个"**"是什么意思?
在Python中,**
是一种特殊的语法,用于将一个字典的内容解包(unpack)并作为关键字参数传递给函数。在这行代码中,**form_obj.cleaned_data
的作用是将 form_obj.cleaned_data
这个字典中的内容解包,并作为关键字参数传递给 MyUser.objects.create_user
函数。
具体解释如下:
-
form_obj.cleaned_data
: 这是一个字典,包含了经过验证和清理处理后的表单数据。每个字段的名称都是键,对应的用户输入数据是值。 -
**
: 这是解包操作符,用于将字典中的内容解包并作为关键字参数传递给函数。 -
MyUser.objects.create_user
: 这是一个用于创建用户的方法。它通常是在Django的用户认证系统中的用户模型(例如User
或MyUser
)中定义的一个方法,用于创建新的用户记录。
所以,MyUser.objects.create_user(**form_obj.cleaned_data)
这行代码的意思是,将经过验证和清理的表单数据作为关键字参数传递给 create_user
方法,以便用这些数据来创建一个新的用户记录。
总之,**form_obj.cleaned_data
是一种用于将字典内容解包的语法,用于方便地将字典的键值对作为关键字参数传递给函数。
⑩能否详细解释下代码:user = MyUser.objects.create_user(**form_obj.cleaned_data)
。
,user = MyUser.objects.create_user(**form_obj.cleaned_data)
这行代码的含义是创建一个新的 MyUser
记录,并使用从表单中清理过的数据填充各个字段。
让我详细解释这段代码的逻辑:
-
MyUser.objects
: 这是与MyUser
模型(数据库表)进行交互的管理器(Manager)对象。 -
.create_user
: 这是一个自定义的方法,它通常在 Django 的用户认证系统中的用户模型类中定义,用于创建新的用户记录。在你提供的代码中,MyUser
类继承了AbstractUser
,所以它继承了create_user
方法。 -
**form_obj.cleaned_data
: 这部分使用了解包操作符**
,将经过验证和清理的表单数据解包并作为关键字参数传递给create_user
方法。因为form_obj.cleaned_data
是一个字典,它包含了经过验证的表单字段及其对应的值。
所以,user = MyUser.objects.create_user(**form_obj.cleaned_data)
的意思是:使用从表单中清理过的数据创建一个新的 MyUser
记录。这将把用户提交的表单数据填充到 MyUser
模型的各个字段中,并创建一个新的用户记录。
总之,这行代码是在数据库中创建一个新的用户记录,并将表单中的数据应用于相应的字段。
11 代码 user_img = user.user_img 的user_img是用户头像路径吗?
答:不是,user_img只是数据库模型MyUser的头像字段ImageFieldFile的一个对象,它代表数据库查询的QuerySet,如果要获取用户的头像的上传路径,要用下面的代码:
python
user_img_path = user.user_img.url
即通过 .url 属性可以获取头像上传路径。
所以这句代码是存疑的。
12 最后一个return语句: return render(request, 'shop/user_reg.html', {"form_obj": form_obj})
是什么意思?
答:在POST请求方法中,不管哪种情况,最终都要再次显示显示表单填写页面。
05-模板文件user_reg.html
05-01-注册模板目录
打开文件:
E:\Python_project\P_001\myshop-test\mall_backend\mall_backend\settings.py
找到配置选项"TEMPLATES",然后将代码
python
'DIRS': [],
修改为:
python
'DIRS': [os.path.join(BASE_DIR, 'templates')],
由于上面这句代码用到了os库,所以还需要在 settings.py 中导入该库:
python
import os
05-02-配置模板文件user_reg.html需要的Bootstrap静态文件
01-创建路径 E:\Python_project\P_001\myshop-test\mall_backend\static\other\css\ 所需的相关目录。
02-然后下载Bootstrap的包"bootstrap-4.5.3-dist.zip",
官网下载方法请参考博文: https://blog.csdn.net/wenhao_ir/article/details/132089650
百度网盘下载链接:https://pan.baidu.com/s/14BM9gpC3K-LKxhyLGh4J9Q?pwd=m02m
下载完成后解压,把css文件夹下的文件"bootstrap.min.css"和文件"bootstrap.min.css.map"都复制到目录:E:\Python_project\P_001\myshop-test\mall_backend\static\other\css\ 中。
关于映射文件"bootstrap.min.css.map"有什么用,请参考下面两篇文章:
文章01 https://blog.csdn.net/wenhao_ir/article/details/132090746
文章02 https://blog.csdn.net/wenhao_ir/article/details/132152980
05-03-注册静态文件目录
打开文件:
E:\Python_project\P_001\myshop-test\mall_backend\mall_backend\settings.py
找到代码:
python
STATIC_URL = '/static/'
将其替换为下面三句代码:
python
STATICFILES_DIRS = [os.path.join(BASE_DIR, 'static')]
STATIC_URL = '/static/'
STATIC_ROOT = os.path.join(BASE_DIR, '/static')
问:
代码:
STATICFILES_DIRS = [os.path.join(BASE_DIR, 'static')]
与代码:
STATIC_ROOT = os.path.join(BASE_DIR, '/static')
有什么区别?
答:这两行代码有不同的目的和用途,它们分别用于不同情况下的静态文件配置。
-
STATICFILES_DIRS:
pythonSTATICFILES_DIRS = [os.path.join(BASE_DIR, 'static')]
这行代码配置了一个列表,其中包含了其他应用或项目级别的静态文件目录。
os.path.join(BASE_DIR, 'static')
表示将项目根目录下的static
文件夹加入到静态文件查找路径中。这个配置用于指定除了应用内部的静态文件目录之外的其他静态文件目录。在这个列表中的目录会被用于开发环境中的静态文件查找,而不会被用于生产环境的静态文件收集(即不会被collectstatic
命令收集到STATIC_ROOT
目录中)。 -
STATIC_ROOT:
pythonSTATIC_ROOT = os.path.join(BASE_DIR, '/static')
这行代码定义了一个目录,用于在生产环境中收集所有静态文件。
STATIC_ROOT
是一个绝对路径,指定了一个位置,collectstatic
命令会将所有应用和项目中的静态文件收集到这个目录中,以便在生产环境中统一提供静态文件的服务。这个目录应该是你部署服务器上的一个真实路径。
在总体架构上,STATICFILES_DIRS
用于开发环境中的静态文件查找,而 STATIC_ROOT
用于生产环境中的静态文件收集。这两个设置在功能上是不同的,它们的目标是为了在不同的环境中处理静态文件。
问:代码STATIC_URL = '/static/'
有什么用?
在Django的settings.py
文件中,STATIC_URL
是一个用于设置静态文件的URL前缀的配置项。它定义了浏览器访问静态文件的基本URL路径。在你的代码中,STATIC_URL
被设置为'/static/'
。
这意味着当你在HTML模板或Django视图中引用静态文件(例如CSS、JavaScript、图像等),Django将使用这个URL前缀来构建静态文件的URL。例如,如果你有一个名为style.css
的CSS文件,并且STATIC_URL
被设置为'/static/'
,那么在模板中引用这个文件时,你可以这样做:
html
<link rel="stylesheet" type="text/css" href="{% static 'style.css' %}">
Django会将{% static 'style.css' %}
转换为/static/style.css
,这是浏览器请求静态文件的URL。所以,STATIC_URL
定义了静态文件的基本URL路径,让你可以方便地引用这些文件,而不需要硬编码整个URL。这使得你的应用更加灵活和可维护,因为你可以轻松地更改STATIC_URL
而不必修改所有静态文件的引用。
05-02-创建模板文件user_reg.html,并写入代码
创建文件 E:\Python_project\P_001\myshop-test\mall_backend\templates\shop\user_reg.html
然后写入下面的代码:
html
{% load static %}
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="utf-8">
<title>页面标题:用户注册</title>
<link rel="stylesheet" href="{% static 'other/css/bootstrap.min.css' %}">
</head>
<body>
<div class="container">
<div class="row">
<div class="col-md-6 col-md-offset-2">
<div class="page-header">
<h2>用户注册</h2>
</div>
<form novalidate action="" method="post" enctype="multipart/form-data" class="form-horizontal">
{% csrf_token %}
<div class="form-group">
<label for="{{form_obj.username.id_for_label}}" class="col-sm-2">{{form_obj.username.label}}</label>
<div class="col-sm-8">{{form_obj.username}}
<span class="help-block">{{ form_obj.errors.username.0 }}</span>
</div>
</div>
<div class="form-group">
<label for="{{form_obj.password.id_for_label}}" class="col-sm-2">{{form_obj.password.label}}</label>
<div class="col-sm-8">{{form_obj.password}}
<span class="help-block">{{ form_obj.errors.password.0 }}</span>
</div>
</div>
<div class="form-group">
<label for="{{form_obj.re_password.id_for_label}}" class="col-sm-2">{{form_obj.re_password.label}}</label>
<div class="col-sm-8">{{form_obj.re_password}}
<span class="help-block">{{ form_obj.errors.re_password.0 }}</span>
</div>
</div>
<div class="form-group">
<label for="{{form_obj.nickname.id_for_label}}" class="col-sm-2">{{form_obj.nickname.label}}</label>
<div class="col-sm-8">{{form_obj.nickname}}
<span class="help-block">{{ form_obj.errors.nickname.0 }}</span>
</div>
</div>
<div class="form-group">
<label for="{{form_obj.nickname.id_for_label}}" class="col-sm-2">{{form_obj.email.label}}</label>
<div class="col-sm-8">{{form_obj.email}}
<span class="help-block">{{ form_obj.errors.email.0 }}</span>
</div>
</div>
<div class="form-group">
<label for="{{form_obj.nickname.id_for_label}}" class="col-sm-2">{{form_obj.mobile.label}}</label>
<div class="col-sm-8">{{form_obj.mobile}}
<span class="help-block">{{ form_obj.errors.mobile.0 }}</span>
</div>
</div>
<div class="form-group">
<label for="{{form_obj.nickname.id_for_label}}" class="col-sm-2">{{form_obj.user_img.label}}</label>
<div class="col-sm-8">{{form_obj.user_img}}
<span class="help-block"></span>
</div>
<img src="/media/{{ user_img }}">
</div>
<div class="col-8">
<label for="agreeTerms">
{{ info }}
</label>
</div>
<div class="form-group">
<div class="col-sm-10">
<input type="submit" class="btn btn-success" value="用户注册">
<a href="/users/login/" class="btn btn-success">用户登录</a>
</div>
</div>
</form>
</div>
</div>
</div>
</body>
</html>
05-03-模板文件user_reg.html代码详解
01 {% load static %}是什么意思?
{% load static %}
是 Django 模板语言的标签,用于加载静态文件(如样式表、JavaScript 文件等)。它告诉 Django 模板引擎在当前模板中启用静态文件的处理,以便能够正确地引用静态文件路径。
当你在Django模板中使用 {% load static %}
标签时,它会通知Django模板引擎去处理与静态文件相关的操作。这包括了为静态文件生成正确的URL路径,以便在模板中正确引用这些文件。
有了上面这句模板语言的声明后,当模板引擎看到 {% static %}
标签时,它会根据对静态文件的设置生成一个完整的路径,使你能够在HTML代码中引用静态文件。
在上面的静态文件路径设置中,用了下面三条语句:
python
STATICFILES_DIRS = [os.path.join(BASE_DIR, 'static')]
STATIC_URL = '/static/'
STATIC_ROOT = os.path.join(BASE_DIR, '/static')
设置了上面三条语句后,如果在你的代码片段中有下面这一行:
html
<link rel="stylesheet" href="{% static 'other/css/bootstrap.min.css' %}">
那么{% static 'other/css/bootstrap.min.css' %}
会根据在settting.py中设置的静态目录的路径分两步
进行路径:
第1步:根据STATIC_URL = '/static/'
将路径解析为:/static/other/css/bootstrap.min.css
第2步:根据STATICFILES_DIRS = [os.path.join(BASE_DIR, 'static')]
【这一句用于调试开发环境】或STATIC_ROOT = os.path.join(BASE_DIR, '/static')
【这一句用于生产部署环境】,将路径中的/static/
替换为E:\Python_project\P_001\myshop-test\mall_backend\static\
,最终得到完整的E:\Python_project\P_001\myshop-test\mall_backend\static\other\css\bootstrap.min.css
总之,通过在模板中使用 {% load static %}
标签和 {% static %}
标签,你可以确保在Django项目中正确处理和引用静态文件,使你的网页可以正确加载样式、脚本和其他静态资源。
02 为什么通过语句 将语言设置为英文,但是仍然可以有中文字符?
答:在代码中,通过语句 <html lang="en">
设置了页面的主要语言为英文,但是仍然可以在页面中包含中文字符,这是因为 <html lang="en">
标签只是一种声明,它向浏览器和搜索引擎指示页面的主要语言是英文,以便它们能够正确地对页面进行处理和显示。
然而,这并不意味着页面只能包含英文内容。HTML 中的语言设置主要是为了支持浏览器和搜索引擎的语言处理、搜索和优化。你仍然可以在页面中包含其他语言(如中文)的内容,而不会影响页面的显示。
实际上,在网页中,你可以同时使用多种语言,这对于国际化和多语言支持的网站是很常见的。浏览器可以根据内容自动调整显示的语言,同时搜索引擎也能够正确地处理多种语言的内容。
所以,尽管你在代码中设置了 <html lang="en">
,但是这并不妨碍你在页面中使用中文字符或其他语言的内容。这个设置更多地是为了提供一种指示,以帮助浏览器和搜索引擎更好地处理你的网页。
值得注意的是,虽然在网页中设置 <html lang="en">
可以帮助浏览器和搜索引擎更好地理解和处理网页内容的语言,但并不是必需的。许多网页在实际开发中可能会省略 <html lang>
设置,特别是当网页内容包含多种语言时。
在现代的网页开发中,浏览器和搜索引擎已经变得足够智能,能够自动识别和适应多种语言的内容。这意味着,即使你不设置 <html lang>
,浏览器仍然可以正确地显示多语言内容,并且搜索引擎也可以适当地处理你的网页。
03-代码<link rel="stylesheet" href="{% static 'other/css/bootstrap.min.css' %}">
中的参数rel是什么英文的缩写?代表什么意思?
详情和答案见博文:https://blog.csdn.net/wenhao_ir/article/details/132091300
04-如何彻底理解模板文件user_reg.html中的各种Bootstrap的前端样式?
答:没有捷径,只有认真系统的学习一次bootstrap,所以本篇博文对于模板文件user_reg.htm中的各种Bootstrap前端样式不作展开介绍。
04-Bootstrap的类col-md-offset-2是什么效果?
在 Bootstrap 3 中,col-md-offset-2
是用来创建栅格偏移(grid offset)的类。它的作用是在大屏幕(medium-sized devices)上将列(column)向右偏移两个列宽的位置。这个偏移可以用来调整列的位置,以实现更复杂的布局。
具体来说,Bootstrap 使用一个12列的栅格系统,其中 col-md-
类用于定义列的宽度,而 col-md-offset-
类用于定义列的偏移量。所以,col-md-offset-2
将列向右偏移两个列的宽度,等于 16.6667% * 2 * 100% = 33.3334% 的宽度。
以下是一个示例,展示了如何在 Bootstrap 3 中使用 col-md-offset-2
:
html
<div class="container">
<div class="row">
<div class="col-md-2">左侧内容</div>
<div class="col-md-offset-2 col-md-8">这个列向右偏移了两个列的宽度,然后占据了8个列的宽度</div>
</div>
</div>
在上面的示例中,第一个列占据了两个列的宽度,而第二个列使用 col-md-offset-2
偏移了两个列的宽度,然后占据了剩余的八个列的宽度,实现了一个复杂的布局效果。
05-代码 <form novalidate action="" method="post" enctype="multipart/form-data" class="form-horizontal">
详解。
问: 关键词 novalidate 是什么意思?
详见博文 https://blog.csdn.net/wenhao_ir/article/details/132488987
问:action=""是什么意思?
详见博文 https://blog.csdn.net/wenhao_ir/article/details/132488987
问:enctype="multipart/form-data" 是什么意思?
详见博文 https://blog.csdn.net/wenhao_ir/article/details/132488987
06-代码 <label for="{``{form_obj.username.id_for_label}}" class="col-sm-2">{``{form_obj.username.label}}</label>
详解一下。
要理解这句代码,关键是理解label标签和对象form_obj,关于label标签,请参考博文 https://blog.csdn.net/wenhao_ir/article/details/132500712
当然,实际上上面这句代码实际上并不需要设置这个for属性,因为并无别的HTML元素与其联系。
而对象form_obj,请参阅视图函数代码中关于函数render()的代码,而关于函数函数render()的详解,请参阅博文:https://blog.csdn.net/wenhao_ir/article/details/132501337
另外还要问"在HTML板模中的"{{form_obj.username.id_for_label}}"和"{{form_obj.username.label}}"得到的结果有什么不同?"
回答:在Django中,{``{ form_obj.username.id_for_label }}
和 {``{ form_obj.username.label }}
都用于获取表单字段 username
的标签信息,但它们有一些不同之处:
-
{``{ form_obj.username.id_for_label }}
:这将生成一个用于关联标签和表单字段的 HTML
id
属性的值。通常情况下,这个值会与表单字段的id
属性相同,用于确保标签与相应的输入字段建立关联。在你的示例中,你定义了一个自定义的表单字段username
,但没有显式指定id
属性,所以Django会自动生成一个唯一的id
值。这个值可以用于在HTML中编写自定义的标签,以及用于与输入字段建立关联。示例用法:
html<label for="{{ form_obj.username.id_for_label }}">Username:</label>
-
{``{ form_obj.username.label }}
:这将返回字段的标签文本,即"用户名"。这是你在表单类中通过
label
参数指定的标签文本。这通常用于直接输出标签文本,而不需要手动指定关联的id
属性。示例用法:
html<label>{{ form_obj.username.label }}</label>
总的来说,{``{ form_obj.username.id_for_label }}
主要用于关联标签和输入字段,而 {``{ form_obj.username.label }}
用于直接获取标签文本。具体的用法取决于你在HTML模板中的需求。如果你需要在HTML中自定义标签,并且需要关联标签和输入字段,那么你可以使用 {``{ form_obj.username.id_for_label }}
。如果你只需要显示标签文本,那么 {``{ form_obj.username.label }}
就足够了。
07-代码<span class="help-block">{``{ form_obj.errors.username.0 }}</span>
解释一下吧。
关于span标签的介绍,请参见博文:https://blog.csdn.net/wenhao_ir/article/details/132502816
form_obj.errors.username.0
的介绍,如下:
关于对象form_obj,请参阅视图函数代码中关于函数render()的代码,而关于函数函数render()的详解,请参阅博文:https://blog.csdn.net/wenhao_ir/article/details/132501337
在 Django 的表单处理中,form_obj.errors
是一个包含表单验证错误信息的字典。这个字典以表单字段为键,对应的值是一个列表,列表中包含了与该字段相关的所有错误。这些错误通常会在用户提交表单时进行验证,例如,如果用户名字段为空,那么就会产生一个与用户名字段相关的错误。
在你的代码A中,{``{ form_obj.errors.username.0 }}
这部分的含义是:
form_obj.errors
: 表示获取表单对象form_obj
中的错误信息字典。.username
: 表示从错误字典中获取与用户名字段相关的错误列表。.0
: 表示获取该错误列表的第一个元素。
所以,{``{ form_obj.errors.username.0 }}
的意思是从表单对象的错误字典中获取用户名字段的第一个错误信息。如果你的表单验证中出现了多个关于用户名字段的错误,这将会获取第一个错误。如果没有错误,它将不会显示任何内容。
例如,假设用户没有填写用户名字段,那么在验证表单时会产生一个错误,该错误信息会被存储在 form_obj.errors.username
列表中的第一个位置。你可以使用 {``{ form_obj.errors.username.0 }}
来在模板中显示这个错误信息,以便向用户指出出现的问题。
06-配置URL请求路径(配置路由)
打开文件"E:\Python_project\P_001\myshop-test\mall_backend\mall_backend\urls.py"
写入下面的代码:
python
from django.contrib import admin
from django.urls import path
from users import views
urlpatterns = [
path('user_reg/', views.user_reg),
]
07-配置数据库
第01步:在文件:"E:\Python_project\P_001\myshop-test\mall_backend\mall_backend\settings.py"中写入以下数据库的配置信息:
python
DATABASES = {
'default': {
'ENGINE': 'django.db.backends.mysql',
'NAME': 'mall-backend',
'USER': 'mall-backend',
'PASSWORD': 'swh123456',
'HOST': 'localhost',
'PORT': '3306',
# 取消外键约束,否则多对多模型迁移报django.db.utils.IntegrityError: (1215, 'Cannot add foreign key constraint')
'OPTIONS': {
"init_command": "SET foreign_key_checks = 0;",
'charset': 'utf8'
},
}
}
第02步:在mysql中按上面的信息创建数据库 mall-backend:
第03步:生成数据库的迁移执行文件
bash
CD E:\Python_project\P_001\myshop-test\mall_backend
E:
manage.py makemigrations
第04步:执行数据库迁移模型
bash
CD E:\Python_project\P_001\myshop-test\mall_backend
E:
manage.py migrate
此时,可以查看下数据库中是否有相关的表了:
08-运行应用
bash
CD E:\Python_project\P_001\myshop-test\mall_backend
E:
python manage.py runserver 127.0.0.1:8010
09-访问URL路径测试有无问题
根据:"E:\My_Data\0072-Django教材源码\myshop-back\apps\users\urls.py" 设置的URL。
应该访问下面这个URL:
http://127.0.0.1:8010/user_reg/
运行效果如下:
注册一个用户试下:
点用注册后数据库中出现了下面这条记录: