1

这一定很简单,但我无法理解它。我想为 django 模型实例分配一个简单的单字符值。我已经这样做了一百万次,但在这种情况下,它不起作用。

我的模型.py

class MetaInformation(models.Model):
    # part of the class profile ...
    PROFILE_STATUS = (
        ('C', 'Claimed'),
        ('U', 'Unclaimed'),)
    status = models.CharField(_('Profile'), max_length=1,
        choices = PROFILE_STATUS,)

class Profile(MetaInformation):
    # additional attributes
    ...

现在我正在执行Django shell

In [1]: a = Profile.objects.all()
In [2]: a[1].status
Out[2]: u'U'
In [3]: a[1].status = Profile.PROFILE_STATUS[0] # equal to 'C'
In [4]: a[1].save()

我希望结果是

In [14]: a[1].status
Out[14]: u'C'

但 Django 回来了

In [14]: a[1].status
Out[14]: u'U'

为什么无法识别属性的保存或提供任何错误消息?

4

3 回答 3

4

我相信,Bibhas 的答案大部分是正确的。您总是通过 QuerySet 引用数据这一事实Profile.objects.all()是其中最大的部分。再加上 Django 处理切片的方式(如列表索引,但 QuerySet 并不是真正的列表),以及数据库不必考虑记录排序的事实,您就会有一个奇妙的困惑和挫败感。

序列

a = Profile.objects.all()
a[1].status

将产生类似于此的 SQL:

SELECT * FROM user_profile LIMIT 1 OFFSET 1;

为了从表中获取单个值。这里有几件重要的事情:

  1. 仅获取一条记录。这样做是为了提高效率,因为您只要求一条记录,因此不再检索。这也意味着
  2. Django 不会填充 QuerySet 的缓存。这意味着下次您请求 a[1] 时,它将再次访问数据库。
  3. 此查询没有指定顺序。数据库可以自由地以对其最有效的任何顺序返回行,并且该顺序甚至可以在查询之间更改。

我知道 MySQL 通常会按照最近更新的顺序返回记录。因此,仅通过保存记录,它就不太可能处于与以前相同的位置。它可能会移动到结果集的开头或结尾,但不太可能保持在位置 1(返回的第二项)。

为避免这种情况,不要将查询集视为列表。不要直接对使用切片语法检索到的项目执行操作。如果您需要这样做,请将它们存储在一个临时变量中,并对该变量执行所有操作。

这个小小的修改可以避免所有的麻烦:

>>> a = Profile.objects.all()
>>> b = a[1]
>>> b.status
'U'
>>> b.status = 'C'
>>> b.save()
>>> b.status
'C'

或者,如果您需要将其视为列表,则将其设为一个。从 QuerySet 中创建一个列表将完全评估它,并将整个结果集存储在内存中,每次请求时 a[1] 都保证是同一个对象。

>>> a = list(Profile.objects.all()) # Warning -- may be huge if the Profile table is large
>>> a[1].status
'U'
>>> a[1].status = 'C'
>>> a[1].save()
>>> a[1].status
'C'
于 2013-02-07T06:24:05.067 回答
3

看看这个 -

>>> from apps.users.models import Member
>>> members = Member.objects.all()
>>> members[1].user_type
u'C'
>>> members[1].user_type = 'M'
>>> members[1].save()
>>> members[1].user_type
u'C'
>>> m = members[1]
>>> m.user_type
u'C'
>>> m.user_type = 'M'
>>> m.save()
>>> m.user_type
'M'

这是我认为正在发生的事情:该all()方法返回一个 QuerySet。save()从上面的查询中,当您对 QuerySet 中的项目执行更改时,不会将更改提交到数据库。但是,如果您Member单独对一个对象执行此操作,它确实有效。从 Django 文档中 -

QuerySet 是惰性的——创建 QuerySet 的行为不涉及任何数据库活动。您可以整天将过滤器堆叠在一起,并且在评估 QuerySet 之前,Django 不会实际运行查询。...

通常,在您“请求”查询集的结果之前,不会从数据库中获取它们。执行此操作时,将通过访问数据库来评估 QuerySet。

你可以在这里阅读更多。所以save()on 方法members[1]不会触及数据库或从那里读回。而Member对象上的更改被提交到数据库并立即读回。

于 2013-02-07T05:49:38.887 回答
0

PROFILE_STATUS[0]将返回 the('C', 'Claimed')和它的一个元组。你需要PROFILE_STATUS[0][0]

a[1].status = Profile.PROFILE_STATUS[0][0]
于 2013-02-06T20:36:59.433 回答