Django学习入门笔记(三)

模型部分讲解

本文是自己学习做的笔记,如有雷同纯属巧合,如有问题请指出,谢谢!

基于环境Ubuntu16.04

python3.6

Django 2.07


[储备知识] ORM(对象object-关系relationship-映射map)

ORM的作用->完全面向对象不需要对任何sql语句进行操作,这样修改数据库(orcale/sqlite3/mysql)时只需要对少量代码进行修改

(1)数据库的更换
1-在mysql下创建数据库,如名为test2(Django默认是sqlite3的数据库,换库配置需要换)
2-更换mysql数据库: 在settings文件中的数据库做如下配置:

DATABASES = {
    'default': {
        'ENGINE': 'django.db.backends.mysql',
        'NAME': 'test2',
        'USER': 'root',
        'PASSWORD': 'mysql',
        'HOST': 'localhost',
        'PORT': '3306'

    }
}


这里的name为需要使用到的mysql数据库的名称,USER PASSWORD为登陆数据库的用户名密码
HOST为数据库的IP地址,PORT默认为3306

注意python3可能安装不上MYSQLdb可以用pymysql代替
方法为

import pymysql
pymysql.install_as_MySQLdb()

Django根据属性的类型确定:当前选择数据库支持字段的类型以及默认的控件类型,比如搜索框等
Django会为表自动添加primary key每个模型只有一个pk(primary key)如果已经赋予了一个PK则不会自动添加
在models里面定义表时,表名注意:1-不能设为python预留的名称 2-不能包含__两个以上连续的下划线(__在后面做查询的时候用到)

[一]表内属性的定义类型
1-字段类型

常用的字段类型有:
    from django.db.models as models
    models.BooleanField
    models.FloatField
    models.TimeField
    models.DateTimeField
    models.DecimalField(max_digits,decimal_places) # 第一个参数是总的数的位数,第二个数是小数的位数
    models.FileField: 一个上传文件的字段   models.ImageField:保证上传是个有效的image 
    # 这二者一般而言不会用,一般只会把文件上传到云
    # 然后上传磁盘上的地址就可以
    ………………等字段的类型,都以Field结尾
    (在设计表的时候可以定义一个isDelete的属性做逻辑删除不做物理删除)

2-字段选项
    null      --->True Django将null存储到数据库
    blank     --->True允许该字段为空白
    db_column --->字段的名称,如果未指定就用属性的名称
    db_index  --->如果为True则在表中为字段创建索引(加速查询)
    default   --->默认值
    primary   --->是否为主键 此时字段设置为AutoField。
    unique    --->如果为True这个字段在表中必须有唯一值

用法如下

    hname = models.CharField(max_length=20, db_column='Hero Name')
    hgender = models.BooleanField(default=True)

3-关系(都在models中)
    ForeignKey: 一对多,将字段定义在多的端中
    ManyToMany: 多对多,定义在连短中
    OneToOneField: 一对一将字段定义在任意一端中
    用一访问多: 对象.模型类小写_set  book.heroinfo_set
    用一对一访问: 对象.模型类小写    hero.bookinfo
    访问id           对象.属性_id       hero.book_id
    Example:

class HeroInfo(models.Model):
    hname = models.CharField(max_length=20, db_column='Hero Name')
    hgender = models.BooleanField(default=True)
    isDelete = models.BooleanField(default=False)
    hcontent = models.CharField(max_length=100)
    hbook = models.ForeignKey(BookInfo)

book = BookInfo()

    这里就和之前的学习笔记一样,一对多的情况把外键放在多的一端

    查询一本书里面的英雄 book.heroinfo_set   

    在这里把一对多    一认为是上端,而多那个端为下端

    查询下端的方法  实例名.类属性名_set用来查询外键信息(在子关联的地方会讲到上端的查询方法)

    查询英雄所在的书籍   对象.属性 hero.book 

    比如:肯德基里面有很多食品,做相互关联

class food():
    """定义了很多类属性"""
    h_shop = models.ForeignKey(Shop)

[二]元选项
在模型类中定义类Meta用于设置元信息

class BookInfo(models.Model):
    btitle = models.CharField(max_length=20, null=False)
    bpub_date = models.DateTimeField(null=True, db_column='publication date')
    bread = models.IntegerField(default=0)
    bcomment = models.IntegerField(default=0)
    isDelete = models.BooleanField(default=False)
    class Meta:
        db_table='bookinfo'
        ordering=['id']
    books1 = models.Manager()
    books2 = BookInfoManaer()


db_table: 定义数据表名, 默认名称<app_name>_<model_name>   这个数据表的名字更改的是他在数据库中的名字
例如:原模型类名为BookInfo
      这里将db_table = 'book1'
      则在数据库中创建表的时候名为book1而不是bookinfo(这里都转换为了小写)
      ordering: 对象的默认排序字段,获取对象的列表时使用(获取查询时候的顺序)
      ordering=['id']按照id正向输出,ordering=[-'id']倒序输出
元选项是在表生成前定义的表的属性,例如排序以及表的名称……  

[三]管理器的说明
模型定义完后有很多类方法
比如BookInfo.objects.all()等 objects 是继承的Manager类型对象,负责和数据库进行交互,一般不作修改
每个Django模型必须有至少一个管理器,如果自己重新定义,objects默认就没了


自定义管理器的主要作用:
1-向管理器中添加额外的方法(例如下面提到的create方法)
2-修改管理器返回的原始查询集:重写get_queryset()方法 修改原始的方法

class BookInfoManager(models.Manager):
    def get_queryset(self):
    # 修改了查询的方法
    # 这个改写的意思是首先先继承父类的get_queryset方法,然后过滤出未被逻辑删除的显示
        return super(BookInfoManager, self).get_queryset().filter(isDelete=False)


class BookInfo(models.Model):
    ……
    books1 = BookInfoManager()
    books2 = models.Manger()

# 如果自己定义了一个管理器那么一定要创建一个指向models.Manger(),因为自己创建了管理器,默认的
# object 就不会被生成

接下来总结管理器的第二个功能:

定义了上述的books1, books2可以在shell中
b = BookInfo(), b.btitle=xxx
BookInfo.books2.all() #进行查询 会显示所有数据
若用BookInfo.books1.all()  按照上面改写的Manager()只会现实未被逻辑删除的结果

但是这样的方法太繁琐,如果能够直接增加删除数据就更加方便了:

首先对property属性、类方法和静态方法进行复习:


特性property:property可以保护似有属性,通过一段函数返回特定的类属性,保护了私有属性

import math
class Circle:
    def __init__(self,radius): #圆的半径radius
       self.__radius=radius
 
    @property
    def area(self):
        return math.pi * self.radius**2 #计算面积

    @property
    def perimeter(self):
        return 2*math.pi*self.radius #计算周长
 
 c=Circle(10)
 print(c.area) #可以向访问数据属性一样去访问area,会触发一个函数的执行,动态计算出一个值
 print(c.perimeter) #同上

输出结果:
314.1592653589793
62.83185307179586
私有属性 radius不被外界访问
静态方法:一般用于类内使用,在继承的过程中不能继承,用于类内的动态赋值

class Date:
    def __init__(self,year,month,day):
        self.year=year
        self.month=month
        self.day=day
    @staticmethod
    def now(): #用Date.now()的形式去产生实例,该实例用的是当前时间
        t=time.localtime() #获取结构化的时间格式
        return Date(t.tm_year,t.tm_mon,t.tm_mday) #新建实例并且返回
    @staticmethod
    def tomorrow():#用Date.tomorrow()的形式去产生实例,该实例用的是明天的时间
        t=time.localtime(time.time()+86400)
        return Date(t.tm_year,t.tm_mon,t.tm_mday)

a=Date('1987',11,27) #自己定义时间
b=Date.now() #采用当前时间
c=Date.tomorrow() #采用明天的时间

print(a.year,a.month,a.day)
print(b.year,b.month,b.day)
print(c.year,c.month,c.day)

这里b c 通过调用内部的静态方法,来定义了各自类内部的self.year, self.month, self.day
 
类方法:在继承过程中能进行传递

import time
class Date:
    def __init__(self,year,month,day):
        self.year=year
        self.month=month
        self.day=day
    # @staticmethod
    # def now():
    #     t=time.localtime()
    #     return Date(t.tm_year,t.tm_mon,t.tm_mday)

    @classmethod #改成类方法
    def now(cls):
        t=time.localtime()
        return cls(t.tm_year,t.tm_mon,t.tm_mday) #哪个类来调用,即用哪个类cls来实例化

class EuroDate(Date):
    def __str__(self):
        return 'year:%s month:%s day:%s' %(self.year,self.month,self.day)

e=EuroDate.now() # 类方法的调用是可以不是实例对象,是面向类的
print(e) #我们的意图是想触发EuroDate.__str__,此时e就是由EuroDate产生的,所以会如我们所愿


输出结果:
year:2017 month:3 day:3

若不用类方法,仅用静态方法输出结果如下

输出结果:
<__main__.Date object at 0x1013f9d68>

静态方法一般用于类内部
类属性用于类之间,因为可继承

 
对表进行操作时,若要快速添加一条信息
用b=BookInfo(),b.btitle=xxx这样程序化的输入可以封装成函数
__init__已经在models里面写了,通过改写init方法很麻烦

方法一:通过类方法进行创建
进入shell模式后
通过b = BookInfo.create(xx, datetime(xx, xx, xx))进行类方法的创建

class BookInfo(models.Model):
    btitle = models.CharField(max_length=20, null=False)
    bpub_date = models.DateTimeField(null=True, db_column='publication date')
    bread = models.IntegerField(default=0)
    bcomment = models.IntegerField(default=0)
    isDelete = models.BooleanField(default=False)
    books1 = models.Manager()
    books2 = BookInfoManaer()


    @classmethod
    def create(cls, btitle, bpub_date):
        b = BookInfo()
        b.btitle = btitle
        b.bpub_date = bpub_date
        b.bread = 0
        b.bcomment = 0
        b.isDelete = False
        b.save()


方法二:通过管理器manager实现模型类的快速创建

class BookInfoManaer(models.Manager):
    def get_queryset(self):
        return super(BookInfoManaer, self).get_queryset().filter(isDelete=False)

    def create(self, btitle, bpub_date):
        b = BookInfo()
        b.btitle = btitle
        b.bpub_date = bpub_date
        b.bread = 0
        b.bcomment = 0
        b.isDelete = False
        b.save()

class BookInfo(models.Model):  # 模型类
    btitle = models.CharField(max_length=20, null=False)
    bpub_date = models.DateTimeField(null=True, db_column='publication date')
    bread = models.IntegerField(default=0)
    bcomment = models.IntegerField(default=0)
    isDelete = models.BooleanField(default=False)
    class Meta:
        db_table='bookinfo'
        ordering=['id']
    books1 = models.Manager() # 自定义管理器是定义一个类 该类是模型类的一个类属性
    books2 = BookInfoManaer()

    def __str__(self):
        return self.btitle

然后可以通过
 

b = BookInfo.books2.create('abc', datetime(xx, xx, xx))

进行数据的增加
上面所属的元选项和管理器都是模型类的成员

[四]模型查询

1-查询集
惰性执行:创建查询集不会有数据库的访问,知道调用数据时才访问数据库
缓存: 每个查询集都包涵一个缓存来最小化对于数据库的访问
查询集=查询的得到数据组成的列表(生成器)
返回查询集的方法——过滤器:all(), filter(), exclude(), order_by(), values()
注意:
(1)values查询将一个对象(所有字段)构成一个字典,然后构成一个列表返回
(2)filter(键1=值1, 键2=值2)  ======> filter(键1=值1).filter(键2=值2) 相当于逻辑与的关系
 
查询单个值的方法
(1)get():返回单个满足条件的对象 # 如果找到多条或为空都会报错
(2)count():返回当前查询的总条数
(3)first():返回第一个对象
(4)last():返回最后一个对象
(5)exisits():判断查询集中是否有数据
注意:
(1)对查询集支持用下标的方式进行索引,但是不支持负索引
(2)在缓存的时候只有用的数据是子集的时候不进行缓存
(3)
2-字段查询
实现where子列名  利用filter()/exclude()/get()参数
语法使用: [属性名称__比较运算符=值] 
例如filter(title__contains='%') 过滤出 一定是等号
对于外键,使用属性名_id表示文件。HeroInfo.book_id即可查询heroInfo中某个英雄对应的书的id

比较运算符
1-exact:表示判等__exat可以不写默认判等
2-contains表示包涵(区分大小写)
3-startswith\endswith 以value开头或结尾的
例如exclude(btitle__endswith='d')
4-isnull\isnotnull
在上述运算符前面加一个i就不区分大小写查询
5-in表示查询在这个范围之内的信息
filter(pk__in=[1, 2, 3])
把主键值为1, 2, 3的都查找出来

gt/gte/lt/lte 大于/大于等于/小于/小于等于

filter(bpub_date__year=1980)
filter(bpub_date__gt=date(1980, 12, 31))

跨关联关系的查询:处理join查询
语法:模型类名<属性名><比较>

相当于inner join:
BookInfo.books1.filter(heroinfo__hname__contains="人民")
用法:外键类名(小写)__需要过滤的列名__比较运算符=值
查询书中英雄名中包含'人民'的书
关联查询的方式


3-聚合查询
聚合函数:Avg, Count, Max, Min, Sum
利用aggregate()返回聚合函数的值
聚合函数的用法

from django.db.models import Max
a = BookInfo.books.all()
a.aggregate(Max('btitle'))
a.count()

F对象,可以使模型的字段A与字段B进行比较,如果字段A写在了等号的左边,字段B写在了等号的右边需要通过
F对象使用在等号两边是两个字段类型的时候用
F对象进行构造
a = BookInfo.books1.filter(bread__gte=F('bcomment'))
这里bread和bcomment是一个模型类里面的两个不同字段
F对象还可以支持算数运算
a = BookInfo.books1.filter(bread__gte=F('bcomment')*2)
F对象还可以支持关联对象查询
a = BookInfo.books1.filter(isDelete=F('heroinfo__isDelete'))

Q对象
用来表达逻辑或 而不是通过filter(条件1, 条件2)做与关系
Q(条件1)|Q(条件2) 表达或对象

Q(条件1)&Q(条件2) 表达逻辑与 但是太麻烦不怎么用

猜你喜欢

转载自blog.csdn.net/qq_24724109/article/details/81544057