Django学习笔记---Model

一、数据库的准备工作

1、修改使用的数据库

一个实例对象就是一个记录,django默认使用sqlite3,用mysql需要更改配置文件。在settings修改DATABASES,默认用的是mysqldb。

DATABASES = {
    'default': {

        'ENGINE': 'django.db.backends.mysql', 

        'NAME': 'books',    #你的数据库名称

        'USER': 'root',   #你的数据库用户名

        'PASSWORD': '', #你的数据库密码

        'HOST': '', #你的数据库主机,留空默认为localhost

        'PORT': '3306', #你的数据库端口
    }
}

2、在项目的__init__文件中加上导入pymysql是语句

import pymysql
pymysql.install_as_MySQLdb()

3、打印日志,可以看见每一步操作的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',
        },
    }
}  

4、在model.py建立表信息

创建一张表:

一个类就代表一张表,类的每个属性代表表中的每个字段。

class Book(models.Model):    //  需要继承这个类,这样django才会知道这是一张表
    name = models.CharField(max_length=20)
    price = models.IntegerField()
    author = models.CharField(max_length=20, null=True)  # 可以为空,默认也是可以为空

 模型常用的字段类型参数:

<1> CharField      #字符串字段,要求必须有一个参数 max_length。  

<2> IntegerField   #用于保存一个整数.

<3> FloatField     # 一个浮点数. 必须提供两个参数:  总位数,小数位数

<4> AutoField
        # 一个 IntegerField, 添加记录时它会自动增长. 你通常不需要直接使用这个字段; 
        # 自定义一个主键:my_id=models.AutoField(primary_key=True)
        # 如果你不指定主键的话,系统会自动添加一个主键字段到你的 model.

 Field重要参数:

<1> null : 数据库中字段是否可以为空

<2> blank: django的 Admin 中添加数据时是否可允许空值

<3> default:设定缺省值

<4> primary_key:设置主键,如果没有设置django创建表时会自动加上:
      id = meta.AutoField('ID', primary_key=True)
      primary_key=True implies blank=False, null=False and unique=True. Only one
      primary key is allowed on an object.

<5> unique:数据唯一

<6> verbose_name:   Admin中显示的字段名称

<7> db_column:  设置数据库中字段的列名

<8> choices:   Admin中显示选择框的内容,用不变动的数据放在内存中从而避免跨表操作,和form组件的choices一样,都是数据源。

<9> db_index:   数据库中该字段是否建立索引。作用是加速查找。

<10> unique:    数据库中该字段是否建立唯一索引。不仅可以加速查找,还可以去重。

<11> primary_key:    数据库中该字段是否为主键。不仅可以加速查找,还可以去重,并且非空。

<12> editable:  Admin中是否可以编辑,会消失掉。

<13> help_text:   Admin中该字段的提示信息。

<14> error_messages: 自定义错误信息(字典类型),从而定制想要显示的错误信息;
                    如:{'null': "不能为空.", 'invalid': '格式错误'}

<15>  validators:   自定义错误验证(列表类型),从而定制想要的验证规则。

外键的参数:

to="Author"   to代表绑定的表

to_field="id"   绑定的列名

related_name="b"   反向查找用.b , 以前反向查询是 小写的类名+_set  定义related_name以后 直接b就OK

related_query_name="b"    反向查找用.b_set,以前反向查询是 小写的类名+_set  定义related_query_name以后 b_set

on_delete    # 当删除关联表中的数据时,当前表与其关联的行的行为
      - models.CASCADE,删除关联数据,与之关联也删除 
      - models.DO_NOTHING,删除关联数据,引发错误IntegrityError
      - models.PROTECT,删除关联数据,引发错误ProtectedError
      - models.SET_NULL,删除关联数据,与之关联的值设置为null(前提FK字段需要设置为可空)
      - models.SET_DEFAULT,删除关联数据,与之关联的值设置为默认值(前提FK字段需要设置默认值)
      - models.SET(func)  func为自定义的函数。

limit_choice_to  # 在Admin或ModelForm中显示关联数据时,提供的条件
        例如:limit_choices_to={'nid__gt': 5}

db_constraint=True    # 是否在数据库中创建外键约束。如果没有外键约束,但是还是可以进行双下划线查询。

多对多参数:

to="Author"   to代表绑定的表,多对多没有to_field 默认绑定的是主键。

related_name="b"   反向查找用.b , 以前反向查询是 小写的类名+_set  定义related_name以后 直接b就OK

related_query_name="b"    反向查找用.b_set,以前反向查询是 小写的类名+_set  定义related_query_name以后 b_set

limit_choices_to=None     # 在Admin或ModelForm中显示关联数据时,提供的条件
        例如:limit_choices_to=Q(nid__gt=10)

through=""              # 自定义第三张表时,使用字段用于指定关系表,参数值是自己定义的第三张表名,可以用双下划线。

through_fields={}        # 自定义第三张表时,使用字段用于指定关系表中哪些字段做多对多关系表
        例如: through_fields={"u","t"}

symmetrical=True   不会创建反向查询的键 / false  会创建反向查询的键,和外键一样

db_constraint=True         # 是否在数据库中创建外键约束

db_table=""              # 默认创建第三张表时,数据库中表的名称

创建联合索引:

class Teacher(models.Model):
    name = model.CharField(max_length=8)
    age = model.IntegerField(max_length=8)

    class Meta:   # 定义一个类
        db_table = "teacher"   #  定义表名 
        unique_together = [("name","age"),]   #  创建联合唯一索引

创建一个一对多的关系:

class Book(models.Model):
    name = models.CharField(max_length=20)
    price = models.IntegerField()
    author = models.CharField(max_length=20, null=True)  # 可以为空,默认也是可以为空
    publish = models.ForeignKey("Publish", on_delete=models.CASCADE)    //  表名(类名), 绑定字段, 删除时的操作(级联)
    // 也可以这样写   publish = models.ForeignKey(Publish, on_delete=models.CASCADE), 这Publish类就必须写在前面。


class Publish(models.Model):
    name = models.CharField(max_length=20)
    city = models.CharField(max_length=10)

publish 代表一个外键对象,publish在数据库中的名字是publish_id, django自动加上了  "_id  "。 

※: django从1.9开始ForeignKey中的on_delete参数是必须的。topic = models.ForeignKey(Topic,on_delete=models.CASCADE) 。

创建完成后,执行下面两条指令。

python manage.py makemigrations   

python manage.py migrate

二、单表对数据的增、删、改、查。

增加数据:

方法一:
    Book.objects.create(name="PHP", price=99, author="Jax",publish_id=1)

方法二:
    b = Book(name="PHP", price=99, author="Jax",publish_id=1)
    b.save()  // 不执行此方法,数据不会保存

当关联表插入时外键的插入方式:

方法一:
    Book.objects.create(name="PHP", price=99, author="Jax",publish_id=1)   //  直接插入数值

方法二:
    pub = Publish.objects.filter(name="Jax")[0]   // 此时取出的是queryset
    Book.objects.create(name="PHP", price=99, author="Jax",publish=pub)  // 直接插入对象

修改数据:

集合(queryset)有update方法,但是实例对象没有,实例直接修改属性值,记得save()。

方法一:
    Book.objects.filter(name="Jax").update(price=100)   // 先找到要修改的信息,然后再修改

方法二:
    b = Book.objects.get(name="pp")
    b.price=20   //  直接对其属性进行修改,相当于对字段直接进行修改
    b.save() 

推荐用方法一,方法二效率低。

删除数据:

Book.objects.filter(name="Jax").delete()

表的基本操作:

Book.objects.filter(条件)    返回的是一个条件集合,每个元素都是对象,集合不能调用多对多外键。

Book.objects.all()  查询所有结果

Book.objects.first()   查询第一条记录   返回一个对象,不可迭代。

Book.objects.last()   查询最后一条记录  返回一个对象,不可迭代。

Book.objects.get(条件)   取一条记录,当找不到数据或者找到多条数据时会报错。同时也无法用values方法,只能通过打点来调用属性。

Book.objects.values("字段名")    取出想取的字段,是字典格式,但是此时也是queryset对象,每个元素是字典格式  

Book.objects.values_list("字段名")   是里面是元组格式

Book.objects.exclude()   与filter的结果相反,筛选不匹配的对象

Book.objects.order_by(字段)   排序

Book.objects.reverse()   反向排序,必须配合order_by使用,是对order by的反转
    # order_by("-id","sid")  reverse 以后  order_by("id","-sid")   负号代表降序(desc)

Book.objects.values("name").distinct()   去重,须和values()方法配合使用,否则没有意义。

Book.objects.values("name").distinct() .count()  计数

Book.objects.filter(name="name").extist()   判断是否有数据,在数据库中找到数据就回

Book.objects.filter(name="name").iterator()   对结果进行生成器的操作,占用内存小,但是每次迭代都需要执行一遍sql查询

Book.objects.all().select_related("c","d") 从数据库查询到外键的数据,这样就会从数据库全部拿到。
    # select * from book left join author on book.c=auhtor.c and book.d=author.d
    # Book.objects.all() select * from book
    # 如果不添加select_related属性,每进行一次跨表查询,就到数据库查询一次。

Book.objects.all().prefetch_related("c")  # 和select_related的功能一样,只不过是进行了两次数据查询
    # 第一次先查主表,拿到FK对其进行去重,然后在子表进行第二次查询
    # 第一次:SELECT blog_book.id, blog_book.name, blog_book.price, blog_book.author,blog_book.publish_id FROM blog_book
    # 第二次:SELECT blog_publish.id, blog_publish.name, blog_publish.city FROM blog_publish WHERE blog_publish.id IN (1, 2, 3)
    # select_related 进行了跨表查询,所以性能不够优

Book.objects.all().only("id","name")   只取id和name两列的数据。
    # only和values的区别
    # only拿到的Queryset的集合是对象的集合,对象封装了数据。
    # values拿到的Queryset的集合是字典的集合。
Book.objects.all().defer("id","name")   除了id和name的两列,剩下的数据都取出来,和only方法相反。

Book.objects.all().using("default")    使用哪个数据库,根据setting的配置进行修改,默认是default。

obj,created = Book.objects.get_or_create(price=81, defaults={"name":".net","price":18})
    # 先查询如果没有就创建price是查询条件,如果不存在按照defaults进行添加数据
    # 返回两个值,一个是对象,一个是否创建对象。

obj,created = Book.objects.update_or_create(defaults={"name":"ruby","price":111},price=0)
    # 先按照price找,找不到就按照defaults创建记录。
    # 找到了就按照default的相应的字段进行更新。
 
objs ={
Book(id=10,name="Java",price="998",author="pp",publish_id=1),
Book(id=11,name="ruby",price="996",author="alex",publish_id=2)
}
Book.objects.bulk_create(objs,10)    #  批量提交数据,10是batch_size 一次提交的数据量。
# 当发生异常会全部回滚

django写原生的SQL语句

方法一: 通过raw方法

#  查询的字段必须包含主键

book_list = Book.objects.all().raw("select * from blog_book") # book_list的结果为   是一个集合
for item in book_list:
    print(item)   #  item是一个个对象

#  当查询其他表时,需要对其列名进行取别名
book_list = Book.objects.all().raw("select id as aid from blog_author")


方法二: 调用django原生的方法

from django.db import connection,connections

cursor = connection.cursor() # 创建游标 
# cursor = connnections["default"].cursor()  也可以选择其他的数据库进行创建游标
cursor.execute("select * from book")
count = cursor.fetchone()  #  查询一条记录
count1 = cursor.fetchmany(6)  #  查询指定条数的记录
count2= cursor.fetchall()  # 查询全部记录


方法三:extra 方法  extra(select=None, where=None, params=None, tables=None, order_by=None, select_params=None)

# Django的查询语法难以简单的表达复杂的 WHERE 子句,用extra方法,需要配合filter函数的使用
# django返回对象的个数是经过filter的个数,只有经过extra的方法返回值不为空

book_list = Book.objects.fiter(price="100").extra(where={"name=%s"}, param=("python",))

book_list = Book.objects.fiter(price="100").extra(select={"name":"select * from book where name=%s"}, select_param=("python"))

模糊查询:

book_list = Book.objects.filter(name__contains="p")    name字段中包含“p”的结果

book_list = Book.objects.filter(name__icontains="p")   name字段中包含“p”的结果,不区分大小写。

book_list = Book.objects.filter(price__gt=100)         筛选价格大于100

book_list = Book.objects.filter(price__lt=100)         筛选价格大于100

book_list = Book.objects.filter(price__range(90,110))  查询在90--110的范围   等价于  between  and

book_list = Book.objects.filter(price__in)             等价于in

book_list = Book.objects.filter(name__startwith="p")   name字段以p开头

book_list = Book.objects.filter(name__endtwith="p")    name字段以p结尾

__gte/__lte  大于等于/小于等于,  istartwith/iendwith   以开头/结尾 不区分大小写

book_list = Book.objects.filter(name__isnull = True)  判断是否为空 

一对多查询:

方法一:通过对象的操作

publish = Publish.objects.filter(id=1)[0]
print(publish.book_set.all().values("name", "price"))   通过publish表反向查找book表,  类名小写+_set 逆向查找该表。结合all()和filter()函数。

方法二:通过双下划线的操作

Book.objects.filter(publish__city="南方").values("name","price")   // 直接查找publish表的city字段。

关联的表可以通过 定义的外键字段名+ "__"(双下划线)+ 字段名 可以直接查询到该表的字段,双下划线也可以用在values里面。

多对多查询:

多对多也可以进行自关联,用于互粉的功能。

需要创建一个多对多的外键  

auth = models.ManyToManyField("Author")  参数是关联表的名字,会自动生成第三张表

查询:

obj = Book.objects.get(id=2)
print(obj.auth.all())  此时取出的是一个queryset,并非一个对象。

abj = Author.objects.get(id=2)
print(abj.book_set.all())     也可以通过反向查找

关于第三张表的查询,只能通过对象的属性赋值,通过定义的多对多的外键进行操作,不能直接操纵。

obj = Book.objects.get(id=1)      获取第三张表的book_id=1的书籍对象
abj = Author.objects.get(id=3)    获取第三张表的Author_id=3的作者对象

第一种添加记录的方法:

    obj.auth.add(abj)     通过书籍的多对多的外键添加作者,对第三张表的book_id=1的对象添加一个Author等于3的对象。

第二种添加记录的方法:
    obj.auth.add(1)      通过obj的多对多外键对象添加 book_id=1 且 Author_id=1 的记录
    obj.auth.add(*[1,2,3])    也可以添加一个列表,需要解包。


第一种删除记录的方法:

    obj.auth.remove(abj)   删除记录, 删除第三张表的book_id=1,且Author_id=3的记录

第二种删除记录的方法
    obj.auth.remove(2)    删除obj的多对多外键对象的id为2的记录 

修改记录:
    obj.auth.set([1,2,3])   对obj进行修改,对于没有的记录进行添加,表中有的记录,但是set的列表中没有就删除表中的记录。

清空记录:
    obj.auth.clear()    把book_id=1的数据全部清除

查询全部记录:
    obj.auth.all()     会拿到所有的Author的对象。

多对多实际上就是一张包含两个外键的表。

关于一对多和多对多的反向查找:

反向查找用到  小写的类名_set  的方法,我们可以通过设置related_name的属性,可以不再使用 _set 的方法

models.py

class Book(models.Model):
    name = models.CharField(max_length=20, verbose_name="姓名")
    publish = models.ForeignKey("Publish", related_name="s", on_delete=models.CASCADE)
    auth = models.ManyToManyField("Author",related_name="ssss")   


class Author(models.Model):
    name = models.CharField(max_length=20)

----------------------------------------------------------------
views.py

一对多:
Author.objects.get(id=1).s.all()   #直接调用设置好的related_name,s代表关联表中外键的值

多对多:
Author.objects.get(id=1).ssss.all()   #直接调用设置好的related_name,ssss代表关联表中外键的值  

聚合函数和分组查询:

from django.db.models import Avg,Count,Sum

aggregate(*args,**kwargs)  
Book.objects.all().aggregate(Avg("price"))  求平均值

也可以自己设置参数名:
Book.objects.all().aggregate(book_price=Avg("price"))

annotate(*args,**kwargs)   
Book.objects.values("name").annotate(Sum("price"))    通过values先查出来name值,然后annotate()计算聚合函数   

也可以自己设置参数名:
Book.objects.values("name").annotate(sp=Sum("price"))

F查询和Q查询:

需要导入 from django.db.models import F,Q

F查询:使用查询条件的值,专门取对象中某列值的操作

Book.objects.all().update(price=F("price")+10)   在原来价格的基础上加10

Q查询:构建"或关系"搜索条件,也可以使用双下划线。

" | " 代表或,"~"代表非,"&"代表且

ret = Book.objects.filter(Q(price=76) | Q(name="python")).values("name")   

ret = Book.objects.filter(Q(price=76) | ~Q(name="python")).values("name")

Q查询也可以和关键字查询也可以一起使用,Q查询必须放在最前面

※:使用双下划线只能在filter()、values()方法中使用。其他的地方通过打点的方法来调用属性。

Queryset的特性:

可以切片,也可以进行迭代。

惰性求值:

Book.objects.get(id=6)   当只执行不打印结果时,这句话没有执行sql,只是生成了对象,当什么时候用才会执行。

for循环的情况:

ret = Book.objects.filter(id="3")

for item1 in ret:    #  执行sql
    print(item1)

for item2 in ret:   # 不会执行sql,直接从缓存中取数据
    print(item2)

在前端页面上可以通过 get_+字段名+_display,直接显示这个字段的choice对应的中文数据。

{
    { item.get_status_display }}

 

你可能感兴趣的:(Django,Python,Django,Model)