# 单表操作

# 前期准备

  • models.py 编写对应生成的数据库的表,创建后makemigration ---> migrate生成表在数据库中

    from django.db import models
    
    class Book(models.Model):  # 注释这个类后,makemigrations再migrate就会在库中删除这个类对应的表
        id = models.AutoField(primary_key=True)
        book = models.CharField(max_length=32,unique=True)
        price = models.DecimalField(max_digits=8,decimal_places=2) # 后面一个参数是小数位数
        pub_date = models.DateField()
        publish = models.CharField(max_length=32)
    
  • 在视图函数中添加一个用于存储数据的函数(前期通过这种方式来演示过程)

    from django.shortcuts import render,HttpResponse
    from app.models import Book  # 导入Book类(表)
    
    def add(request):  
        return HttpResponse('执行成功')
    
  • 中心思想

    在python中ORM的对应关系有三种:
        类(Book)             ----------> 表 (table)
    
        类对象(obj_book)      ----------> 行(记录)
    
        类属性(obj_book.book) ----------> 表的字段(重点)
    

# 添加表记录

  • 在views.py文件中的添加数据方式

    from django.shortcuts import render,HttpResponse
    from app.models import Book
    import  datetime
    
    def index(request):
        return render(request,'index.html')
    
    def add(request):
        #方式一:通过save()来保存,这里的obj_book是Book的一个实例化,也就是一条记录
        time = datetime.datetime.today()   #这里使用datetime结构插入时间,还可以使用引号的字符串插入
        obj_book = Book(book='Python',price = 123,pub_date=time,publish='人民出版社')
        obj_book.save()
        
        #方式二:通过create方法  (Book继承的父类Model中的有create方法,其返回值book_obj就是插入book表中的对象(记录)
        obj_book = Book.objects.create(book='葵花宝典',price=123.4,pub_date="2013-12-12",publish="中国人民音乐出版社")
        print(obj_book.book)  #这里create已经存储好我们的对象了,并且返回这个对象(记录)给我们,我们来打印下名字(对象的属性等于表的字段)
      
    --------------------------------------------------------------------
        #我们甚至可以使用打散的字典来输入,相应的字段属性要使用引号
        dic2={'book':'linux','price':125,'pub_date':'2012-12-14','publish':'老男孩出版社'}
        obj_book1 = Book.objects.create(**dic2)
        print(obj_book1.book)  
        return HttpResponse('执行成功')
    
    # 这个Book.objects就像是一个Book表的管理器一样,提供了增删改查所有的方法(其实是继承的Model)
        #objects:表示管理器。
        #Book.objects:表示管理book表。
        #book.objects.create:表示增加操作。
     
    #如果最终在创建新对象的时候比较麻烦,因为你的每条记录都要给他传参:
        1、先使用name = request.POST.get('name')……
        2、然后将每个name、属性赋值到Book类中实例化,属性一多就很难麻烦
        3、所以我们可以使用字典的打散传参:dic=request.POST.dict(),
           需要注意的是里面有一个crsf的键值对,需要删除:del dic['crf……'] ,然后Book.objects.create(**dic)
    
  • # 批量插入(bulk_create)
    def add(request):
        # 使用 bulk_create() 来批量插入
        book_list = []
        for i in range(10):
            bk_obj = Book(
                book='Python%s' % i,
                price=123 + i,
                pub_date="2013-12-12",
                publish="人民出版社"
            )
            book_list.append(bk_obj)
        Book.objects.bulk_create(book_list)  # bulk_create() 对一个列表中的对象执行save()
        return HttpResponse('执行完成')
    
    #上面的方法太麻烦了,我们可以使用打散的方式,来传参(下面有讲)
    
  • # 有则更新,无则添加(update_or_create)
    def add(request):
        # 需要中注意的是,update_or_create使用get方法,只不过在没有找到时做了异常处理(get不允许找不到)
        # 且在筛选条件能筛选出多条时,也会报错(没有作异常处理:get() returned more than one Book -- it returned 3 重复3次)
        # 所以要尽量筛选唯一的主键
        obj,created = Book.objects.update_or_create(
            book = 'python15', # 筛选条件,有没有,没有执行下面的,有就更新, 这里筛选price=123,会筛选多条而出错
            defaults={
                'book':'Python15',
                'price':123,
                'pub_date':'2014-12-14',
                'publish':'北京出版社'
            }
        )
        print(obj,created)  # Book object    True  True表示没有查询到,重新创建的
        return HttpResponse('执行完成')
    
  • # 注意事项
    #当我们忘了给一个默认不能为空的字段,插入数据时,可能会报错:"Column 'price' cannot be null",我们能可以在models.py中重新修改Book类,比如:price设置为默认可为空,且添加一个新字段:
    
    class Book(models.Model):  # 注释这个类后,makemigrations再migrate就会在库中删除这个类对应的表
        id = models.AutoField(primary_key=True)
        book = models.CharField(max_length=32,unique=True)
        price = models.DecimalField(max_digits=8,decimal_places=2,null=True) # 后面一个参数是小数位数
        pub_date = models.DateField()
        publish = models.CharField(max_length=32)
        is_pub = models.BooleanField()
    

    会出现下面情况:(因为在类中is_pub中没有设置初始值。)

  • 实例:实现表单登陆,登陆成功跳转到一个网址

    #views.py文件中
    from django.shortcuts import render,HttpResponse,redirect
    from app1.models import User
    
    def index(request):
        if request.method == 'GET':
            return render(request,'index2.html')
        elif request.method == 'POST':
            ret = User.objects.filter(name=request.POST['username'],password=request.POST['password'])
            if ret:
                return redirect('https://www.baidu.com/')
            else:
                return HttpResponse('Failed')
    ---------------------------------------------------------------
    #models.py文件
    from django.db import models
    
    class User(models.Model):
        id = models.AutoField(primary_key=True)
        name = models.CharField(max_length=32)
        password = models.CharField(max_length=12)
    

# 查询表记录

  • 还记得表类.objects像是一个管理器,提供了增删改查的方法,Book.objects.all()获取所有的书籍,查询这里大家就掌握谁调用的下面的方法。

    <1> all():                  查询所有结果,结果是queryset类型
      
    <2> filter(**kwargs):       它包含了与所给筛选条件相匹配的对象,结果也是queryset类型 	   	  	   
      							Book.objects.filter(title='linux',price=100) #里面的多个条件用逗号分开,并且这几个条件必须都成  							立,是and的关系,or关系的我们后面再学,直接在这里写是搞不定or的
      
    <3> get(**kwargs):          返回与所给筛选条件相匹配的对象,不是queryset类型,是行记录对象,返回结果有且只有一个,
                                如果符合筛选条件的对象超过一个或者没有都会抛出错误。捕获异常try。  Book.objects.get(id=1)
      
    <4> exclude(**kwargs):      排除的意思,它包含了与所给筛选条件不匹配的对象,没有不等于的操作昂,用这个exclude,返回值是queryset							 类型 Book.objects.exclude(id=6),返回id不等于6的所有的对象,或者在queryset基础上调用,      		                     Book.objects.all().exclude(id=6)
                     
    <5> order_by(*field):       queryset类型的数据来调用,对查询结果排序,默认是按照id来升序排列的,返回值还是queryset类型
                      models.Book.objects.all().order_by('price','id') #直接写price,默认是按照price升序排列,按							 照字段降序排列,就写个负号就行了order_by('-price'),order_by('price','id')是多条件排序,按照     						  price进行升序,price相同的数据,按照id进行升序
            
    <6> reverse():              queryset类型的数据来调用,对查询结果反向排序,返回值还是queryset类型
      
    <7> count():                queryset类型的数据来调用,返回数据库中匹配查询(QuerySet)的对象数量。
      
    <8> first():         	   queryset类型的数据来调用,返回第一条记录 Book.objects.all()[0] = 
    						   Book.objects.all().first(),得到的都是model对象,不是queryset 
      
    <9> last():                queryset类型的数据来调用,返回最后一条记录
      
    <10> exists():               queryset类型的数据来调用,如果QuerySet包含数据,就返回True,否则返回False
                        空的queryset类型数据也有布尔值TrueFalse,但是一般不用它来判断数据库里面是不是有数据,如果有大量的							   数据,你用它来判断,那么就需要查询出所有的数据,效率太差了,用count或者exits
                     例:all_books = models.Book.objects.all().exists() #翻译成的sql是SELECT (1) AS `a` FROM 								`app01_book` LIMIT 1,就是通过limit 1,取一条来看看是不是有数据
    
    <11> values(*field):        用的比较多,queryset类型的数据来调用,返回一个ValueQuerySet——一个特殊的QuerySet,运行后得到的并不							是一系列
                                model的实例化对象,而是一个可迭代的字典序列,只要是返回的queryset类型,就可以继续链式调用queryset类							型的其他的查找方法,其他方法也是一样的。
    <12> values_list(*field):   它与values()非常相似,它返回的是一个元组序列,values返回的是一个字典序列
     
    <13> distinct():            values和values_list得到的queryset类型的数据来调用,从返回结果中剔除重复纪
    
  • # all() 查询所有结果
    它返回的是QuerySet数据类型对象,它是django ROM特有的数据类型。
    	数据格式为:[model对象1,model对象2,model对象3 ...] ;它和下面的 animal_list类似。先是一个列表,列表的元素是一个对象。
    	一个对象,就是一条记录。有多少条记录,就有多少个对象。那么Queryset就是对象(记录)的集合体,我们可以通过下标访问每个对象(记录)
    	
    
  • 实例

     #查询环节的配置,url.py中新添加一个路径
    
    urlpatterns=[
          url('^$',views.index),
          url('^query/$',views.query)
    
    ]
    ----------------------------------------------------------
    修改views.py文件
      def query(request):
          ret = Book.objects.all()  #可进行切片
        print(ret)      #<QuerySet [<Book: Book object>, <Book: Book object>, <Book: Book object>,……]
          print(ret[0].book)  # Python0
        return HttpResponse('执行完成')
    

格式化输出:上面的放回的QuerySet太长了,因为对象没有设置__str__函数,当对象被打印时显示的是自己的名字(没人会叫你身份证的号码)

   class Animal:
      def __init__(self, name, age):
          self.name = name
          self.age = age
      def __str__(self):  
          return self.name
          
  dog = Animal("旺财", 3)
  print(dog)  #旺财
  
  #修改models.py
  class Book(models.Model):  # 注释这个类后,makemigrations再migrate就会在库中删除这个类对应的表
      id = models.AutoField(primary_key=True)
      book = models.CharField(max_length=32,unique=True)
      price = models.DecimalField(max_digits=8,decimal_places=2,null=True) # 后面一个参数是小数位数
      pub_date = models.DateField()
      publish = models.CharField(max_length=32)
      is_pub = models.BooleanField(default=False)
         
      def __str__(self):
          return self.book
        
#再次执行query函数
 def query(request):
      ret = Book.objects.all()
      print(ret)   #<QuerySet [<Book: Python0>, <Book: Python1>, <Book: Python2>,……]
      print(ret[0].book)  #Python0
      return HttpResponse('执行完成') 
  
   #需要注意的是:QuerySet其实就是ORM对表对象的一个存储的数据类型,里面装得就是model对象,只有model对象才能调用属性,QuerySet只能对model对象进行筛选,组织等,也就是说QuerySet通过索引可以讲里面的元素转化为model对象。QuerySet不能直接调用属性,即使它只有一个对象。

  • # filter(**kwargs): 它包含了与所给筛选条件相匹配的对象(可多条件筛选)
    def query(request):
        # ret = Book.objects.filter(publish="北京出版社").filter(book='python15') #等价下面的
        ret = Book.objects.filter(publish="北京出版社",book='python15')  #多条件是and关系,全对才对
        print(ret)    #<QuerySet [<Book: Python15>]>
        print(ret[0].book)   
        return HttpResponse('执行完成')
    
    #fliter 之前本身只能实现and条件判断,即全对才行,下面我们能将然他实现or条件判断:需要导入
    from django.db.models import Q
    
    def search(request,msg):  # 实现搜索出版社或则书名的其中一种
        if request.method == "GET":
            flag = models.Book.objects.filter(Q(title__contains=msg)|Q(publish=msg)).exists()
            if flag:
                ret = models.Book.objects.filter(Q(title__contains=msg)|Q(publish=msg))
                return render(request,'index.html',{'book_list':ret})
            else:
                hint = '<script> alert("没有相应结果,请输入书名搜索");window.location.href="/app/"</script>'
                return HttpResponse(hint)
    
  • # first(): 返回第一条记录
    #first用来取一条记录,如果返回结果有多条,只会取第一条。它的返回结果是model对象。
    #last同样用法:用来取最后一条记录,如果返回结果有多条,只会取最后一条。它的返回结果是model对象
    def query(request):
        ret = Book.objects.filter(publish="北京出版社").filter()
        print(ret)  #Python%s15
        return HttpResponse('执行完成')
    
  • # get(**kwargs): 返回与所给筛选条件相匹配的对象
    #返回结果有且只有一个,如果符合筛选条件的对象超过一个或者没有都会抛出错误。(get取值只能是唯一的)
    #多了会报错: get() return more than one book; 没有:book matching query does not exist
    def query(request):
        ret = Book.objects.get(id=3)
        print(ret)  #Python2
        return HttpResponse('执行完成')
    
    #使用get有且只有一个结果时才有意义。推荐使用get时,利用主键查询,因为主键是唯一的
    
  • # exclude(**kwargs): 它包含了与所给筛选条件不匹配的对象(排除)
    def query(request):
        ret = Book.objects.exclude(publish='人民出版社')  #排除在这个出版社的其他
        print(ret)  #<QuerySet [<Book: Python%s15>, <Book: Python15>]>
        return HttpResponse('执行完成'
    
  • # order_by(*field): 对查询结果排序
    def query(request):
        ret = Book.objects.order_by("price","-id","-pub_date")
        print(ret)  # 先按照price升序,再按照id、时间降序
        return HttpResponse('执行完成')
    
  • # reverse(): 对查询结果反向排序
    def query(request):
        obj = Book.objects.all().order_by("price").reverse()  #先升序,再反序
        print(obj)
        return HttpResponse("查询成功")
    
  • # count(): 返回数据库中匹配查询的对象数量。
    #它的返回结果数据类型是int,也就是说他返回的不再是一个QuerySet类型的结果,所以不能继续链式操作
    
    def query(request):
        ret = Book.objects.order_by("price","-id","-pub_date").count()
        print(ret)  # 12
        return HttpResponse('执行完成')
    
    #链式操作:因为queryset可以调用API接口(各种函数),只要前一个接口的返回值是queryset,它可以可以一直调用API接口,除非遇到返回值不是queryset的情况下,链式操作,才可以终止。因为count的返回值是int,所以到这里,就结束了!不能再调用API接口了!
    
  • # exists(): 如果QuerySet包含数据,就返回True,否则返回False
    def query(request):
        ret = Book.objects.all().exists()  #这样之判断有无,也就避免了大数据被传入打印
        if ret: print('ok')
        return HttpResponse("查询成功")
    
  • # values(*field): 返回一个ValueQuerySet
    #它是一个特殊的QuerySet,运行后得到的并不是一系列model的实例化对象,而是一个可迭代的字典序列。它的返回结果是一个特殊的QuerySet,列表每一个元素都是字典。也就是循环QuerySet中的每一个model对象,将values中的值为键,去每个对象中取值,最后做成一个字典添加到QuerySet列表中
    
    def query(request):
        ret = Book.objects.all().values("book","price")
        print(ret)  #<QuerySet [{'book': 'Python0', 'price': Decimal('123.00')}, {'book': 'Python1', 'price': Decimal('124.00')},……]
        return HttpResponse('执行完成')
    
  • # values_list(*field): values返回的是一个元组序列
    #通过组织等的值,在每个对象中找,返回一个元组
    def query(request):
        ret = Book.objects.all().values_list("book","price")
        print(ret)  #<QuerySet [('Python0', Decimal('123.00')), ('Python1', Decimal('124.00')), ……]
        return HttpResponse('执行完成')
    
  • # distinct(): 从返回结果中剔除重复纪录(针对values、values_list的返回对象QuerySet类型)
    #它的返回结果是QuerySet,注意:是一个常规的QuerySet。如果使用distinct对主键做去重,是没有意义的。因为主键是唯一的!
    
    def query(request):
       	# 对价格进行去重
        ret = Book.objects.all().values("price").distinct()
        print(ret)  #<QuerySet [{'price': Decimal('123.00')}, {'price': Decimal('124.00')}, {'price': Decimal('126.00')}, ……]
        return HttpResponse('执行完成')
    
    #查看所有书籍的价格以及出版社,价格和出版社同时不能重复
    def query(request):
        ret = Book.objects.all().values("price","publish").distinct()
        print(ret)
        return HttpResponse("查询成功")
    
    #总结
        # all_books = models.Book.objects.all().distinct() #这样写是表示记录中所有的字段重复才叫重复,但是我们知道有主键的存在,所以不可能所有字段数据都重复(无意义,不可能所有有重复)
        # all_books = models.Book.objects.all().distinct('price') #报错,不能在distinct里面加字段名称
        # all_books = models.Book.objects.all().values('price').distinct()#<QuerySet [(Decimal('11.00'),), (Decimal('111.00'),), (Decimal('120.00'),), (Decimal('11111.00'),)]>
        all_books = models.Book.objects.all().values_list('price').distinct()#<QuerySet [{'price': Decimal('11.00')}, {'price': Decimal('111.00')}, {'price': Decimal('120.00')}, {'price': Decimal('11111.00')}]> 只能用于valuse和values_list进行去重
      all_books = models.Book.objects.all().values_list('title','price').distinct() #title和price两个同时重复才算一条重复的记录
    

# 基于双下划线的模糊查询

#其实在底层转换为like的sql语句

Book.objects.filter(price__in=[100,200,300])        #price值等于这三个里面的任意一个的对象
Book.objects.filter(price__gt=100)                  #大于,大于等于是price__gte=100,别写price>100,这种参数不支持
Book.objects.filter(price__lt=100)
Book.objects.filter(price__range=[100,200])          #sql的between and,大于等于100,小于等于200
Book.objects.filter(title__contains="python")        #title值中包含python的
Book.objects.filter(title__icontains="python")       #不区分大小写
Book.objects.filter(title__startswith="py")          #以什么开头,istartswith  不区分大小写
Book.objects.filter(pub_date__year=2012)             #查询某一年的

def query(request):
    ret = Book.objects.filter(book__contains='Py').count()  #含有py的
    ret1 = Book.objects.filter(price__gte=130).count()  #大于130的个数
    print(ret,ret1)  #12  3
    return HttpResponse('执行完成')
  • # 字段名__startswith(以什么开头的)
    def query(request):
        ret = Book.objects.filter(title__startswith="北京")
        print(ret)
        return HttpResponse("查询成功")
    
  • # 字段名__gte(大于等于)(同样适用于小于等于)
    def query(request):
        ret = Book.objects.filter(price__lte=100)
        print(ret)
        return HttpResponse("查询成功")
    

# 删除表记录

  • 删除方法就是 delete()。它运行时立即删除对象而不返回任何值。例如:

    model_obj.delete()  #delete()方法的调用者可以是一个model对象,也可以是一个queryset集合。
    
    #你也可以一次性删除多个对象。每个 QuerySet 都有一个 delete() 方法,它一次性删除 QuerySet 中所有的对象。
    #例如,下面的代码将删除 pub_date 是2005年的 Entry 对象:
    Book.objects.filter(pub_date__year=2005).delete()
    
    #等学到外键的时候再说,在 Django 删除对象时,会模仿 SQL 约束 ON DELETE CASCADE 的行为,换句话说,删除一个对象时也会删除与它相关联的外键对象。例如:
    b = Blog.objects.get(pk=1)  #pk就是主键 primary key
    b.delete()
    
    #要注意的是: delete() 方法是 QuerySet 上的方法,但并不适用于 Manager 本身。这是一种保护机制,是为了避免意外地调用 Book.objects.delete() 方法导致 所有的记录被误删除。如果你确认要删除所有的对象,那么你必须显式地调用:
    Book.objects.all().delete()
    
    #修改是基于查询的结果来修改的,所以先查询再修改
    #举例:删除价格等于100的
    def delbook(request):
        ret = Book.objects.filter(price=100).delete()
        print(ret)
        return HttpResponse("删除成功")
    
    #如果不想级联删除,可以设置为:
    pubHouse = models.ForeignKey(to='Publisher', on_delete=models.SET_NULL, blank=True, null=True)
    
  • 还可以结合视图函数的参数来进行操作:

    url.py文件中
      urlpatterns = [
          url('delbook/(?P<id>\d+)', views.delbook),
      ]
    
        ---------------------------------------------------------------
    views.py文件中
    def delbook(request,id):
          ret = Book.objects.filter(nid=id).delete()
        print(ret)
          print(ret,type(ret))
          if ret[0]:
              return HttpResponse("删除成功")
          else:
              return HttpResponse("删除失败")
    

# 修改表记录

  • 修改表记录都是基于查询之上

    ret = Book.objects.filter(book__startswith="py").update(price=120)  #update只能是querset类型才能调用,model对象不能直接调用更新方法,所以使用get方法获取对象的时候是不能update的,ret为一个数字,表示受影响的记录数
    
    def query(request):
        ret = Book.objects.filter(book__contains='Py').update(price=1222)
        print(ret)  #12
        return HttpResponse('执行完成')
      #此外,update()方法对于任何结果集(QuerySet)均有效,这意味着你可以同时更新多条记录update()方法会返回一个整型数值,表示受影响的记录条数。
      #注意:<input type="date" class="form-control" id="book_pub_date" placeholder="出版日期" name="book_pub_date" value="{{ edit_obj.pub_date|date:'Y-m-d' }}">,type='date'的input标签,value的值必须是'Y-m-d'的格式,这个标签才能认识并被赋值,所以,要通过date过滤给它改变格式。