django 操作数据库--orm(object relation mapping)---models

 

思想

django为使用一种新的方式,即:关系对象映射(Object Relational Mapping,简称ORM)。

  PHP:activerecord

  Java:Hibernate 

    C#:Entity Framework

django中遵循 Code Frist 的原则,即:根据代码中定义的类来自动生成数据库表。

  • 创建数据库,设计表结构和字段
  • 使用 MySQLdb 来连接数据库,并编写数据访问层代码
  • 业务逻辑层去调用数据访问层执行数据库操作

三层架构

创建表

1、创建Model,之后可以根据Model来创建数据库表

 

完整表的orm创建语句,当忘记的时候可以作为参考

 

 

1、models.AutoField  自增列 = int(11)
  如果没有的话,默认会生成一个名称为 id 的列,如果要显示的自定义一个自增列,必须将给列设置为主键 primary_key=True。
2、models.CharField  字符串字段
  必须 max_length 参数
3、models.BooleanField  布尔类型=tinyint(1)
  不能为空,Blank=True
4、models.ComaSeparatedIntegerField  用逗号分割的数字=varchar
  继承CharField,所以必须 max_lenght 参数
5、models.DateField  日期类型 date
  对于参数,auto_now = True 则每次更新都会更新这个时间;auto_now_add 则只是第一次创建添加,之后的更新不再改变。
6、models.DateTimeField  日期类型 datetime
  同DateField的参数
7、models.Decimal  十进制小数类型 = decimal
  必须指定整数位max_digits和小数位decimal_places
8、models.EmailField  字符串类型(正则表达式邮箱) =varchar
  对字符串进行正则表达式
9、models.FloatField  浮点类型 = double
10、models.IntegerField  整形
11、models.BigIntegerField  长整形
  integer_field_ranges = {
    'SmallIntegerField': (-32768, 32767),
    'IntegerField': (-2147483648, 2147483647),
    'BigIntegerField': (-9223372036854775808, 9223372036854775807),
    'PositiveSmallIntegerField': (0, 32767),
    'PositiveIntegerField': (0, 2147483647),
  }
12、models.IPAddressField  字符串类型(ip4正则表达式)
13、models.GenericIPAddressField  字符串类型(ip4和ip6是可选的)
  参数protocol可以是:both、ipv4、ipv6
  验证时,会根据设置报错
14、models.NullBooleanField  允许为空的布尔类型
15、models.PositiveIntegerFiel  正Integer
16、models.PositiveSmallIntegerField  正smallInteger
17、models.SlugField  减号、下划线、字母、数字
18、models.SmallIntegerField  数字
  数据库中的字段有:tinyint、smallint、int、bigint
19、models.TextField  字符串=longtext
20、models.TimeField  时间 HH:MM[:ss[.uuuuuu]]
21、models.URLField  字符串,地址正则表达式
22、models.BinaryField  二进制
23、models.ImageField   图片
24、models.FilePathField 文件
更多字段
1null=True
  数据库中字段是否可以为空
2、blank=True
  django的 Admin 中添加数据时是否可允许空值
3、primary_key = False
  主键,对AutoField设置主键后,就会代替原来的自增 id 列
4、auto_now 和 auto_now_add
  auto_now   自动创建---无论添加或修改,都是当前操作的时间
  auto_now_add  自动创建---永远是创建时的时间
5、choices
GENDER_CHOICE = (
        (u'M', u'Male'),
        (u'F', u'Female'),
    )
gender = models.CharField(max_length=2,choices = GENDER_CHOICE)
6、max_length
7default  默认值
8、verbose_name  Admin中字段的显示名称
9、name|db_column  数据库中的字段名称
10、unique=True  不允许重复
11、db_index = True  数据库索引
12、editable=True  在Admin里是否可编辑
13、error_messages=None  错误提示
14、auto_created=False  自动创建
15、help_text  在Admin中提示帮助信息
16、validators=[]
17、upload-to
更多参数

写完models 的orm语句,要生成表执行下面的语句

然后复制粘贴到models里面,,可惜,syncdb命令取消了,有哪些命令可以用python manage.py ---help查看,悲伤那么大

那么我们把生成的实体类代码到blog/models也是可以的

 

2.数据库配置

1      django默认支持sqlite,mysql, oracle,postgresql数据库。

    <1> sqlite

            django默认使用sqlite的数据库,默认自带sqlite的数据库驱动

            引擎名称:django.db.backends.sqlite3

     <2>mysql

            引擎名称:django.db.backends.mysql

2    mysql驱动程序

          MySQLdb(mysql python)

          mysqlclient

          MySQL

          PyMySQL(纯python的mysql驱动程序)

 3     在django的项目中会默认使用sqlite数据库,在settings里有如下设置:

 如果我们想要更改数据库,需要修改如下:

DATABASES = {
    'default': {
        'ENGINE': 'django.db.backends.mysql',
        'NAME':'django_com',
        'USER':'root',
        'PASSWORD':'',
        'host':'localhost' ,
    }
}

 

注意:NAME即数据库的名字,在mysql连接前该数据库必须已经创建,而上面的sqlite数据库下的db.sqlite3则是项目自动创建

         USER和PASSWORD分别是数据库的用户名和密码。

         设置完后,再启动我们的Django项目前,我们需要激活我们的mysql。

         然后,启动项目,会报错:no module named MySQLdb

         这是因为django默认你导入的驱动是MySQLdb,可是MySQLdb对于py3有很大问题,所以我们需要的驱动是PyMySQL

         所以,我们只需要找到项目名文件下的__init__,在里面写入:

                 import pymysql

                 pymysql.install_as_MySQLdb()

         问题就解决了!

         这时就可以正常启动了。

连表结构

  • 一对多:models.ForeignKey(其他表)
  • 多对多:models.ManyToManyField(其他表)
  • 一对一:models.OneToOneField(其他表)
应用场景:

一对多:当一张表中创建一行数据时,有一个单选的下拉框(可以被重复选择)
  例如:创建用户信息时候,需要选择一个用户类型【普通用户】【金牌用户】【铂金用户】等。
多对多:在某表中创建一行数据是,有一个可以多选的下拉框
  例如:创建用户信息,需要为用户指定多个爱好
一对一:在某表中创建一行数据时,有一个单选的下拉框(下拉框中的内容被用过一次就消失了
  例如:原有含10列数据的一张表保存相关信息,经过一段时间之后,10列无法满足需求,需要为原来的表再添加5列数据

数据库操作 ---增删改查

    • 增加:创建实例,并调用save
    • 更新:a.获取实例,再sava;b.update(指定列)
    • 删除:a. filter().delete(); b.all().delete()
    • 获取:a. 单个=get(id=1) ;b. 所有 = all()
    • 过滤:filter(name='xxx');filter(name__contains='');(id__in = [1,2,3]) ;
      icontains(大小写无关的LIKE),startswith和endswith, 还有range(SQLBETWEEN查询)'gt', 'in', 'isnull', 'endswith', 'contains', 'lt', 'startswith', 'iendswith', 'icontains','range', 'istartswith'
    • 排序:order_by("name") =asc ;order_by("-name")=desc
    • 返回第n-m条:第n条[0];前两条[0:2]
    • 指定映射:values
    • 数量:count()
    • 聚合:from django.db.models import Min,Max,Sum objects.all().aggregate(Max('guest_id'))
    • 原始SQL
   # 增
    #
    # models.Tb1.objects.create(c1='xx', c2='oo')  增加一条数据,可以接受字典类型数据 **kwargs

    # obj = models.Tb1(c1='xx', c2='oo')
    # obj.save()

    # 查
    #
    # models.Tb1.objects.get(id=123)         # 获取单条数据,不存在则报错(不建议)
    # models.Tb1.objects.all()               # 获取全部
    # models.Tb1.objects.filter(name='seven') # 获取指定条件的数据

    # 删
    #
    # models.Tb1.objects.filter(name='seven').delete() # 删除指定条件的数据

    # 改
    # models.Tb1.objects.filter(name='seven').update(gender='0')  # 将指定条件的数据更新,均支持 **kwargs
    # obj = models.Tb1.objects.get(id=1)
    # obj.c1 = '111'
    # obj.save() 
案例-增删改查

进阶操作

利用双下划线将字段和对应的操作连接起来

    # 获取个数
    #
    # models.Tb1.objects.filter(name='seven').count()

    # 大于,小于
    #
    # models.Tb1.objects.filter(id__gt=1)              # 获取id大于1的值
    # models.Tb1.objects.filter(id__lt=10)             # 获取id小于10的值
    # models.Tb1.objects.filter(id__lt=10, id__gt=1)   # 获取id大于1 且 小于10的值

    # in
    #
    # models.Tb1.objects.filter(id__in=[11, 22, 33])   # 获取id等于11、22、33的数据
    # models.Tb1.objects.exclude(id__in=[11, 22, 33])  # not in

    # contains
    #
    # models.Tb1.objects.filter(name__contains="ven")
    # models.Tb1.objects.filter(name__icontains="ven") # icontains大小写不敏感
    # models.Tb1.objects.exclude(name__icontains="ven")

    # range
    #
    # models.Tb1.objects.filter(id__range=[1, 2])   # 范围bettwen and

    # 其他类似
    #
    # startswith,istartswith, endswith, iendswith,

    # order by
    #
    # models.Tb1.objects.filter(name='seven').order_by('id')    # asc
    # models.Tb1.objects.filter(name='seven').order_by('-id')   # desc

    # limit 、offset
    #
    # models.Tb1.objects.all()[10:20]

    # group by
    from django.db.models import Count, Min, Max, Sum
    # models.Tb1.objects.filter(c1=1).values('id').annotate(c=Count('num'))
    # SELECT "app01_tb1"."id", COUNT("app01_tb1"."num") AS "c" FROM "app01_tb1" WHERE "app01_tb1"."c1" = 1 GROUP BY "app01_tb1"."id"
对应代码

表结构实例

class UserProfile(models.Model):
    user_info = models.OneToOneField('UserInfo')
    username = models.CharField(max_length=64)
    password = models.CharField(max_length=64)

    def __unicode__(self):
        return self.username


class UserInfo(models.Model):
    user_type_choice = (
        (0, u'普通用户'),
        (1, u'高级用户'),
    )
    user_type = models.IntegerField(choices=user_type_choice)
    name = models.CharField(max_length=32)
    email = models.CharField(max_length=32)
    address = models.CharField(max_length=128)

    def __unicode__(self):
        return self.name


class UserGroup(models.Model):

    caption = models.CharField(max_length=64)

    user_info = models.ManyToManyField('UserInfo')

    def __unicode__(self):
        return self.caption


class Host(models.Model):
    hostname = models.CharField(max_length=64)
    ip = models.GenericIPAddressField()
    user_group = models.ForeignKey('UserGroup')

    def __unicode__(self):
        return self.hostname
实例

 

# --*-- coding:utf-8 -*-
from django.db import models
 
 
class UserType(models.Model):
    caption = models.CharField(max_length=32)
 
 
class UserInfo(models.Model):
    user_type = models.ForeignKey(UserType)
    username = models.CharField(max_length=32)
    age = models.IntegerField()

  

1、添加数据:


  传id的方式:(字典的形式传参)

user_dict = {"username": "chenchao", "age": "18", "user_type_id": 1}
 
models.UserInfo.objects.create(**user_dict)

  传对象的方式:

user_type_obj = models.UserType.objects.get(id=1)   #先获取外键表中的数据对象
user_dict = {"username": "chenchao", "age": "18", "user_type": user_type_obj}        #将对象传入字典
 
models.UserInfo.objects.create(**user_dict)

  

讲解:在我们写的models类时,外键的字段是”user_type",而django在数据库中保存字段时默认会在后面加“_id”,所以可以直接通过传id的方式。

      而在表中的外键字段“user_type”又代表的是字典表中的一行数据对象。于是同样可以传对象的方式来传参。

 

 

create和save方法

注意:如果每次创建一个对象,想显示对应的raw sql,需要在settings加上日志记录部分:

LOGGING = {
#     'version': 1,
#     'disable_existing_loggers': False,
#     'handlers': {
#         'console':{
#             'level':'DEBUG',
#             'class':'logging.StreamHandler',
#         },
#     },
#     'loggers': {
#         'django.db.backends': {
#             'handlers': ['console'],
#             'propagate': True,
#             'level':'DEBUG',
#         },
#     }
# }
View Code

那么如何插入存在外键和多对多关系的一本书的信息呢?

 总结:

      1   objects:   model默认管理器。

      2   插入主外键关系的时候,可以用对象的方式,也可以用关联id的方式。

      3   插入多对多关系的时候要分步操作。

      4   create是管理器objects的方法

           save是model对象的方法

    update和save方法

实例:

注意:<1>不能用get的原因是:update是QuerySet对象的方法,get返回的是一个model对象,它没有update方法,而filter返回的是一个QuerySet对象。

    

         <2>  filter:

>>> Publisher.objects.filter(name__contains="press")
[   <Publisher: Apress>]

  orm:  __contains相当于like"%%"的模糊匹配

<3> 在“插入和更新数据”小节中,我们有提到模型的save()方法,这个方法会更新一行里的所有列。 而某些情况下,我们只需要更新行里的某几列。例如说我们现在想要将Apress Publisher的名称由原来的”Apress”更改为”Apress Publishing”。若使用save()方法,如:

>>> p = Publisher.objects.get(name='Apress')
>>> p.name = 'Apress Publishing'
>>> p.save()

 这等同于如下SQL语句:

SELECT id, name, address, city, state_province, country, website
FROM books_publisher
WHERE name = 'Apress';
  
UPDATE books_publisher SET
    name = 'Apress Publishing',
    address = '2855 Telegraph Ave.',
    city = 'Berkeley',
    state_province = 'CA',
    country = 'U.S.A.',
    website = 'http://www.apress.com'
WHERE id = 52;

  注意在这里我们假设Apress的ID为52)

在这个例子里我们可以看到Django的save()方法更新了不仅仅是name列的值,还有更新了所有的列。 若name以外的列有可能会被其他的进程所改动的情况下,只更改name列显然是更加明智的。 更改某一指定的列,我们可以调用结果集(QuerySet)对象的update()方法: 示例如下:

>>> Publisher.objects.filter(id=52).update(name='Apress Publishing')

 与之等同的SQL语句变得更高效,并且不会引起竞态条件。

UPDATE books_publisher
SET name = 'Apress Publishing'
WHERE id = 52;

 update()方法对于任何结果集(QuerySet)均有效,这意味着你可以同时更新多条记录。 以下示例演示如何将所有Publisher的country字段值由’U.S.A’更改为’USA’:

>>> Publisher.objects.all().update(country='USA')

 删

      >>> Book.objects.filter(id=1).delete()

       (3, {'app01.Book_authors': 2, 'app01.Book': 1})

我们表面上删除了一条信息,实际却删除了三条,因为我们删除的这本书在Book_authors表中有两条相关信息,这种删除方式就是django默认的级联删除。

先记住一句话,values是列表里面是字典,value_list是列表里面是元组

上图只是当自己忘记了看一眼怎么写(完整样式)

下面开始讲解

>>> Publisher.objects.all()
[<Publisher: 中国机械出版社>, <Publisher: American publisher>]

注意:

这相当于这个SQL语句:

SELECT id, name, address, city, state_province, country, website
FROM books_publisher;

注意到Django在选择所有数据时并没有使用 SELECT* ,而是显式列出了所有字段。

惰性机制:

所谓惰性机制:Publisher.objects.all()只是返回了一个QuerySet(查询结果集对象),并不会马上执行sql,而是当调用QuerySet的时候才执行。

QuerySet特点:

       1   可迭代的

       2   可切片

一  查询相关API:

 <1>get(**kwargs):        返回与所给筛选条件相匹配的对象,返回结果有且只有一个,如果符合筛选条件的对象超过一个或者没有都会抛出错误。

 <2>all():                       查询所有结果

 <3>filter(**kwargs):      它包含了与所给筛选条件相匹配的对象

 <4>exclude(**kwargs):  它包含了与所给筛选条件不匹配的对象

 <5>order_by(*field):      对查询结果排序

 <6>reverse():                对查询结果反向排序

 <7>distinct():                从返回结果中剔除重复纪录

 <8>values(*field):         返回一个ValueQuerySet——一个特殊的QuerySet,运行后得到的并不是一

                                     系列 model的实例化对象,而是一个可迭代的字典序列
                [{'user': 'uesr', 'user_type__caption': '普通用户'}, {'user': 'uesr', 'user_type__caption': '普通用户'}] <9>values_list(*field): 它与values()非常相似,它返回的是一个元组序列,values返回的是一个字典序列 -----【(),(),()】 <10>count(): 返回数据库中匹配查询(QuerySet)的对象数量。 <11>first(): 返回第一条记录,等价于[:1][0] <12>last(): 返回最后一条记录,等价于[::1][0] <13>exists(): 如果QuerySet包含数据,就返回True,否则返回False。

实例:

      1   查询id为2的书籍信息,并只显示书籍名称和出版日期    

>>> Book.objects.filter(id=2).values("title","publication_date")
        [{'title': 'python Gone', 'publication_date': datetime.date(2019, 5, 24)}]

       2   查询所有的出版信息,并按id降序排列,并尝试使用reverse方法进行反向排序。   

>>> Publisher.objects.all().order_by("id")
        [<Publisher: 中国机械出版社>, <Publisher: Boo>, <Publisher: 人民出版社>]
>>> Publisher.objects.all().order_by("id").reverse()
        [<Publisher: 人民出版社>, <Publisher: Boo>, <Publisher: 中国机械出版社>]                 

>>> Publisher.objects.all().order_by("-id")
        [<Publisher: 人民出版社>, <Publisher: Boo>, <Publisher: 中国机械出版社>] 

      3    查询出版社所在的城市信息,城市信息不要重复      

>>> Publisher.objects.all().values("city").distinct()
        [{'city': '北京'}, {'city': 'beijing'}]

       4    查询城市是北京的出版社,尝试使用exclude方法  

>>> Publisher.objects.all().filter(city='beijing')
        [<Publisher: Boo>]

       5    查询男作者的数量    

>>> AuthorDetail.objects.filter(sex=0).count()

查询一对一

user_info_obj = models.UserInfo.objects.filter(id=1).first()
print user_info_obj.user_type
print user_info_obj.get_user_type_display()
print user_info_obj.userprofile.password
 
user_info_obj = models.UserInfo.objects.filter(id=1).values('email', 'userprofile__username').first()
print user_info_obj.keys()
print user_info_obj.values()

查询一对多

多表关联查询

 

多表正向查询

 多表反向查询

创建多对多的表

 1   外键关联查询

 >>> AuthorDetail.objects.all().values("author")

        [{'author': 1}]
>>> AuthorDetail.objects.all().values("author__name")
        [{'author__name': 'alex'}]

2  多对多关联查询

>>> Book.objects.all().filter(title='python Gone').values("authors")

        [{'authors': 1}, {'authors': 2}]
>>> Book.objects.all().filter(title='python Gone').values("authors__name")
        [{'authors__name': 'alex'}, {'authors__name': 'alvin'}]

>>> Book.objects.all().filter(authors__name='alex').values('title')
        [{'title': 'python Gone'}]

多表查询技巧:

       __:两个下划线可以生成连接查询,查询关联的字段信息

      _set:提供了对象访问相关联表数据的方法。但是这种方法只能是相关类访问定义了关系的类(主键类访问外键类)

 

聚合查询和分组查询

1  annotate(*args,**kwargs):可以为QuerySet每一个对象添加注解。可以通过计算查询结果中每一个对象所关联的对象集合,从而得出总计值(也可以是平均值或总和),用于分组查询

2  aggregate(*args,**kwargs):通过对QuerySet进行计算,返回一个聚合值的字典。

aggregate()中每一个参数都指定一个包含在字典中的返回值。用于聚合查询。

聚合函数(Aggregation Functions)

所在位置:django.db.models

1 Avg:  返回所给字段的平均值

2 Count: 根据所给的关联字段返回被关联的model的数量

3 Max:返回所给字段的最大值

4 Sum:计算所给字段的总和

  为了演示实例,在这里我们给Book加一个price字段:

  然后同步数据库:makemigrations, migrate

 索引

db_index = True  数据库索引

复合索引

唯一索引

 

条件选取 filter表示=,exclude表示!=

querySet.distinct()  去重复
__exact        精确等于 like 'aaa'
 __iexact    精确等于 忽略大小写 ilike 'aaa'
 __contains    包含 like '%aaa%'
 __icontains    包含 忽略大小写 ilike '%aaa%',但是对于sqlite来说,contains的作用效果等同于icontains。
__gt    大于
__gte    大于等于
__lt    小于
__lte    小于等于
__in     存在于一个list范围内
__startswith   以...开头
__istartswith   以...开头 忽略大小写
__endswith     以...结尾
__iendswith    以...结尾,忽略大小写
__range    在...范围内
__year       日期字段的年份
__month    日期字段的月份
__day        日期字段的日
__isnull=True/False

  

例子:
>> q1 = Entry.objects.filter(headline__startswith="What")
>> q2 = q1.exclude(pub_date__gte=datetime.date.today())
>> q3 = q1.filter(pub_date__gte=datetime.date.today())
>>> q = q.filter(pub_date__lte=datetime.date.today())
>>> q = q.exclude(body_text__icontains="food")

即q1.filter(pub_date__gte=datetime.date.today())表示为时间>=now,q1.exclude(pub_date__gte=datetime.date.today())表示为<=now

  

 

 

 


posted @ 2016-09-12 09:39  我当道士那儿些年  阅读(1996)  评论(1编辑  收藏  举报