多对多三种创建方式
-
全自动
class Book(models.Model): title = models.CharField(max_length=32) # 多对多关系字段 authors = models.ManyToManyField(to='Authors') class Authors(models.Model): name = models.CharField(max_length=32)
好处:至始至终你都没有操作第三张表 全部都是由orm自动帮你创建的
字段内置了四个操作第三张表的方法
add remove set clear
不足:自动创建的第三张表无法扩展个修改字段 表的扩展性较差
-
纯手撸
class Book(models.Model): title = models.CharField(max_length=32) class Authors(models.Model): name = models.CharField(max_length=32) class Book2Authors(models.Model): book = models.ForeignKey(to="Book") author = models.ForeignKey(to="Authors") create_time = models.DateField(auto_now_add = True)
好处:第三张表中字段个数和字段名称全都可以自己定义
不足:不再支持orm跨表查询 不再由正反向的概念
add remove set clear
-
半自动(推荐使用)
class Book(models.Model): title = models.CharField(max_length=32) # 多对多关系字段 authors = models.ManyToManyField(to='Authors',through='Book2Author',through_fields=("book","authors")) # 不通过orm创建,而是通过第三张表中->through='Book2Author'的through_fields=("authors","book"),authors和book字段 # through_fields=("authors","book")中的"authors"字段,可以这样记:在哪张表里面该字段就放在最前面 """ 当你的ManyToManyField只有一个参数to的情况下 orm会自动帮你创建第三张表 如果你加了through和through_fields那么orm就不会自动帮你创建第三张表 但是它会在内部帮你维护关系 让你能够继续使用orm的跨表查询 through 自己指定第三张关系表 through_fields 自己指定第三张关系表中 到底哪两个字段维护者表与表之间的多对多关系 """ class Authors(models.Model): name = models.CharField(max_length=32) # 多对多关系字段 等价 books = models.ManyToManyField(to='Book', through='Book2Author', through_fields=("authors","book")) class Book2Author(models.Model): book = models.ForeignKey(to='Book') authors = models.ForeignKey(to='Authors')
不通过orm创建,而是通过第三张表中->through='Book2Author'的through_fields=("authors","book"),authors和book字段
through_fields=("authors","book")中的"authors"字段,可以这样记:在哪张表里面该字段就放在最前面
该表可以有任意多的外键字段
可以扩展任意的字段
好处:可以任意的添加和修改第三张表中的字段
并且支持orm跨表查询
不足:不支持
add
remove
set
clear
forms校验性组件
forms组件
前戏
需求:
1.写一个注册页面 获取用户输入的用户名和密码
提交到后端之后 后端需要对用户名和密码做校验
用户名里面不能含有葫芦娃
密码不能少于三位
如果不符合 展示对应的错误信息
forms组件 能够做的事情
1.手动书写html代码获取用户输入 》》》 渲染标签
2.将数据转递给后端做数据校验 》》》 校验数据
3.如果数据有错误 你还展示了错误信息 》》》 展示信息
使用forms组件的前提是 你需要提前写一个类
from django import forms
class MyForm(forms.Form):
# username字段 最少三位 最多八位
username = forms.CharField(max_length=8,min_length=3)
# password字段 最少三位 最多八位
password = forms.CharField(max_length=8,min_length=3)
# email字段 必须是邮箱格式
email = forms.EmailField()
校验数据
# 1.给写好的类 传字典数据(待校验的数据)
form_obj = views.MyForm({'username':'jason','password':'12','email':'123'})
# 2.如何查看校验的数据是否合法
form_obj.is_valid()
False # 只有当你的数据全部符合校验规则的情况下 结果才是True 否则都为False
# 3.如何查看不符合规则的字段及错误的理由
form_obj.errors
{
'password': ['Ensure this value has at least 3 characters (it has 2).'],
'email': ['Enter a valid email address.']
}
# 4.如何查看符合校验规则的数据
form_obj.cleaned_data
{'username': 'jason'}
# 5.forms组件中 定义的字段默认都是必须传值的 不能少传
form_obj = views.MyForm({'username':'jason','password':'12345'})
form_obj.is_valid()
False
form_obj.errors
{'email': ['This field is required.']}
# 6.forms组件只会校验forms类中定义的字段 如果你多传了 不会有任何影响
form_obj = views.MyForm({'username':'jason','password':'12345','email':'123@qq.com','xxx':'嘿嘿嘿'})
form_obj.is_valid()
True
渲染标签
forms组件只会帮你渲染获取用户输入的标签 不会帮你渲染提交按钮 需要你自己手动添加
<p>forms组件渲染标签方式1:封装程度态高 不推荐使用 但是可以用在本地测试</p>
{{ form_obj.as_p }} <!--自动渲染所有input框 -->
{{ form_obj.as_ul }}
{{ form_obj.as_table }}
<p>forms组件渲染标签方式2:不推荐使用 写起来太烦了</p>
{{ form_obj.username.label }}{{ form_obj.username }}
{{ form_obj.username.label }}{{ form_obj.password }}
{{ form_obj.username.label }}{{ form_obj.email }}
<p>forms组件渲染标签方式3:推荐使用 </p>
{% for form in form_obj %}
<p>{{ form.label }}{{ form }}</p> <!--form 等价于你方式2中的对象点字段名-->
{% endfor %}
展示信息
<p>forms组件渲染标签方式3:推荐使用 </p>
<form action="" method="post" novalidate>
{% for forms in form_obj %}
<p>
{{ forms.label }}{{ forms }}
<span>{{ forms.errors.0 }}</span>
</p> <!--form 等价于你方式2中的对象点字段名-->
{% endfor %}
<input type="submit">
</form>
使用forms组件实现注册功能
先定义好一个ReGForm类:
from django import forms
# 按照Django form组件的要求自己写一个类
class RegForm(forms.Form):
name = forms.CharField(label="用户名")
pwd = forms.CharField(label="密码")
再写一个视图函数
# 使用form组件实现注册方式
def register2(request):
form_obj = RegForm()
if request.method == "POST":
# 实例化form对象的时候,把post提交过来的数据直接传进去
form_obj = RegForm(request.POST)
# 调用form_obj校验数据的方法
if form_obj.is_valid():
return HttpResponse("注册成功")
return render(request, "register2.html", {"form_obj": form_obj})
login.htmll
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>注册2</title>
</head>
<body>
<form action="/reg2/" method="post" novalidate autocomplete="off">
{% csrf_token %}
<div>
<label for="{{ form_obj.name.id_for_label }}">{{ form_obj.name.label }}</label>
{{ form_obj.name }} {{ form_obj.name.errors.0 }}
</div>
<div>
<label for="{{ form_obj.pwd.id_for_label }}">{{ form_obj.pwd.label }}</label>
{{ form_obj.pwd }} {{ form_obj.pwd.errors.0 }}
</div>
<div>
<input type="submit" class="btn btn-success" value="注册">
</div>
</form>
</body>
</html>
看网页效果发现也验证了form的功能:
前端页面是form类的对象生成的——>生成HTML标签功能
当用户名和密码输入为空或输错之后 页面都会提示——>用户提交校验功能
当用户输错之后再次输入上次的内容还保留在input框中——>保留上次输入内容
form常用字段与插件
initial
初始值,input框里面的初始值
class LoginForm(forms.Form):
username = forms.CharField(
min_length=8,
label="用户名",
initial="张三" # 设置默认值
)
pwd = forms.CharField(min_length=6, label="密码")
error_messages
重写错误信息
class LoginForm(forms.Form):
username = forms.CharField(
min_length=8,
label="用户名",
initial="张三",
error_messages={
"required": "不能为空",
"invalid": "格式错误",
"min_length": "用户名最短8位"
}
)
pwd = forms.CharField(min_length=6, label="密码")
password
class LoginForm(forms.Form):
...
pwd = forms.CharField(
min_length=6,
label="密码",
widget=forms.widgets.PasswordInput(attrs={'class': 'c1'}, render_value=True)
)
radioSelect
单radio值为字符串
class LoginForm(forms.Form):
username = forms.CharField(
min_length=8,
label="用户名",
initial="张三",
error_messages={
"required": "不能为空",
"invalid": "格式错误",
"min_length": "用户名最短8位"
}
)
pwd = forms.CharField(min_length=6, label="密码")
gender = forms.fields.ChoiceField(
choices=((1, "男"), (2, "女"), (3, "保密")),
label="性别",
initial=3,
widget=forms.widgets.RadioSelect()
)
单选select
class LoginForm(forms.Form):
...
hobby = forms.ChoiceField(
choices=((1, "篮球"), (2, "足球"), (3, "双色球"), ),
label="爱好",
initial=3,
widget=forms.widgets.Select()
)
多选select
class LoginForm(forms.Form):
...
hobby = forms.MultipleChoiceField(
choices=((1, "篮球"), (2, "足球"), (3, "双色球"), ),
label="爱好",
initial=[1, 3],
widget=forms.widgets.SelectMultiple()
)
单选checkbox
class LoginForm(forms.Form):
...
keep = forms.ChoiceField(
label="是否记住密码",
initial="checked",
widget=forms.widgets.CheckboxInput()
)
多选checkbox
class LoginForm(forms.Form):
...
hobby = forms.MultipleChoiceField(
choices=((1, "篮球"), (2, "足球"), (3, "双色球"),),
label="爱好",
initial=[1, 3],
widget=forms.widgets.CheckboxSelectMultiple()
)
数据校验
数据的校验通常前后端都必须有
但是前端的校验可有可无 并且弱不禁风
后端的校验必须要有 并且必须非常的全面
如何告诉浏览器不做校验 form表单中加一个novalidate
参数即可
<form action="" method="post" novalidate>
内置的校验器
from django.core.validators import RegexValidator
validators=[
RegexValidator(r'^[0-9]+$', '请输入数字'),
RegexValidator(r'^159[0-9]+$', '数字必须以159开头'),
]
钩子函数 HOOK
当你觉得上面的所有的校验还不能够满足你的需求 你可以考虑使用钩子函数
是一个函数 函数体内你可以写任意的校验代码
局部钩子
你想校验单个字段
全局钩子
你想校验多个字段
补充知识点
其他字段及参数
label
input对应的提示信息
initial
input框默认值
widget
给input框设置样式及属性
required
默认为True,控制字段是否必填 ,如果为false代表为空(一个空字典)
widget 后面可以自定义内容
widget=forms.widgets.TextInput({'class':'form-control c1 c2','username':'jason'})
widget=forms.widgets.TextInput(attrs={'class':'form-control c1 c2','username':'jason'})
给username所对应的框展示错误信息
self.add_error('username','光喊666是不行的')
源码:raise ValidationError('到底对不对啊')等价于上面的代码
将全局的数据返回
return self.cleaned_data
def index(request):
# 渲染标签 第一步 需要生成一个空的forms类的对象
form_obj = MyForm()
# 如何校验前端用户传入的数据
if request.method == 'POST':
# 获取用户的数据 request.POST中 forms组件校验数据
form_obj = MyForm(request.POST) # 改变量名一定要跟上面的form_obj变量名一致
if form_obj.is_valid(): # 研究forms组件入口就是is_valid()
print(form_obj.cleaned_data)
return HttpResponse('数据全部OK')
# 直接将生成的对象 传递给前端页面
return render(request,'index.html',locals())
注意:第三行的form_obj和第七行的form_obj变量名一致
forms组件渲染标签的三种方式
<p>forms组件渲染标签方式1:封装程度太高 不推荐使用 但是可以用在本地测试</p>
{{ form_obj.as_p }} <!--自动渲染所有input框 -->
{{ form_obj.as_ul }} #无序的列表
{{ form_obj.as_table }}
<p>forms组件渲染标签方式2:不推荐使用 写起来太烦了</p>
{{ form_obj.username.label }}{{ form_obj.username }}
{{ form_obj.username.label }}{{ form_obj.password }}
{{ form_obj.username.label }}{{ form_obj.email }}
<p>forms组件渲染标签方式3:推荐使用 </p>
<form action="" method="post" novalidate>
{% for forms in form_obj %}
<p>
{{ forms.label }}{{ forms }}
<span>{{ forms.errors.0 }}</span>
</p> <!--form 等价于你方式2中的对象点字段名-->
{% endfor %}
<input type="submit">
今日考题
1.什么是序列化,截止目前为止你所接触过的序列化有哪些
序列化 :将对象的状态信息转换为可以存储或传输的形式的过程。在序列化期间,对象将其当前状态写入到临时或持久性存储区。以后,可以通过从存储区中读取或反序列化对象的状态,重新创建该对象。
python js
json.dumps json.stringuify
json.loads json.pares
pickle.dmps
pickle.loads
2.批量插入数据需要注意什么,orm批量插入数据的语法?
容易出现插入重复数据的问题
先用列表生成数据,再通过bulk_create批量插入数据
def index(request):
book_list = []
for i in range(10000):
book_list.append(models.Book(title=f'第{i}本书'))
# 批量插入数据
models.Book.objects.bulk_create(book_list)
book_queryset = models.Book.objects.all()
return render(request,'index.html',locals())
3.当需要展示的数据量特别多的情况下,会采取什么优化措施,你能否简要描述一下该措施的实施思路,以及该措施具体应用下的操作步骤
一般分页查询
一般的分页查询使用简单的 limit 子句就可以实现。limit 子句声明如下:
SELECT * FROM table LIMIT [offset,] rows | rows OFFSET offset
LIMIT 子句可以被用于指定 SELECT 语句返回的记录数。需注意以下几点:
- 第一个参数指定第一个返回记录行的偏移量,注意从0开始
- 第二个参数指定返回记录行的最大数目
- 如果只给定一个参数:它表示返回最大的记录行数目
- 第二个参数为 -1 表示检索从某一个偏移量到记录集的结束所有的记录行
初始记录行的偏移量是 0(而不是 1)
应用实例:
select * from orders_history where type=8 limit 1000,10;
该条语句将会从表 orders_history 中查询offset: 1000开始之后的10条数据,也就是第1001条到第1010条数据(1001 <= id <= 1010)。
数据表中的记录默认使用主键(一般为id)排序,上面的结果相当于:
select * from orders_history where type=8 order by id limit 10000,10;
另外我还做了十来次查询,从查询时间来看,基本可以确定,在查询记录量低于100时,查询时间基本没有差距,随着查询记录量越来越大,所花费的时间也会越来越多。
针对查询偏移量的测试:
select * from orders_history where type=8 limit 100,100; select * from orders_history where type=8 limit 1000,100; select * from orders_history where type=8 limit 10000,100; select * from orders_history where type=8 limit 100000,100; select * from orders_history where type=8 limit 1000000,100;
三次查询时间如下: 查询100偏移:25ms 24ms 24ms 查询1000偏移:78ms 76ms 77ms 查询10000偏移:3092ms 3212ms 3128ms 查询100000偏移:3878ms 3812ms 3798ms 查询1000000偏移:14608ms 14062ms 14700ms
随着查询偏移的增大,尤其查询偏移大于10万以后,查询时间急剧增加。
这种分页查询方式会从数据库第一条记录开始扫描,所以越往后,查询速度越慢,而且查询的数据越多,也会拖慢总查询速度。
使用子查询优化
这种方式先定位偏移位置的 id,然后往后查询,这种方式适用于 id 递增的情况。
select * from orders_history where type=8 limit 100000,1;
select id from orders_history where type=8 limit 100000,1;
select * from orders_history where type=8 and
id>=(select id from orders_history where type=8 limit 100000,1)
limit 100;
select * from orders_history where type=8 limit 100000,100;
4条语句的查询时间如下:
第1条语句:3674ms
第2条语句:1315ms
第3条语句:1327ms
第4条语句:3710ms
针对上面的查询需要注意:
- 比较第1条语句和第2条语句:使用 select id 代替 select * 速度增加了3倍
- 比较第2条语句和第3条语句:速度相差几十毫秒
- 比较第3条语句和第4条语句:得益于 select id 速度增加,第3条语句查询速度增加了3倍
这种方式相较于原始一般的查询方法,将会增快数倍。
使用id限定优化
这种方式假设数据表的id是连续递增的,则我们根据查询的页数和查询的记录数可以算出查询的id的范围,可以使用 id between and 来查询:
select * from orders_history where type=2
and id between 1000000 and 1000100 limit 100;
查询时间:15ms 12ms 9ms
这种查询方式能够极大地优化查询速度,基本能够在几十毫秒之内完成。
限制是只能使用于明确知道id的情况,不过一般建立表的时候,都会添加基本的id字段,这为分页查询带来很多便利。
还可以有另外一种写法:
select * from orders_history where id >= 1000001 limit 100;
当然还可以使用 in 的方式来进行查询,这种方式经常用在多表关联的时候进行查询,使用其他表查询的id集合,来进行查询:
select * from orders_history where id in (select order_id from trade_2 where goods = 'pen') limit 100;
这种 in 查询的方式要注意:某些 mysql 版本不支持在 in 子句中使用 limit。
使用临时表优化
这种方式已经不属于查询优化,这儿附带提一下。
对于使用 id 限定优化中的问题,需要 id 是连续递增的,但是在一些场景下,比如使用历史表的时候,或者出现过数据缺失问题时,可以考虑使用临时存储的表来记录分页的id,使用分页的id来进行 in 查询。
这样能够极大的提高传统的分页查询速度,尤其是数据量上千万的时候。
关于数据表的id说明
一般情况下,在数据库中建立表的时候,强制为每一张表添加 id 递增字段,这样方便查询。
如果像是订单库等数据量非常庞大,一般会进行分库分表。这个时候不建议使用数据库的 id 作为唯一标识,而应该使用分布式的高并发唯一 id 生成器来生成,并在数据表中使用另外的字段来存储这个唯一标识。
使用先使用范围查询定位 id (或者索引),然后再使用索引进行定位数据,能够提高好几倍查询速度。即先 select id,然后再 select *;
4.简述面相对象的三大特性及特点,其中你认为哪个特性使用频率最高,为什么
封装、继承、多态
目的:封装和继承的目的是为了代码重用,多态是为了接口重用。
封装:封装是把客观事物抽象成类,并且把自己的属性和方法让可信的类或对象操作,对不可信的隐藏。
继承:它可以使用现有类的所有功能,并在无需重新编写原来的类的情况下对这些功能进行扩展。
继承得到的新类称为“子类”或“派生类”。被继承的父类称为“基类”、“父类”或“超类”。
多态:是允许你将父对象设置成为和一个或更多的他的子对象相等的技术,赋值之后,父对象就可以根据当前赋值给它的子对象的特性以不同的方式运作。
实现多态的两种方式:覆盖,重载
覆盖:是指子类重新定义父类的虚拟函数的做法。
重载:是指允许存在多个同名函数,而这些函数的参数表不同(或许参数个数不同,或许参数类型不同,或许两者都不同)
5.什么是反射,请写出反射对应的四个方法,列举出类中可以使用的装饰器及作用,再列举出类中双下开头双下结尾的方法及自动触发的应用场景
在Python中,能够通过一个对象,找出其type、class、attribute或method的能力,称为反射或自省。
方法一:
//方法1
private void TestInvoke1(Type t)
{
object obj = t.InvokeMember(null, flags | BindingFlags.CreateInstance, null, null, null);
Console.WriteLine(obj.GetType().ToString());
string testresult= t.InvokeMember("testType2", flags | BindingFlags.GetField, null, obj, null).ToString();
Console.WriteLine(testresult);
t.InvokeMember("testType2", flags | BindingFlags.SetField, null, obj,new object[]{"草泥马"});
testresult= t.InvokeMember("testType2", flags | BindingFlags.GetField, null, obj, null).ToString();
Console.WriteLine(testresult);
testresult = t.InvokeMember("test1", BindingFlags.InvokeMethod, null, obj, null).ToString();
Console.WriteLine(testresult);
}
方法二:
//方法2
private void TestInvoke2(Type t)
{
ConstructorInfo ctor = t.GetConstructor(new Type[] { });
object obj = ctor.Invoke(new object[] { });
FieldInfo fi = t.GetField("testType", flags);
fi.SetValue(obj, "尼玛");
Console.WriteLine(fi.GetValue(obj));
MethodInfo mi = t.GetMethod("test1", flags);
string result = mi.Invoke(obj, null).ToString();
Console.WriteLine(result);
}
方法三:
//方法3
delegate int testdelegate();
private void TestInvoke3(Type t)
{
object obj = Activator.CreateInstance(t);
MethodInfo mi = t.GetMethod("test1", flags);
var test1 = (testdelegate)(mi.CreateDelegate(typeof(testdelegate), obj));
int i = test1();
Console.WriteLine(i.ToString());
PropertyInfo pi = t.GetProperty("TestType", flags);
var SetTestType = (Action<string>)Delegate.CreateDelegate(typeof(Action<string>), obj, pi.GetSetMethod());
try
{
SetTestType("尼玛");
}
catch
{
Console.WriteLine("无法写入!");
}
var GetTestType = (Func<string>)Delegate.CreateDelegate(typeof(Func<string>), obj, pi.GetGetMethod());
Console.WriteLine("获取结果:" + GetTestType());
}
方法四:
//方法4
private void TestInvoke4(Type t)
{
dynamic obj = Activator.CreateInstance(t);
try
{
obj.testType = "泥煤";
Console.WriteLine(obj.testType.ToString());
}
catch
{
Console.WriteLine("私有字段不能读取或写入");
}
try
{
obj.TestType = "泥煤2";
Console.WriteLine(obj.TestType);
}
catch
{
Console.WriteLine("私有属性不能读取或写入");
}
try
{
Func<int> test = ()=>obj.test1();
Console.WriteLine(test());
}
catch
{
Console.WriteLine("私有方法不能读取或写入");
}
}
首先观察一下这四个方法传入的参数,都有一个type类型的参数,这个参数获取的是利用反射想要访问的类的类型,这个先搁着,先介绍获得这个类型之后如何对这个类型的类进行访问。
首先,方法1,利用最直接的方式,但是反射是一种比较消耗性能的调用方式,方法1每调用一次方法或属性都会进行一次绑定,对性能的消耗比较大,速度也比较慢,首先利用Type的 InvokeMember方法调用t类型的构造方法构造一个实例,但因为我们在运行过程中并不知道它是什么类型的,所以只能将它赋给一个object。InvokeMember的第一个参数代表要访问的内容的名字,如一个属性或一个方法的名字,因为是访问构造方法,我们传入空值null。第二个参数是控制绑定执行的标志,指定要绑定那些类型,用|符号隔开,如例子里面的flag,定义的DeclaredOnly指定反射不考虑继承类型,NonPublic指定反射非公开成员等等,所以反射在数据没有做特殊处理的情况下是可以访问私有成员的,第四个参数为绑定的实例,调用构造方法时传入空值即可,调用属性或字段或方法需传入由构造方法返回的object实例,最后一个参数是要传入的参数,用object[]类型传入。了解了这些,就基本能够用InvokeMember方法调用一个未知类里面的各种类型了,实例中给出了调用字段以及方法的示例,而且注意,访问的testType2是一个私有字段,但是也依然能够访问得到。
接着,方法2,,方法2提供了一种绑定一种属性,多次调用的方式,当多个实例需要重复调用同一个方法是,这个方法能够提供更好的性能,因为所有的操作只绑定了一次,这一次,使用了另外一个调用构造方法的方式,调用Type 类型的GetConstructor方法,这个方法会根据传入的参数指定的参数类型,搜索程序集里面匹配的构造方法并且返回一个ConstructorInfo实例,这个实例提供对构造函数的访问权,上面方法2我们传入了一个空的Type[]数组,那么这个方法返回的实例就会提供对无参构造方法的访问权,接着调用这个实例的invoke方法并且传入参数即可获得类型的实例,同样是一个object。接着我们通过Type类型的GetField和GetMethod方法获取对特定字段和方法的访问权,之后便能对相应的方法和字段进行访问,同理,可以通过GetProperty方法获取对属性的访问权。
方法3提供了一种利用委托访问类型的方法,这种方法适合需要重复调用同一个实例里面的相同的成员,当多次调用同一个对象里面的同一个成员时此方法的性能要优于方法2提供的方法。依然是构造一个实例,接着获取对成员的访问权,但这次是构造一个委托,并将这个委托指向这个成员,但是这个方法不提供对字段的访问方式,对属性的访问则通过构造一个委托指向属性的get访问器或set访问器,达到对属性访问的目的。
方法4提供了一种利用dynamic 对类进行访问的方法,之前我们构造完实例之后都是返回一个object,但这次我们将返回值赋给一个dynamic,这个类型将会在运行时才进行解析,并不会在编译时进行解析,用这种方式调用主要是易于理解,直观,并且它也提供了不错的性能,但有个缺点是,由于不在编译的时候进行解析,所以就无法利用vs强大的智能提示功能,编译的时候也不会报错,真正实现纯手打,可能你会主要到里面用了很多try catch,主要就是因为用这种方式有可能访问到不存在的成员或者私有成员,这个时候错误就会被捕捉到,这个也是必须的。
类中使用的装饰器及作用
类的装饰器,主要功能可以给类添加属性(类属性)
例子,类的装饰器,给类添加了一个类属性
由该类实例出来的对象,都具备文件保存的能力
def deco(item):
def inner():
print('开始类的操作')
item.file = open("a.txt", "w", encoding="utf8")
print("结束")
return item
return inner
@deco # Cat = deco(Cat)
class Cat:
pass
c = Cat()
print(c.__dict__)
c.file.write("haha")
写出一个单例的装饰器(使一个本来不是单例类的类变成单例类))
def set_func(func):
__singleton = None
def call_func(*args, **kwargs):
nonlocal __singleton
if not __singleton:
__singleton = func(*args, **kwargs)
return __singleton
else:
return __singleton
return call_func
@set_func
class Std(object):
def __init__(self, name, age):
self.name = name
self.age = age
s2 = Std('jack',18)
print(id(s2),s2.name,s2.age)
s1 = Std('leo',23)
print(id(s1),s1.name,s1.age)
运行结果:
139727292442832 jack 18
139727292442832 jack 18
登录判断装饰器:
def login_required(view_func):
"""自定义装饰器判断用户是否登录"""
@wraps(view_func)
def wrapper(*args, **kwargs):
"""具体实现判断用户是否登录的逻辑"""
user_id = session.get('user_id')
if not user_id:
return jsonify(errno=RET.SESSIONERR, errmsg='用户未登录')
else:
g.user_id = user_id
return view_func(*args, **kwargs)
return wrapper
总结:装饰器是python三大神器(迭代器,生成器,装饰器)中比较难理解的,但是它们的本质实际上就是闭包,我们在闭包函数或者类外面封装了一套逻辑,因此可以增强函数的功能,增强权限校验,事务一致性,缓存等功能,这就是装饰器,使漂亮的姑娘(函数)变的更加漂亮。
魔法方法:
在[Python](http://lib.csdn.net/base/python)中,有的名称会在前面和后面都加上两个下划线,例如__future__、__init__、__del__以及__new__等等,这些特殊的名称,在[python](http://lib.csdn.net/base/python)中就称为魔法方法或魔法属性。
例如:
(1)new 是创建类的对象的函数,相当于C++中构造函数。
(2)init 会在__new__之后被调用,用来初始化对象的。
(3)del 是对象将要被销毁的时候被调用,用来将对象所占用的内存资源释放给操作系统,相当于C++中的析构函数。
魔法方法 | 什么时候被调用 | 解释 |
---|---|---|
new(cls [,...]) | instance = MyClass(arg1, arg2) | __new__在实例创建时调用 |
init(self [,...]) | instance = MyClass(arg1,arg2) | __init__在实例创建时调用 |
cmp(self) | self == other, self > other 等 | 进行比较时调用 |
pos(self) | +self | 一元加法符号 |
neg(self) | -self | 一元减法符号 |
invert(self) | ~self | 按位取反 |
index(self) | x[self] | 当对象用于索引时 |
nonzero(self) | bool(self) | 对象的布尔值 |
getattr(self, name) | self.name #name不存在 | 访问不存在的属性 |
setattr(self, name) | self.name = val | 给属性赋值 |
_delattr(self, name) | del self.name | 删除属性 |
getattribute(self,name) | self.name | 访问任意属性 |
getitem(self, key) | self[key] | 使用索引访问某个元素 |
setitem(self, key) | self[key] = val | 使用索引给某个元素赋值 |
delitem(self, key) | del self[key] | 使用索引删除某个对象 |
iter(self) | for x in self | 迭代 |
contains(self, value) | value in self, value not in self | 使用in进行成员测试 |
call(self [,...]) | self(args) | “调用”一个实例 |
enter(self) | with self as x: | with声明的上下文管理器 |
exit(self, exc, val, trace) | with self as x: | with声明的上下文管理器 |
getstate(self) | pickle.dump(pkl_file, self) | Pickling |
setstate(self) | data = pickle.load(pkl_file) | Pickling |
个人理解:
注册功能:先去urls.py
中导入from app01 import views
定义路由url(r'^register/',views.register)
,然后去views中定义函数
默认导入render是因为render用的比较多,页面交互。然后导入HttpResponse,redirect
def register(request):
return render(request,'register.html')
然后去templates文件夹中新建一个register.html
,定义一个form表单,定义两个p标签(p*2>input),再定义一个input标签按钮
<body>
<form action="" method="post"></form>
<p>username<input type="text" name="username"></p>
<p>password<input type="text" name="password"></p>
<input type="submit">
</body>
运行项目,然后在网页上输入register,有一个需求,在框中输入信息,然后提交,朝后端发送post请求,
然后判断用户名和密码合不合法,如果不合法,在输入框后面提示相应的信息(不能用前端代码做或ajax,只能用后端代码做)
def register(request):
errors = {'username':'','password':''}
if request.method == "POST":
username = request.POST.get('username')
password = request.POST.get('password')
if 葫芦娃' in username:
errors['username'] = '输入的内容不符合'
if len(password) < 4:
errors['password'] = '输入密码太短了'
# errors = errors = {'username':'不符合社会主义核心价值观','password':'太短了 不好!'}
return render(request,'register.html',locals())
可以在register.html
中,分别在username
和password
行内,添加一个span行内标签,什么都不写,在返回的网页后面加,locals()
,注意locals()前面是逗号,然后定义一个字典,value为空,无论返回的是post请求还是get请求,都会通过locals()传到页面上,然后去register.html
中分别在span(行内标签)中添加{{errors,username}}
和``{{errors,password}}`然后去views中第二三条if判断语句下分别添加errors['username'] = '输入的内容不符合';errors['password'] = '输入密码太短了'
总结步骤:
1.手动书写html代码获取用户输入——>渲染标签
2.将数据传递给后端做数据校验——>校验数据
3.如果数据有错误,你还展示了错误信息——>展示信息
使用forms组件的第一步必须先在views中写一个类from django import forms
from django import forms
class MyForm(forms.Form):
username = forms.CharField(max_length=8,min_length=3)
#最大8位,最少3位
password = forms.CharField(max_length=8,min_length=3)
email = forms.EmailField()#必须是邮箱格式
相当于搭建了测试环境,想测试哪个文件,点击在pycharm下面的python console,输入from app01 import views,用上面定义的类产生一个对象,继续输入from_obj = views.MyForm({'username':'jason','password':'12','email':'123'})(解析:在引用对象里面需要添加一个字典,里面放键值对)