Django 之 模型层

模型层

Django 模型是与数据库相关的,与数据库相关的代码一般写在 models.py 中。在前面了解了对MySQL数据库的基本操作,下面学习对数据库的更多的查询操作,首先创建表模型,进行数据库迁移,并添加对应的数据。

# 单表查询表
class User(models.Model):
    name = models.CharField(max_length=32)
    age = models.IntegerField()
    register_time = models.DateField()
    def __str__(self):
        return '对象的名字:%s'%self.name

多表查询表
class Book(models.Model):
    title = models.CharField(max_length=32)
    price = models.DecimalField(max_digits=8,decimal_places=2)
    publish_date = models.DateField(auto_now_add=True)
    # 外键关系
    publish = models.ForeignKey(to='Publish')
    authors = models.ManyToManyField(to='Author')  # 虚拟字段,信号字段
    def __str__(self):
        return '书籍对象的名字:%s'%self.title

class Publish(models.Model):
    name = models.CharField(max_length=32)
    addr = models.CharField(max_length=32)
    email = models.EmailField()  # 对应就是varchar类型
    def __str__(self):
        return '出版社对象的名字:%s'%self.name

class Author(models.Model):
    name = models.CharField(max_length=32)
    age = models.IntegerField()
    authordetail = models.OneToOneField(to='AuthorDetail')
    def __str__(self):
        return  '作者对象的名字:%s'%self.name

class AuthorDetail(models.Model):
    phone = models.CharField(max_length=32)
    addr = models.CharField(max_length=32)

单表查询

单表查询数据基本操作:

查询数据
- all(): 查询所有结果

- filter(**kwargs): 它包含了与所给筛选条件相匹配的对象
	res = models.User.objects.filter(name='linwow',age=23)
	filter内可以放多个限制条件但是需要注意的是多个条件之间是and关系
	
- get(**kwargs): 返回与所给筛选条件相匹配的对象,返回结果有且只有一个,如果符合筛选条件的对象超过一个或者没有都会抛出错误。

- exclude(**kwargs): 它包含了与所给筛选条件不匹配的对象
	res = models.User.objects.exclude(name='linwow')
	
- order_by(*field): 对查询结果排序
	res = models.User.objects.order_by('age')   # 默认是升序
	res = models.User.objects.order_by('-age')  # 可以在排序的字段前面加一个减号就是降序

- reverse(): 对查询结果反向排序,前面要先有排序才能反向
	res = models.User.objects.order_by('age').reverse()
	
- count(): 返回数据库中匹配查询(QuerySet) 的对象数量。
	res = models.User.objects.count()
	res = models.User.objects.all().count()

- first(): 返回第一条记录
	res = models.User.objects.all().first()
	res = models.User.objects.all()[0]  # 不支持负数的索引取值

- last(): 返回最后一条记录
    res = models.User.objects.all().last()

- exists(): 如果QuerySet包含数据,就返回True,否则返回False
    res = models.User.objects.all().exists()
    res = models.User.objects.filter(name='linwow',age=3).exists() 

- values(*field): 返回一个ValueQuerySet——一个特殊的QuerySet,运行后得到的并不是一系列,model的实例化对象,而是一个可迭代的字典序列
    res = models.User.objects.values('name')  # 列表套字典
    res = models.User.objects.values('name','age')  # 列表套字典 

- values_list(*field): 它与values()非常相似,它返回的是一个元组序列,values返回的是一个字典序列
    res = models.User.objects.values_list('name','age')  # 列表套元祖

- distinct(): 从返回结果中剔除重复纪录  去重的对象必须是完全相同的数据才能去重
    res = models.User.objects.values('name','age').distinct()

字段名称字段中不能有 __(双下划线,因为在Django QuerySet API中有特殊含义(用于关系,包含,不区分大小写,以什么开头或结尾,日期的大于小于,正则等)。

下面是利用双下划线查询的操作:

查询年轻大于44岁的用户
res = models.User.objects.filter(age__gt=44)
查询年轻小于44岁的用户
res = models.User.objects.filter(age__lt=44)
查询年轻大于等于44岁的用户
res = models.User.objects.filter(age__gte=44)

查询年龄是44或者22或者73的用户
res = models.User.objects.filter(age__in=[44,22,73])

查询年龄在2244范围内
res = models.User.objects.filter(age__range=[22,44])

查询名字中包含字母w的用户,sqlite数据库演示不出来大小写的情况!!!
res = models.Author.objects.filter(name__contains='w')
res = models.User.objects.filter(name__icontains='w')  # 无视大小写

查询名字以l开头的用户
res = models.User.objects.filter(name__startswith='l')

查询名字以w结尾的用户
res = models.User.objects.filter(name__endswith='w')

查询注册是在2019年的用户,使用mysql数据库,sqlite对日期格式不太精准
res = models.User.objects.filter(register_time__year=2019)

多表新增

直接写id:
	models.Book.objects.create(title='红楼梦',price=66.66,publish_id=1)
传数据对象:
    publish_obj = models.Publish.objects.filter(pk=2).first()
    models.Book.objects.create(title='三国演义',price=199.99,publish=publish_obj)

多表修改

queryset修改:
    models.Book.objects.filter(pk=1).update(publish_id=3)
    # 传数据对象
    publish_obj = models.Publish.objects.filter(pk=2).first()
    models.Book.objects.filter(pk=1).update(publish=publish_obj)

对象修改:
    book_obj = models.Book.objects.filter(pk=1).first()
    book_obj.publish_id = 3  # 点表中真实存在的字段名
    book_obj.save()
    # 传数据对象
    publish_obj = models.Publish.objects.filter(pk=2).first()
    book_obj.publish = publish_obj  # 点orm中字段名 传该字段对应的表的数据对象
    book_obj.save()

多表删除

queryset删除:如果有多个数据会一起删除
	models.Book.objects.filter(pk=1).delete()
外键的删除:会将book中对应的数据一起删除
	models.Publish.objects.filter(pk=1).delete()
对象删除:
    book_obj = models.Book.objects.filter(pk=3).first()
    book_obj.delete()

绑定关系

添加关系 add:支持传数字或对象,并且都可以传多个
    book_obj = models.Book.objects.filter(pk=3).first()
    book_obj.authors.add(1)
    book_obj.authors.add(2,3)
    # 传数据对象
    author_obj = models.Author.objects.filter(pk=1).first()
    author_obj1 = models.Author.objects.filter(pk=3).first()
    book_obj.authors.add(author_obj)
    book_obj.authors.add(author_obj,author_obj1)

修改书籍与作者的关系set():传入的必须是可迭代对象!!!
    book_obj = models.Book.objects.filter(pk=3).first()
    book_obj.authors.set((1,))  可以传数字和对象,并且支持传多个
    book_obj.authors.set((1,2,3))
    # 传数据对象
    author_list = models.Author.objects.all()
    book_obj = models.Book.objects.filter(pk=3).first()
    book_obj.authors.set(author_list)

删除书籍与作者的绑定关系remove():
    book_obj = models.Book.objects.filter(pk=3).first()
    book_obj.authors.remove(1)
    book_obj.authors.remove(2,3)
    # 传数据对象
    author_obj = models.Author.objects.all().first()
    author_list = models.Author.objects.all()
    book_obj.authors.remove(*author_list)  需要将queryset打散

清空clear():清空的是你当前这个表记录对应的绑定关系
    book_obj = models.Book.objects.filter(pk=3).first()
    book_obj.authors.clear()
    
对于ForeignKey对象,clear()和remove()方法仅在null=True时存在。

多表查询

表与表之间的关系:

  • 一对一(OneToOneField):一对一字段无论建在哪张关系表里面都可以,但是推荐建在查询频率比较高的那张表里面
  • 一对多(ForeignKey):一对多字段建在多的那一方
  • 多对多(ManyToManyField):多对多字段无论建在哪张关系表里面都可以,但是推荐建在查询频率比较高的那张表里面

正向与反向

正向查询按字段,反向查询按表名小写

一对一:
正向:author---关联字段在author表里--->authordetail	按字段
反向:authordetail---关联字段在author表里--->author	按表名小写
查询某个作者的手机号   正向查询
查询地址是山东的作者名字   反向查询
			  
一对多:
正向:book---关联字段在book表里--->publish	按字段
反向:publish---关联字段在book表里--->book	按表名小写_set.all() 因为一个出版社对应着多个图书

多对多:
正向:book---关联字段在book表里--->author		按字段
反向:author---关联字段在book表里--->book		按表名小写_set.all() 因为一个作者对应着多个图书
正向
 查询书籍是三国演义的出版社邮箱
book_obj = models.Book.objects.filter(title='三国演义').first()
print(book_obj.publish.email)
查询书籍是西游记的作者的姓名
book_obj = models.Book.objects.filter(title='西游记').first()
print(book_obj.authors.all())
查询作者为linwow电话号码
user_obj = models.Author.objects.filter(name='linwow').first()
print(user_obj.authordetail.phone)

反向
查询出版社是东方出版社出版的书籍	  一对多字段的反向查询
publish_obj = models.Publish.objects.filter(name='东方出版社').first()
print(publish_obj.book_set.all())

查询作者linwow写过的所有的书		  多对多字段的反向查询
author_obj = models.Author.objects.filter(name='linwow').first()
print(author_obj.book_set.all())

查询作者电话号码是188的作者姓名		一对一字段的反向查询
authordetail_obj = models.AuthorDetail.objects.filter(phone=188).first()
print(authordetail_obj.author.name)

基于双下滑线的查询

正向
查询书籍为三国演义的出版社地址
res = models.Book.objects.filter(title='三国演义').values('publish__addr','title')
print(res)
查询书籍为西游记的作者的姓名
res = models.Book.objects.filter(title='西游记').values("authors__name",'title')
print(res)
查询作者为linwow的家乡
res = models.Author.objects.filter(name='linwow').values('authordetail__addr')
print(res)

反向
查询南方出版社出版的书名
res = models.Publish.objects.filter(name='南方出版社').values('book__title')
print(res)
查询电话号码为188的作者姓名
res = models.AuthorDetail.objects.filter(phone=188).values('author__name')
print(res)
查询作者为linwow的写的书的名字
res = models.Author.objects.filter(name='linwow').values('book__title')
print(res)
查询书籍为三国演义的作者的电话号码
res = models.Book.objects.filter(title='三国演义').values('authors__authordetail__phone')
print(res)

查询linwow作者的手机号
正向
res = models.Author.objects.filter(name='linwow').values('authordetail__phone')
print(res)
反向
res = models.AuthorDetail.objects.filter(author__name='linwow').values('phone')
print(res)

查询出版社为东方出版社的所有图书的名字和价格
正向
res = models.Publish.objects.filter(name='东方出版社').values('book__title','book__price')
print(res)
反向
res = models.Book.objects.filter(publish__name='东方出版社').values('title','price')
print(res)

查询东方出版社出版的价格大于400的书
正向
res = models.Publish.objects.filter(name="东方出版社",book__price__gt=400).values('book__title','book__price')
print(res)
反向
res = models.Book.objects.filter(price__gt=400,publish__name='东方出版社').values('title','price')
print(res)

聚合查询:aggregate

from django.db.models import Max,Min,Count,Sum,Avg
查询所有书籍的作者个数
res = models.Book.objects.filter(pk=3).aggregate(count_num=Count('authors'))
print(res)
查询所有出版社出版的书的平均价格
res = models.Publish.objects.aggregate(avg_price=Avg('book__price'))
print(res) 
统计东方出版社出版的书籍的个数
res = models.Publish.objects.filter(name='东方出版社').aggregate(count_num=Count('book__id'))
print(res)

分组查询(group_by):annotate

统计每个出版社出版的书的平均价格
res = models.Publish.objects.annotate(avg_price=Avg('book__price')).values('name','avg_price')
print(res)
统计每一本书的作者个数
res = models.Book.objects.annotate(count_num=Count('authors')).values('title','count_num')
print(res)
统计出每个出版社卖的最便宜的书的价格
res = models.Publish.objects.annotate(min_price=Min('book__price')).values('name','min_price')
print(res)
查询每个作者出的书的总价格
res = models.Author.objects.annotate(sum_price=Sum('book__price')).values('name','sum_price')
print(res)

F查询和Q查询

先创建表模型

class Product(models.Model):
    name = models.CharField(max_length=32)  # 都是类实例化出来的对象
    price = models.DecimalField(max_digits=8,decimal_places=2)
    sold = models.IntegerField()
    stock = models.IntegerField()

F查询

Django 提供 F() 进行字段值与某个我们自己设定的常量做比较。F() 的实例可以在查询中引用字段,来比较同一个 model 实例中两个不同字段的值。F可以帮我们取到表中某个字段对应的值来当作我的筛选条件,而不是我认为自定义常量的条件了,实现了动态比较的效果。

查询卖出数大于库存数的商品
from django.db.models import F
res = models.Product.objects.filter(sold__gt=F('stock'))

Django 支持 F() 对象之间以及 F() 对象和常数之间的加减乘除和取模的操作

将所有的商品的价格提高100from django.db.models import F
models.Product.objects.update(price=F('price')+100)

对字符串进行拼接Concat操作,并且要加上拼接值Value)

将所有商品的名字后面都加一个爆款
from django.db.models.functions import Concat
from django.db.models import Value,F
models.Product.objects.update(name=Concat(F('name'),Value('爆款')))

Q查询

filter() 等方法中逗号隔开的条件是与的关系。 如果需要执行更复杂的查询(例如OR语句),可以使用 Q对象。可以组合& 和| 操作符以及使用括号进行分组来编写任意复杂的Q 对象。同时,Q 对象可以使用~ 操作符取反,这允许组合正常的查询和取反(NOT) 查询。

from django.db.models import F, Q
res = models.Product.objects.filter(Q(price=188.88),Q(name='连衣裙爆款'))  # and
res = models.Product.objects.filter(Q(price=188.88)|Q(name='连衣裙爆款'))  # or
res = models.Product.objects.filter(Q(price=188.88)|~Q(name='连衣裙爆款'))  # not

查询函数可以混合使用Q 对象和关键字参数。所有提供给查询函数的参数(关键字参数或Q 对象)都将"AND”在一起。但是,如果出现Q 对象,它必须位于所有关键字参数的前面。

混合使用:需要注意的是Q对象必须放在普通的过滤条件前面
res = models.Product.objects.filter(~Q(name='连衣裙爆款'),price=188.88)  # not

Q对类的实例化操作

from django.db.models import F, Q
q = Q()
q.connector = 'or'  # 通过这个参数可以将Q对象默认的and关系变成or
q.children.append(('price',188.88))
res = models.Product.objects.filter(q)  # Q对象查询默认也是and

事务

事务的定义:将多个 sql 语句操作变成原子性操作,要么同时成功,有一个失败则里面回滚到原来的状态,保证数据的完整性和一致性(NoSQL数据库对于事务则是部分支持)。

from django.db import transaction
from django.db.models import F
with transaction.atomic():
	# 在with代码块儿写你的事务操作  									 						models.Product.objects.filter(id=1).update(kucun=F('stock')-1)
	models.Product.objects.filter(id=1).update(maichu=F('sold')+1)
# 写其他代码逻辑

拓展:配置文件配置参数查看所有orm操作内部的sql语句

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

猜你喜欢

转载自blog.csdn.net/linwow/article/details/91869920