本篇内容接上篇Python基础(Django二)


七、Model

1、说明:

Model是Django为方便程序操作数据库而诞生的,使用的是ORM模式。

对象关系映射(Object Relational Mapping,简称ORM)模式是一种为了解决面向对象与关系数据库存在的互不匹配的现象的技术。简单的说,ORM是通过使用描述对象和数据库之间映射的关系,将程序中的对象自动持久化到关系数据库中。


2、使用:

 2-1、创建Model(编辑应用目录下的models.py)

from django.db import models

class Author(models.Model):    #定义一个类(表),类名即是表名
    #定义一个叫first_name的字段(列),字段类型为字符串类型,参数设置最大长度为32个字节
    first_name = models.CharField(max_length=32)	
    last_name = models.CharField(max_length=32)
    email = models.EmailField(null=True,blank=True)     #字段类型为邮箱,会有格式验证
    def __unicode__(self):	    #定义查询类的数据时显示的值
        return '%s %s'%(self.first_name,self.last_name)

字段类型:

1、models.AutoField  自增列 = int(11)

  如果没有的话,默认会生成一个名称为 id 的列,如果要显示的自定义一个自增列,必须将给列设置为主键 primary_key=True。

2、models.CharField  字符串字段

  必须 max_length 参数

3、models.BooleanField  布尔类型 =tinyint(1)

  不能为空

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


参数:

1、null=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

7、default  默认值

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

 

 #相关命令

 python manage.py validate   #用来检测models.py语法的正确性

 python manage.py makemigrations

 python manage.py migrate   #根据代码中定义的类来自动创建数据库表


 2-2、操作数据库表

from app01 import models

# 增 #
#第一种:
models.Author.objects.create(first_name='zhang',last_name='san',email='z@test.com')

#第二种:可以接受字典类型的数据
dic = {'first_name': 'li', 'last_name': 'si', 'email': 'z@test.com'}
models.Author.objects.create(**dic)

#第三种:
obj = models.Author(**dic)
obj.save()

# 查 #
models.Author.objects.all()	#获取Author表里的所有数据

#过滤查询
filter()方法获取的是一个列表,get()方法获取的是单个对象

models.Author.objects.get(first_name='zhang')    #Author是表名称,first_name是字段名称,zhang是查询的关键字

models.Author.objects.filter(last_name='san')	

models.Author.objects.filter(first_name='zhang', last_name='san') #查询条件可以有多个	

models.Author.objects.exclude(first_name='zhang') #查询first_name不等于zhang的

models.Author.objects.filter(first_name='zhang').count() #获取满足查询条件的数据个数

#多重查询
models.Author.objects.filter(first_name='zhang').order_by("email")

#限制查询
models.Author.objects.order_by('first_name')[0]	#取查询结果QuerySet的第一个值

#排序
models.Author.objects.order_by("first_name")   #根据某一个字段值对结果排序
models.Author.objects.order_by("first_name","last_name") #根据多个字段排序,第二个字段会在第一个字段值相同的情况下被使用到
models.Author.objects.order_by("-first_name")	#逆向排序,字段前面加减号-前缀
  
#利用双下划线将查询字段和相应操作连接起来(摘自其他博客)
# 大于,小于
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")	  #用于模糊匹配name字段。
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

# 改 #
# 在查询的基础上增加一个修改操作
# 第一种:
models.Author.objects.filter(last_name='si').update(last_name='SI')

# 第二种:可以接受字典类型的数据
dic = {'first_name': 'li', 'last_name': 'si', 'email': 'z@test.com'}
models.Author.objects.filter(last_name='si').update(**dic)

# 第三种:
obj = models.Author.objects.get(last_name='si')
obj.email = 'new@test.com'
obj.save()

# 删 #
# 在查询的基础上增加一个删除操作
models.Author.objects.filter(last_name='san').delete()

    

 2-3、连表结构

   说明:

    Django的ORM有多种关系:

     一对一:一个只属于一个

     一对多:一个属于多个

     多对多:一个既有很多个,又属于很多个

    各自定义的方式为 :

       一对一: models.OneToOneField(其他表)

       一对多: models.ForeignKey(其他表)

       多对多: models.ManyToManyField(其他表)    

    应用场景:

      一对多:当一张表中创建一行数据时,有一个单选的下拉框(可以被重复选择)
       例如:创建用户信息时候,需要选择一个用户类型【普通用户】【金牌用户】【铂金用户】等。

       多对多:在某表中创建一行数据是,有一个可以多选的下拉框
       例如:创建用户信息,需要为用户指定多个爱好

       一对一:在某表中创建一行数据时,有一个单选的下拉框(下拉框中的内容被用过一次就消失了


  举例:

 #models.py

# -*- coding:utf-8 -*-
from __future__ import unicode_literals
from django.db import models

# Create your models here.

# OneToMany example 1
class UserType(models.Model):
    caption = models.CharField(max_length=32)
    def __unicode__(self):
        return self.caption

class UserInfo(models.Model):
    user_type = models.ForeignKey(UserType)
    username = models.CharField(max_length=32)
    age = models.IntegerField()

    def __unicode__(self):
        return self.username

# OneToMany example 2

class MyUser(models.Model):
    username = models.CharField(max_length=16)
    password = models.CharField(max_length=16)

    def __unicode__(self):
        return self.username

class News(models.Model):
    title = models.CharField(max_length=32)
    content = models.CharField(max_length=32)

    def __unicode__(self):
        return self.title

class Favor(models.Model):
    user_obj = models.ForeignKey(MyUser)
    new_obj = models.ForeignKey(News)

    def __unicode__(self):
        return "%s --> %s"%(self.user_obj.username,self.new_obj.title)

# ManyToMany example 1

class Host(models.Model):
    hostname = models.CharField(max_length=32)
    port = models.IntegerField()
    def __unicode__(self):
        return self.hostname

class HostAdmin(models.Model):
    username = models.CharField(max_length=32)
    email = models.CharField(max_length=32)
    host = models.ManyToManyField(Host)
    def __unicode__(self):
        return self.username

# ManyToMany example 2
'''手动创建多对多关系表,不需要Django自动创建,优点是第三张表的字段可以自定义'''
class Host2(models.Model):
    hostname = models.CharField(max_length=32)
    port = models.IntegerField()
    def __unicode__(self):
        return self.hostname

class HostAdmin2(models.Model):
    username = models.CharField(max_length=32)
    email = models.CharField(max_length=32)
    host = models.ManyToManyField(Host2,through="HostRelation")
    def __unicode__(self):
        return self.username

class HostRelation(models.Model):
    host_obj = models.ForeignKey(Host2)
    admin_obj = models.ForeignKey(HostAdmin2)


 #views.py

# -*- coding:utf-8 -*-
from django.shortcuts import render,HttpResponse
from app01 import models

# Create your views here.

def user_info(request):
   # 一对多 #

   # models 为"OneToMany example 1"
   #创建数据#
   #第一种: ForeignKey所在的字段加 _id 进行直接赋值
   models.UserInfo.objects.create(username='test1',age=13,user_type_id=1) #user_type_id是一对多关系表中Django自动生成的字段名
   #第二种:ForeignKey所在的字段使用对象进行赋值
   user_type_obj = models.UserType.objects.get(id=2)                          #先从user_type表中获取一个对象
   models.UserInfo.objects.create(username='test2',age=14,user_type=user_type_obj)    #使用刚获取的对象进行赋值(注意字段名称)

   #正向查找#(就是从ForeignKey所在字段的表去查询数据)
   #单表查询
   models.UserInfo.objects.filter(username='test1')   #查询UserInfo表中username字段值是‘test1’的数据

   # 跨表查询,通过ForeignKey所在的字段 + 双下划线 + 被跨的表中的字段来查询(ForeignKey所在的字段user_type是一个对象)
   # models.UserInfo.objects.filter(user_type__caption='CEO') #查询UserInfo表中user_type对象中caption字段的值是CEO的数据
   ret = models.UserInfo.objects.filter(user_type__id=2)     #语法同上,ret是一个QuerySet对象
   for i in ret:
      #打印获取到的QuerySet对象中每个值的username属性,以及user_type对象的caption属性(跨表操作时,查询使用__(双下划线),访问字段使用.(点))
       print i.username,i.user_type.caption

   #反向查找#(就是从被ForeignKey字段关联的表去查询数据)
   #单表查询
   line = models.UserType.objects.get(id=1)   #从UserType表中获取一个id=1的对象

   #跨表查询
   # 第一种: 通过被跨的表的小写表名 + 双下划线 + 要查询的字段  来查询
   models.UserType.objects.get(userinfo__username='test1')   #userinfo是django自动在UserType表中创建的一个对象,也就是对端表的小写表名
   # 第二种:
   line.userinfo_set.filter(username='test1')
      # line是一个对象(参考如上的获取条件)
      # userinfo_set是UserInfo表中满足line条件的所有值。也就是UserInfo表中所有user_type=1 的值
      # 本次查询的目标是:通过UserType表去反向查询UserInfo表中user_type=1且username=test1的所有数据
   line.userinfo_set.all().count()    #计算满足查询条件的数据个数

   #多级查询, models 为"OneToMany example 2"
   ret = models.News.objects.filter(favor__user_obj__username='zhangsan')
      # 查询的表顺序为 News -> Favor -> MyUser
      # 先是反向跨表查询:通过News表查询Favor表中的user_obj对象
      # 再是正向跨表查询:通过user_obj对象查询Myuser表中username字段值为zhangsan的用户
      # 最终得到zhangsan赞过的所有文章
   for i in ret:
      print i.title              #文章的标题
      print i.favor_set.all().count()    #文章一共几个赞

   # 多对多 #
   #  models为 "ManyToMany example 1"

   models.HostAdmin.objects.create(username='a1',email='1@qq.com')    #创建一个用户,管理的主机为空
   #正向添加#
   admin_obj = models.HostAdmin.objects.get(username='a1')   #先获取一个用户对象
   host_list = models.Host.objects.filter(id__lt=3)          #再获取多个主机对象
   admin_obj.host.add(*host_list)                            #为一个用户添加多个可管理的主机

   #反向添加#
   host_obj = models.Host.objects.get(hostname='h1')       #先获取一个主机对象
   admin_list = models.HostAdmin.objects.filter(id__gt=1)  #再获取多个用户对象
   host_obj.hostadmin_set.add(*admin_list)                 #为一个主机添加多个用户,注意这里用到了hostadmin_set

   #正向查询#
   admin_obj = models.HostAdmin.objects.get(username='a1')    #先获取一个用户对象
   host_list = admin_obj.host.all()                     #查询该用户管理的所有主机
   for i in host_list:
      print i.hostname,i.port

   #反向查询#
   host_obj = models.Host.objects.get(hostname='h1')     #先获取一个主机对象
   admin_list = host_obj.hostadmin_set.all()           #查询该主机的所有管理员
   for i in admin_list:
      print i.username,i.email

   #自定义多对多关系#
   # models为 "ManyToMany example 2"
   # 添加数据
   models.HostRelation.objects.create(
       host_obj_id = 1,    #等于 host_obj = models.Host2.objects.get(id=1)
       admin_obj_id = 1,   #等于 admin_obj = models.HostAdmin2.objects.get(id=1)
   )

   # 查询
   # relation_list = models.HostRelation.objects.all()
   relation_list = models.HostRelation.objects.filter(admin_obj__username='user1')
   #通过HostRelation表查询用户名是user1的所有数据
   for i in relation_list:
      print i.admin_obj.username #用户名
      print i.host_obj.hostname  #用户所管理主机的主机名

   # Django 的F #
   # 对对象中某一列值的操作
   from django.db.models import F
   models.Host.objects.filter(hostname='h1').update(port=F('port')+1) #把Host表中所有hostname=h1的数据的port值加1
   models.Host.objects.update(port=F('port')+1)   #把Host表中所有的port值加1

   # Django 的Q #
   # 对象的复杂查询
   from django.db.models import Q

   #单Q多条件查询
   q1 = Q()            #创建一个Q对象
   q1.connector = 'OR'       #定义查询条件是 '或'
   q1.children.append(('hostname__contains','h1'))       #children添加的是元祖,查询字段支持使用一些自带的方法
   q1.children.append(('hostname','h2'))           #添加多个查询条件
   q1.children.append(('hostname','h5'))
   ret_list = models.Host.objects.filter(q1)        #使用q1对象进行查询(针对Host表)
   for i in ret_list:
      print '%s->%s'%(i.hostname,i.port)

   #多Q多条件查询
   con = Q()     #创建一个外层Q对象,Q可以嵌套
   q2 = Q()
   q2.connector = 'OR'
   q2.children.append(('port','23'))        #查询port字段值等于23的数据
   q2.children.append(('port__gt','23'))     #查询port字段值大于23的数据

   con.add(q1,'AND')     #把q1添加到之前定义的最外层的Q对象中,查询条件是 '和'
   con.add(q2,'AND')     #把q2添加到之前定义的最外层的Q对象中,查询条件是 '和'

   ret_list = models.HostAdmin.objects.filter(con)       #查询的结果是同时满足q1和q2条件的数据(针对HostAdmin表)
   for i in ret_list:
      print '%s->%s'%(i.hostname,i.port)

   #多Q多条件跨表查询
   q1.children.append(('username','a1'))
   q1.children.append(('username','a2'))
   q1.children.append(('username','a3'))
   q2.children.append(('host__hostname','h4'))       #支持跨表查询,host是表名 + 双下划线 + 查询的字段

   con.add(q1,'AND')
   con.add(q2,'AND')

   ret_list = models.HostAdmin.objects.filter(con)       #查询用户名是a1或a2或a3 且管理主机名为h4的用户(针对HostAdmin表)
   for i in ret_list:
      print i.username


   return HttpResponse('ok')    #无特别意义,只是函数需要一个返回值。


博客的部分内容和思路整理自武沛齐的博客