【Django】Django ORM - CRUD

Posted by 西维蜀黍 on 2019-11-10, Last Modified on 2021-09-21

Retrieve

Retrieving Objects - get()

To retrieve objects from your database, construct a QuerySet via a Manager on your model class.

A QuerySet represents a collection of objects from your database. It can have zero, one or many filters. Filters narrow down the query results based on the given parameters. In SQL terms, a QuerySet equates to a SELECT statement, and a filter is a limiting clause such as WHERE or LIMIT.

You get a QuerySet by using your model’s Manager. Each model has at least one Manager, and it’s called objects by default. Access it directly via the model class, like so:

# Import the models we created from our "news" app
>>> from news.models import Article, Reporter

>>> 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."

get() raises MultipleObjectsReturned if more than one object was found. The MultipleObjectsReturned exception is an attribute of the model class.

get() raises a DoesNotExist exception if an object wasn’t found for the given parameters. This exception is an attribute of the model class. Example:

Entry.objects.get(id='foo') # raises Entry.DoesNotExist

The DoesNotExist exception inherits from django.core.exceptions.ObjectDoesNotExist, so you can target multiple DoesNotExist exceptions. Example:

from django.core.exceptions import ObjectDoesNotExist
try:
    e = Entry.objects.get(id=3)
    b = Blog.objects.get(id=1)
except ObjectDoesNotExist:
    print("Either the entry or blog doesn't exist.")

Retrieving all objects

The simplest way to retrieve objects from a table is to get all of them. To do this, use the all() method on a Manager:

>>> all_entries = eporter.objects.all()
<QuerySet [...]>

The all() method returns a QuerySet of all the objects in the database.

筛选对象 - filter()

filter()Returns a new QuerySet containing objects that match the given lookup parameters.

The lookup parameters (**kwargs) should be in the format described in Field lookups below. Multiple parameters are joined via AND in the underlying SQL statement.

If you need to execute more complex queries (for example, queries with OR statements), you can use Q objects.

# Django provides a rich database lookup API.
>>> Reporter.objects.get(id=1)
<Reporter: John Smith>
>>> Reporter.objects.get(full_name__startswith='John')
<Reporter: John Smith>
>>> Reporter.objects.get(full_name__contains='mith')
<Reporter: John Smith>
>>> Reporter.objects.get(id=2)
Traceback (most recent call last):
    ...
DoesNotExist: Reporter matching query does not exist.

QuerySets的很大一部分功能是对它们进行筛选。 譬如,我们想要发现所有都由用户ola编写的文章。 我们将使用 filter,而不是 allPost.objects.all()。 我们需要在括号中申明哪些条件,以在我们的 queryset 结果集中包含一篇博客文章。 在我们的情况是 author,它等于 me。 把它写在 Django 的方式是: author = me。 现在我们的代码段如下所示:

>>> Post.objects.filter(author=me)
<QuerySet [<Post: Sample title>, <Post: Post number 2>, <Post: My 3rd post!>, <Post: 4th title of post>]>

或者,也许我们想看到包含在 title 字段标题的所有帖子吗?

>>> Post.objects.filter(title__contains='title')
<QuerySet [<Post: Sample title>, <Post: 4th title of post>]>

注意,在titlecontains 之间有两个下划线字符 (_)。 Django 的 ORM 使用此语法来分隔字段名称 (“title”) 和操作或筛选器 (“contains”)。 如果您只使用一个下划线,您将收到类似"FieldError: 无法解析关键字 title_contains"的错误。

你也可以获取一个所有已发布文章的列表。我们通过筛选所有含published_date为过去时间的文章来实现这个目的:

>>> from django.utils import timezone 
>>> Post.objects.filter(published_date__lte=timezone.now())[]

过滤条件

  • __exact:精确等于, like ‘aaa’
  • __iexact:精确等于,忽略大小写,ilike ‘aaa’
  • __contains:包含, like ‘%aaa%’
  • __icontains:包含,忽略大小写,ilike ‘%aaa%’,但是对于sqlite来说,contains的作用效果等同于icontains。
  • __gt:大于
  • __gte:大于等于
  • __lt:小于
  • __lte:小于等于
  • __in:存在于一个list范围内
  • __startswith:以…开头
  • __istartswith:以…开头,忽略大小写
  • __endswith:以…结尾
  • __iendswith:以…结尾,忽略大小写
  • __range:在…范围内
  • __year:日期字段的年份
  • __month:日期字段的月份
  • __day:日期字段的日
  • __isnull:为True或者False

Creating Objects

To represent database-table data in Python objects, Django uses an intuitive system: A model class represents a database table, and an instance of that class represents a particular record in the database table.

To create an object, instantiate it using keyword arguments to the model class, then call save() to save it to the database.

Assuming models live in a file mysite/blog/models.py, here’s an example:

>>> from news.models import Article, Reporter

# Create a new Reporter.
>>> r = Reporter(full_name='John Smith')

# Save the object into the database. You have to call save() explicitly.
>>> r.save()

# Now it has an ID.
>>> r.id
1

This performs an INSERT SQL statement behind the scenes. Django doesn’t hit the database until you explicitly call save().

The save() method has no return value.


总结

增加一条数据其实有两种等价(equivalent)的方法。

方法 1

>>> Post.objects.create(author=me, title='Sample title', text='Test')

方法 2

# Create an article.
>>> from datetime import date
>>> a = Article(pub_date=date.today(), headline='Django is cool',
...     content='Yeah.', reporter=r)
>>> a.save()

Update Objects

Updating and Deleting Results

Querysets can be used to update rows instead of retrieving them. Simply call update() on a normal (not sliced or specially altered) queryset.

>>> richards = Employee.objects.filter(first_name="Dick", age__gte=20)
>>> richards.update(first_name="Richard")

The update() function will return the number of matched rows from the filter(). It does not necessarily represent the number of rows actually updated by the query, as it’s possible certain values already matched the new value in the update().

Deleting results uses a similar queryset API, which takes the form of the delete() function:

>>> richards = Employee.objects.filter(first_name="Dick", age__gte=20)
>>> richards.delete()

Unlike update(), delete() does not return anything. It is also the only queryset method to not be directly exposed in the manager (i.e. the objects attribute of a model). If you really want to clear out an entire table, you must use the following:

>>> Employee.objects.all().delete()

Deleting is inherently a risky action (hence the inconvenient API), so do this with caution.

更新数据的两种方法

假如我们的表结构是这样的

class User(models.Model):
    username = models.CharField(max_length=255, unique=True, verbose_name='用户名')
    is_active = models.BooleanField(default=False, verbose_name='激活状态')

那么我们修改用户名和状态可以使用如下两种方法:

方法 1

User.objects.filter(id=1).update(username='nick',is_active=True)

方法2

_t = User.objects.get(id=1)
_t.username='nick'
_t.is_active=True
_t.save()

方法一适合更新一批数据,类似于mysql语句update user set username='nick' where id = 1

方法二适合更新一条数据,也只能更新一条数据,当只有一条数据更新时推荐使用此方法,

具有auto_now属性字段的更新

我们通常会给表添加三个默认字段

  • 自增ID,这个django已经默认加了。
  • 创建时间,用来标识这条记录的创建时间,具有auto_now_add属性,创建记录时会自动填充当前时间到此字段
  • 修改时间,用来标识这条记录最后一次的修改时间,具有auto_now属性,当记录发生变化时填充当前时间到此字段

就像下边这样的表结构

class User(models.Model):
    create_time = models.DateTimeField(auto_now_add=True, verbose_name='创建时间')
    update_time = models.DateTimeField(auto_now=True, verbose_name='更新时间')
    username = models.CharField(max_length=255, unique=True, verbose_name='用户名')
    is_active = models.BooleanField(default=False, verbose_name='激活状态')

当表有字段具有auto_now属性且你希望他能自动更新时,必须使用上边方法二的更新,不然auto_now字段不会更新,也就是:

_t = User.objects.get(id=1)
_t.username='nick'
_t.is_active=True
_t.save()

删除数据的两种情况

单个删除

user_obj.delete()

批量删除

user_qs = User.objects.filter(sex=1)
user_qs.delete()

Reference