美文网首页我爱编程
Django之模型(二)

Django之模型(二)

作者: Python野路子 | 来源:发表于2018-04-08 17:09 被阅读0次

    模型元选项

    1. 每个model都可以定义一个Meta类,使用内部的class Meta 定义模型的元数据,这个类中可以定义一些关于你的配置,Meta是一个model内部的类
    2. 模型元数据是“任何不是字段的数据”,比如排序选项(ordering),数据库表名(db_table)或者人类可读的单复数名称(verbose_name 和verbose_name_plural)。在模型中添加class Meta是完全可选的,所有选项都不是必须的。
    class Meta:
            db_table = "topic"
            managed = True
            ordering = ['-id']
            verbose_name = u"主题"
            verbose_name_plural = u"主题列表"
    

    db_table:string,在数据库中的表名,否则Django自动生成为app名字_类名
    managed: bool, 默认值为True,这意味着Django可以使用syncdb和reset命令来创建或移除对应的数据库。
    ordering: 数组, 默认排序规则,每个字符串是一个字段名,前面带有可选的“-”前缀表示倒序。前面没有“-”的字段表示正序。使用"?"来表示随机排序。
    verbose_name: string model对象的描述
    verbose_name_plural: string 复数时的描述

    模型字段 image.png

    模型字段常用的参数 image.png

    null与blank:

    • blank 是针对表单的,如果 blank=True,表示你的表单填写该字段的时候可以不填,比如 admin 界面下增加 model 一条记录的时候。直观的看到就是该字段不是粗体,设置为False时,字段是必须填写的。字符型字段CharField和TextField是用空字符串来存储空值的。默认不允许为空。
    • null 是针对数据库而言,如果 null=True, 表示数据库的该字段可以为空。默认不允许。日期型、时间型和数字型字段不接受空字符串。所以设置IntegerField,DateTimeField型字段可以为空时,需要将blank,null均设为True。

    总之:
    blank,只是在form表单验证时可以为空,而在数据库上存储的是一个空字符串;null是在数据库上表现NULL,而不是一个空字符串;
    需要注意的是,日期型(DateField、TimeField、DateTimeField)和数字型(IntegerField、DecimalField、FloatField)不能接受空字符串,如要想要在填写表单的时候这两种类型的字段为空的话,则需要同时设置null=True、blank=True;

    auto_now 与auto_now_add区别:
    auto_now_add: 只有第一次才会生效,比如可以用于文章创建时间
    auto_now: 每一次修改保存对象时都会将当前时间更新进去,只有调用Model.save()时更新,在以其他方式(例如 QuerySet.update())更新其他字段时,不会更新该字段,但您可以在此类更新中为字段指定自定义值。可用于文章修改时间;

    模型中的关系

    一对一:一本书籍有一个编号;
    多对一:一本书籍有多个评论;--注意Django只有多对一关系,站在多的角度去看待;
    多对多:一本书籍有多个作者,一个作者可以写多本书籍;

    class Author(models.Model):
        name = models.CharField(u'姓名', max_length=200)
        email = models.EmailField(u'邮箱')
    class Number(models.Model):
        number = models.CharField(u'编号', max_length=200)
    class Book(models.Model):
        headline = models.CharField(u'大标题', max_length=255)
        pub_date = models.DateTimeField(u'出版时间')
        authors = models.ManyToManyField(Author)
        number = models.OneToOneField(Number)
    class Reply(models.Model):
        book = models.ForeignKey(Book) #外键字段
        content = models.CharField(u'内容', max_length=255)
    
    • ForeignKey外键
      多对一关系,关联模型关联的类,定义在多的类中,如上Reply类中;
      属性:
      db_constraint:bool 是否建立外键约束
      to_field:string 关联到的关联对象的字段名称。默认地,Django 使用关联对象的主键。
      related_name: string 关联对象反向引用描述符。.如果你不想让Django 自动创建一个反向关联(Django自动是以子表_set命名的),则可以自己定义。
      on_delete: string 可以取如下值
      CASCADE:级联删除,如果删除,相关联的那个也会删除
      PROTECT:保护类型。如果删除,将会抛出一个ProtectedError错误
      SET_NULL:如果删除了本条数据,外键的那条数据将会设置为null,这个只有在外键null为True的情况下才可以使用
      SET_DEFAULT:如果删除了本条数据,外键那条数据将会职位默认值,这个只有在外键那个字段设置了default参数才可以使用
    • ManyToManyField
      多对多关系,定义在哪个类中都可以,如上Book类中;
      属性:
      related_name: string 与ForeignKey相同
      db_table:string 需要建立关联表的表名
      db_constraint: 同上
    • OneToOneField
      一对一关系,定义在哪个类中都可以,如书籍和编号;
      属性:
      on_delete 同ForeignKey
      to_field 同ForeignKey

    持久操作

    Model.save()
    这个方法可以用来插入一条新的数据,也可以用来更新一条数据。如果这个数据在之前数据库中存在了,就只是调用sql的update语句。如果这条数据是新的,就会调用sql的insert语句。示例代码如下:

    class UpdateArticle(View):
        def get(self,request):
            # 批量修改
            # UPDATE hello_article SET status=3 WHERE status=2
            # Article.objects.filter(status=2).update(status=3)
    
            # 单个修改
            # 实际上save方法如果当前实例已经存在于数据库中,它就会当作一个update操作
            # Article.objects.filter(id=7).update(status=4)
            article = Article.objects.get(pk=8) #取出pk=7的对象,然后修改其状态,再保存
            article.status = 0
            article.save()
    
            #更新之后在查询
            articleAll = Article.objects.all()
            return render(request, 'displayArticle.html', locals())
    

    注意:save操作不止是更新修改的字段,而是会将所有的字段全部更新一遍,不管有没有修改。当你的所有操作都是串行,没有什么并发同时操作同一个model的时候,这样的处理方式,一般不会给你带来任何麻烦。但是并行的时候可能会有影响。
    Django后面在save方法里面新增加了一个update_fields参数。这样就可以只修改特定字段了:

    user.name = name
    user.save(update_fields=['name'])
    

    这样便有效避免了并行save产生的数据冲突。

    检索对象

    • get(**kwargs)方法:查询单个数据,只会返回一个对象,如果所有条件都不满足或者是满足条件的有多个,将抛出一个异常。
    article = Article.objects.get(pk=1) #在django 的ORM查询中,数据库的主键可以用PK代替, 官方推荐使用pk
    article = Article.objects.get(id=1)#等同于select * from hello_article where id=1;
    
    • all() 获取所有
    articleAll = Article.objects.all()
    
    • filter(**kwargs)方法:根据参数提供的提取条件,获取一个过滤后的QuerySet。
    Article.objects.filter(status = 0)
    
    • exclude(**kwargs)方法:根据参数提供的条件,排除符合条件的数据,返回一个QuerySet对象。
    Article.objects.exclude(status = 0)
    
    • order_by(*args):根据给定的参数进行排序.
    #正序:
    Book .objects.filter(headline=u'标题').order_by('pub_date')
    #倒序,字段前面加-
    Book .objects.filter(headline=u'标题').order_by('-pub_date')
    
    • values()方法:将返回来的QuerySet中的Model转换为字典
    #<QuerySet [<Classesgrade: Classesgrade object>, <Classesgrade: Classesgrade object>]>
    cgrade =  Classesgrade.objects.all()
    #<QuerySet [{'update_time': datetime.datetime(2018, 4, 7, 4, 44, 56, 181307,
     cninfo=<UTC>), 'create_time': datetime.datetime(2018, 4, 7, 4, 44, 56, 181269, 
    cninfo=<UTC>), u'number_id': 1L, u'id': 1L, 'name': u'django\u6846\u67b6\u73ed'},
     {'update_time': datetime.datetime(2018, 4, 7, 4, 44, 56, 405850, cninfo=<UTC>), 
    'create_time': datetime.datetime(2018, 4, 7, 4, 44, 56, 405816, cninfo=<UTC>), 
    u'number_id': 2L, u'id': 2L, 'name': u'django\u6846\u67b6\u73ed'}]>
    
    cgrade.values()
    
    • count()方法:将返回当前查询到的数据的总数,这个比length方法更有效
    Book .objects.count()
    
    • latest(field_name=None)方法:根据提供的参数field_name来进行排序,field_name这个参数必须是时间字段,然后提取离现在最近的一条数据
    Book .objects.latest('pub_date')
    
    • earliest()方法:用法和latest一样,只是这个是获取最久远的一个。
    Book .objects.latest('pub_date')
    
    • first()方法:获取查询到的数据的第一条数据。如果用了order_by,那么将获取排序后的第一条。如果没有用order_by,那么将根据id进行默认排序。
    Book .objects.first()
    
    • last()方法:用法和first一样,只不过是获取的是最后一条的数据。
    Book .objects.last()
    
    • update(**kwargs):更新数据方法,这个方法可以对查询出来的QuerySet里面的所有元素进行更新,并且更新参数的个数也是不限的。另外要注意的是,因为get方法返回的不是QuerySet对象,因此使用get方法提取出来的数据不能使用update方法。出于这种情况,建议应该使用filter(pk=xx)来替代get(pk=xxx)方法。并且,使用get出来的模型,修改数据后再save,会更新所有的数据,比update的效率更低。
    Book .objects.filter(id=1).update(headline="大标题")
    
    • delete()方法:删除QuerySet中的模型。
    Book .objects.filter(id=1).delete()
    

    查找对象的条件

    查找对象的条件的意思是传给以上方法的一些参数。比如name__contains=’abc’这个就代表name这个字段包含了abc的意思,相当于是SQL语句中的where语句后面的条件,语法为字段名__规则,以下将对这些规则进行说明:

    • exact:相当于等于号
    Book .objects.filter(headline__exact=u'大标题')
    或
    Book .objects.filter(headline=u'大标题')
    
    • iexact:跟exact,只是忽略大小写的匹配。
    Book .objects.filter(headline__iexact=u'大标题')
    
    • contains:字符数据中包含等号后面的数据。
    Book .objects.filter(headline__contains=u'标题')
    
    • icontains:跟contains,唯一不同是忽略大小写。
    Book .objects.filter(headline__icontains=u'标题')
    
    • in:判断字符的数据是否处在一个给定的列表中,如果在,则提取出来。
    Book .objects.filter(id__in=[1, 2, 3])
    
    • gt:大于。
    Book .objects.filter(id__gt=1)
    
    • gte:大于等于。
    Book .objects.filter(id__gte=1)
    
    • lt:小于。
    Book .objects.filter(id__lt=10)
    
    • lte:小于等于。
    Book.objects.filter(id__lte=10)
    
    • startswith:以什么开始。
    Book .objects.filter(headline__startswith='title')
    
    • istartswith:同startswith,忽略大小写。
    Book .objects.filter(headline__istartswith='title')
    
    • endswith:同startswith,以什么结尾。
    Book .objects.filter(headline__iendswith='le')
    
    • iendswith:同istartswith,以什么结尾忽略大小写。
    Book .objects.filter(headline__iendswith='le')
    
    • range:区间查询。
    import datetime
    start_date = datetime.date(2005, 1, 1)
    end_date = datetime.date(2018, 1, 1)
    Book .objects.filter(pub_date__range=(start_date, end_date))
    
    • isnull:判断是否是空。
    Book .objects.filter(pub_date__isnull=True) zz  ..,. MJ HM 
    
    • 切片:对查找出来的数据进行切片,跟用数组是一样的。
    Book.objects.all()[:2]
    

    *ps:负值的操作,是无法进行。切片可以用来实现翻页显示内容,比如每一页显示10条内容可以利用切片,第page页

            cnumber = Classesnumber.objects.all() #获取Classesnumber的Queryset集
            cn_counts = cnumber.count() #总记录数
            page_count = 4 #每页显示记录
            #求出分多少页
            for page in range((cn_counts / page_count + cn_counts % page_count)): 
                #利用for遍历出每一页的记录数据
                for pagenum in (cnumber[page*page_count:page*page_count + page_count]):
                    print(pagenum.classgrade_number)
    

    外键操作

    一对一
    • 访问
      在建立关联属性的类里面直接.关联属性即可
    book = Book.objects.get(pk=1) #先获取从表的对象,Book为从表
    book.number #得到book主键为1的对应Number对象主表
    
    • 增加
    book = Book.objects.get(pk=1)
    book.number = number #获取Number主类的对象,再赋给book中的关联属性
    book.save()  #再save保存
    
    • 反查
    number = Number.objects.get(pk=1) #Number为主类,Book为从类
    number.book     #查询编号对应的书籍
    
    多对一
    • 访问
      访问外键:Reply(多)的对象要访问他所持有的book(一)对象
    reply = Reply.objects.get(pk=1) #先获取多类(子表)对象
    reply.book   # 字表
    
    • 反查(一查多,主查从)
      外键反向访问:如果Book对象想要访问所有引用了他的Reply对象,因为没有外键属性,不能采用“.”方式访问,可以通过“模型名_set“进行访问。

    • 如果模型I有一个ForeignKey,那么该ForeignKey 所指的模型II实例可以通过一个管理器回前面有ForeignKey的模型I的所有实例。默认情况下,这个管理器的名字为foo_set,其中foo 是源模型的小写名称。默认是以子表小写名称_set()来表示(上面默认以b_set访问),

    可以在从表定义时设置related_name 参数来覆盖foo_set 的名称。通过主表来查询子表信息用到反向查询(返回的是管理器,需要all之类返回QuerySet),

    # 本身Number对象是没有book属性,django提供的一个反查机制
    # 如果设置related_name, 我们就通过related_name反查
    # 如果没有设置, django默认设置的是反查class的小写名字
    # related_name 可以当成反查所用的别名
    book = Book.objects.get(pk=1)
    book.reply_set.all()
    #还可以进行查询
    book = Book.objects.get(pk=1)
    book.reply_set.filter(content__contains=’I love’).all()
    
    • 添加
    # 多对一创建的对象,它是默认没有加载外键的,只有当你引用时,它才会加载
    reply = Reply.objects.get(pk=1)  #首先主表要有数据,从表才能与之相对应
    reply.book = book   #book是从主类获取的对象,此处省略
    reply.save()   #再保存
    
    #  反向操作
    
    
    多对多
    • 访问
      多对多访问:跟外键访问一样,“模型名_set“进行反向访问。
    Book .authors 等于同Book .objects.filter(reply_id=Reply.id).all()
    book = Book.objects.get(pk=1)
    book.authors
    
    • 反查
      “模型名_set“进行反向访问。与多对一类似

    • 添加
      多对多数据添加需要先保存子表对象,然后再add进行添加主类对象

    book = Book(headline="标题",number=number)
    book.save()
    book.authors.add(author)
    book.authors.add(author1)
    book.save()
    
    • 删除
    book.authors.remove(author)
    book.save()
    
    处理关联对象方法

    add(obj1, obj2, ...) 添加的已经存在数据库的数据

    添加一指定的模型对象到关联的对象集中。
    d = Department.objects.get(pk=1)
    s = Student.objects.get(pk=1)
    d.student_set.add(s) # 学院d添加学生s

    create(**kwargs) 添加不存在的数据 ,将数据直接存入数据库

    创建一个新的对象,将它保存并放在关联的对象集返回新创建的对象。
    d.student_set.create(name='小明') # 创建一个叫小明的学生,并将他添加到学院d中

    remove(obj1, obj2, ...)
    从关联的对象集中删除指定的模型对象。删除的是关系表中的数据

    d.student_set.remove(s) # 从学院d中删除学生s

    clear() 从关联的对象集中删除所有的对象
    d.student_set.clear() # 清除学院d中所有学生

    注意对于所有类型的关联字段,add()、create()、remove()和clear()都会马上更新数据库。换句话说,在关联的任何一端,都不需要再调用save()方法

    多表查询

    跨关联关系的查询
    Django 提供一种强大而又直观的方式来“处理”查询中的关联关系,它在后台自动帮你处理JOIN。 若要跨越关联关系,只需使用关联的模型字段的名称,并使用双下划线分隔,直至你想要的字段:


    image.png

    举例:

    # 查询学院名字为‘软件’的学生的信息 
    Student.objects.filter(department__d_name='软件') 
    #这种跨越可以是任意的深度。
    
    #查询学生名字中包含‘xiao’的学生的学院信息
    Department.objects.filter(student__s_name__contains='xiao')
    
    # 查询学号为1的学生所有的课程
    Course.objects.filter(student__s_id=1)
    
    # 查询报了课程1的所有的学生
    Student.objects.filter(course__c_id=1)
    
    # 查询报了'python'课程的的学生的所属学院的信息
    Department.objects.filter(student__course__c_name='python') #三个表关联查询
    
    总结:
    1. Object对象获取某一列值(或者说是获取某个属性)的时候,使用点来获取。我们跨表查询时,也是使用点来获取。或者用_set反查询,_set:提供了对象访问相关联表数据的方法。但这种方法只能是相关类访问定义了关系的类(主键类访问外键类)。
    2. QuerySet查询集做跨表查询时,使用双下划线"",:两个下划线可以生成连接查询,查询关联的字段信息,可以跨表关联查询,只要一层接一层,可以从头查到尾。

    聚合操作

    使用aggregate方法进行数据库的聚合操作, 使用QuerySet.aggregate 代表需要使用聚合操作,它返回的是一个字典:

    • 引入对应的聚合函数
      from django.db.models import Avg, Max, Min, Count, Sum
    • Avg, 平均数
    #查询当前作者的平均年龄
    Author.objects.aggregate(Avg("age"))
    
    • Max 最大数
    #取最大年龄的作者
    Author.objects.aggregate(Max("age"))
    
    • Min 最小数
    #取最小年龄的作业
    Author.objects.aggregate(Max("age"))
    
    • Count 统计行数
    #统计有多少个作者
    Author.objects.aggregate(Count("id"))
    #如果需要去重
    Author.objects.aggregate(Count("age", distinct=True))
    
    • Sum 统计和
    #获取所有作者的年龄总和
    
    Author.objects.aggregate(Sum("age"))
    
    • 多个聚合结果
    Author.objects.aggregate(Sum("age"),Min("age"), Max("age"))
    
    • 自定义名称聚合字段别名, 你的参数名就是你需要自定义的聚合结果名称
    Author.objects.aggregate(avg_age=Avg("age"))   >>>{avg_age:平均年龄值}
    
    • 使用annotate进行聚合查询
      1 它与aggregate类似,但是aggregate的结果一定只有一个值,且aggregate执行后就是最终结果。
      2 aggregate返回的是一个字典,annotate返回的是一个QuerySet,可以继续进行查询。
      3 annotate的聚合结果是针对每行数据的,而不是整个查询结果。
      在博客常见侧边栏有分类列表,显示博客已有的全部文章分类。现在想在分类名后显示该分类下有多少篇文章,该怎么做呢?这个就可以用到annotate函数,先按类名进行分组,然后再统计每个组分别为多少数量即可。
    • 使用annotate进行集合查询
    #统计每本书的作者有多少
    Book.objects.annotate(Count("anthors"))
    
    #统计每个年龄分别有多少人
    # values 就等同于group by,返回的是一个字典
    # values_list 就等同于group by,, 返回的是一个元祖
    Author.objects.values("age").annotate(Count("id"))
    

    事务

    如果需要支持事务,mysql引擎必须是InnoDB类型。

    • 引入django处理事务的包
     from django.db import transaction
    
    • 使用装饰器事务控制
      ps: 使用装饰器的事务必须是view视图函数
      ps: 通用视图类还未提供装饰器事务控制器
    # 这个装饰器不能在通用View视图中使用
    # 现在django还只提供了在view函数中使用的装饰器
    @transaction.atomic
    def model_study(request):
        # 使用事务装饰器以后
        # 整个view函数里面的数据库操作
        # 要么全部成功
        # 要么全部失败
        # 下面的代码在一个事务中执行,一但出现异常,整个函数中所有的数据库操作全部都会回滚
        book = Book.objects.create(headline="事务操作1")
        author = Author.objects.create(name="kevin", email="test@qq.com", age=28)
        book.authors.add(author)
        book.save()
        # assert not book.headline.find("操作") >= 0, "这是敏感关键字"
        return render(request, "model_study/model_study.html")
    
    • 使用上下文管理器的方式
    from django.db import transaction
    class ViewClass(View)
        def get(self, request)
            # 下面的代码在自动提交模式下执行(Django的默认模式)
            with transaction.atomic():   # with中的代码,全部都会保持原子性     
                # 下面的代码在一个事务中执行,一但出现异常,整个with函数内部的数据库操作都会回滚
                book = Book.objects.create(headline="事务操作3")
                author = Author.objects.create(name="kevin", email="test@qq.com", age=28)
                book.authors.add(author)
                book.save()
                assert 1==1
                # raise Exception, "这里出现了一个bug"
    

    ps: with 内部最好不要使用try...catch...模块,否则可能会影响django的事务异常判断。

    课后练习

    1、创建一个班级的模型(名称、班号(一对一外键),创建时间(自动添加),修改时间(自动更新))
    2、创建一个班号的模型(号码)
    3、创建一个学生的模型(名称、班级(多对一外键 ),老师(多对多),年龄,性别)
    4、创建一个老师的模型( 名称,年龄,性别,班级(多对一外键))

    • app应用models.py中建立模型类
    #班号模型
    class Classesnumber(models.Model): #类名代表了数据库表名,且继承了models.Model,类里面的字段代表数据表中的字段(name)
        # 属性=models.字段类型(选项)
        # 如果没有添加主键,django会默认添加一个ID的主键
        classgrade_number = models.CharField(u'号码',max_length=20) #备注也可用verbose_name
        class Meta: #模型元选项
            db_table = u'classnumber'   #在数据库中的表名,否则Django自动生成为app名字_类名
            managed = True
            # 数据的默认排序, 如果需要倒序,则添加"-"号即可,这里可以按多个排序
            # 多个排序会按你的数组列表顺序进行排序
            ordering = ['id','classgrade_number']
            # 描述,当查询结果是一条记录时,它的描叙
            verbose_name = u'班号' #对象的描述
            # 如果查询结果为多个记录,则返回verbose_name_plural的描叙
            verbose_name_plural = u'班号集' #复数时的描述
    #班级模型
    class Classesgrade(models.Model):
        name = models.CharField(u'名称',max_length=20)
        create_time = models.DateTimeField(u'创建时间',auto_now_add=True)  #auto_now_add只有第一次才会生效
        update_time = models.DateTimeField(verbose_name= u'修改时间',auto_now=True)#auto_now每一次修改的动作,都会更新一个时间
        #与班级一对一关系,关联属性,Django会自动建立个属性_id字段
        number = models.OneToOneField(Classesnumber)
        class Meta:
            db_table = 'classesgrade'
            managed = True
    #老师模型
    class Teacher(models.Model):
        name = models.CharField(u'姓名',max_length=20)
        age = models.IntegerField(u'年龄')
        sex = models.CharField(u'性别',max_length=2)
        #老师与班级多对一,关联属性在多方定义
        #db_constraint:bool  是否建立外键约束,
        #to_field:string 关联到的关联对象的字段名称。默认地,Django 使用关联对象的主键。
        #CASCADE:级联删除,如果删除,相关联的那个也会删除
        classesno = models.ForeignKey(Classesgrade,db_constraint = True,to_field='id',on_delete = models.CASCADE)
        class Meta:
            db_table = 'teacher'
            managed = True
    #学生
    class Student(models.Model):
        name = models.CharField(u'姓名',max_length=20)
        age = models.IntegerField(u'年龄')
        sex = models.CharField(u'性别',max_length=2)
        classesno = models.ForeignKey(Classesgrade,db_constraint = True,to_field='id',on_delete = models.CASCADE)
        # 多对多,Django会自动创建中间表
        teacher = models.ManyToManyField(Teacher,related_name = 'teacher_set')
        class Meta:
            db_table = 'student'
            managed = True
    
    • 添加数据
      班号(“001”,“002“)
      老师 ("k", “28”, "男",“django框架班001”)
      老师 ("山", “28”, "男",“django框架班001” )
      老师 ("不", “28”, "男",“django框架班002” )
      班级(“django框架班” ,“班号(001) ”,“自动创建”, “自动更新”)
      班级(“django框架班” ,“班号(002) ”,“自动创建”, “自动更新”)
      学生(“学生1”,20,“男”,“001 ”,[“k老师”,“山”])
      学生(“学生2”,22,“女”,“002 ”,["山", “不”])
      学生(“学生3”,21,“男”,“001 ”, ["k", “不”])
    class AddInfor(View):
        def get(self,request):
            #添加班号数据
            #可以用save和create方式
            cnumber1 = Classesnumber.objects.create(classgrade_number = '001')
            cnumber2 = Classesnumber.objects.create(classgrade_number = '002')
    
            #添加班级数据:
            #如果存在外键关联,主表必须首先得先有数据,一对一关系的添加数据
            #一对一赋值必须保持一个对象只对应一个外键
            cnumber3 = Classesnumber.objects.get(pk=1)  #1对1,先取出主表的pk=1对象
            Classesgrade.objects.create(name = u'django框架班',number = cnumber3 ) #再赋给外键
            cnumber4 = Classesnumber.objects.get(pk=2)
            cgrade = Classesgrade(name = u'django框架班',number = cnumber4)
            cgrade.save()
    
            # 添加老师数据:
            #获取班级对象
            cgrade1 = Classesgrade.objects.get(pk=1)  #1对多,先取出主表的pk=1对象
            cgrade2 = Classesgrade.objects.get(pk=2)  #1对多,先取出主表的pk=2对象
    
            # # 通过获取一端对象,则这里要用类属性而不是表字段的名字
            Teacher.objects.create(name = u'k',age = 28,sex = u'男',classesno = cgrade1)
            Teacher.objects.create(name = u'山',age = 28,sex = u'男',classesno = cgrade1)
            Teacher(name = u'不',age = 28,sex = u'男',classesno = cgrade2).save()
    
            #添加学生数据views.py中:
            #与班级,多对一外键
            stu1 = Student()
            stu1.name = u'学生1'
            stu1.age = 20
            stu1.sex = u'男'
            stu1.classesno = cgrade1
            stu1.save()
    
            stu2 = Student(name = u'学生2',age = 22,sex = u'女',classesno = cgrade2)
            stu2.save()
            stu3 = Student.objects.create(name = u'学生3',age = 21,sex = u'男',classesno = cgrade1)
            #与老师多对多
            #多对多的添加,需要使用add方法
            #多对多的添加,必须主类必须在数据库已经存在
            tch1 = Teacher.objects.get(name__exact= u'k')
            tch2 = Teacher.objects.get(name__exact= u'山')
            tch3 = Teacher.objects.get(name__exact= u'不')
    
            stu1.teacher.add(tch1)
            stu1.teacher.add(tch2)
            stu1.save()
    
            stu2.teacher.add(tch2,tch3)
            stu2.save()
    
            stu3.teacher.add(tch1,tch3)
            stu3.save()
            return render(request,'result.html',locals())
    
    • 查询数据views.py中
    #app应用urls.py中
    urlpatterns = [
        url(r'^register/$', views.Register.as_view(),name='register'),
        url(r'^login/$', views.Login.as_view(),name = 'login'),
        url(r'^index/$', views.Index.as_view(),name='index'),
        url(r'^logout/$', views.Logout.as_view(),name='logout')
    ]
    
    class QueryInfor(View):
        def get(self,request):
            #查询性别为男的学生
            stu_boys = Student.objects.filter(sex__exact = u'男')
            #查询年龄大于20岁的学生
            stu_20 = Student.objects.filter(age__gt = 20)
            #获取所有学生、并按年龄排序
            stu_all = Student.objects.all().order_by('age')
            #获取所有学生并排除性别为女的学生
            stu_nogirls = Student.objects.exclude(sex = u'女')
            #获取学生总数
            stu_sum = Student.objects.count()
            #获取最后创建的班级
            last_grade = Classesgrade.objects.latest('create_time')
    
            cgrade1 = Classesgrade.objects.get(pk = 1)
            cgrade2 = Classesgrade.objects.get(pk = 2)
    
            #查询班级下老师
            #本身班级对象是没有老师属性,django提供的一个反查机制
            # 如果设置related_name, 我们就通过related_name反查
            # 如果没有设置, django默认设置的是反查class的小写名字
            # related_name 可以当成反查所用的别名
            tchs1 = cgrade1.teacher_set.all() #xxx_set 实际返回的是一个空值,如需访问它们,则要进行一个查询QuerySet操作
            tchs2 = cgrade2.teacher_set.all()
    
            #查询班级下学生
            stu1 = cgrade1.student_set.all()
            stu2 = cgrade2.student_set.all()
    
            #查询一个班级的编号,1对1 反查
            cgradeno1 =cgrade1.number #cgrade1.number是个<Classesnumber: Classesnumber object>
    
            cgradeno2 = cgrade2.number
    
            #查询年龄最大的学生
            sage = Student.objects.aggregate(max_age = Max('age')) #以字典形式返回年龄最大值,{'age__max': 22}
            maxage_stu = Student.objects.filter(age = sage['max_age'])
            #filter(**kwargs)方法:根据参数提供的提取条件,获取一个过滤后的QuerySet。所以在前端不能直接获取对象的方式来,需要遍历来获取,即使只有一个;
    
            #查询年龄最小的学生
            sage = Student.objects.aggregate(Min('age')) #以字典形式返回年龄最大值,{'age__max': 22}
            minage_stu = Student.objects.filter(age = sage[sage.keys()[0]]) #sage.keys()[0]获取字典的键
    
            #avgage_stu查询学生的平均年龄
            sage = Student.objects.aggregate(Avg('age'))
            
            for key,value in sage.items():
                keys = key
                age = value
    
            #查询每个年龄的学生数量,按年龄进行分组,然后统计数量select age,count(age) from student group by age;
            agestucount = Student.objects.values('age').annotate(Count('id')) #annotate返回的是一个QuerySet,可以继续进行查询
            return render(request,'queryInfor.html',locals())
    

    页面效果

    <!DOCTYPE html>
    <html lang="en">
    <head>
        <meta charset="UTF-8">
        <title>查询信息</title>
        <style>
            * {
                margin: 0;
                padding: 0;
            }
        </style>
    </head>
    <body>
    
    <h2>1.查询性别为男的学生:</h2>
    <p>
    {%for stu_boy in stu_boys%}
    姓名:{{stu_boy.name}}<br/>
    性别:{{stu_boy.sex}}<br/>
    年龄:{{stu_boy.age}}<br/>
    班级:{{stu_boy.classesno_id}}<br/>
    {%endfor%}
    </p>
    <hr/>
    
    <h2>2.查询年龄大于20岁的学生:</h2>
    <p>
    {%for stu in stu_20%}
    姓名:{{stu.name}}<br/>
    性别:{{stu.sex}}<br/>
    年龄:{{stu.age}}<br/>
    班级:{{stu.classesno_id}}<br/>
    {%endfor%}
    </p>
    <hr/>
    
    <h2>3.获取所有学生、并按年龄排序:</h2>
    <p>
    {%for stu in stu_all%}
    姓名:{{stu.name}}<br/>
    性别:{{stu.sex}}<br/>
    年龄:{{stu.age}}<br/>
    班级:{{stu.classesno_id}}<br/>
    {%endfor%}
    </p>
    <hr/>
    
    <h2>4.获取所有学生并排除性别为女的学生:</h2>
    <p>
    {%for stu in stu_nogirls%}
    姓名:{{stu.name}}<br/>
    性别:{{stu.sex}}<br/>
    年龄:{{stu.age}}<br/>
    班级:{{stu.classesno_id}}<br/>
    {%endfor%}
    </p>
    <hr/>
    
    <h2>5.获取学生总数:</h2>
    <p>
        {{stu_sum}}
    </p>
    <hr/>
    
    <h2>6.获取最后创建的班级:</h2>
    <p>
    名称:{{last_grade.name}}<br/>
    创建时间:{{last_grade.create_time}}<br/>
    更新时间:{{last_grade.update_time}}<br/>
    对应班号:{{last_grade.number_id}}
    </p>
    <hr/>
    
    <h2>11.查询一个班级下有那些老师:</h2>
    <p>
    {{cgrade1.name}}第{{cgrade1.number.id}}个班老师:<br/>
    {%for tch in tchs1%}
    姓名:{{tch.name}}<br/>
    性别:{{tch.sex}}<br/>
    年龄:{{tch.age}}<br/>
    班级:{{tch.classesno_id}}<br/> <!--tch.classesno 对象.类属性形式不行,因为还是一个对象,要么用对象.表字段名,要么对象.类属性.主表主键 -->
    {%endfor%}
    <hr/>
    {{cgrade2.name}}第{{cgrade2.number_id}}个班老师:<br/>
    {%for tch in tchs2%}
    姓名:{{tch.name}}<br/>
    性别:{{tch.sex}}<br/>
    年龄:{{tch.age}}<br/>
    班级:{{tch.classesno.id}}<br/>
    {%endfor%}
    </p>
    <h2>22.查询一个班级下有那些学生:</h2>
    <p>
    {{cgrade1.name}}第{{cgrade1.number.id}}个班学生:<br/>
    {%for stu in stu1%}
    姓名:{{stu.name}}<br/>
    性别:{{stu.sex}}<br/>
    年龄:{{stu.age}}<br/>
    班级:{{stu.classesno_id}}<br/>
    {%endfor%}
    <hr/>
    {{cgrade2.name}}第{{cgrade2.number_id}}个班学生:<br/>
    {%for stu in stu2%}
    姓名:{{stu.name}}<br/>
    性别:{{stu.sex}}<br/>
    年龄:{{stu.age}}<br/>
    班级:{{stu.classesno_id}}<br/>
    {%endfor%}
    </p>
    <h2>33.查询一个班级的编号:</h2>
    <p>
    第{{cgrade1.number_id}}个班编号:{{cgradeno1.classgrade_number}}<br/>
    第{{cgrade2.number_id}}个班编号:{{cgrade2.number.classgrade_number}}
    </p>
    <h2>44、查询年纪最大的学生</h2>
    <p>
    {% for stu in maxage_stu %}
    姓名:{{stu.name}}<br/>
    性别:{{stu.sex}}<br/>
    年龄:{{stu.age}}<br/>
    班级:{{stu.classesno_id}}<br/>
    {% endfor %}
    </p>
    <h2>55、查询年纪最小的学生</h2>
    <p>
    {% for stu in minage_stu %}
    姓名:{{stu.name}}<br/>
    性别:{{stu.sex}}<br/>
    年龄:{{stu.age}}<br/>
    班级:{{stu.classesno_id}}<br/>
    {% endfor %}
    </p>
    <h2>66、查询学生的平均年龄</h2>
    <p>
    平均{{keys}}:{{value}}z
    </p>
    <h2>77、查询每个年龄的学生数量</h2>
    {% for dic in agestucount%} <!--agestucount是一个QuerySet,需要从中迭代出来 -->
        {% for age,countstu in dic.items %} <!--再将每一个遍历出来的字典通过items将键值取出来 -->
            {{age}}:{{countstu}}
        {% endfor %}
        ;<br/>
    {% endfor %}
    </body>
    </html>
    

    相关文章

      网友评论

        本文标题:Django之模型(二)

        本文链接:https://www.haomeiwen.com/subject/jknucftx.html