Python Django:在视图中,最好是为对象添加属性还是创建数据字典?

Fur*_*tor 6 python django dictionary properties object

在这种情况下,我的模型并不重要,我认为这是一个基本的Python问题.

假设我有一个项目的查询集,我想计算每个要在模板中显示的东西.

在我看来,我可以创建一个对象列表,对于每个对象,我可以在该对象上设置一个属性进行计算,然后我可以在模板中显示它.或者我可以创建一个字典列表,只获取我需要在每个字典中显示的字段以及计算字段.对于性能和一般实践来说哪个更好?

一个过于简化的示例,为了清晰起见(我知道我可以从模板中调用getAge(),我真正计算的是更复杂的,对于性能,我想在视图代码中进行计算):

models.py:

class Person(models.Model):
    first_name = ...
    last_name = ...
    date_of_birth = ...
    .
    .
    .
    def getAge(self):
        return ... # return the calculated years since date_of_birth
Run Code Online (Sandbox Code Playgroud)

views.py:

def method1_object_property(request):
    people = Person.objects.all()
    for p in people:
        p.age = p.getAge()
    return render_to_response('template.htm', {'people': people})

def method2_dictionary(request):
    people = Person.objects.all()
    data = list()
    for p in people:
        row = dict()
        row['first_name'] = p.first_name
        row['last_name'] = p.last_name
        row['age'] = p.getAge()
        data.append(row)
    return render_to_response('template.htm', {'people': data})
Run Code Online (Sandbox Code Playgroud)

template.htm:

<ul>
    {% for p in people %}
        {{ p.first_name }} {{ p.last_name }} (Age: {{ p.age }})
    {% endfor %}
</ul>
Run Code Online (Sandbox Code Playgroud)

到目前为止,这两种方法都可以正常工作,我只是好奇首选方法是什么以及为什么.是否存在使用对象点属性方法(object.new_field ='some_detail')将新字段动态分配给内存中现有对象的性能问题?

更新:

是的,我知道在我的例子中我可以从模板调用getAge(),是的,这是方法的不正确的命名标准,应该是带有下划线的小写.我认为我的例子太简单了,而且让我真正想知道的事情蒙上阴影.

将信息添加到我想要在视图中显示的对象的最佳方法是什么,该视图不是模型层的一部分.假设我得到一个Person对象的QuerySet,并想要计算他们在过去30,60和90天登录我网站的次数.我想动态为每个Person对象创建三个"属性".我可以在视图中设置它

for p in people:
    p.last_30 = Login.objects.filter(person=p, login_date__gt=date.today()-timedelta(days=30))
    p.last_60 = Login.objects.filter(person=p, login_date__gt=date.today()-timedelta(days=60))
    p.last_90 = Login.objects.filter(person=p, login_date__gt=date.today()-timedelta(days=90))
Run Code Online (Sandbox Code Playgroud)

然后在我的模板中,我可以显示那些"属性".我只是想确保我没有违反某些Python标准或欺骗系统.或者,我可以将这些其他查找存储在字典中,其中对象在一个键/对中,并且各个细节在单独的键中.在视图中这是一个更多的工作,但我很好奇是否更好的性能或标准的合规性这样做?

对不起,如果我的原始问题不够清楚,或者我的例子增加了混乱.

Ane*_*pic 8

绝对方法1.

方法2毫无意义,您可以直接在模板中迭代查询集,无需在视图中构建中间的"词典列表".例如,你可以这样做:

def method2_dictionary(request):
    people = Person.objects.all()
    return render_to_response('template.htm', {'people': people})
Run Code Online (Sandbox Code Playgroud)

在您的模板中:

{% for p in people %}
    {{ p.first_name }}
    etc
{% endfor %}
Run Code Online (Sandbox Code Playgroud)

回到方法1 ......

这个:p.age = p.getAge()也没有意义,你可以直接调用你模板中的方法{{ p.getAge }}(只要你的方法不带参数),请看这里的文档:https:
//docs.djangoproject.com/en/dev/topics/templates/ #访问法通话

请注意,在Python中,我们通常更喜欢使用'带下划线的小写'作为方法名称,例如def get_age(self){{ p.get_age }}
(请参阅官方的'PEP8'样式指南,在这里http://www.python.org/dev/peps/pep- 0008/#function-names)

如果你的get_age方法没有副作用并且不带任何参数,你可能想让它成为propertyPython的一种方法,即你可以在没有括号的情况下访问getter方法.

在这种情况下,将它命名为age:

@property
def age(self):
    return ... # return the calculated years since date_of_birth
Run Code Online (Sandbox Code Playgroud)

并在您的模板中:

{% for p in people %}
    {{ p.first_name }}
    {{ p.age }}
    etc
{% endfor %}
Run Code Online (Sandbox Code Playgroud)

有关Python属性的更多信息,请参见此处:http:
//docs.python.org/2/library/functions.html#property

在这个SO问题中的更多信息:
关于如何在python中使用属性功能的真实世界示例?

UPDATE

参考您更新的问题...作为样式问题,我仍然会last_30在模型上制作这些(etc)方法,而不是在视图代码中的每个模型实例上添加ad hoc属性.

从性能的角度来看,在大多数现实世界中,方法查找与字典等的内存,处理时间等的差异是微不足道的......到目前为止,这种代码中最大的性能考虑通常是数据库查询数量.

如果您知道要为查询集中的每个项目执行额外的查询(或三个),那么值得寻找在一个或多个大查询中获取所有内容的方法.

在某些情况下,您可以使用以下annotate()方法:https:
//docs.djangoproject.com/en/dev/ref/models/querysets/#annotate

(我认为这不可能在你的例子中)

在上面的特定代码中,您只需查询90天(最早的间隔),您可以在Python中过滤60天和30天的集,而无需再次查询数据库.

但是,对于查询集中的每个项目,仍然会进行一次额外查询people.Login对所有人(或任何子集)的对象做一个大的查询会更好.由于有一个外键关系PersonLogin,我们可以用select_related()得到Person的实例在一个大的查询时,我们查询Login模式:

def method3(request):
    logins = Login.objects.filter(
        person__in=Person.objects.all(),
        login_date__gt=date.today() - timedelta(days=90)
    ).order_by('person', 'login_date').select_related()
    return render_to_response('template.htm', {'logins': logins})
Run Code Online (Sandbox Code Playgroud)

请注意,如果你真的这样做,Person.objects.all()你就不需要person__in上面的过滤器,只要你想以某种方式过滤Person集.

现在我们在一个大查询中获得了所有数据,我们可以在python端执行我们需要的操作来显示数据.例如,在模板中我们可以使用regroup标签:

{% regroup logins by person as people %}
{% for person in people %}
    {% with person.grouper as p %}
        {{ p.first_name }}
        {% for login in person.list %}
            {{ login.login_date }}
        {% endfor %}
    {% endwith %}
{% endfor %}
Run Code Online (Sandbox Code Playgroud)

你可以更进一步,为登录日期范围写一个自定义标签...我不会在这里详细说明,但在你的模板中它可能看起来像:

{% regroup logins by person as people %}
{% for person in people %}
    {% with person.grouper as p %}
        {{ p.first_name }}
        {% logins_since person.list 60 as last_60_days %}
        {% logins_since person.list 30 as last_30_days %}
        {% for login in last_30_days %}
            {{ login.login_date }}
        {% endfor %}
        {% for login in last_60_days %}
            {{ login.login_date }}
        {% endfor %}
    {% endwith %}
{% endfor %}
Run Code Online (Sandbox Code Playgroud)

  • 直接使用模型方法的另一个好处是可测试性:您可以轻松地测试单个模型方法和属性; 测试整个视图'blob'可能会更困难 (2认同)