美文网首页
Django ORM

Django ORM

作者: whenitsallover | 来源:发表于2018-02-11 20:01 被阅读0次

    ORM

    映射关系
    表名 <-------> 类名

    字段 <-------> 属性

    表记录 <------->类实例对象

    models.py

    class Author(models.Model):
        name = models.CharField(max_length=32)
        age = models.IntegerField()
    
        # 与AuthorDetail建立一对一的关系
        authorDetail = models.OneToOneField(to="AuthorDetail")
    
    
    class AuthorDetail(models.Model):
        birthday = models.DateField()
        telephone = models.BigIntegerField()
        addr = models.CharField(max_length=64)
    
    
    class Publish(models.Model):
        name = models.CharField(max_length=32)
        city = models.CharField(max_length=32)
        email = models.EmailField()
    
    
    class Book(models.Model):
        title = models.CharField(max_length=32)
        publishDate = models.DateField()
        price = models.DecimalField(max_digits=5, decimal_places=2)
        # 与Publish建立一对多的关系,外键字段建立在多的一方
        publish = models.ForeignKey(to="Publish")
        # 与Author表建立多对多的关系,ManyToManyField可以建在两个模型中的任意一个,自动创建第三张表
        authors = models.ManyToManyField(to='Author')
    

    注意事项:
    1、id字段是自动添加的
    2、对于外键字段,Django会在字段名上添加‘_id’来创建数据库中的列名
    3、null=True就是数据库相应字段中可为空,blank=True是指在django admin中可为空。
    3、choices:由二元组组成的一个可迭代对象(如列表或元组),用来给字段提供选择项。如果设置了choices,默认表达将是一个选择框而不是标准的文本框,而且这个选择框的选项就是choices中的选项。
    这是一个关于 choices 列表的例子:

    YEAR_IN_SCHOOL_CHOICES = (
        ('FR', 'Freshman'),
        ('SO', 'Sophomore'),
        ('JR', 'Junior'),
        ('SR', 'Senior'),
        ('GR', 'Graduate'),
    )
    

    每个元组中的第一个元素,是存储在数据库中的值;第二个元素是在管理界面或 ModelChoiceField 中用作显示的内容。 在一个给定的 model 类的实例中,想得到某个 choices 字段的显示值,就调用 get_FOO_display 方法(这里的 FOO 就是 choices 字段的名称 )。例如:

    from django.db import models
    
    class Person(models.Model):
        SHIRT_SIZES = (
            ('S', 'Small'),
            ('M', 'Medium'),
            ('L', 'Large'),
        )
        name = models.CharField(max_length=60)
        shirt_size = models.CharField(max_length=1, choices=SHIRT_SIZES)
    
    
    >>> p = Person(name="Fred Flintstone", shirt_size="L")
    >>> p.save()
    >>> p.shirt_size
    'L'
    >>> p.get_shirt_size_display()
    'Large'   
    

    添加表记录

    普通字段
    方式一
    publish_obj = Publish(name='Parker',city='北京',email='parker@live.com')
    publish_obj.save()
    方式二
    models.Publish.objects.create(name='Alicce',city='上海',email='Alice@live.com')
    
    外键字段
    方式一、
    publish_obj = models.Publish.objects.get(id=1)
    models.Book.objects.create(title='The Great Expectations',publishDate='2001-12-10',price=32,publish=publish_obj)
    方式二、
    models.Book.objects.create(title='金瓶梅',publishDate='2014-12-13',price=22,publish_id=2)
    
    

    多对多字段

    book_obj = models.Book.objects.create(title='Jean of Arc',publishDate='2013-12-12',publish_id=1,price=22.13)
    Jack = models.Author.objects.create(name='Jack',age=13,authorDetail_id=3)
    Ben = models.Author.objects.create(name='Ben',age=13,authorDetail_id=4)
    book_obj.authors.add(Jack,Ben)
    

    解除关系

    book_obj.authors.remove()     # 将某个特定的对象从被关联对象集合中去除。    ======   book_obj.authors.remove(*[])
    book_obj.authors.clear()       #清空被关联对象集合。
    

    查询表记录

    1.  all()
    2.  filter(**kwargs)
    3.  get(**kwargs)
    4.  exclude(**kwargs)
    5.  values(*fields)    ##返回一个可迭代的字典序列
    6.  values_list(*fields)   ##返回一个元组序列
    7.  order_by(*fields) ##对查询结果排序
    8.  reverse() ##对查询结果反向排序
    9.  distinct()  
    10. count()
    11. first()
    12. last()
    13. exists()
    #双下划线之单表查询
    
    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 
    

    基于对象的跨表查询

    一对多查询

    正向查询按字段(publish)

    #查询id=1的书籍插板社所在城市
    book_obj = models.Book.objects.filter(id=1).first()
    print(book_obj.publish) #Publish object
    print(book_obj.publish.name)
    

    反向查询案表名+下划线set

    # 查询Parker出版社出版的所有书籍
    publishes = models.Publish.objects.filter(name='Parker').first() #拿到出版社对象
    books = publishes.book_set.all()  ##拿到book queryset
    for book in books:
        print(book.title)
    

    一对一查询

    正向查询(按字段 authorDetail)

    # 查询Jack作者的手机号
    author_egon=Author.objects.get(name="egon")
    print(author_egon.authorDetail.telephone)
    

    反向查询(按表名:author)

    查询所有住址在Parker的作者姓名
    authorDetails = models.AuthorDetail.objects.filter(addr='123123123')
    for authorDetail in authorDetails:
        print(authorDetail.author.name) #按author表名称
    

    多对多查询(Author与Book)

    正向查询(按字段:authors)
    #查询金瓶梅的作者名称
    book_obj = models.Book.objects.filter(title='Jean of Arc').first()
    authors = book_obj.authors.all()
    for author in authors:
        print(author.name)
    
    反向查询(按表明+下划线set)
    查询Jack出版过的书籍名称
    author = models.Author.objects.filter(name='Jack').first()
    books = author.book_set.all()
    for book in books:
        print(book.title)
    

    Reminder!

    你可以通过在 ForeignKey() 和ManyToManyField的定义中设置 related_name 的值来覆写 表名_set 的名称。

    基于双下划线的跨表查询

    Key:正向查询按字段,反向查询按表名。

    # 练习1:  查询Parker出版社出版过的所有书籍的名字与价格(一对多)
    
        # 正向查询 按字段:publish
    
        queryResult=Book.objects
                .filter(publish__name="Parker")
                .values_list("title","price")
    
        # 反向查询 按表名:book
    
        queryResult=Publish.objects
                  .filter(name="Parker")
                  .values_list("book__title","book__price")
    
    
    
    # 练习2: 查询Jack出过的所有书籍的名字(多对多)
    
        # 正向查询 按字段:authors:
        queryResult=Book.objects
                .filter(authors__name="Jack")
                .values_list("title")
    
        # 反向查询 按表名:book
        queryResult=Author.objects
                  .filter(name="Jack")
                  .values_list("book__title","book__price")
    
    
    # 练习3: 查询Parker版社出版过的所有书籍的名字以及作者的姓名
    
    
        # 正向查询
        queryResult=Book.objects
                .filter(publish__name="Parker")
                .values_list("title","authors__name")
        # 反向查询
        queryResult=Publish.objects
                  .filter(name="Parker")
                  .values_list("book__title","book__authors__age","book__authors__name")
    
    
    # 练习4: 手机号以151开头的作者出版过的所有书籍名称以及出版社名称
    
        queryResult=Book.objects
                .filter(authors__authorDetail__telephone__regex="151")
                .values_list("title","publish__name")
    
    注意:

    反向查询时,如果定义了related_name ,则用related_name替换表名,例如: publish = ForeignKey(Blog, related_name='bookList'):

    聚合查询&分组查询

    聚合:aggregate(args,*kwargs)
    # 计算所有图书的平均价格
    from django.db.models import Avg 
    models.Book.objects.all().aggregate(Avg('price'))
    

    以下是aggregate源码:

        def aggregate(self, *args, **kwargs):
            """
            Returns a dictionary containing the calculations (aggregation)
            over the current queryset
    
            If args is present the expression is passed as a kwarg using
            the Aggregate object's default alias.
            """
            if self.query.distinct_fields:
                raise NotImplementedError("aggregate() + distinct(fields) not implemented.")
            for arg in args:
                # The default_alias property may raise a TypeError, so we use
                # a try/except construct rather than hasattr in order to remain
                # consistent between PY2 and PY3 (hasattr would swallow
                # the TypeError on PY2).
                try:
                    arg.default_alias
                except (AttributeError, TypeError):
                    raise TypeError("Complex aggregates require an alias")
                kwargs[arg.default_alias] = arg
    
            query = self.query.clone()
            for (alias, aggregate_expr) in kwargs.items():
                query.add_annotation(aggregate_expr, alias, is_summary=True)
                if not query.annotations[alias].contains_aggregate:
                    raise TypeError("%s is not an aggregate expression" % alias)
            return query.get_aggregation(self.db, kwargs.keys())
    

    aggregate()是queryset的一个终止子句,返回一个包含一些键值对的字典。键的名称是聚合值的标识符,值是计算出来的聚合值。键的名称是按照字段和聚合函数名称自动生成的别名。此名称也可以人为指定。

     models.Book.objects.aggregate(average_price=Avg('price'))
    

    当然,也可以向aggregate()子句中添加另一个参数。例如:

    from django.db.models import Avg, Max, Min
    models.Book.objects.aggregate(Avg('price'), Max('price'), Min('price'))
    
    分组annotate()

    为调用的queryset中每一个对象都生成的独立的统计值(统计方法用聚合函数)。

    books=Book.objects.annotate(authorsNum=Count('authors'))
    for bookin books:
        print(book_obj.title,book_obj.authorsNum)
    

    以下是annotate源码

        def annotate(self, *args, **kwargs):
            """
            Return a query set in which the returned objects have been annotated
            with extra data or aggregations.
            """
            annotations = OrderedDict()  # To preserve ordering of args
            for arg in args:
                # The default_alias property may raise a TypeError, so we use
                # a try/except construct rather than hasattr in order to remain
                # consistent between PY2 and PY3 (hasattr would swallow
                # the TypeError on PY2).
                try:
                    if arg.default_alias in kwargs:
                        raise ValueError("The named annotation '%s' conflicts with the "
                                         "default name for another annotation."
                                         % arg.default_alias)
                except (AttributeError, TypeError):
                    raise TypeError("Complex annotations require an alias")
                annotations[arg.default_alias] = arg
            annotations.update(kwargs)
    
            clone = self._clone()
            names = self._fields
            if names is None:
                names = {f.name for f in self.model._meta.get_fields()}
    
            for alias, annotation in annotations.items():
                if alias in names:
                    raise ValueError("The annotation '%s' conflicts with a field on "
                                     "the model." % alias)
                clone.query.add_annotation(annotation, alias, is_summary=False)
    
            for alias, annotation in clone.query.annotations.items():
                if alias in annotations and annotation.contains_aggregate:
                    if clone._fields is None:
                        clone.query.group_by = True
                    else:
                        clone.query.set_group_by()
                    break
    
            return clone
    
    annotate()返回的是一个queryset集合
    统计每一个出版社的最便宜的书
    
    publishes=models.Publish.objects.annotate(MinPrice=Min('book__price'))
    for book in publishes:
        print(book.name,book.MinPrice)
    

    也可以用values_list:

    法一:
    queryResult= Publish.objects
                .annotate(MinPrice=Min("book__price"))
                .values_list("name","MinPrice")
    print(queryResult)
    
    

    法二:

    queryResult=Book.objects.values("publish__name").annotate(MinPrice=Min('price')) # 思考: if 有一个出版社没有出版过书会怎样?
    

    按author表的所有字段 group by

    queryResult=Author.objects.annotate(SumPrice=Sum("book__price")).values_list("name","SumPrice")
    print(queryResult)
    

    按authors__name group by

    print(queryResult2)
    

    Keep in mind:

    基于双下划线跨表查询,正向按字段,反向按表名。

    Django ORM中批量操作

    models.py

    from django.db import models
    
    class Product(models.Model):
        name = models.CharField(max_length=200)
        price = models.DecimalField(max_digits=10, decimal_places=2)
    
    批量插入数据

    批量插入数据的时候,首先要创建一个对象的列表,然后调用bulk_create()方法,一次将列表中的数据插入到数据库中。

    incoming_product_list_waiting_to_insert = list()
    for x in range(10):
        incoming_product_list_waiting_to_insert.append(Product(name='product name ' + str(x), price=x))
    Product.objects.bulk_create(product_list_to_insert)
    
    批量更新数据

    批量更新数据时,先进行数据过滤,然后再调用update方法进行一次性更新。类似于update...from...的SQL语句。

    Product.objects.filter(name__contains='name').update(name='new name')
    
    批量删除数据

    批量删除数据时,与update类似,先进行数据过滤,然后再调用delete方法进行一次性进行删除。下面的语句生成类似delete...from...的SQL语句。

    Product.objects.filter(name__contains='name query').delete()
    

    相关文章

      网友评论

          本文标题:Django ORM

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