本节内容前端
- 路由系统
- models模型
- admin
- views视图
- template模板
引子
讲django的models以前, 先来想想, 让你经过django操做数据库,你怎么作? 作苦思冥想,可能会这样写。python
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
|
import
pymysql
def
index(request):
# 建立链接
conn
=
pymysql.connect(host
=
'127.0.0.1'
, port
=
3306
, user
=
'root'
, passwd
=
'alex123'
, db
=
'luffy_dev'
)
# 建立游标
cursor
=
conn.cursor()
cursor.execute(
"select username,email,mobile from web_account"
)
data_set
=
cursor.fetchall()
cursor.close()
conn.close()
return
HttpResponse(data_set)
|
很方便就实现了从数据库里取数据,事实上,不少人确实就是这么作的。但这样作会带来2个问题mysql
- SQL注入危险,由于有的时候你操做数据库的语句不是写死在代码里的,而是经过前端传参数拼接的,这就给黑客有了可趁之机,经过拼接参数实现sql注入。
- 语句跟代码揉在一块儿了,增长后续维护成本
那怎么办呢?ORM提供了新思路。linux
什么是ORM呢?
对象关系映射(Object Relational Mapping),它的实质就是将关系数据(库)中的业务数据用对象的形式表示出来,并经过面向对象(Object-Oriented)的方式将这些对象组织起来,实现系统业务逻辑的过程。web
在ORM过程当中最重要的概念是映射(Mapping),经过这种映射可使业务对象与数据库分离。从面向对象来讲,数据库不该该和业务逻辑绑定到一块儿,ORM则起到这样的分离做用,使数据库层透明,开发人员真正的面向对象。sql
上面的解释有点蒙蔽对不?其实你只须要抓住2个关键词, “映射” 和 “对象”,就能知道orm是什么干什么的了。shell
- 映射(Mapping) —— 把表结构映射成类
- 对象 —— 像操做类对象同样,操做数据库里的数据
映射
看下面的图,就是直观的例子,把右边的表结构映射成了左边的类数据库
Sql语句到对象
ORM可使你不用再写原生SQL, 而是像操做对象同样就能够实现对表里数据的增删改查express
好棒棒,妈妈不再用逼你写原生sql啦!django
可是不要开心太早,ORM确实提升了开发效率,而且下降了数据操做与代码之间的耦合,不过有利就有弊,咱们总结一下orm的优缺点。
优势:
- 实现了代码与数据操做的解耦合
- 不需本身写原生sql, 提升开发效率
- 防止SQL注入, 经过对象操做的方式,默认就是防止sql注入的。
缺点:
- 牺牲性能, 对象到原生SQL势必会有转换消耗,对性能有必定的影响
- 复杂语句力不从心, 一些复杂的sql语句,用orm对象操做的方式很难实现,就还得用原生sql
讲Django为何说ORM? 哈, 好啦,是时候该引出主角啦,由于Django的models基于架构ORM实现的。
Models模型
Django 的models把数据库表结构映射成了一个个的类, 表里的每一个字段就是类的属性。咱们都知道数据库有不少字段类型,int,float,char等, Django的models类针对不一样的字段也设置了不一样的类属性。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
|
AutoField
#An IntegerField that automatically increments according to available IDs
BigAutoField
#A 64-bit integer, guaranteed to fit numbers from 1 to 9223372036854775807.
BigIntegerField
#-9223372036854775808 to 9223372036854775807
BinaryField
#A field to store raw binary data. It only supports bytes assignment
BooleanField
CharField
DateField
#e.g 2019-04-27
DateTimeField
#e.g 2019-04-27 17:53:21
DecimalField
DurationField
#storing periods of time ,e.g [DD] [HH:[MM:]]ss[.uuuuuu]"
EmailField
FileField
#存储文件
FloatField
ImageField
#Inherits all attributes and methods from FileField, but also validates that the uploaded object is a valid image.
IntegerField
GenericIPAddressField
#IP地址,支持ipv4
NullBooleanField
#Like a BooleanField, but allows NULL as one of the options
PositiveIntegerField
#Like an IntegerField, but must be either positive or zero (0). Values from 0 to 2147483647
PositiveSmallIntegerField
#only allows positive values from 0 to 32767
SlugField
# A slug is a short label for something, containing only letters, numbers, underscores or hyphens.
SmallIntegerField
TextField
#A large text field.
TimeField
#A time, represented in Python by a datetime.time instance.
URLField
UUIDField
#A field for storing universally unique identifiers. Uses Python’s UUID class.
|
除了普通的表字段,针对外键也有映射
1
2
3
4
|
ForeignKey
# 外键关联
ManyToManyField
#多对多
OneToOneField
# 1对1
|
好啦,接下来就用django的orm来设计一个博客表。
需求
- 每一个用户有本身的帐户信息
- 用户能够发文章
- 文章能够打多个标签
根据需求,咱们设计3张表
注意Article表和Tag表是属于多对多关系,什么是多对多?即一个文章有多个标签,一个标签又能够属于多个文章。
好比上图的Article表中id为3的文章 ,它的标签是4,26, 即投资、大文娱、社交, 你看“投资”这个标签同时还属于文章2。 这就是多对多关系 , 即many to many .
那这种多对多的关系如何在表中存储呢?难道真的像上图中同样,在Article表中加个tags字段,关联Tag表里的多条数据,经过逗号区分?
这倒确实是个解决办法。可是也有问题,一个字段里存多条纪录的id,就没办法作查询优化了。好比不能作索引等。
因此若想实现多对多关系的高效存储+查询优化,能够在Article and Tag表之间再搞出一张表。
这样是否是就实现了多对多关联?
yes, 没错, django也是这么作的, django 有个专门的字段,叫ManyToManyField, 就是用来实现多对多关联的,它会自动生成一个如上图同样的第3张表来存储多对多关系。
正式的表结构
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
|
from
django.db
import
models
# Create your models here.
class
Account(models.Model):
username
=
models.CharField(max_length
=
64
,unique
=
True
)
email
=
models.EmailField()
password
=
models.CharField(max_length
=
128
)
register_date
=
models.DateTimeField(
"注册日期"
,auto_now_add
=
True
)
signature
=
models.CharField(verbose_name
=
"签名"
,max_length
=
128
,blank
=
True
,null
=
True
)
class
Article(models.Model):
"""文章表"""
title
=
models.CharField(max_length
=
255
,unique
=
True
)
content
=
models.TextField(
"文章内容"
)
account
=
models.ForeignKey(
"Account"
,verbose_name
=
"做者"
,on_delete
=
models.CASCADE)
tags
=
models.ManyToManyField(
"Tag"
,blank
=
True
)
pub_date
=
models.DateTimeField()
read_count
=
models.IntegerField(default
=
0
)
class
Tag(models.Model):
"""文章标签表"""
name
=
models.CharField(max_length
=
64
,unique
=
True
)
date
=
models.DateTimeField(auto_now_add
=
True
)
|
咱们发现,每一个字段其实都是一个独立的对象,一张表实际上是不少类的组合。
上面好多字段里还跟了些参数,咱们来看如下经常使用的:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
|
null
#If True, Django will store empty values as NULL in the database. Default is False.
blank
#If True, the field is allowed to be blank. Default is False.
db_column
#The name of the database column to use for this field. If this isn’t given, Django will use the field’s name.
db_index
#If True, a database index will be created for this field.
default
#The default value for the field. This can be a value or a callable object. If callable it will be called every time a new object is created.
editable
# django admin中用,后面讲
help_text
# django admin中用,后面讲
primary_key
# If True, this field is the primary key for the model.
unique
#If True, this field must be unique throughout the table
unique_for_date
#Set this to the name of a DateField or DateTimeField to require that this field be unique for the value of the date field. For example, if you have a field title that has unique_for_date="pub_date", then Django wouldn’t allow the entry of two records with the same title and pub_date.
unique_for_month
#Like unique_for_date, but requires the field to be unique with respect to the month.
unique_for_year
verbose_name
#A human-readable name for the field. If the verbose name isn’t given, Django will automatically create it using the field’s attribute name
|
还有几个特殊的字段属性须要单独介绍下
choices
An iterable (e.g., a list or tuple) consisting itself of iterables of exactly two items (e.g. [(A, B), (A, B) ...]) to use as choices for this field.
The first element in each tuple is the actual value to be set on the model, and the second element is the human-readable name.
1
2
3
4
5
6
7
8
9
10
11
12
|
class
Student(models.Model):
YEAR_IN_SCHOOL_CHOICES
=
(
(
'FR'
,
'Freshman'
),
(
'SO'
,
'Sophomore'
),
(
'JR'
,
'Junior'
),
(
'SR'
,
'Senior'
),
)
year_in_school
=
models.CharField(
max_length
=
2
,
choices
=
YEAR_IN_SCHOOL_CHOICES,
default
=
FRESHMAN,
)
|
ForeignKey.on_delete
当一条记录关联的外键纪录被删除时,django 也会根据外键关联限制的配置来决定如何处理当前这条纪录。举例,若是你有个能够为null的外键关联,而且你想在本纪录关联的数据被删除时,把当前纪录的关联字段设为null,那就配置以下
1
2
3
4
5
6
|
user
=
models.ForeignKey(
User,
on_delete
=
models.SET_NULL,
blank
=
True
,
null
=
True
,
)
|
这个on_delete就是决定在关联对象被删除时,如何处理当前纪录的,经常使用的参数以下:
- CASCADE——Cascade deletes. Django emulates the behavior of the SQL constraint ON DELETE CASCADE and also deletes the object containing the ForeignKey.
- PROTECT——Prevent deletion of the referenced object by raising ProtectedError, a subclass of django.db.IntegrityError.
- SET_NULL——Set the ForeignKey null; this is only possible if null is True.
- SET_DEFAULT——Set the ForeignKey to its default value; a default for the ForeignKey must be set.
配置Django数据库链接信息
Django支持多种数据库,Sqlite、Mysql、Oracle、PostgreSQL,默认的是小型文件数据库Sqlite
1
2
3
4
5
6
|
DATABASES
=
{
'default'
: {
'ENGINE'
:
'django.db.backends.sqlite3'
,
'NAME'
: os.path.join(BASE_DIR,
'db.sqlite3'
),
}
}
|
我们是干大事的人,怎么也得用个Mysql呀, 改为mysql 也so easy.
1
2
3
4
5
6
7
8
9
10
|
DATABASES
=
{
'default'
: {
'ENGINE'
:
'django.db.backends.mysql'
,
'NAME'
:
'my_db'
,
'USER'
:
'mydatabaseuser'
,
'PASSWORD'
:
'mypassword'
,
'HOST'
:
'127.0.0.1'
,
'PORT'
:
'3306'
,
}
}
|
不过注意,python3 链接mysql的得使用pymysql,MysqlDB模块300年没更新了,但django默认调用的仍是MySQLdb, so pymysql有个功能可让django觉得是用了MySQLdb. 即在项目目录下的__init__.py中加上句代码就好
1
2
3
|
import
pymysql
pymysql.install_as_MySQLdb()
|
不加的话,一会链接数据时会报错噢 。
同步数据库
你在ORM定义的表结构如何同步到真实的数据库里呢? 只需2条命令。但django只能帮你自动建立表,数据库自己仍是得你本身来。
1
|
create
database
my_db charset utf8;
|
好了,能够同步了,说好只需2步。
1. 生成同步文件, django自带一个专门的工具叫migrations, 负责把你的orm表转成实际的表结构,它不旦能够帮自动建立表,对表结构的修改,好比增删改字段、改字段属性等也都能自动同步。只需经过下面神奇的命令。
1
|
python manage.py makemigrations
|
不出意外的话,会显示相似如下信息
1
2
3
4
5
6
7
|
$ python manage.py makemigrations
Migrations
for
'app01'
:
app01
/migrations/0001_initial
.py
- Create model Account
- Create model Article
- Create model Tag
- Add field tags to article
|
此时你会发现,你的app下的migrations目录里多了一个0001_initial.py的文件 ,这个文件就是由于你这条命令而建立的,migrations工具就会根据这个文件来建立数据库里的表。
2. 同步到数据
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
|
$ python manage.py migrate
Operations to perform:
Apply all migrations: admin, app01, auth, contenttypes, sessions
Running migrations:
Applying contenttypes.0001_initial... OK
Applying auth.0001_initial... OK
Applying admin.0001_initial... OK
Applying admin.0002_logentry_remove_auto_add... OK
Applying app01.0001_initial... OK
Applying contenttypes.0002_remove_content_type_name... OK
Applying auth.0002_alter_permission_name_max_length... OK
Applying auth.0003_alter_user_email_max_length... OK
Applying auth.0004_alter_user_username_opts... OK
Applying auth.0005_alter_user_last_login_null... OK
Applying auth.0006_require_contenttypes_0002... OK
Applying auth.0007_alter_validators_add_error_messages... OK
Applying auth.0008_alter_user_username_max_length... OK
Applying auth.0009_alter_user_last_name_max_length... OK
Applying sessions.0001_initial... OK
(venv_django2) Alexs-MacBook-Pro:mysite alex$
|
此时登陆你的数据库,会发现建立了好多张表
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
|
mysql> show tables;
+----------------------------+
| Tables_in_luffy_dev2 |
+----------------------------+
| app01_account |
#对应Account表
| app01_article |
#对应Article表
| app01_article_tags |
#自动建立的Article to Tag的多对多关联表
| app01_tag |
#对应Tag表
| auth_group |
#下面这些,都是django 自带的表,这个是自动用户系统的组
| auth_group_permissions |
#自带的组与权限的多对多关联表
| auth_permission |
#自带权限表
| auth_user |
#用户表
| auth_user_groups |
| auth_user_user_permissions |
| django_admin_log |
#如今你的没法理解
| django_content_type |
#如今你的没法理解
| django_migrations |
#纪录migartions工具同步纪录的表
| django_session |
#如今你的没法理解
+----------------------------+
14 rows
in
set
(0.00 sec)
|
好啦,表结构也有了,咱们能够往里面插数据啦。
以前说好的是能够不用SQL语句的,一点不骗你。
用orm对表数据进行增删改查
先进入已经链接好数据库的django python环境
1
2
3
4
5
6
7
|
(venv_django2) Alexs-MacBook-Pro:mysite alex$ python manage.py shell
Python 3.5.2 (v3.5.2:4def2a2901a5, Jun 26 2016, 10:47:25)
[GCC 4.2.1 (Apple Inc. build 5666) (dot 3)] on darwin
Type
"help"
,
"copyright"
,
"credits"
or
"license"
for
more
information.
(InteractiveConsole)
>>>
>>> from app01
import
models
|
建立
建立数据简单的使人发指
查
filter 支持不少的过滤条件,咱们来看下:
contains
包含,至关于sql的like条件
1
|
Entry.objects.get(headline__contains=
'Lennon'
)
|
SQL equivalent:
1
|
SELECT
...
WHERE
headline
LIKE
'%Lennon%'
;
|
Note this will match the headline 'Lennon honored today' but not 'lennon honored today'.
icontains 大小写不敏感
in
In a given iterable; often a list, tuple, or queryset.
1
|
Entry.objects.
filter
(id__in
=
[
1
,
3
,
4
])
|
SQL equivalent:
1
|
SELECT
...
WHERE
id
IN
(1, 3, 4);
|
You can also use a queryset to dynamically evaluate the list of values instead of providing a list of literal values:
1
2
|
inner_qs
=
Blog.objects.
filter
(name__contains
=
'Cheddar'
)
entries
=
Entry.objects.
filter
(blog__in
=
inner_qs)
|
This queryset will be evaluated as subselect statement:
1
|
SELECT
...
WHERE
blog.id
IN
(
SELECT
id
FROM
...
WHERE
NAME
LIKE
'%Cheddar%'
)
|
gt
1
|
Entry.objects.
filter
(id__gt
=
4
)
|
SQL equivalent:
1
|
SELECT
...
WHERE
id > 4;
|
gte
Greater than or equal to.
lt
Less than.
lte
Less than or equal to.
startswith
Case-sensitive starts-with.
1
|
Entry.objects.
filter
(headline__startswith
=
'Lennon'
)
|
SQL equivalent:
1
|
SELECT
...
WHERE
headline
LIKE
'Lennon%'
;
|
SQLite doesn’t support case-sensitive LIKE statements; startswith acts like istartswith for SQLite
istartswith
Case-insensitive starts-with.
endswith
Case-sensitive ends-with.
iendswith
Case-insensitive ends-with
range
区间过渡,可对数字、日期进行过滤
1
2
3
4
|
import
datetime
start_date
=
datetime.date(
2005
,
1
,
1
)
end_date
=
datetime.date(
2005
,
3
,
31
)
Entry.objects.
filter
(pub_date__range
=
(start_date, end_date))
|
SQL equivalent:
1
|
SELECT ... WHERE pub_date BETWEEN
'2005-01-01'
and
'2005-03-31'
;
|
Warning!
Filtering a DateTimeField with dates won’t include items on the last day, because the bounds are interpreted as “0am on the given date”. If pub_date was a DateTimeField, the above expression would be turned into this SQL:
SELECT ... WHERE pub_date BETWEEN '2005-01-01 00:00:00' and '2005-03-31 00:00:00';
Generally speaking, you can’t mix dates and datetimes.
date
For datetime fields, casts the value as date. Allows chaining additional field lookups. Takes a date value.
1
2
|
Entry.objects.
filter
(pub_date__date
=
datetime.date(
2005
,
1
,
1
))
Entry.objects.
filter
(pub_date__date__gt
=
datetime.date(
2005
,
1
,
1
))
|
year
For date and datetime fields, an exact year match. Allows chaining additional field lookups. Takes an integer year.
1
2
|
Entry.objects.
filter
(pub_date__year
=
2005
)
Entry.objects.
filter
(pub_date__year__gte
=
2005
)
|
SQL equivalent:
1
2
|
SELECT
...
WHERE
pub_date
BETWEEN
'2005-01-01'
AND
'2005-12-31'
;
SELECT
...
WHERE
pub_date >=
'2005-01-01'
;
|
When USE_TZ is True, datetime fields are converted to the current time zone before filtering. 简单解决办法是把USE_TZ=False
month
For date and datetime fields, an exact month match. Allows chaining additional field lookups. Takes an integer 1 (January) through 12 (December).
1
2
|
Entry.objects.
filter
(pub_date__month
=
12
)
Entry.objects.
filter
(pub_date__month__gte
=
6
)
|
When USE_TZ
is True
, datetime fields are converted to the current time zone before filtering. This requires time zone definitions in the database.
SQL equivalent:
1
2
|
SELECT
...
WHERE
EXTRACT(
'month'
FROM
pub_date) =
'12'
;
SELECT
...
WHERE
EXTRACT(
'month'
FROM
pub_date) >=
'6'
;
|
day
For date and datetime fields, an exact day match. Allows chaining additional field lookups. Takes an integer day.
1
2
|
Entry.objects.filter(pub_date__day=3)
Entry.objects.filter(pub_date__day__gte=3)
|
SQL equivalent:
1
2
|
SELECT
...
WHERE
EXTRACT(
'day'
FROM
pub_date) =
'3'
;
SELECT
...
WHERE
EXTRACT(
'day'
FROM
pub_date) >=
'3'
;
|
week
For date and datetime fields, return the week number (1-52 or 53) according to ISO-8601, i.e., weeks start on a Monday and the first week contains the year’s first Thursday.
Example:
1
2
|
Entry.objects.
filter
(pub_date__week
=
52
)
Entry.objects.
filter
(pub_date__week__gte
=
32
, pub_date__week__lte
=
38
)
|
week_day
For date and datetime fields, a ‘day of the week’ match. Allows chaining additional field lookups.
Takes an integer value representing the day of week from 1 (Sunday) to 7 (Saturday).
Example:
1
2
|
Entry.objects.filter(pub_date__week_day=2)
Entry.objects.filter(pub_date__week_day__gte=2)
|
hour
For datetime and time fields, an exact hour match. Allows chaining additional field lookups. Takes an integer between 0 and 23.
Example:
1
2
3
|
Event.objects.
filter
(timestamp__hour
=
23
)
Event.objects.
filter
(time__hour
=
5
)
Event.objects.
filter
(timestamp__hour__gte
=
12
)
|
SQL equivalent:
1
2
3
|
SELECT
...
WHERE
EXTRACT(
'hour'
FROM
timestamp
) =
'23'
;
SELECT
...
WHERE
EXTRACT(
'hour'
FROM
time
) =
'5'
;
SELECT
...
WHERE
EXTRACT(
'hour'
FROM
timestamp
) >=
'12'
;同
|
同时,还支持mintue,second
1
2
3
4
|
Event.objects.filter(time__minute=46)
Event.objects.filter(timestamp__second=31)
|
isnull
Takes either True
or False
, which correspond to SQL queries of IS NULL
and IS NOT NULL
, respectively.
Example:
1
|
Entry.objects.
filter
(pub_date__isnull
=
True
)
|
SQL equivalent:
1
|
SELECT
...
WHERE
pub_date
IS
NULL
;
|
regex
Case-sensitive regular expression match.
Example:
1
|
Entry.objects.get(title__regex
=
r
'^(An?|The) +'
)
|
SQL equivalents:
1
2
3
4
5
6
7
|
SELECT
...
WHERE
title REGEXP
BINARY
'^(An?|The) +'
;
-- MySQL
SELECT
...
WHERE
REGEXP_LIKE(title,
'^(An?|The) +'
,
'c'
);
-- Oracle
SELECT
...
WHERE
title ~
'^(An?|The) +'
;
-- PostgreSQL
SELECT
...
WHERE
title REGEXP
'^(An?|The) +'
;
-- SQLite
|
iregex 大小写不敏感
改删
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
|
# 批量修改
models.Account.objects.
filter
(username
=
'elina'
).update(password
=
"Luffy#21"
)
# 单条修改
obj
=
models.Account.objects.get(username
=
'linux'
)
obj.username
=
'python'
obj.save()
# 批量删除
models.User.objects.get(password
=
'oldboy'
).delete()
# 单条删除
obj
=
models.User.objects.get(
id
=
3
)
obj.delete()
|
数据返回后的展现
values()
Returns a QuerySet
that returns dictionaries, rather than model instances, when used as an iterable.
1
2
3
4
|
>>> Blog.objects.values()
<QuerySet [{
'id'
:
1
,
'name'
:
'Beatles Blog'
,
'tagline'
:
'All the latest Beatles news.'
}]>
>>> Blog.objects.values(
'id'
,
'name'
)
<QuerySet [{
'id'
:
1
,
'name'
:
'Beatles Blog'
}]>
|
order_by()
By default, results returned by a QuerySet
are ordered by the ordering tuple given by the ordering
option in the model’s Meta
. You can override this on a per-QuerySet
basis by using the order_by
method.
1
|
Entry.objects.
filter
(pub_date__year
=
2005
).order_by(
'-pub_date'
,
'headline'
)
|
The result above will be ordered by pub_date
descending, then by headline
ascending. The negative sign in front of "-pub_date"
indicates descending order. Ascending order is implied.
reverse()
Use the reverse()
method to reverse the order in which a queryset’s elements are returned. Calling reverse()
a second time restores the ordering back to the normal direction.
To retrieve the “last” five items in a queryset, you could do this:
1
|
my_queryset.reverse()[:
5
]
|
ORM对象操做
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
|
单表对象操做
o
=
models.Article.objects.
all
()[
0
]
o.tilte
外键关联
>>> o.account.username
'jack'
>>> o.account.username
=
rain
外键反向关联操做
>>> a
=
models.Account.objects.get(username
=
'alex'
)
>>> a.article_set.
all
()
<QuerySet [<Article: 你好,
2018
>]>
>>> a.article_set.select_related()
<QuerySet [<Article: 你好,
2018
>]>
多对多操做
>>> o
=
models.Article.objects.
all
()[
1
]
>>> o.tags.
all
()
<QuerySet [<Tag: 投资>, <Tag: 科技>]>
多对多反向操做
>>> t
=
models.Tag.objects.get(name
=
"投资"
)
>>> t.article_set.
all
()
<QuerySet [<Article: 你好,
2018
>, <Article: 粉丝超过
10
万后,我经历了抖音盗号风波>]>
|
好啦,orm的操做先点到为止,后面学项目时再带你搞复杂的。
练习题
- 基于前面课程设计的表结构,完成如下练习:
- 建立5条account和5条新tag纪录
- 建立5条article信息,关联上面的不一样的用户和tag
- 在account表里找到用户名包含al的纪录,而后把密码改掉
- 在article表找到文章内容包含“电影”2个字的,把这些文章加上”大文娱”tag
- 把用户elina发表的文章找出来,而且把做者都改为alex
- 找到用户表里注册日期在2018-04月,而且signature为空的纪录
- 打到文章中标签为“投资”的全部文章
- 找到每月8号注册的用户
- 找到每一年5月发表的文章
- 找到2015-2017年5月发表的文章
- 找到文章做者以’a’或’k’开头的文章