Python入门自学进阶-Web框架——6Django的ORM-多对多admin应用

Posted kaoa000

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了Python入门自学进阶-Web框架——6Django的ORM-多对多admin应用相关的知识,希望对你有一定的参考价值。

对于多对多关系,如前面的Book和Author表,进行多对多关联插入时,有两种方法:
第一种是前面介绍的通过book.author.add(*作者对象列表)来增加,这叫做正向查询插入,因为多对多字段定义在Book中,为author,通过book.author就是正向。
book = models.Book.objects.filter(id=2)[0]
authors = models.Author.objects.filter(id__gt=2)
book.author.add(*authors)
第二种是反向查询插入,通过author进行插入
author = models.Author.objects.filter(id=2)[0]
books = models.Book.objects.filter(id__gt=2)
author.book_set.add(*books)

 

这里要注意的是book_set是固定的写法,就是表名的小写加下划线set(表名小写_set),因为在Author表中没有显示的字段与Book表关联,Django自动使用这个名来关联,而books是上面获取的书的对象集合。通过author.book_set就是反向插入

多对多关系表,可以使用Django的ManyToManyField()建立,也可以自己手工建立,假设我们自己建立,定义类Book2Author:

class Book2Author(models.Model):
    book = models.ForeignKey("Book",on_delete=models.DO_NOTHING)
    author = models.ForeignKey("Author",on_delete=models.DO_NOTHING)
    # 这两个字段都不需要加_id,Django会自动增加
    # 手动创建多对多的表,定义了外键后,还需要定义联合唯一
    class Meta:
        unique_together = ["author","book"]
        # 这用来确定book和author两个字段联合唯一

操作这张表,可以使用:

models.Book2Author.objects.create(book_id=2,author_id=3);

一对一的表:是一对多的特例,也可以使用ForeignKey来生成:

publisher = models.ForeignKey("Publish",unique=true)

表记录的删除:

models.Book.objects.filter(id=3).delete()
删除表Book中的id=3的记录,同时,对于关联表中设置了on_delete为级联操作的表中的记录也同时删除。

表记录的修改:

models.Book.objects.filter(id=3).update(name="dsafdsa")
这里要注意,使用update的对象必须是QuerySet,所以前面的查找只用是filter,不能用get,get返回的是model对象,没有update方法。

使用save方法也可实现修改,但是效率不高,对全部字段都做了保存,即使有的字段没做修改,update只修改更改的字段。update()方法会返回一个整型数值,表示受影响的记录条数

表记录的查询:

filter(**kwargs)、all()、get(**kwargs)

对查询的结果再进行处理:values(*field)、values_list(*field)、exclude(**kwargs)、order_by(*field)、reverse()、distinct()、count()、exists()、first()、last()等

惰性机制:

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

对于每次创建一个对象,想显示对应的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',
        ,
    

会打印出SQL语句

QuerySet可迭代,可切片,这两种操作都是对QuerySet的使用,这时操作数据库,即执行sql语句。

多表关联查询

对象形式的查找:

还是前面的例子,我找到了一本书《骆驼祥子》,现在想打印出这本书的出版社的名字:

book_obj = models.Book.objects.filter(title="骆驼祥子")[0]  # 取到书对象
print(book_obj.publisher.name)   # 通过外键publisher拿到对应出版社对象,直接点name即打印出出版社名称。这是正向查找

有一个出版社,想找出此出版社出版的所有书的名字:

pub_obj = models.Publish.objects.filter(name="人民出版社")[0]
pub_obj.book_set.values("title")

以上是反向查找

了不起的双下划线(__)之单表条件查询

#    models.Tb1.objects.filter(id__lt=10, id__gt=1)   # 获取id大于1 且 小于10的值,这里逗号分隔是and关系。
#    models.Tb1.objects.filter(id__in=[11, 22, 33])   # 获取id等于11、22、33的数据
#    models.Tb1.objects.exclude(id__in=[11, 22, 33])  # not in
#    models.Tb1.objects.filter(name__contains="ven") # 包含
#    models.Tb1.objects.filter(name__icontains="ven") # icontains大小写不敏感
#    models.Tb1.objects.filter(id__range=[1, 2])   # 范围bettwen and
#    startswith,istartswith, endswith, iendswith,

了不起的双下划线(__)之关联表查询:

以上面的例子:找到了一本书《骆驼祥子》,现在想打印出这本书的出版社的名字models.Publish.objects.filter(book__title="骆驼祥子").values("name")
models.Book.objects.filter(title="骆驼祥子").values("publisher__name")
人民出版社出版的所有书的名字:
models.Book.objects.filter(publisher__name="人民出版社").values("title")

对象、QuerySet都有一个query属性,存储着这个结果的sql语句。

聚合查询和分组查询

都叫聚合函数,区别是处理的对象不同,聚合是处理的一个对象,分组是处理的多个对象。

聚合查询

aggregate(*args,**kwargs):通过对QuerySet进行计算,返回一个聚合值的字典。aggregate()中每一个参数都指定一个包含在字典中的返回值。即在查询集上生成聚合。

Book.objects.all().aggregate(Avg('price'))  #求所有书的平均价格
Book.objects.filter(authors_id=1).aggregate(Avg('price'))

先查询出一个对象,即QuerySet,再对这个对象执行聚合查询

aggregate()子句的参数描述了我们想要计算的聚合值,在这个例子中,是Book模型中price字段的平均值 aggregate()是QuerySet 的一个终止子句,意思是说,它返回一个包含一些键值对的字典。键的名称是聚合值的 标识符,值是计算出来的聚合值。键的名称是按照字段和聚合函数的名称自动生成出来的。如果你想要为聚合值指定 一个名称,可以向聚合子句提供它: Book.objects.aggregate(average_price=Avg('price')) 'average_price': 34.35

所有图书价格的最大值和最小值,可以这样查询:
Book.objects.aggregate(Avg('price'), Max('price'), Min('price'))
结果: 'price__avg': 34.35, 'price__max': Decimal('81.20'), 'price__min': Decimal('12.99')

分组查询

annotate(*args,**kwargs):可以通过计算查询结果中每一个对象所关联的对象集合,从而得出总计值(也可以是平均值或总和),即为查询集的每一项生成聚合。

查询每一个作者出的书的价格的平均值:

Book.objects.values("authors__name").annotate(Sum('price'))  #先根据作者名字把书分成多组,然后对每组的price字段执行求和操作,这就是分组查询,其查询结果可能如下:

['authors__name':'老舍','price__sum':Decimal('123.10'),'authors__name':'鲁迅','price__sum':Decimal('110.50'),'authors__name':'巴金','price__sum':Decimal('163.10')]

F查询和Q查询

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

如:想要修改所有Book表中书的价格,价格提高10%
models.Book.objects.all().update(price=price*1.1)  #这是按照经验做的更新,提示错误,price是字段,这里当做变量用了,修改如下:
models.Book.objects.all().update(price=F("price")*1.1) 

Q查询:查询条件为或的关系

models.Book.objects.filter(Q(id=3) | Q(title="php"))  #找id为3或者title为php的书,|符号,即管道符是或的关系,&是与的关系,~是非的关系。

#1 Q对象(django.db.models.Q)可以对关键字参数进行封装,从而更好地应用多个查询
    q1=models.Book.objects.filter(Q(title__startswith='P')).all()
    print(q1)#[<Book: Python>, <Book: Perl>]
# 2、可以组合使用&,|操作符,当一个操作符是用于两个Q的对象,它产生一个新的Q对象。
    Q(title__startswith='P') | Q(title__startswith='J')
# 3、Q对象可以用~操作符放在前面表示否定,也可允许否定与不否定形式的组合
    Q(title__startswith='P') | ~Q(pub_date__year=2005)

admin的配置

Django admin是Django自带的一个后台app,提供了后台管理功能

启用admin的过程,创建好项目后,执行数据库表的创建,即migrate,然后创建用户名和密码

python manage.py createsuperuser

 

 

 Django admin相当于一个页面版的数据库管理系统,可以管理数据库表,是通过注册model类来实现的。

1、语言的设置:在setting.py 文件中修改以下选项
LANGUAGE_CODE = 'en-us'        #LANGUAGE_CODE = 'zh-hans'

2、ModelAdmin类
管理界面的定制类,如需要扩展特定的model界面需要从这个类继承

3、注册model类到admin,实现对相应表的管理,两种方式

     <1>   使用register的方法
admin.site.register(Book,MyAdmin)
     <2>   使用register的装饰器
@admin.register(Book)

在相关应用的admin.py中进行注册:

 使用第一种方法,在我的测试环境中,即3.2.11中,是没有register方法的,只能使用装饰器来进行注册:

from django.contrib import admin
from app01.models import *
# Register your models here.
@admin.register(Book)
class AuthorAdmin(admin.ModelAdmin):
    pass

结果:

 可以看到,增加了app01应用,增加了一个Books,但是我们注册的Book,这里显示的是Books,自动增加了一个s。点击Books进入:

这里为什么显示的是书名呢?因为在定义Book这个model时,定义了:
def __str__(self):
       return self.title

这里就是打印各个Book对象,修改:return self.title + "===>"+str(self.price),结果:

 点击“ADD BOOK”

这里作者,即Authors显示了作者名,也是应为Authors中定义了__str__(self),否则会显示Author object

 设置LANGUAGE_CODE='zh-hans'后,界面

 现在Book显示只显示了书的名字,如果想显示所有字段,需要进行定制,即修改前面装饰器装饰的AuhtorAdmin类:

from django.contrib import admin
from app01.models import *
# Register your models here.
@admin.register(Book)
class AuthorAdmin(admin.ModelAdmin):
    list_display = ("title","price","publisher")

 相应的还有:

  •     list_display:     指定要显示的字段
  •     search_fields:  指定搜索的字段
  •     list_filter:        指定列表过滤器
  •     ordering:       指定排序字段

显示的Books列标题是类中定义的字段名,想修改一下,如想显示一个汉字名称,这时要在注册的类,这里就是Book中,字段定义中使用verbose_name=参数:

class Book(models.Model):
    title = models.CharField(max_length=100,verbose_name="书名")
    authors = models.ManyToManyField(Author) # 多对多,外键可以放到任意一方
    publisher = models.ForeignKey(Publisher,on_delete=models.CASCADE,verbose_name="出版社") # 一对多,表中字段是publisher_id,Django自动处理的
    publication_date = models.DateField()
    price = models.DecimalField("价格",max_digits=5,decimal_places=2,default=10)
    def __str__(self):
        return self.title

注意,直接在第一参数位置写别名和使用verbose_name=别名效果相同。如下图

 search_fields:  指定搜索的字段,添加一个搜索框,按照指定的字段查询:

 list_filter:        指定列表过滤器:添加  : list_filter = ("title",)

以上是关于Python入门自学进阶-Web框架——6Django的ORM-多对多admin应用的主要内容,如果未能解决你的问题,请参考以下文章

Python入门自学进阶-Web框架——20Django其他相关知识2

Python入门自学进阶-Web框架——2Django初识

Python入门自学进阶-Web框架——3Django的URL配置

Python入门自学进阶-Web框架——21DjangoAdmin项目应用

Python入门自学进阶-Web框架——21DjangoAdmin项目应用

Python入门自学进阶-Web框架——4HttpRequest和HttpResponse及模板