zoukankan      html  css  js  c++  java
  • Django Query

    Making Qeries

    一旦创建了数据模型,Django就会自动为您提供一个数据库抽象API,允许您创建、检索、更新和删除对象。本文档解释了如何使用这个API。

    The models

    一个class代表一个数据库中的table

    from django.db import models
    
    class Blog(models.Model):
        name = models.CharField(max_length=100)
        tagline = models.TextField()
    
        def __str__(self):
            return self.name
    
    class Author(models.Model):
        name = models.CharField(max_length=200)
        email = models.EmailField()
    
        def __str__(self):
            return self.name
    
    class Entry(models.Model):
        blog = models.ForeignKey(Blog, on_delete=models.CASCADE)
        headline = models.CharField(max_length=255)
        body_text = models.TextField()
        pub_date = models.DateField()
        mod_date = models.DateField()
        authors = models.ManyToManyField(Author)
        n_comments = models.IntegerField()
        n_pingbacks = models.IntegerField()
        rating = models.IntegerField()
    
        def __str__(self):
            return self.headline
    

    Creating objects

    insert a record in table

    >>> from blog.models import Blog
    >>> b = Blog(name='Beatles Blog', tagline='All the latest Beatles news.')
    >>> b.save()
    

    Saving changes to objects

    update a record in table

    >>> b5.name = 'New name'
    >>> b5.save()
    

    Saving ForeignKey and ManyToManyField fields

    保存一对多关系和多对多关系

    # The ForeignKey
    >>> from blog.models import Blog, Entry
    >>> entry = Entry.objects.get(pk=1)
    >>> cheese_blog = Blog.objects.get(name="Cheddar Talk")
    >>> entry.blog = cheese_blog
    >>> entry.save()
    

    # The ManyToManyField
    >>> from blog.models import Author
    >>> joe = Author.objects.create(name="Joe")
    >>> entry.authors.add(joe,...)
    

    Retrieving objects

    检查对象请通过模型类上的Manager构造一个QuerySet。

    每个模型至少拥有一个Manager管理器,默认为objects,可以通过类来获取它。

    >>> Blog.objects
    <django.db.models.manager.Manager object at ...>
    >>> b = Blog(name='Foo', tagline='Bar')
    >>> b.objects
    Traceback:
        ...
    AttributeError: "Manager isn't accessible via Blog instances."
    

    注:管理器只能通过模型类访问,而不是从模型实例访问,以强制“表级”操作和“记录级”操作之间的分离

           管理器是模型的查询集的主要来源。

    Retrieving all objects

    获取所有对象

    >>> all_entries = Entry.objects.all()
    

    Retrieving specific objects with filters

    获取过滤后的对象

    Entry.objects.filter(pub_date__year=2006)
    # is the same as
    Entry.objects.all().filter(pub_date__year=2006)
    

    Chaining filters

    提炼一个QuerySet的结果本身就是一个QuerySet,所以可以将提炼链接在一起。

    >>> Entry.objects.filter(
    ...     headline__startswith='What'
    ... ).exclude(
    ...     pub_date__gte=datetime.date.today()
    ... ).filter(
    ...     pub_date__gte=datetime.date(2005, 1, 30)
    ... )
    

    Filtered QuerySets are unique

    每次提炼一次QuerySet,就会得到一个全新的QeurySet,与之前的QeurySet没有任何联系

    QuerySets are lazy

    提炼QuerySet是惰性查询,本身不涉及到任何数据库查询,所以可以随意拼接它

    Retrieving a single object with get()

    filter()将得到一个QuerySet,如果您想得到一个单独的实体对象 可以使用get()

    >>> one_entry = Entry.objects.get(pk=1)
    

    Retrieving QuerySets with exclude

    排除条件

    Entry.objects.exclude(pub_date__gt=datetime.date(2005, 1, 3), headline='Hello')
    
    # transform SQL
    SELECT ... WHERE NOT (pub_date > '2005-1-3' AND headline = 'Hello')
    

    Limiting QuerySets

    截取QuerySet,与python list类似 不支持使用负数

    >>> Entry.objects.all()[:5]
    
    >>> Entry.objects.all()[5:10]
    
    >>> Entry.objects.all()[:10:2]
    

    to get a single object

    >>> Entry.objects.order_by('headline')[0]
    # is the same as
    >>> Entry.objects.order_by('headline')[0:1].get()
    

    Field lookups

    字段查找是指定SQL WHERE子句的核心内容的方式。它们被指定为QuerySet方法filter()、exclude()和get()的关键字参数。
    基本查找关键字参数的形式为field__lookuptype=value。(这是一个双下划线)。

    __lte

    >>> Entry.objects.filter(pub_date__lte='2006-01-01')
    # transform SQL
    SELECT * FROM blog_entry WHERE pub_date <= '2006-01-01'
    

    _id

    查找中指定的字段必须是模型字段的名称。但是有一个例外,对于ForeignKey,您可以指定以_id为后缀的字段名。在这种情况下,值参数应该包含外部模型主键的原始值。

    >>> Entry.objects.filter(blog_id=4)
    

    exact 默认的,可以忽略

    >>> Blog.objects.get(id__exact=14)
    # is the same as
    >>> Blog.objects.get(id=14)
    # transform SQL
    SELECT ... WHERE id = 14;
    

    iexact 与exact类似,不区分大小写

    contains

    Entry.objects.get(headline__contains='Lennon')
    # transform SQL
    SELECT ... WHERE headline LIKE '%Lennon%';
    

    Lookups that span relationships

    跨域查找

    要跨越关系,只需跨模型使用相关字段的字段名(用双下划线分隔),直到到达您想要的字段为止
    >>> Entry.objects.filter(blog__name='Beatles Blog')

    它也可以反向工作。要引用“反向”关系,只需使用模型的小写名称。

    >>> Blog.objects.filter(entry__headline__contains='Lennon')
    

    spanning multi-value relationships

    跨越多值关系

    Blog.objects.filter(entry__headline__contains='Lennon', entry__pub_date__year=2008)
    # is the same as
    Blog.objects.filter(entry__headline__contains='Lennon').filter(entry__pub_date__year=2008)
    

    Filters can reference fields on the model

    过滤器可以引用模型上的字段 F表达式

    >>> from django.db.models import F
    >>> Entry.objects.filter(n_comments__gt=F('n_pingbacks'))
    
    >>> Entry.objects.filter(n_comments__gt=F('n_pingbacks') * 2)
    
    >>> Entry.objects.filter(rating__lt=F('n_comments') + F('n_pingbacks'))
    
    >>> Entry.objects.filter(authors__name=F('blog__name'))  #跨域
    
    >>> from datetime import timedelta
    >>> Entry.objects.filter(mod_date__gt=F('pub_date') + timedelta(days=3))
    

    The pk lookup shortcut

    为了方便起见,Django提供了一个pk查找快捷方式,它表示“主键”。

    >>> Blog.objects.get(id__exact=14) # Explicit form
    >>> Blog.objects.get(id=14) # __exact is implied
    >>> Blog.objects.get(pk=14) # pk implies id__exact
    

    Caching and QuerySets

    每个QuerySet包含一个缓存,以最小化数据库访问。了解它的工作原理将使您能够编写最高效的代码。
    在新创建的QuerySet中,缓存是空的。当第一次计算QuerySet时(因此发生了数据库查询),Django将查询结果保存在QuerySet的缓存中,并返回显式请求的结果(例如,下一个元素,如果迭代QuerySet的话)。QuerySet的后续评估重用缓存的结果。
    请记住这种缓存行为,因为如果您没有正确使用queryset,它可能会影响您。例如,下面将创建两个查询集,计算它们,并将它们丢弃:

    >>> print([e.headline for e in Entry.objects.all()])
    >>> print([e.pub_date for e in Entry.objects.all()])
    

    这意味着相同的数据库查询将被执行两次,从而使数据库负载加倍。另外,两个列表可能不包含相同的数据库记录,因为在两个请求之间的瞬间,可能添加或删除了一个条目。

    避免这个问题,只需保存QuerySet并重用它

    >>> queryset = Entry.objects.all()
    >>> print([p.headline for p in queryset]) # Evaluate the query set.
    >>> print([p.pub_date for p in queryset]) # Re-use the cache from the evaluation.
    

    When QuerySets are not cached

    查询集并不总是缓存它们的结果。当只计算queryset的一部分时,会检查缓存,但是如果它没有被填充,那么后续查询返回的项就不会被缓存。具体来说,这意味着使用数组切片或索引限制queryset不会填充缓存。

    >>> queryset = Entry.objects.all()
    >>> print(queryset[5]) # Queries the database
    >>> print(queryset[5]) # Queries the database again
    

    但是,如果已经计算了整个queryset的值,那么缓存将被检查:

    >>> queryset = Entry.objects.all()
    >>> [entry for entry in queryset] # Queries the database
    >>> print(queryset[5]) # Uses cache
    >>> print(queryset[5]) # Uses cache
    

    other methods to populate the cache

    >>> [entry for entry in queryset]
    >>> bool(queryset)
    >>> entry in queryset
    >>> list(queryset)
    

    简单地打印queryset不会填充缓存。这是因为对__repr__()的调用只返回整个queryset的一部分。

    Complex lookups with Q objects

    复杂查询使用Q方法

    Q对象(django.db.models.Q)是用来封装关键字参数集合的对象。这些关键字参数在上面的“字段查找”中指定。

    from django.db.models import Q
    Q(question__startswith='What')
    

    Q对象可以使用&和|运算符组合。当对两个Q对象使用运算符时,它会生成一个新的Q对象。
    例如,该语句生成一个Q对象,该对象表示两个“question__startswith”查询的“或”

    Q(question__startswith='Who') | Q(question__startswith='What')
    

    通过将Q对象与&和|运算符组合起来并使用括号分组,您可以组合任意复杂的语句。同样,可以使用~操作符来否定Q对象,允许组合查找,将普通查询和否定(非)查询结合起来

    Q(question__startswith='Who') | ~Q(pub_date__year=2005)
    

    每个获取关键字参数的查找函数(例如filter()、exclude()、get())也可以作为位置(非命名)参数传递一个或多个Q对象。如果您为查找函数提供多个Q对象参数,这些参数将“AND”ed一起使用。例如:

    Poll.objects.get(
        Q(question__startswith='Who'),
        Q(pub_date=date(2005, 5, 2)) | Q(pub_date=date(2005, 5, 6))
    )
    # transform SQL
    SELECT * from polls WHERE question LIKE 'Who%'
    AND (pub_date = '2005-05-02' OR pub_date = '2005-05-06')
    

    查找函数可以混合使用Q对象和关键字参数。为查找函数提供的所有参数(无论是关键字参数还是Q对象)都是“ed”在一起的。但是,如果提供了Q对象,那么它必须先于任何关键字参数的定义。

    # INVALID QUERY. Q() is must be before keyword arguments
    Poll.objects.get(
        question__startswith='Who',
        Q(pub_date=date(2005, 5, 2)) | Q(pub_date=date(2005, 5, 6))
    )
    

    Comparing objects

    要比较两个模型实例,只需使用标准的Python比较运算符:==。在幕后,比较两个模型的主要关键值。

    >>> some_entry == other_entry
    >>> some_entry.id == other_entry.id
    

    Deleting objects

    >>> e.delete()
    
    >>> Entry.objects.filter(pub_date__year=2005).delete()
    (5, {'webapp.Entry': 5})
    

    当Django删除一个对象时,默认情况下它会模拟SQL约束在DELETE CASCADE上的行为——换句话说,任何有外键指向要删除的对象的对象都将被删除。这个级联行为可以通过对ForeignKey的on_delete参数进行定制。

    b = Blog.objects.get(pk=1)
    # This will delete the Blog and all of its Entry objects.
    b.delete()
    

    注意,delete()是唯一没有在管理器本身上公开的QuerySet方法。这是一种安全机制,可以防止您意外地请求Entry.objects.delete()和删除所有条目。如果您确实想删除所有对象,那么您必须显式地请求完整的查询集:

    Entry.objects.all().delete()
    

    Copying model instances

    blog = Blog(name='My blog', tagline='Blogging is easy')
    blog.save() # blog.pk == 1
    
    blog.pk = None
    blog.save() # blog.pk == 2
    

    如果使用继承,事情会变得更加复杂。考虑博客的一个子类

    class ThemeBlog(Blog):
        theme = models.CharField(max_length=200)
    
    django_blog = ThemeBlog(name='Django', tagline='Django is easy', theme='python')
    django_blog.save() # django_blog.pk == 3
    

    django_blog.pk = None
    django_blog.id = None
    django_blog.save() # django_blog.pk == 4
    

    这个过程不会复制不属于模型数据库表的关系。例如,Entry有很多个要编写的字段。复制条目后,必须为新Entry设置多对多关系:

    entry = Entry.objects.all()[0] # some previous entry
    old_authors = entry.authors.all()
    entry.pk = None
    entry.save()
    entry.authors.set(old_authors)
    

    对于一个OneToOneField,必须复制相关对象并将其分配给新对象的字段,以避免违反一对一唯一约束。例如,假设Entry已经复制到上面:

    detail = EntryDetail.objects.all()[0]
    detail.pk = None
    detail.entry = entry
    detail.save()
    

    Updating multiple objects at once

    有时,您希望为QuerySet中的所有对象将字段设置为特定值。您可以使用update()方法来做到这一点。

    # Update all the headlines with pub_date in 2007.
    Entry.objects.filter(pub_date__year=2007).update(headline='Everything is the same')
    

    >>> Entry.objects.all().update(n_pingbacks=F('n_pingbacks') + 1)
    

    注:update操作中的F()不能进行join连接 否则会报错FieldError

    当建立了一个关系在模型中(ForeignKey,OneToOneField,ManyToManyField),该模型的实例将具有方便的API来访问相关对象。

    >>> e = Entry.objects.get(id=2)
    >>> e.blog # Returns the related Blog object.
    

    >>> e = Entry.objects.get(id=2)
    >>> e.blog = some_blog
    >>> e.save()
    

    >>> e = Entry.objects.get(id=2)
    >>> e.blog = None
    >>> e.save() # "UPDATE blog_entry SET blog_id = NULL ...;"
    

    Following relationships “backward”

    如果模型有一个ForeignKey,那么外键模型的实例将访问返回第一个模型的所有实例的管理器。默认情况下,该管理器名为FOO_set,其中FOO是源模型名,小写。此管理器返回QuerySets,可以按照上面“检索对象”一节的描述对其进行过滤和操作。

    >>> b = Blog.objects.get(id=1)
    >>> b.entry_set.all() # Returns all Entry objects related to Blog.
    
    # b.entry_set is a Manager that returns QuerySets.
    >>> b.entry_set.filter(headline__contains='Lennon')
    >>> b.entry_set.count()
    

    通过在ForeignKey定义中设置related_name参数,可以覆盖FOO_set名称。

    Using a custom reverse manager

    默认情况下,用于反向关系的RelatedManager是该模型的默认管理器的子类。如果您想为给定的查询指定不同的管理器,可以使用以下语法

    from django.db import models
    
    class Entry(models.Model):
        #...
        objects = models.Manager()  # Default Manager
        entries = EntryManager()    # Custom Manager
    
    b = Blog.objects.get(id=1)
    b.entry_set(manager='entries').all()
    

    如果EntryManager在其get_queryset()方法中执行默认过滤,则该过滤将应用于all()调用。
    当然,指定一个定制的反向管理器还可以调用它的定制方法:

    b.entry_set(manager='entries').is_published()
    

    add(obj1, obj2, ...) 添加模型实例

    create(**kwargs) 创建一个新的模型对象

    remove(obj1, obj2, ...) 从相关对象集中删除指定的模型对象

    clear() 清空所有对象

    set(objs) 替换相关对象集。

    Many-to-many relationships

    多对多关系的两端都可以自动访问另一端的API。该API的工作方式类似于上面的“向后”一对多关系。
    一个区别是属性命名:定义ManyToManyField的模型使用的是该字段本身的属性名,而“反转”模型使用的是原始模型的小写模型名加上“_set”(就像反向一对多关系一样)。

    e = Entry.objects.get(id=3)
    e.authors.all() # Returns all Author objects for this Entry.
    e.authors.count()
    e.authors.filter(name__contains='John')
    
    a = Author.objects.get(id=5)
    a.entry_set.all() # Returns all Entry objects for this Author.
    

    与ForeignKey一样,ManyToManyField可以指定related_name。在上面的例子中,如果条目中的ManyToManyField指定了related_name='entries',那么每个Author实例都有一个entries属性,而不是entry_set。
    与一对多关系的另一个区别是,除了模型实例之外,多对多关系上的add()、set()和remove()方法还接受主键值。例如,如果e1和e2是条目实例,那么这些set()调用的工作方式相同:

    a = Author.objects.get(id=5)
    a.entry_set.set([e1, e2])
    a.entry_set.set([e1.pk, e2.pk])
    

    One-to-one relationships

    一对一关系非常类似于多对一关系。如果您在模型上定义了一个OneToOneField,该模型的实例将通过模型的简单属性访问相关对象。

    class EntryDetail(models.Model):
        entry = models.OneToOneField(Entry, on_delete=models.CASCADE)
        details = models.TextField()
    
    ed = EntryDetail.objects.get(id=2)
    ed.entry # Returns the related Entry object.
    

    区别在于“反向”查询。一对一关系中的相关模型也可以访问Manager对象,但该管理器表示单个对象,而不是对象的集合:

    e = Entry.objects.get(id=2)
    e.entrydetail # returns the related EntryDetail object
    

    如果没有给这个关系分配对象,Django将会引发一个DoesNotExist异常。

    实例可以以与分配正向关系相同的方式分配反向关系

    e.entrydetail = ed
    

    Performing raw SQL queries

    class Person(models.Model):
        first_name = models.CharField(...)
        last_name = models.CharField(...)
        birth_date = models.DateField(...)
    
    
    >>> for p in Person.objects.raw('SELECT * FROM myapp_person'):
    ...     print(p)
    John Smith
    Jane Jones
    

    raw()自动将查询中的字段映射到模型中的字段。与查询字段的顺序无关,下列的例子相同

    >>> Person.objects.raw('SELECT id, first_name, last_name, birth_date FROM myapp_person')
    ...
    >>> Person.objects.raw('SELECT last_name, birth_date, first_name, id FROM myapp_person')
    ...
    

    匹配是通过名称来完成的。这意味着您可以使用SQL AS子句将查询中的字段映射到建模字段。所以如果你有另外一个表里面有Person数据,你可以很容易地把它映射到Person实例

    >>> Person.objects.raw('''SELECT first AS first_name,
    ...                              last AS last_name,
    ...                              bd AS birth_date,
    ...                              pk AS id,
    ...                       FROM some_other_table''')
    

    raw()支持索引,所以如果你只需要第一个结果,你可以写:

    >>> first_person = Person.objects.raw('SELECT * FROM myapp_person')[0]
    

    然而,索引和切片并不是在数据库级别执行的。如果您的数据库中有大量Person对象,那么在SQL级别上限制查询会更有效:

    >>> first_person = Person.objects.raw('SELECT * FROM myapp_person LIMIT 1')[0]
    

    # Deferring model fields

    这个查询返回的Person对象将是deferred model实例(参见defer())。这意味着查询中省略的字段将根据需要加载。例如

    您还可以执行包含模型中未定义的字段的查询。例如,我们可以使用PostgreSQL 's age()函数得到数据库计算出的年龄人员列表:

    >>> people = Person.objects.raw('SELECT *, age(birth_date) AS age FROM myapp_person')
    >>> for p in people:
    ...     print("%s is %s." % (p.first_name, p.age))
    John is 37.
    Jane is 42.
    

    #Passing parameters into raw()

    >>> lname = 'Doe'
    >>> Person.objects.raw('SELECT * FROM myapp_person WHERE last_name = %s', [lname])
    

        * 不要对原始查询使用字符串格式,也不要在SQL字符串中引用占位符!这有sql注入风险

    Executing custom SQL directly

    有时候,甚至Manager.raw()也不够:您可能需要执行不能清晰映射到模型的查询,或者直接执行更新、插入或删除查询。
    在这些情况下,您总是可以直接访问数据库,完全围绕模型层路由。
    对象django.db。连接表示默认的数据库连接。要使用数据库连接,请调用connection.cursor()来获得一个游标对象。然后,叫游标。执行(sql, [params])以执行sql和cursor.fetchone()或cursor.fetchall()以返回结果行。

    如果您在MySQL上执行查询,请注意MySQL的静默类型强制可能会在混合类型时导致意外结果。如果查询字符串类型列,但使用整数值,MySQL将强制表中所有值的类型为整数,然后再执行比较。例如,如果表中包含值“abc”、“def”,并且查询mycolumn=0的位置,那么这两行都将匹配。为了防止这种情况,在使用查询中的值之前执行正确的类型转换。

  • 相关阅读:
    pycharm 代码块缩进、左移
    os.popen(cmd) .read() 获取执行后的结果且带有换行符
    数据分析基础路线了解
    Jupyter中python3之numpy练习
    Mysql数据库操作命令行小结
    Mysql配置主从同步的基本步骤
    百度翻译爬虫-Web版(自动生成sign)
    windows中的常用Dos命令
    Cookie安全隐患DOM演示
    bash漏洞技术层面分析(cgi-bin)
  • 原文地址:https://www.cnblogs.com/LTEF/p/9619473.html
Copyright © 2011-2022 走看看