C++ 是一门面向对象的编程语言,理解 C++,首先要理解类(Class)和对象(Object)这两个概念。
C++ 中的类(Class)可以看做C语言中结构体(Struct)的升级版。结构体是一种构造类型,可以包含若干成员变量,每个成员变量的类型可以不同;可以通过结构体来定义结构体变量,每个变量拥有相同的性质。例如:
#include <stdio.h>
//定义结构体 Student
struct Student{
//结构体包含的成员变量
char *name;
int age;
float score;
};
//显示结构体的成员变量
void display(struct Student stu){
printf("%s的年龄是 %d,成绩是 %f\n", stu.name, stu.age, stu.score);
}
int main(){
struct Student stu1;
//为结构体的成员变量赋值
stu1.name = "小明";
stu1.age = 15;
stu1.score = 92.5;
//调用函数
display(stu1);
return 0;
}
运行结果:
小明的年龄是 15,成绩是 92.500000
C++ 中的类也是一种构造类型,但是进行了一些扩展,类的成员不但可以是变量,还可以是函数;通过类定义出来的变量也有特定的称呼,叫做“对象”。例如:
#include <stdio.h>
//通过class关键字类定义类
class Student{
public:
//类包含的变量
char *name;
int age;
float score;
//类包含的函数
void say(){
printf("%s的年龄是 %d,成绩是 %f\n", name, age, score);
}
};
int main(){
//通过类来定义变量,即创建对象
class Student stu1; //也可以省略关键字class
//为类的成员变量赋值
stu1.name = "小明";
stu1.age = 15;
stu1.score = 92.5f;
//调用类的成员函数
stu1.say();
return 0;
}
运行结果与上例相同。
对于熟悉 C++ 的读者,这段代码并不规范,请忽略这一细节,本节的重点是引入类和对象的概念。这套 C++ 教程是在C的基础上编写的,我并不希望一开始就提出过多的 C++ 概念,让读者吃不消,我希望读者从C逐渐过渡到 C++,从第二章开始编写规范的 C++ 代码。也就是说,包括本节在内,第一章有很多不规范的 C++ 代码,再次请熟悉 C++ 的读者忽略。
class 和 public 都是 C++ 中的关键字,初学者请先忽略 public(后续会深入讲解),把注意力集中在 class 上。
C语言中的 struct 只能包含变量,而 C++ 中的 class 除了可以包含变量,还可以包含函数。display() 是用来处理成员变量的函数,在C语言中,我们将它放在了 struct Student 外面,它和成员变量是分离的;而在 C++ 中,我们将它放在了 class Student 内部,使它和成员变量聚集在一起,看起来更像一个整体。
结构体和类都可以看做一种由用户自己定义的复杂数据类型,在C语言中可以通过结构体名来定义变量,在 C++ 中可以通过类名来定义变量。不同的是,通过结构体定义出来的变量还是叫变量,而通过类定义出来的变量有了新的名称,叫做对象(Object)。
在第二段代码中,我们先通过 class 关键字定义了一个类 Student,然后又通过 Student 类创建了一个对象 stu1。变量和函数都是类的成员,创建对象后就可以通过点号.来使用它们。
可以将类比喻成图纸,对象比喻成零件,图纸说明了零件的参数(成员变量)及其承担的任务(成员函数);一张图纸可以生产出多个具有相同性质的零件,不同图纸可以生产不同类型的零件。
类只是一张图纸,起到说明的作用,不占用内存空间;对象才是具体的零件,要有地方来存放,才会占用内存空间。
在 C++ 中,通过类名就可以创建对象,即将图纸生产成零件,这个过程叫做类的实例化,因此也称对象是类的一个实例(Instance)。
有些资料也将类的成员变量称为属性(Property),将类的成员函数称为方法(Method)。
面向对象编程(Object Oriented Programming,OOP)
类是一个通用的概念,C++、Java、C#、PHP 等很多编程语言中都支持类,都可以通过类创建对象。可以将类看做是结构体的升级版,C语言的晚辈们看到了C语言的不足,尝试加以改善,继承了结构体的思想,并进行了升级,让程序员在开发或扩展大中型项目时更加容易。
因为 C++、Java、C#、PHP 等语言都支持类和对象,所以使用这些语言编写程序也被称为面向对象编程,这些语言也被称为面向对象的编程语言。C语言因为不支持类和对象的概念,被称为面向过程的编程语言。
在C语言中,我们会把重复使用或具有某项功能的代码封装成一个函数,将拥有相关功能的多个函数放在一个源文件,再提供一个对应的头文件,这就是一个模块。使用模块时,引入对应的头文件就可以。
而在 C++ 中,多了一层封装,就是类(Class)。类由一组相关联的函数、变量组成,你可以将一个类或多个类放在一个源文件,使用时引入对应的类就可以。下面是C和C++项目组织方式的对比:
热门标签
快讯
MySQL TRUNCATE:清空表记录
MySQL 提供了 DELETE 和 TRUNCATE 关键字来删除表中的数据。本节主要讲解 TRUNCATE 关键字的使用。
TRUNCATE 关键字用于完全清空一个表。其语法格式如下:TRUNCATE [TABLE] 表名
其中,TABLE 关键字可省略。
例 1
新建表 tb_student_course,插入数据并查询,SQL 语句和运行结果如下:mysql> CREATE TABLE `tb_student_course` ( -> `id` int(4) NOT NULL AUTO_INCREMENT, -> `name` varchar(25) NOT NULL, -> PRIMARY KEY (`id`) -> ); Query OK, 0 rows affected (0.04 sec) mysql> INSERT INTO tb_student_course(name) VALUES ('Java'),('MySQL'),('Python'); Query OK, 3 rows affected (0.05 sec) Records: 3 Duplicates: 0 Warnings: 0 mysql> SELECT * FROM tb_student_course; +----+--------+ | id | name | +----+--------+ | 1 | Java | | 2 | MySQL | | 3 | Python | +----+--------+ 3 rows in set (0.00 sec)使用 TRUNCATE 语句清空 tb_student_course 表中的记录,SQL 语句和运行结果如下:mysql> TRUNCATE TABLE tb_student_course; Query OK, 0 rows affected (0.04 sec) mysql> SELECT * FROM tb_student_course; Empty set (0.00 sec)
TRUNCATE 和 DELETE 的区别
从逻辑上说,TRUNCATE 语句与 DELETE 语句作用相同,但是在某些情况下,两者在使用上有所区别。
- DELETE 是 DML 类型的语句;TRUNCATE 是 DDL 类型的语句。它们都用来清空表中的数据。
- DELETE 是逐行一条一条删除记录的;TRUNCATE 则是直接删除原来的表,再重新创建一个一模一样的新表,而不是逐行删除表中的数据,执行数据比 DELETE 快。因此需要删除表中全部的数据行时,尽量使用 TRUNCATE 语句, 可以缩短执行时间。
- DELETE 删除数据后,配合事件回滚可以找回数据;TRUNCATE 不支持事务的回滚,数据删除后无法找回。
- DELETE 删除数据后,系统不会重新设置自增字段的计数器;TRUNCATE 清空表记录后,系统会重新设置自增字段的计数器。
- DELETE 的使用范围更广,因为它可以通过 WHERE 子句指定条件来删除部分数据;而 TRUNCATE 不支持 WHERE 子句,只能删除整体。
- DELETE 会返回删除数据的行数,但是 TRUNCATE 只会返回 0,没有任何意义。
总结
当不需要该表时,用 DROP;当仍要保留该表,但要删除所有记录时,用 TRUNCATE;当要删除部分记录时,用 DELETE。Django数据表关联关系映射(一对一、一对多、多对多)
我们知道涉及到数据表之间的对应关系就会想到一对一、一对多、多对多,在学习 MySQL 数据库时表关系设计是需要重点掌握的知识。Django 中定义了三种关系类型的字段用来描述数据库表的关联关系:一对多(Foreignkey)、一对一(OneToOneFiled)、以及多对多(ManyToManyFiled),在本节我们对它们做简单的介绍。1. 一对多关系类型
这种类型在数据库中体现是外键关联关系,它在和其他的 Model 建立关联同时也和自己建立关联,用来描述一对多的关系,例如一个作者可以写很多不同的书,但是这些书又只能对应这一个作者,再比如一本图书只能属于一个出版社,一个出版社可以出版很多不同种类的图书,这就是一对多的关系。Django 会自动将字段的名称添加“_id”作为列名,ForgienKey 的定义如下:
class django.db,model.ForeignKey(to,on_delete,**options)
1) 必填参数
它有两个必填参数。to,指定所关联的 Model,它的中取值可以是直接引用其他的 Model,也可以是 Model 所对应的字符串名称;on_delete,当删除关联表的数据时,Django 将根据这个参数设定的值确定应该执行什么样的 SQL 约束。
on_delete 可以理解为 MySQL 外键的级联动作,当主表执行删除操作时对子表的影响,即子表要执行的操作,Django 提供的可选值如下所示:- CASCADE,级联删除,它是大部分 ForeignKey 的定义时选择的约束。它的表现是删除了“主”,则“子”也会被自动删除。
- PROTECT,删除被引用对象时,将会抛出 ProtectedError 异常。当主表被一个或多个子表关联时,主表被删除则会抛出异常。
- SET_NULL,设置删除对象所关联的外键字段为 null,但前提是设置了选项 null 为True,否则会抛出异常。
- SET_DEFAULT:将外键字段设置为默认值,但前提是设置了 default 选项,且指向的对象是存在的。
- SET(value):删除被引用对象时,设置外键字段为 value。value 如果是一个可调用对象,那么就会被设置为调用后的结果。
- DO_NOTHING:不做任何处理。但是,由于数据表之间存在引用关系,删除关联数据,会造成数据库抛出异常。
2) 可选参数
除了必填参数以外,ForeignKey 还有一些常用的可选参数需要关注。如下所示:- to_field:关联对象的字段名称。默认情况下,Django 使用关联对象的主键(大部分情况下是 id),如果需要修改成其他字段,可以设置这个参数。但是,需要注意,能够关联的字段必须有 unique=True 的约束。
- db_constraint:默认值是 True,它会在数据库中创建外键约束,维护数据完整性。通常情况下,这符合大部分场景的需求。如果数据库中存在一些历史遗留的无效数据,则可以将其设置为 False,这时就需要自己去维护关联关系的正确性了。
- related_name:这个字段设置的值用于反向查询,默认不需要设置,Django 会设置其为“小写模型名 _set”。
- related_query_name:这个名称用于反向过滤。如果设置了 related_name,那么将用它作为默认值,否则 Django 会把模型的名称作为默认值。
3) 语法格式
#一个A类实例对象关联多个B类实例对象 class A(model.Model): .... class B(model.Model): 属性 = models.ForeignKey(多对一中"一"的模型类, ...)4) 实例应用
修改原来在《Django ORM进阶之项目实战》定义的代码,将出版社与图书之间修改为一对多的关系,添加如下代码:from django.db import models #新建出版社表 class PubName(models.Model): pubname=models.CharField('名称',max_length=255,unique=True) #更改书籍信息表 class Book(models.Model): title=models.CharField(max_length=30,unique=True, verbose_name='书名') price=models.DecimalField(max_digits=7,decimal_places=2,verbose_name='定价') #添加默认价格 def default_price(self): return '¥30' #零售价格 retail_price=models.DecimalField(max_digits=7,decimal_places=2,verbose_name='零售价',default=default_price) pub=models.ForeignKey(to=PubName,on_delete=models.CASCADE ,null=True) #创建Foreign外键关联pub,以pub_id关联 def __str__(self) return "title:%s pub:%s price:%s" % (self.title, self.pub, self.price)此处需要注意每次更改完 models 都需要进行数据库迁移操作,依次执行以下命令即可:python manager.py makemigrations
插入数据创建一对多对象,如下所示:
python manager.py migrate#创建PubName实例化对象pub1并插入书籍信息 pub1=PubName.objects.create(pubname="清华出版社") Book.objects.create(title="Python",price="59.00",retail_price="59.00",pub=pub1) Book.objects.create(title="Redis",price="25.00",retail_price="25.00",pub=pub1) Book.objects.create(title="Java",price="45.00",retail_price="45.00",pub=pub1) #创建PubName实例化对象pub2并插入书籍信息 pub2=PubName.objects.create(pubname="c出版") Book.objects.create(title="Django",price="65.00",retail_price="65.00",pub=pub2) Book.objects.create(title="Flask",price="45.00",retail_price="45.00",pub=pub2) Book.objects.create(title="Tornado",price="35.00",retail_price="35.00",pub=pub2)
访问 MySQL 数据库分别查询 index_book、index_pubname 数据表(如下所示),index_pubname 数据表的 id 字段作为唯一值关联多个书籍信息,ForeignKey 外键关联键自动在 index_book 表中生成 pub_Id 字段并作为关联字段。此时 index_pubname 作为主表而 index_book 是子表,主表的 id 是子表的外键,两者之间存在外键约束 CASCADE。
mysql> select * from index_book; +----+---------+--------+-------+--------------+ | id | title | pub_id | price | retail_price | +----+---------+--------+-------+--------------+ | 1 | Python | 1 | 59.00 | 59.00 | | 2 | Redis | 1 | 25.00 | 25.00 | | 3 | Java | 1 | 45.00 | 45.00 | | 4 | Django | 2 | 65.00 | 65.00 | | 5 | Flask | 2 | 45.00 | 45.00 | | 6 | Tornado | 2 | 35.00 | 35.00 | +----+---------+--------+-------+--------------+ 6 rows in set (0.01 sec) mysql> select * from index_pubname; +----+-----------------+ | id | pubname | +----+-----------------+ | 1 | c出版 | | 2 | 清华出版社 | +----+-----------------+ 6 rows in set (0.00 sec)
2. 一对一关系类型
OneToOneFiled 继承自 ForeignKey,在概念上,它类似 unique=Ture 的 ForeignKey,它与 ForeignKey 最显著的区别在于反向查询上,ForeignKey 反向查询返回的是一个对象实例列表,而 OneToOneFiled 反向查询返回的是一个对象实例。
一对关系类型的使用和场景相对其他两种关联关系要少,经常用于对已有 Model 的扩展,例如我们可以对 UserInfo 表进行扩展,添加类似用户昵称、个性签名等字段。此时就可以新建一个 Model,并定义一个字段与 UserInfo 表一对一关联。这样就实现了用户信息拓展表与 UserInfo 表一对一关联,下面会用通过实例进行说明。
1) 语法格式
class A(model.Model): ... class B(model.Model): 属性 = models.OneToOneField(A)2) 实例应用
新建 index\models.py 下添加以下代码:#新建一对一关用户信息表拓展表,添加完成后执行数据库迁移同步操作 class ExtendUserinfo(models.Model): user=models.OneToOneField(to=UserInfo,on_delete=models.CASCADE) signature=models.CharField(max_length=255,verbose_name='用户签名',help_text='自建签名') nickname=models.CharField(max_length=255,verbose_name='昵称',help_text='自建昵称')使用 Django shell 创建数据,如下所示:
from index.models import UserInfo,ExtendUserinfo username=UserInfo.objects.create(username="xiaoming",password="******") username=UserInfo.objects.create(username="xiaohong",password="*******",gender="F") #创建一对一表关联 ExtendUserinfo.objects.create(user=username,signature="good good study,day day up",nickname="XH")
3) MySQL数据表显示
最后通过访问 MySQL 数据库,我们可以得到如下所示数据表,使用 user_id 进行表之间的关联:mysql> select * from index_userinfo; +----+----------+----------+--------+ | id | username | password | gender | +----+----------+----------+--------+ | 1 | xiaoming | ****** | M | | 2 | xiaohong | ******* | F | +----+----------+----------+--------+ 2 rows in set (0.00 sec) mysql> select * from index_extenduserinfo; +----+----------------------------+----------+---------+ | id | signature | nickname | user_id | +----+----------------------------+----------+---------+ | 1 | good good study,day day up | XH | 2 | +----+----------------------------+----------+---------+ 1 row in set (0.00 sec)
3. 多对多关系类型
多对多关系也是比较常见的,比如一个作者可以写很多本书,一本书也可以由很多作者一起完成,那么这时候 Author 和 Book 之间就是多对多的关系。 Django 通过中间表的方式来实现 Model 之间的多对多的关系,这和 MySQL 中实现方式是一致的。这个中间表我们可以自己提供,也可以使用 Django 默认生成的中间表。
1) ManyToManyFiled定义
class django.db.models.ManyToManyFiled(to,**options)
它只有一个必填的参数即 to,与其他两个关联词在一样,用来指定与当前的 Model 关联的 Model。2)可选参数
当然 ManyToManyFiled 还有一些重要的可选参数,下面我们对它们依次进行介绍:- relate_name 与 ForeignKey 中的相同都用于反向查询。
- db_table 用于指定中间表的名称,如果没有提供,Django 会使用多对多字段的名称和包含这张表的 Model 的名称组合起来构成中间表的名称,当然也会包含 index 前缀。
- through 用于指定中间表,这个参数不需要设置,Django会自动生成隐式的 through Model。由于 Django可以生成自身默认的中间表,该参数可以让用户自己去控制表之间的关联关系或者增加一些额外的信息。
3) 语法格式
class Author(models.Model): ... class Book(models.Model): ... authors = models.ManyToManyField(Author)4) 多对多中间表
创建 Author 与 Book 之间多对多关联关系,在 Author Model 中添加如下代码:books=models.ManyToManyField(to="Book") #创建多对多映射关系
然后再执行数据库迁移命令,我们可以执行以下命令可以查看 Django 执行 sql 语句:
python manage.py sqlmigrate index 0007_author_books
sql 语句如下所示:CREATE TABLE `index_author_books` (`id` integer AUTO_INCREMENT NOT NULL PRIMARY KEY, `author_id` integer NOT NULL, `book_id` integer NOT NULL); ALTER TABLE `index_author_books` ADD CONSTRAINT `index_author_books_author_id_2bfd143c_fk_index_author_id` FOREIGN KEY (`author_id`) REFERENCES `index_author` (`id`); ALTER TABLE `index_author_books` ADD CONSTRAINT `index_author_books_book_id_1c280bc9_fk_index_book_id` FOREIGN KEY (`book_id`) REFERENCES `index_book` (`id`); ALTER TABLE `index_author_books` ADD CONSTRAINT `index_author_books_author_id_book_id_b0dd3503_uniq` UNIQUE (`author_id`, `book_id`);
由 sql 语句可以看出,Django 默认隐式的创建了 index_author_books 表(Django 的命名规范),即维护关联关系的中间表。这个表有三个字段分别是主键 id,与index_author 表关联的 author_id,以及 index_book 表关联的 book_id。同时为这两个关联 id 创建了外键约束(FORGIEN KEY),最后还为 index_author_books 表创建了唯一性约束 author_id 和 book_id。
上面介绍了 Django 自身默认创建中间表的过程,当然我们也可以自己建立中间表,然后通过 Author 表中 books 字段的 through 参数指向这张中间表。如果大家对于 MySQL 自建中间表或其它知识不熟悉,推荐学习本网站的《MySQL教程》来查漏补缺。5) 实例应用
插入作者信息数据,如下所示:author1=Author.objects.create(name="Luncy",email="123456@qq.com") author2=Author.objects.create(name="Tom",email="456789@163.com")
因为书籍信息之前已经准备完毕,所以下面我们开始创建多对多映射关系,我们在 Django shell 进行如下操作:author1.books.add(Book.objects.get(id="1")) author1.books.add(Book.objects.get(id="2")) author1.books.add(Book.objects.get(id="3")) author2.books.add(Book.objects.get(id="1")) author2.books.add(Book.objects.get(id="4")) author2.books.add(Book.objects.get(id="5")) author2.books.add(Book.objects.get(id="3")) author2.books.add(Book.objects.get(id="6")) author1.books.add(Book.objects.get(id="6"))
多对多关系在中间表插入数据需要使用 add() 方法,books 是对应的多对多字段。通过以上代码就完成多对多关系的创建,最后在 MySQL 中查看多对多相关联的三张数据表,如下所示:#书籍信息表(index_book) +----+---------+--------+-------+--------------+ | id | title | pub_id | price | retail_price | +----+---------+--------+-------+--------------+ | 1 | Python | 8 | 59.00 | 59.00 | | 2 | Redis | 8 | 25.00 | 25.00 | | 3 | Java | 8 | 45.00 | 45.00 | | 4 | Django | 9 | 65.00 | 65.00 | | 5 | Flask | 9 | 45.00 | 45.00 | | 6 | Tornado | 9 | 35.00 | 35.00 | +----+---------+--------+-------+--------------+ #作家信息表(index_author) +----+-------+----------------+ | id | name | email | +----+-------+----------------+ | 1 | Luncy | 123456@qq.com | | 2 | Tom | 456789@163.com | +----+-------+----------------+ #中间表(index_author_books) +----+-----------+---------+ | id | author_id | book_id | +----+-----------+---------+ | 4 | 1 | 1 | | 7 | 1 | 3 | | 5 | 1 | 4 | | 6 | 1 | 5 | | 8 | 1 | 6 | | 1 | 2 | 1 | | 2 | 2 | 2 | | 3 | 2 | 3 | | 9 | 2 | 6 | +----+-----------+---------+
本节用了较长的篇幅给大家讲解了 Django 中数据表的关联关系,它和 MySQL 的思想是一致的,只是 Django 提供了自己的一套方法,所以我们也要学会使用它,在后续章节我们将基于此节的内容介绍 Django QuerySet API 即与数据库接口相关的表查询、更新、删除操作。C++ const常量在多文件编程中的3种用法
《C++多文件编程是什么》一节提到,多文件编程中代码的划分原则是:将变量、函数或者类的声明部分存放在 .h 文件,对应的实现部分放在 .cpp 文件中。值得一提得是,此规律适用于大部分场景,但本节要讲的 const 常量是一个例外。
我们知道,用 const 修饰的变量必须在定义的同时进行初始化操作(除非用 extern 修饰,本节后续会讲解)。与此同时,C++ 中 const 关键字的功能有 2 个,除了表明其修饰的变量为常量外,还将所修饰变量的可见范围限制为当前文件。这意味着,除非 const 常量的定义和 main 主函数位于同一个 .cpp 文件,否则该 const 常量只能在其所在的 .cpp 文件中使用。
那么,如何定义 const 常量,才能在其他文件中使用呢?接下来给读者介绍 3 种在 C++ 多文件编程中定义 const 常量的方法。
1) 将const常量定义在.h头文件中
首先介绍一种最常用也最简单的方法,就是将 const 常量定义在 .h 文件中。
举个例子(实例一):显然此方式违背了“声明位于 .h 文件,定义(实现)位于 .cpp 文件”的规律。在 C++ 多文件编程中,还有 2 种特殊情况是违背此规律的,分别是类的定义和内联函数的定义,通常情况下它们也都定义在 .h 文件中。
//demo.h #ifndef _DEMO_H #define _DEMO_H const int num = 10; #endif //main.cpp #include <iostream> #include"demo.h" int main() { std::cout << num << std::endl; return 0; }项目执行结果为:10
可以看到,使用此方式定义的 const 常量,只需引入其头文件(比如这里的 demo.h),就可以使用它。注意,将 const 常量定义在 .h 文件中,为了避免头文件被重复引入,推荐使用 #ifndef/#define/#endif 结构。除此之外,还有 2 中方式可以避免头文件被重复引入,读者可阅读《C++防止头文件被重复引入的3种方法》一文做详细了解。
2) 借助extern先声明再定义const常量
当然,const 常量的定义也可以遵循“声明在 .h 文件,定义在 .cpp 文件”,借助 extern 关键字即可。例如(实例二):
//demo.h #ifndef _DEMO_H #define _DEMO_H extern const int num; //声明 const 常量 #endif //demo.cpp #include "demo.h" //一定要引入该头文件 const int num =10; //定义 .h 文件中声明的 num 常量 //main.cpp #include <iostream> #include "demo.h" int main() { std::cout << num << std::endl; return 0; }项目执行结果为:10
前面讲过,C++ const 关键字会限定变量的可见范围为当前文件,即无法在其它文件中使用该常量。而 extern 关键字会 const 限定可见范围的功能,它可以使 const 常量的可见范围恢复至整个项目。
3) 借助extern直接定义const常量
值得一提的是,第 2 种使用 extern 定义 const 常量的方式,还可以做进一步优化。
以实例二为例,demo.cpp 和 main.cpp 中都引入了 demo.h,而 demo.h 文件中只包含对 const num 常量的声明。要知道 C++ 编译器在运行项目时,会在预处理阶段直接将 #include 引入的头文件替换成该头文件中的内容(就可以理解为傻瓜式的复制粘贴)。
因此,我们可以将实例二中的项目做如下修改:
//demo.cpp extern const int num =10; //main.cpp #include <iostream> extern const int num; int main() { std::cout << num << std::endl; return 0; }项目执行结果为:10
显然相比实例二,此项目中省略了 demo.h 头文件的创建,一定程序上提高了项目的编译效率。
本节介绍了 3 种在多文件编程中使用 const 常量的方法,相比后 2 种借助 extern 修饰 const 常量的方式,第一种方式更简单、更常用,推荐读者使用。
PHP屏蔽错误
在编写 PHP 代码的时候难免会出现错误,当很多错误信息堆积到一起的时候,可能会使我们感到反感,而且错误信息还会影响用户体验。最重要的是,如果这些错误被有心人士看到的话,可能对整个程序带来威胁,那么我们应该如何屏蔽 PHP 中的错误呢?
PHP 中屏蔽错误的方法有 3 中,下面就来分别介绍一下。
1、使用错误控制运算符:@
PHP 支持使用错误控制运算符@。将其放置在一个 PHP 表达式之前,该表达式可能产生的任何错误信息都将被忽略掉。
如果用 set_error_handler() 设定了自定义的错误处理函数,这个错误处理函数仍然会被调用,而如果在出错语句前使用了@的话,错误处理函数将返回 0。
需要注意的是,@运算符只对表达式有效。简单来说就是,如果能从某处得到值,就能在它前面加上 @ 运算符。例如可以在变量、函数、include 调用、常量等等之前使用 @ 运算符,但不能把它放在函数或类的定义之前,也不能用于条件结构例如 if 和 foreach 等语句前。
@运算符对于可以导致程序终止的严重错误也是有效的,这意味着如果在某个不存在或者敲错了字母的函数调用前用了@来抑制错误信息,那么程序将没有任何提示的死在那里。
【示例】使用 @ 错误控制运算符屏蔽代码中的错误。<?php $link = @mysqli_connect("127.0.0.1", "my_user", "my_password", "my_db") or die('数据库连接失败!'); ?>运行结果如下:数据库连接失败!
2、使用 error_reporting() 函数屏蔽错误
PHP 有诸多错误级别,使用 error_reporting() 函数可以设置 PHP 会报告何种错误,函数的语法格式如下:
error_reporting([int $level])
其中参数 $level 为设置错误级别,具体可以使用的值可以在《PHP错误级别》一节中查看。如果将 $level 设置为 0,将关闭所有 PHP 错误报告;如果设置为 -1,将返回所有的错误报告。
【示例】使用 error_reporting() 函数屏蔽代码中的错误。<?php error_reporting(0); $link = mysqli_connect("127.0.0.1", "my_user", "my_password", "my_db") or die('数据库连接失败!'); ?>3、通过 display_errors 参数屏蔽错误
这种方法应该是最彻底的一种解决办法,因为前两种方法只能作用于单行或者单个文件,而这个则是作用于所有的 PHP 文件。
具体的操作方法就是,打开 php.ini 配置文件,在其中搜索 display_errors,然后将 display_errors 的值设置为 Off 即可关闭所有的 PHP 错误报告。如下所示:
; This directive controls whether or not and where PHP will output errors,
; notices and warnings too. Error output is very useful during development, but
; it could be very dangerous in production environments. Depending on the code
; which is triggering the error, sensitive information could potentially leak
; out of your application such as database usernames and passwords or worse.
; For production environments, we recommend logging errors rather than
; sending them to STDOUT.
; Possible Values:
; Off = Do not display any errors
; stderr = Display errors to STDERR (affects only CGI/CLI binaries!)
; On or stdout = Display errors to STDOUT
; Default Value: On
; Development Value: On
; Production Value: Off
; http://php.net/display-errors
display_errors = OffDjango实现数据表数据插入(创建Model实例)
在《Django ORM进阶之项目实战》一节,我们利用 Model 模型类创建了 index 应用所需的三张数据表,在本节我们介绍如何实现在 Django 项目中创建 Model 的实例对象,即向数据表中插入数据。
Django 为创建 Model 实例提供了两种方法,一种是直接调用 Model 的 save 方法,另一种是通过对象管理器(objects Manager) 的 create 方法。这种中方法都可以实现创建 Model 实例对象。下面我们使用 Django shell 交互环境依次对这两种方法进行解析。
Django shell 是 Django 提供一种交互环境,类似于 Python shell,开发人员可直接通过 shell 环境 与 Django 项目进行交互,它一种很实用的工具。
1. save方法创建Model实例
在 cmd 命令行进入项目 manage.py 目录下,然后使用如下命令进入 Django shell 环境。
python manage.py shell
然后创建 Model 实例,使用 save() 方法保存数据,如下所示:from index.models import Book #导入相关model类 #创建Book实例化对象 book=Book(title="Python",public="a",price="59.00",retail_price="59.00") book.save()# 调用save方法进行保存 book=Book(title="Flask",public="b",price="39.00",retail_price="39.00") book.save()# 调用save方法进行保存 book=Book(title="Django",public="b",price="40.00",retail_price="40.00") book.save()#
最后调用 save 方法进行保存。通过上述代码,我们就在数据库中添加了三条有关书籍的记录。
创建 Book 实例,其实就是填充有关 Model 实例的各个字段,如果设置了默认值或者有允许为 null 的字段可以不填,最后调用 save() 保存实例(该方法没有返回值)。在 save() 执行以后,Django 会将 book实例化数据保存到数据库中。
注意,每创建一条数据都需要调用一次 save() 方法,我们通过查看数据库可以得知数据信息已经保存完毕。如下所示:mysql> select * from index_book; +----+--------+--------+-------+--------------+ | id | title | public | price | retail_price | +----+--------+--------+-------+--------------+ | 1 | Python | a | 59.00 | 59.00 | | 2 | Flask | b | 39.00 | 39.00 | | 3 | Django | b | 40.00 | 40.00 | +----+--------+--------+-------+--------------+ 3 rows in set (0.00 sec)
避坑提示,若在实例化数据对象的过程中,Django shell 出现以这样的报错信息:
InternalError: (1366, "Incorrect string value: '\\xE8\\xAF\\xAD\\xE8\\xA8\\x80...' for column 'public' at row 1")
说明我们添加的 public 字段的信息超过可字段本身的最大长度限制,即 manx_length=50。解决方法是将字段 max_length 值变大然后需要重新执行数据库迁移的操作,或者将你输入的信息缩短从而符合其规定的长度。
save() 方法还可以保存已有对象更改后的信息,比如:book.title="JavaScript" book.save()
2. create方法创建Model实例
除了以上方法外,我们也可以使用管理器对象提供的 create() 方法,使用方法如下所示:In [1]: Book.objects.create(title="Java",public="a",price="30.00",retail_price="30.00") Out[1]: <Book: title:Java public:a price:30.00> #执行create方法存在返回值
然后继续插入如下数据:from index.models import Book In [2]: Book.objects.create(title="MySQL",public="c",price="35.00",retail_price="35.00") Out[2]: <Book: title:MySQL public:c price:35.00> In [3]: Book.objects.create(title="C#",public="a",price="45.00",retail_price="40.00") Out[3]: <Book: title:C# public:a price:45.00> In [4]: Book.objects.create(title="Redis",public="c",price="25.00",retail_price="25.00") Out[4]: <Book: title:Redis public:c price:25.00>
这种创建 Model 实例对象的过程比较简单,create 执行之后就在数据库中插入了一条记录,这也是我们推荐使用的方法。最后访问数据库可以看到最新插入的数据记录:mysql> select * from index_book; +----+--------+--------+-------+--------------+ | id | title | public | price | retail_price | +----+--------+--------+-------+--------------+ | 1 | Python | a | 59.00 | 59.00 | | 2 | Flask | b | 39.00 | 39.00 | | 3 | Django | b | 40.00 | 40.00 | | 4 | Java | a | 30.00 | 30.00 | | 5 | MySQL | c | 35.00 | 35.00 | | 6 | C# | a | 45.00 | 40.00 | | 7 | Redis | c | 25.00 | 25.00 | +----+--------+--------+-------+--------------+
本节我们完成了向数据表插入数据记录的操作,不管是在 shell 环境下,还是项目的代码中都可以使用以上两种方法实现创建 Model 实例。后续章节会将涉及 Django 表的数据查询,我们还会用到本节内容。Django查询数据库操作详解(一)
在 web 开发过程中,Django 与后台数据库的交互是必不可少的一项,也是实现业务逻辑所需数据的重要方式,所以 Django 的表查询操作就显的尤为重要。在本节我们将全面阐述 Django 的表查询 API。本节知识属于重中之重,希望各位小伙伴能够尽可能的掌握这些 API,懂得活学活用,熟悉每个 API 的使用场景,这将对后续学习 Django 框架有很大的帮助。
1. 返回单条查询结果
我们知道 Manager 查询管理器提供了查询 Model 实例的接口,这些接口通常会返回三种类型:单实例、RawQuerySet、QuerySet。通常的实际的开发工作中,我们会根据给定的条件查询数据库记录。 Django 为实现返回单条查询数据提供了两个查询方法 get 和 get_or_create。
get_or_create方法和 get 区别在于,当被查询数据不存在的时候,get_or_create 方法会创建新的实例对象,而 get 方法会抛出DoesNotExist异常。而当这两个方法的查询条件都能够匹配多条数据记录时,都会抛出MultipleObjectsReturned异常。
提示:这两个方法都只能返回一条数据。
1) 使用get查询
因为 get 查询可能会抛出异常,所以若在项目使用 get 查询的时,经常会与 try..except 异常处理语句一起使用。示例如下:#使用 Django shell Author.objects.create(name="Tom",email="456789@163.com") #添加 Tom 此时数据表有两个Tom Author.objects.get(name="Tom") #查询 name="Tom",就会报错 #报错信息如下 MultipleObjectsReturned: get() returned more than one Author -- it returned 2! #查询不存在数据 Author.objects.get(id=4) #报错信息如下: DoesNotExist:Traceback (most recent call last)
2) 使用get_or_create查询
该方法的查询过程与 get 类似,都需要传递查询参数,但是不 get 不同的是,它返回值是一个 tuple 对象,通过举例子来说明。Author.objects.get_or_create(name="Xiaolong")#先查询是否存在若不存在则新建该实例对象 (<Author: 作家:Xiaolong>, True) #返回值是一个元组有两个元素
元组中的第一个元素代表实例对象,第二个元素是布尔值,标识返回的实例对象是否是新创建的,其中 True 代表新建实例对象,False 代表原有实例对象。
查询结果为单实例的类似方法还有 first、last 等方法,用法与语法格式别无二致,在这里就不加赘述了。
2. 原生数据库操作方法
所谓原生数据库操作即使用 SQL 语句来进行数据库的相关的查询操作。那你可能会问,Django 已经有 ORM 了,为什么还会提供原生语句的操作呢?其实这个问题也很容易想明白,ORM 虽然为我解决了大部分的有业务场景,但是对于复杂的查询来说,ORM 还是有点力不从心,有时无法满足开发者的要求或者书写起来比相较 SQL 语句来说很麻烦,那么在这个时候使用原生 SQL 语解决问题就是不错的选择。
在 Django 中,可以使用模型管理器(Marager)的 raw 方法来执行 select 语句进行数据的查询。raw 方法的返回值是一个 RawQuerySet 对象,该对象支持索引和切片,同样也可以对它进行迭代得到 Model 实例对象。但是,与 QuerySet 不同的是,它不能执行 fillter、exclude 等方法。下面通过举例说明 raw 方法如何使用。1) raw方法语法格式
使用 raw 方法的语法格式如下所示:
MyModel.objects.raw('sql语句')
2) raw方法查询实例
添加如下代码在index\views.py 中:def BookName(request): books=Book.objects.raw("select * from index_book") #书写sql语句 return render(request,"index/allbook.html",locals())
在 index\templates\index 中创建 allbook.html,代码如下:{% for book in books %} <p>{{book.title}}</p> {% endfor %}最后配置路由,通过访问 127.0.0.1:8000/index/allbook/,可以得到所有书籍的 title。3) raw方法与SQL注入
在很多场景下,我们需要使用参数化查询,比如根据传递的 title 查询 book 对象,在这个时候我们不能手动填充 SQL 字符串,这会带来 SQL 注入的风险,raw 方法充分考虑到这一点,提供了 params 参数来解决这个问题。对于参数化查询的 SQL 来说,只需要在语句中加上占位符号(%s)即可。如下所示:authors=Author.objects.raw("select id from index_author where name= %s",['Tom']) ...: for author in authors: ...: print('%s:%s'%(author.id,author.name)) 2:Tom #返回结果raw 方法可以接受一个列表或字典类型的参数,并将 SQL 语句中的占位符替换,最终完成查询。4) 游标cursor执行SQL语句
那么执行非查询语句呢?如何使用原生的 SQL 语句呢? Django 提供了游标 cursor 对数据库进行增删改操作,在 Django 中执行非查询语句必须使用游标进行操作。游标 cursor 定义在 django.db.connection 包中,使用前用下面的方式进行导入:
from django.db import connection
用创建 cursor 类的构造函数创建 cursor 对象,再使用 cursor 对象执行 SQL 语句。为确保能够在出现异常时释放 cursor 游标,通常使用 with 语句进行创建操,如下所示:from django.db import connection with connection.cursor() as cur: cur.execute('执行SQL语句')使用示例如下:from django.db import connection with connection.cursor() as cur: #调用游标对象的execute方法,更新author的名字 cur.execute('update index_author set name="Jack" where id=3;') with connection.cursor() as cur: # 删除id为3的一条author记录 cur.execute('delete from index_author where id=3;')虽然 Django 提供了 raw SQL的查询方法以及 cursor 游标对象的使用,但是它们很少的被使用,应为这些方法都需要考虑不同数据库的不同特性。Django 官网建议我们尽量使用 ORM 模块来完成相关操作。
热门文章
- hosteons免费赠送DirectAdmin面板,1G内存即可,支持支付宝微信支付,可看netflix2019-06-05
- MySQL TRUNCATE:清空表记录2022-10-08
- hosteons注册教程:教您如何购买hosteons vps2019-05-18
- hosteons一周年促销:OpenVZ年付9.6美元/KVM年付16.8美元 可看netflix 支持支付宝微信支付2019-05-18
- hosteons补货OpenVZ套餐:洛杉矶机房/无限流量/支付宝微信/可看netflix/18美元/年2019-06-16
- hosteons增加年付套餐免费换IP优惠码/无限流量/支付宝微信/可看netflix/12美元/年起2019-06-17
觉得文章有用就打赏一下文章作者
非常感谢你的打赏,我们将继续提供更多优质内容,让我们一起创建更加美好的网络世界!
支付宝扫一扫

微信扫一扫

