加入收藏 | 设为首页 | 会员中心 | 我要投稿 李大同 (https://www.lidatong.com.cn/)- 科技、建站、经验、云计算、5G、大数据,站长网!
当前位置: 首页 > 编程开发 > Python > 正文

Django 14天从小白到进阶- Day2 搞定Models组件

发布时间:2020-12-17 00:07:51 所属栏目:Python 来源:网络整理
导读:本节内容 路由系统 models模型 admin? views视图 template模板 引子 讲django的models之前, 先来想一想, 让你通过django操作数据库,你怎么做? 做苦思冥想,可能会这样写。 def index(request): 创建连接 conn = pymysql.connect(host='127.0.0.1',port=3

本节内容

  • 路由系统
  • models模型
  • admin?
  • views视图
  • template模板

引子

讲django的models之前, 先来想一想, 让你通过django操作数据库,你怎么做? 做苦思冥想,可能会这样写。

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个问题

  • SQL注入危险,因为有的时候你操作数据库的语句不是写死在代码里的,而是通过前端传参数拼接的,这就给黑客有了可趁之机,通过拼接参数实现sql注入。
  • 语句跟代码揉在一起了,增加后续维护成本

那怎么办呢?ORM提供了新思路。

什么是ORM呢?

上面的解释有点蒙蔽对不?其实你只需要抓住2个关键词, “映射” 和 “对象”,就能知道orm是什么干什么的了。

  • 映射(Mapping) —— 把表结构映射成类
  • 对象 —— 像操作类对象一样,操作数据库里的数据

映射

看下面的图,就是直观的例子,把右边的表结构映射成了左边的类

?Sql语句到对象

ORM可以使你不用再写原生SQL,而是像操作对象一样就可以实现对表里数据的增删改查

好棒棒,妈妈再也不用逼你写原生sql啦!

但是不要开心太早,ORM确实提高了开发效率,并且降低了数据操作与代码之间的耦合,不过有利就有弊,我们总结一下orm的优缺点。

优点:

  1. 实现了代码与数据操作的解耦合
  2. 不需自己写原生sql,提高开发效率
  3. 防止SQL注入,通过对象操作的方式,默认就是防止sql注入的。

缺点:

  1. 牺牲性能, 对象到原生SQL势必会有转换消耗,对性能有一定的影响?
  2. 复杂语句力不从心, 一些复杂的sql语句,用orm对象操作的方式很难实现,就还得用原生sql

讲Django为什么说ORM? 哈,? 好啦,是时候该引出主角啦,因为Django的models基于架构ORM实现的。

Models模型

Django 的models把数据库表结构映射成了一个个的类, 表里的每个字段就是类的属性。我们都知道数据库有很多字段类型,int,float,char等, Django的models类针对不同的字段也设置了不同的类属性。

除了普通的表字段,针对外键也有映射

OneToOneField # 1对1

  

好啦,接下来就用django的orm来设计一个博客表。

需求

  1. 每个用户有自己的账户信息
  2. 用户可以发文章
  3. 文章可以打多个标签

根据需求,我们设计3张表

注意Article表和Tag表是属于多对多关系,什么是多对多?即一个文章有多个标签,一个标签又可以属于多个文章。?

比如上图的Article表中id为3的文章 ,它的标签是4,26,即投资、大文娱、社交, 你看“投资”这个标签同时还属于文章2。 这就是多对多关系 , 即many to many .?

那这种多对多的关系如何在表中存储呢?难道真的像上图中一样,在Article表中加个tags字段,关联Tag表里的多条数据,通过逗号区分?

这倒确实是个解决办法。但是也有问题,一个字段里存多条纪录的id,就没办法做查询优化了。比如不能做索引等。

所以若想实现多对多关系的高效存储+查询优化,可以在Article and Tag表之间再搞出一张表。

这样是不是就实现了多对多关联?

yes,没错, django也是这么做的, django 有个专门的字段,叫ManyToManyField,就是用来实现多对多关联的,它会自动生成一个如上图一样的第3张表来存储多对多关系。

正式的表结构

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)

  

我们发现,每个字段其实都是一个独立的对象,一张表其实是很多类的组合。

上面好多字段里还跟了些参数,我们来看以下常用的:

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.

ForeignKey.on_delete

当一条记录关联的外键纪录被删除时,django 也会根据外键关联限制的配置来决定如何处理当前这条纪录。举例,如果你有个可以为null的外键关联,并且你想在本纪录关联的数据被删除时,把当前纪录的关联字段设为null,那就配置如下

这个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

咱们是干大事的人,怎么也得用个Mysql呀, 改成mysql 也so easy.

不过注意,python3 连接mysql的得使用pymysql,MysqlDB模块300年没更新了,但django默认调用的还是MySQLdb,so pymysql有个功能可以让django以为是用了MySQLdb. 即在项目目录下的__init__.py中加上句代码就好

pymysql.install_as_MySQLdb()

不加的话,一会连接数据时会报错噢 。

同步数据库  

你在ORM定义的表结构如何同步到真实的数据库里呢? 只需2条命令。但django只能帮你自动创建表,数据库本身还是得你自己来。

  

好了,可以同步了,说好只需2步。

1. 生成同步文件,django自带一个专门的工具叫migrations,负责把你的orm表转成实际的表结构,它不旦可以帮自动创建表,对表结构的修改,比如增删改字段、改字段属性等也都能自动同步。只需通过下面神奇的命令。

?不出意外的话,会显示类似以下信息

此时你会发现,你的app下的migrations目录里多了一个0001_initial.py的文件 ,这个文件就是因为你这条命令而创建的,migrations工具就会根据这个文件来创建数据库里的表。

2. 同步到数据

此时登录你的数据库,会发现创建了好多张表

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环境

>> >>> from app01 import models

  

创建

创建数据简单的令人发指

filter 支持很多的过滤条件,我们来看下:

?

contains

包含,相当于sql的like条件

SQL equivalent:

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.

SQL equivalent:

You can also use a queryset to dynamically evaluate the list of values instead of providing a list of literal values:

This queryset will be evaluated as subselect statement:

  

gt

SQL equivalent:

4;

gteGreater than or equal to.

ltLess than.

lteLess than or equal to.

startswithCase-sensitive starts-with.

SQL equivalent:

SQLite doesn’t support case-sensitive LIKE statements; startswith acts like istartswith for SQLite  

istartswithCase-insensitive starts-with.

endswithCase-sensitive ends-with.

iendswithCase-insensitive ends-with

  

range区间过渡,可对数字、日期进行过滤

SQL equivalent:

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.  

yearFor date and datetime fields,an exact year match. Allows chaining additional field lookups. Takes an integer year.

SQL equivalent:

= '2005-01-01';

When USE_TZ is True,datetime fields are converted to the current time zone before filtering. 简单解决办法是把USE_TZ=False

monthFor date and datetime fields,an exact month match. Allows chaining additional field lookups. Takes an integer 1 (January) through 12 (December).

When??is?,datetime fields are converted to the current time zone before filtering. This requires?.

SQL equivalent:

= '6';

dayFor date and datetime fields,an exact day match. Allows chaining additional field lookups. Takes an integer day.

SQL equivalent:

= '3';

  

For date and datetime fields,return the week number (1-52 or 53) according to?,i.e.,weeks start on a Monday and the first week contains the year’s first Thursday.

Example:

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:

For datetime and time fields,an exact hour match. Allows chaining additional field lookups. Takes an integer between 0 and 23.

Example:

SQL equivalent:

= '12';同  

同时,还支持mintue,second

Event.objects.filter(timestamp__second=31)

Takes either??or?,which correspond to SQL queries of??and?,respectively.

Example:

SQL equivalent:

Case-sensitive regular expression match.

Example:

SQL equivalents:

SELECT ... WHERE REGEXP_LIKE(title,'^(An?|The) +','c'); -- Oracle

SELECT ... WHERE title ~ '^(An?|The) +'; -- PostgreSQL

SELECT ... WHERE title REGEXP '^(An?|The) +'; -- SQLite  

iregex 大小写不敏感    

   

改删

单条修改

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()

数据返回后的展示

(*fields,?**expressions)

Returns a??that returns dictionaries,rather than model instances,when used as an iterable.

>> Blog.objects.values() >>> Blog.objects.values('id','name')

(*fields)

By default,results returned by a??are ordered by the ordering tuple given by the??option in the model’s?. You can override this on a per-?basis by using the??method.

The result above will be ordered by??descending,then by??ascending. The negative sign in front of?indicates?descending?order. Ascending order is implied.?

Use the??method to reverse the order in which a queryset’s elements are returned. Calling??a second time restores the ordering back to the normal direction.

To retrieve the “last” five items in a queryset,you could do this:

  

ORM对象操作

外键关联

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的操作先点到为止,后面学项目时再带你搞复杂的。

练习题

  1. 基于前面课程设计的表结构,完成以下练习:
  2. 创建5条account和5条新tag纪录
  3. 创建5条article信息,关联上面的不同的用户和tag
  4. 在account表里找到用户名包含al的纪录,然后把密码改掉
  5. 在article表找到文章内容包含“电影”2个字的,把这些文章加上”大文娱”tag
  6. 把用户elina发表的文章找出来,并且把作者都改成alex
  7. 找到用户表里注册日期在2018-04月,并且signature为空的纪录
  8. 打到文章中标签为“投资”的所有文章
  9. 找到每个月8号注册的用户
  10. 找到每年5月发表的文章?
  11. 找到2015-2017年5月发表的文章?
  12. 找到文章作者以’a’或’k’开头的文章

  

 

(编辑:李大同)

【声明】本站内容均来自网络,其相关言论仅代表作者个人观点,不代表本站立场。若无意侵犯到您的权利,请及时与联系站长删除相关内容!

    推荐文章
      热点阅读