您好, 欢迎来到 !    登录 | 注册 | | 设为首页 | 收藏本站

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

5b51 2022/1/14 8:24:07 python 字数 36452 阅读 600 来源 www.jb51.cc/python

本节内容 路由系统 models模型 admin views视图 template模板 引子 讲django的models之前, 先来想一想, 让你通过django操作数据库,你怎么做? 做苦思冥想,

概述

本节内容

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

MysqL

def index(request):

创建连接

conn = py<a href="https://www.jb51.cc/tag/MysqL/" target="_blank" class="keywords">MysqL</a>.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)
conn = py<a href="https://www.jb51.cc/tag/MysqL/" target="_blank" class="keywords">MysqL</a>.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)

def index(request):

def index(request):

conn = py<a href="https://www.jb51.cc/tag/MysqL/" target="_blank" class="keywords">MysqL</a>.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个问题

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

数据库分离。从面向对象来说,数据库不应该和业务逻辑绑定到一起,ORM则起到这样的分离作用,使数据库层透明,开发人员真正的面向对象。

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

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

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

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

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

优点:

缺点:

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

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

文件
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.

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

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张表来存储多对多关系。

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(maxlength=64,unique=True)
date = models.DateTimeField(auto
Now_add=True)

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(maxlength=64,unique=True)
date = models.DateTimeField(auto
Now_add=True)

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(maxlength=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

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

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就是决定在关联对象被删除时,如何处理当前纪录的,常用的参数如下:

Django支持多种数据库sqlite、MysqL、Oracle、Postgresql认的是小型文件数据库sqlite

sqlite3','NAME': os.path.join(BASE_DIR,'db.sqlite3'),}
}

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

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中加上代码就好

MysqL

pyMysqL.installasMysqLdb()

pyMysqL.installasMysqLdb()

pyMysqL.installasMysqLdb()

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

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

sql;gutter:true;">create database my_db charset utf8;  

  

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

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

sql;gutter:true;">python manage.py makemigrations  

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

此时你会发现,你的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语句的,一点不骗你。

先进入已经连接好数据库的django python环境

>> 
>>> from app01 import models

  

创建

创建数据简单的令人发指

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

 

contains

包含,相当于sql的like条件

sql;gutter:true;">Entry.objects.get(headline__contains='Lennon')

sql equivalent:

sql;gutter:true;">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.

sql equivalent:

sql;gutter:true;">SELECT ... WHERE id IN (1,4);

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:

sql;gutter:true;">SELECT ... WHERE blog.id IN (SELECT id FROM ... WHERE NAME LIKE '%Cheddar%')

  

gt

sql equivalent:

 4;

gteGreater than or equal to.

ltLess than.

lteLess than or equal to.

startswithCase-sensitive starts-with.

sql equivalent:

sql;gutter:true;">SELECT ... WHERE headline LIKE 'Lennon%';

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;gutter:true;">Entry.objects.filter(pub_date__day=3)
Entry.objects.filter(pub_date__day__gte=3)

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

sql;gutter:true;">Event.objects.filter(time__minute=46)

Event.objects.filter(timestamp__second=31)

Event.objects.filter(timestamp__second=31)

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:

sql;gutter:true;">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  

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

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

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

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

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

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

iregex 大小写不敏感    


如果您也喜欢它,动动您的小指点个赞吧

除非注明,文章均由 laddyq.com 整理发布,欢迎转载。

转载请注明:
链接:http://laddyq.com
来源:laddyq.com
著作权归作者所有。商业转载请联系作者获得授权,非商业转载请注明出处。


联系我
置顶