首页 > 技术文章 > django之orm

fangjie0410 2017-12-06 16:39 原文

一 models模型

 models模型:与数据库打交道的,对数据库约束管理的模块

 orm框架:orm在做映射关系,将class类转成对应的sql语句。

 models.Model类:python于orm链接起来,提醒orm,哪个django需要进行数据库的迁移。

 映射关系:

  python的类名对应sql语句中的表名

  python中的属性对应sql语句中的字段

  python中的属性值对应sql语句中的字段类型

   实例:

from django.db import models

# Create your models here.

# ------------------创建表-------------------------

class Student(models.Model):
    nid=models.AutoField(primary_key=True)         #  主键约束
    name=models.CharField(max_length=32)           #  字符串字段
    birth=models.DateField()                       #  日期类型
    class_id=models.IntegerField(default=0)

  同过这个类实例化出来的对象就是对应的每一条记录。

 save:保存数据到数据库

from django.shortcuts import render,HttpResponse

# Create your views here.


from  app01.models import *



def add(request):
    s=Student(name="yuan",birth="2012-12-12",class_id=3)
    s.save()  # 保存到数据库

    return HttpResponse("添加成功")

  类型介绍:AutoField:自增,primary_key=True:设置为主键

   charField:字符串类型,max_length=数组,字符的长度

   dataField:日期对象

   IntegerField:整数类型,default:默认值

nid=models.AutoField(primary_key=True)         #  主键约束
name=models.CharField(max_length=32)           #  字符串字段
birth=models.DateField()                       #  日期类型
class_id=models.IntegerField(default=0)

  类型补充:

  FileField:文件类型,上传文件的。Upload_to:上传到哪个目录下面。

  ImageField:图片文件类型

avatar = models.FileField(upload_to = 'avatar/',default="/avatar/default.png")

  TextField:文本字符串的类型。

  参数:null=True:为空;unique=True:唯一
  关联表的参数:to:关联到那一张表;to_field:关联哪个字段

class ArticleDetail(models.Model):
    """
    文章详细表
    """
    nid = models.AutoField(primary_key=True)
    content = models.TextField()
    article = models.OneToOneField(to='Article', to_field='nid')

  unqueic_together:联合唯一,限制了某些字段不能为空。

class ArticleUpDown(models.Model):
    """
    点赞表
    """
    nid = models.AutoField(primary_key=True)
    user = models.ForeignKey('UserInfo', null=True)
    article = models.ForeignKey("Article", null=True)
    is_up=models.BooleanField(default=True)

    class Meta:
        unique_together = [
            ('article', 'user'),
        ]

二 表操作

 类名.objects:需要用objects来调用类的属性

 增:

  create:添加纪录

def add(request):
    if request.method=="POST":
        name=request.POST.get("name")
        birth=request.POST.get("birth")
        class_id=request.POST.get("class_id")
        # 方式1:
        # s=Student(name=name,birth=birth,class_id=class_id)
        # s.save()
        # 方式2:
        stu_obj=Student.objects.create(name=name,birth=birth,class_id=class_id)

        return redirect("/index/")

    return render(request,"add.html")
View Code

 删:

  delete:删除记录

def delStu(request,id):
    # 删除主键为ID值得学生记录

    # 过滤查询  filter()
    #ret=Student.objects.filter(nid=id) # <QuerySet [<Student: Student object>]>
    Student.objects.filter(nid=id).delete()
    return redirect("/")   # 重定向的是路径
View Code

 改:

  update:修改,更新记录

def set_student(request,id):
    if request.method=='POST':
        name=request.POST.get('name')
        date=request.POST.get('date')
        class_id=request.POST.get('class_id')
        ret=models.Student.objects.filter(sid=id).update(name=name,date=date,cid=class_id)
        return redirect('/student_list/')

    student=models.Student.objects.filter(sid=id)[0]
    return render(request,'set_student.html',{'student':student})
View Code

三 表的查看操作

 查看记录API:

  all():是一个列表,里面放的是一条条的记录,返回的是一个QuerySet对象

  filter(过滤条件):过滤查找,查找出来的是符合条件的对象,返回的是一个QuerySet对象

  exclade():查找出来的都是不符合条件的,相当于sql语句中的not,返回的是一个QuerySet对象

  first:第一条记录,主键先升序,在取第一条记录,返回的是一个models对象

  last:最后的一条记录。主键先降序再取第一条记录,返回的是一个models对象

  order_by:做排序,默认是升序,-是降序。

  values:是一个列表里面放的是一个个字典,是一种显示方式,返回一个QuerySet对象

  values_list:是一个列表里面放的是一个个的元组,也是一种显示方式,返回一个QuerySet对象

  count:计数,返回的是一个数字。

  reverse:对表的查询结果反向排序。

  get:只查找拿到一个models对象,不过拿到的不是一个就会报错,返回的是一个models对象

  distinck:去掉具体某些相同的记录,主要用在values和values_list里面的。

  exists:限制取出的条数,优化查询结果

(1)Student.objects.all()    #返回的QuerySet类型 查询所有记录    [obj1,obj2....] 
                   
                   (2)Student.objects.filter() #返回的QuerySet类型 查询所有符合条件的记录  
                   (3)Student.objects.exclude()#返回的QuerySet类型 查询所有不符合条件的记录  
                   (4)Student.objects.get()    #返回的models对象   查询结果必须有且只有一个,否则报错  
                   (5)Student.objects.all().first()      #返回的models对象   查询结果集合中的第一个 
                   (6)Student.objects.filter().last()    #返回的models对象   查询结果集合中的最后一个  
                   (7)Student.objects.all().values("name","class_id")  #返回的QuerySet类型  ,列表中存放的字典 
                   (8)Student.objects.all().values_list("name","class_id")  #返回的QuerySet类型  ,列表中存放的元组 
                   (9)Student.objects.all().order_by("class_id")   # 按指定字段排序,不指定,按主键排序
                   (10)Student.objects.all().count()  # 返回的记录个数
                   (11)Student.objects.all().values("name").distinct()
                   (12)Student.objects.all().exist()
View Code
# 1 all()   QuerySet类型
    # Student.objects.all()
    # 2 filter  QuerySet类型
    #stu_list=Student.objects.filter(birth__year="2017",birth__month="12")
    #stu_list=Student.objects.filter(name="龙华",class_id=6)

    # for stu in stu_list:
    #     print(stu.name)

    #3 exclude()   QuerySet类型

    # stu_list=Student.objects.exclude(name="alex")
    # for stu in stu_list:
    #     print(stu.name)


    # 4 first()  5 last()    models对象

    #stu_obj=Student.objects.all().first()
    # stu_obj=Student.objects.filter(birth__year="2017").first()
    # print(stu_obj.name)

    # 6 order_by  :  QuerySet类型

    # stu_list=Student.objects.all().order_by("-class_id")
    # #
    # for stu in stu_list:
    #     print(stu.name)

    # ret=Student.objects.order_by("class_id")
    # print(ret)
    # for i in ret:
    #     print(i.name)


    # 7    values  values_list  : QuerySet
    # ret1=Student.objects.filter(birth__year="2017").values("name","class_id")
    # ret2=Student.objects.filter(birth__year="2017").values_list("name","class_id")

    # 8 count

    # ret=Student.objects.all().count()
    # ret=Student.objects.filter(nid=15).count()
    # print(ret)

    # 9 get  必须有且只有一个记录,否则报错
    # Student.objects.filter(nid=5).first()
    # ret=Student.objects.get(class_id="5")
    # print(ret.name)  # Student object

    # 10  : distinct
    # [obj1,obj2,..........]
    #ret=Student.objects.all().values("class_id").distinct()

    # exist()

    #stu_list=Student.objects.all().exists()

    # if stu_list:
    #     print("OK")
View Code

 查询下面的__双下划线的方法:

  __gt:大于号>

  __endswith:以什么结尾

  __iendswith:以什么结尾,不区分大小写

  __shartswith:以什么开头

  __ishartswith:以什么开头,不区分大小写

  __contains:包含什么

  __icontains:包含什么,并不区分大小写

  __lt:小于<

  __in:是否在这里面

  __range:某个范围

models.Tb1.objects.filter(id__lt=10, id__gt=1)   # 获取id大于1 且 小于10的值
 
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 
View Code
def select(request):
    

    # ret=models.Student.objects.filter(date__year='2017',cid=2)
    # for stu in ret:
    #     print(stu.name,stu.date,stu.cid)

    # ret=models.Student.objects.filter(cid__gt=2)
    # for stu in ret:
    #     print(stu.name,stu.cid)

    # ret=models.Student.objects.filter(name__endswith='g')
    # for stu in ret:
    #     print(stu.name)

    # ret=models.Student.objects.filter(name__icontains='ng')
    # for stu in ret:
    #     print(stu.name)
    #
    #
    # ret=models.Student.objects.filter(name__contains='e')
    # for stu in ret:
    #     print(stu.name)


    # ret=models.Student.objects.filter(name__startswith='f')
    # for stu in ret:
    #     print(stu.name)

    # ret=models.Student.objects.filter(cid__in=[2,5])
    # for stu in ret:
    #     print(stu.cid,stu.name)


    # ret=models.Student.objects.filter(cid__range=[3,7])
    # for stu in ret:
    #     print(stu.cid,stu.name)

    # ret=models.Student.objects.filter(cid__lt=4)
    # for stu in ret:
    #     print(stu.cid,stu.name)

    return HttpResponse('OK')
View Code

  查看转换的sql语句:将下面一段代码添加到setting.py文件中就可以了

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

四 多表操作

 数据类型补充:

  DecimalField:浮点数,decimal_places:小数位的个数。

  EmailField:邮箱数据类型

  一对多表的创建:ForeignField("关联的表名"):设置外键,orm默认指定关联表的主键,是多表的某个字段的关联表的一个对象。

   related_name='名字':反向查找的时候可以直接使用这个名字。

  to_field:可以设置多表与关联表的那个字段相关联。

class Book(models.Model):
    title = models.CharField(max_length=32)
    publishDate = models.DateField()
    price = models.DecimalField(max_digits=5, decimal_places=2)   #  999.99

    # 创建一对多的关联字段    :    是与某个书籍对象关联的出版社对象(注意,只有一个对象)
    publish=models.ForeignKey("Publish")

  操作方法:

   增:1 外键关联表对象字段可以直接赋值一个单表的对象。

     2 外键字段可以直接赋值一个单表关联的字段的值。

#pub_obj=Publish.objects.get(name="沙河出版社")

    ########################### 一对多添加##################################
    # 方式1:
    # Book.objects.create(title="金瓶",publishDate="2001-12-12",price=122,publish=pub_obj)
    # 方式2:
    #book_obj=Book.objects.create(title="金瓶3",publishDate="2011-08-12",price=112,publish_id=1)

    查:1 可以使用单表查询将多表的外键的值取出来,然后在到单表里面将对象给过滤出来。

     2 可以将多表的那条对象取出来,然后可以使用点方法给关联对象就可以操作取值。

 #  #################一对多查询
    # 金瓶的出版社名称
    # book_obj=Book.objects.filter(title="金瓶2").first()
    # print(book_obj.title)
    # print(book_obj.price)
    # print(book_obj.publishDate)
    # print(book_obj.publish_id) # 1
    ##########
    # pid=book_obj.publish_id  # 1
    # pub_obj=Publish.objects.filter(nid=pid).first()  # 与这本书关联的出版社对象
    # print(pub_obj.name)
    #-----
    # print(book_obj.publish.name)
View Code

 多对多表创建:

  ManyToManyFodel:不用手动创建第三张表,orm自动给你创建。创建的是绑定表某个对象的关联对象集合。

class Book(models.Model):
    title = models.CharField(max_length=32)
    publishDate = models.DateField()
    price = models.DecimalField(max_digits=5, decimal_places=2)  

    # 创建的多对多的关系
    authors=models.ManyToManyField("Author")

    def __str__(self):
        return self.title

 多对多操作:需要借助单表的查询方法,返回的是一个QuerySet类型。

   增:orm分别拿到的是两个表的对象,然后在添加到关系表里面去。

     可以将没有ManyToManymodel约束条件的表的关联字段值拿到,然后在添加到关系表去。也可以直接写数字添加,

     add:追加一条记录

     *:直接加上一个QuerySet类型对象就可以添加

    ########################### 多对多添加##################################
    # book_obj = Book.objects.create(title="金瓶3", publishDate="2011-08-12", price=112, publish_id=1)
    #
    # alex=Author.objects.get(name="alex")  # aler.id
    # egon=Author.objects.get(name="egon")  # egon.id
    #
    # # 绑定关系
    # book_obj.authors.add(alex,egon)

    ######################
    # 追加一个作者
    # book_obj=Book.objects.filter(title="金瓶3").first()
    #
    # wenzhou=Author.objects.get(name="文州")
    # book_obj.authors.add(wenzhou)

    # 绑定关系2:
    book_obj = Book.objects.create(title="金瓶5", publishDate="2011-08-12", price=112, publish_id=1)

    # authors=Author.objects.all()
    # book_obj.authors.add(*authors)
    #绑定关系3:
    book_obj.authors.add(1,2)
View Code

   查:get():获取某一个对象

     all():获取记录

 ################### 多对多查询
    # 查询金瓶3所有作者的名字

    # book_obj=Book.objects.get(title="金瓶3")

    #########

    # book_obj.authors.all()

    # print(book_obj.authors.all())  # <QuerySet [<Author: alex>, <Author: egon>]>
    #
    # for author in book_obj.authors.all():
    #     print(author.name)

   删:remove:删除相关记录

    clear:清空相关记录

# 删除一个作者
    # alex=Author.objects.get(name="alex")
    # book_obj = Book.objects.filter(title="金瓶3").first()
    # book_obj.authors.remove(alex)

    # 清空所有作者
    # book_obj = Book.objects.filter(title="金瓶3").first()
    # book_obj.authors.clear()

  一对一表创建:unique=True:唯一约束

        OneToOneField:创建一对一的外键

class AuthorDetail(models.Model):
    addr=models.CharField(max_length=32)
    email=models.EmailField()
    author=models.OneToOneField("Author")

  反向查询:__set:多表反向查询字段,返回的是一个集合。使用方法是:表名__ser.all():自己拼接。

                     .publish
            ==== 一对多(Book----------------->Publish)
                            <----------------
                              .book_set.all()                        
            
                查询python这本书的出版社的名字   (正向查询,按字段)
                    book_obj=Book.objects.get(title="python")
                    print(book_obj.publish.name)
                
                查询沙河出版社出版过的书籍名称    (反向查询按:表名_set)
                    pub_obj=Publish.objects.get(name="人民出版社")                    
                    print(pub_obj.book_set.all())

                                .authors.all()
            ==== 多对多(Book------------------->Author)
                            <--------------------         
                                .book_set.all()
            
                查询python这本书的所有作者的名字 (正向查询,按字段)
                    book_obj=Book.objects.get(title="python")
                    print(book_obj.authors.all())    
                    
                查询alex出版过的所有书籍          (反向查询按:表名_set)
                    author_obj=Author.objects.get(name="alex")
                    print(author_obj.book_set.all()) # <QuerySet [<Book: 金瓶4>, <Book: 金瓶5>]>
View Code

      一对一表反向查寻是不需要__set的,因为返回的结果只有一个。使用方法:表名.all():直接调用

                             .author
            ==== 一对一(AuthorDetail--------------->Author)
                                     <----------------
                                       .authordetail

                                       
                查询的地址在烟台并且email是789的作者的名字  (正向查询,按字段)
                    authordetail=AuthorDetail.objects.get(addr="烟台",email=789)
                    print(authordetail.author.name)
                

                查询文州的email                              (反向查询按:表名)
                    wenhzou=Author.objects.get(name="文州")
                    print(wenhzou.authordetail.email) # 123@qq.com                
                            
                # 查询住在沙河的作者出版过的所有书籍的名称以及出版社名称
                
                   author_obj=Author.objects.get(name="alex")
                   print(author_obj.book_set.all()) # <QuerySet [<Book: 金瓶4>, <Book: 金瓶5>]>
                
View Code

  总结:反向查询靠的是表名,而正向查询靠的的是字段名。

 基于__双下划线查询,join方法:

  表名__字段名:反向查询,按照表名查询;字段对象__字段名:正向查询,按照字段查询

 ##########################基于双下划线的查询:正向查询,按字段;反向查询,按表名#####################
                 
                    # 查询沙河出版社出版过的书籍名称
                    # ret=Publish.objects.filter(name="人民出版社").values("book__title")
                    # print(ret)
                    
                    # Book.objects.filter(publish__name="人民出版社").values("title")
                 
                 
                    #  email以456开头的作者出版过的所有书籍名称以及出版社名称

                        ret=Book.objects.filter(authors__authordetail__email__startswith="456").values("title","publish__name")
                        print(ret)
                     sql:
                     
                     SELECT 
                          "app01_book"."title", "app01_publish"."name"

                     FROM "app01_book" 
                         INNER JOIN "app01_book_authors" ON ("app01_book"."id" = "app01_book_authors"."book_id") 
                         INNER JOIN "app01_author" ON ("app01_book_authors"."author_id" = "app01_author"."id") 
                         INNER JOIN "app01_authordetail" ON ("app01_author"."id" = "app01_authordetail"."author_id") 
                         INNER JOIN "app01_publish" ON ("app01_book"."publish_id" = "app01_publish"."nid") 

                         WHERE "app01_authordetail"."email" LIKE '456%' ESCAPE '\' LIMIT 21; args=('456%',)
View Code

 分组和聚合:

  aggregate:统计信息返回的是一个人对象,是QuerySet对象额终结者。

  annotate:统计信息,返回的是一个QuerySet对象。

def query3(request):
    from django.db.models import Avg,Sum,Count,Min
    #############################聚合函数:aggregate###################

    #查询所有书籍的平均价格

    # ret=Book.objects.all().aggregate(avgPrice=Avg("price"))
    # print(ret) # {}

    #############################分组函数:annotate ###################

    # 查询每一个出版社出版过的书籍个数
    # ret=Publish.objects.all().annotate(c=Count("book__title"))
    # for pub_obj  in ret:
    #     print(pub_obj.name,pub_obj.c)

    # 查询每一本书的作者个数

    # ret=Book.objects.all().annotate(counts=Count("authors__id")).values("title","counts")
    # print(ret)

    # 查询每一个作者出版过的书籍的平均价格

    # ret=Author.objects.all().annotate(avgprice=Avg("book__price")).values("name","avgprice")
    # print(ret)



    return HttpResponse("OK")
View Code

 五 F与Q查询

 F:主要用于数值的查询和修改,主要可以用在两个字段的比较个重新计算和赋值。

def update_book(request):
    # models.Book.objects.all().update(price=F('price')-100)
    print(models.Book.objects.filter(comment__gt=F('likeBook')).values_list('title'))
    return HttpResponse('OK')

  Q:在Q里面包含了逻辑运算符,或|  与&  非~。可以使用与多个约束条件的查看

def Q_book(request):
    print(models.Book.objects.filter((Q(price__gt=2000)|Q(title__startswith='方合意'))&Q(pubdate__day='15')).values_list('title'))
    
    return HttpResponse('OK')

 六 用户认证模块(auth)

 导入方式:

from django.contrib.auth.models import User
from django.contrib import auth

 request.user:获取浏览中的字符串,如果登陆,显示的是当前用户登陆的那一个对象,如果没有登陆,显示的是一个匿名用户。

 auth模块:django将多个用户表的信息全部封装到了user这个表的下面,由于表不是我们手动创建的,所以不能直接的操作用户表。

  createsuperuser:创建超级用户

  auth.authenticate:验证方法,将输入的密文加密,然后在到user表里面进行比较认证,返回的是一个对象。

  auth.login(请求对象,登陆对象):创建session对象,并发送到浏览器。

def login(request):
    if request.method == "POST":
        user = request.POST.get("user")
        pwd = request.POST.get("pwd")

        user=auth.authenticate(username=user,password=pwd)
        if user:
            auth.login(request,user) #  request.session["user"]=user.username
            return redirect("/index/")

    return render(request, "login.html")
View Code

  auth.logout:注销,将某个对象session对应的记录全部删除

def log_out(request):

    auth.logout(request)  #  request.session.flush()

    return redirect("/login/")
View Code

 user对象UPI:

  is_authenticated:判断当前的用户是否登陆,放回的是布尔值。

def index(request):
    user=request.user  #  显示当前登录用户对象
    if not user.is_authenticated():
         return redirect("/login/")

    name=request.user.username
    return render(request,"index.html",{"name":name})
View Code

  user.objects.create_user:将传入的password值转成密文,然后在保存到user表里面去。

def reg(request):
    if request.method == "POST":
        user = request.POST.get("user")
        pwd = request.POST.get("pwd")

        user=User.objects.create_user(username=user,password=pwd)
        return redirect("/login/")

    return render(request,"reg.html")
View Code

  check_password:用户注册时使用,检测密码

  set_password:删除原来的密码,将新的值存放到user表里面去。

  save:提交

def setpwd(request):
    if request.method == "POST":
        user = request.POST.get("user")
        pre_pwd = request.POST.get("pre_pwd")
        new_pwd = request.POST.get("new_pwd")
        repeat_pwd = request.POST.get("repeat_pwd")
        user=auth.authenticate(username=user,password=pre_pwd)
        if user:
            user.set_password(new_pwd)
            user.save()
            return redirect("/login/")
    return render(request,"setpwd.html")
View Code
用户认证 

    auth模块--------------auth.user表
    
auth模块API:
     
    user=request.user:  
          if 用户登录状态:    user就是登录对象,否则就是一个匿名对象
          
    验证方法: auth.authenticate(username=user,password=pwd)
    
    插入session记录:auth.login(request,user) #  request.session["user"]=user.username
    
    删除session记录:auth.logout(request)  #  request.session.flush()
        
    user对象的API:
        user.is_authenticated():返回布尔值,该用户登录则为True,否则为False
        
    注册:user=User.objects.create_user(username=user,password=pwd)   
    
    修改密码:
          user.set_password(new_pwd)
          user.save()
          
View Code

 

  

 

推荐阅读